资讯详情 TerraExplorer二次开发C#示例:三维GIS场景联动与COM互操作实战
📅 2026/10/17 2:54:21
简介这份资源是面向GIS与三维可视化开发者的TerraExplorer二次开发C#示例代码包基于Skyline平台适合已掌握C#基础语法、希望快速上手TerraExplorer SDK的初中级开发者。它解决了从零搭建三维地球应用时缺少可运行参考的问题覆盖事件驱动编程、地图对象与控件操作、Shapefile与KML等数据集成、3D模型加载、空间查询与缓冲区分析、自定义界面及多线程优化等典型场景。压缩包共21个文件约42KB以cs源码为主辅以resx资源文件、csproj与sln工程文件并附带shp、dbf、shx、prj等Shapefile样例数据及fly飞行文件可直接在Visual Studio中打开编译运行。目前已有483人学习下载。通过研读这些示例读者能掌握TerraExplorer API的调用方式、地图与图层对象的组织逻辑、空间分析实现思路以及调试优化技巧为定制化三维GIS项目提供可复用的代码骨架与排错参考。1. 从一次三维场景联动需求说起这套 C# 示例代码到底能干什么有个做 GIS 可视化的朋友接过一个活客户要在三维地球上叠加实时轨迹、点击模型弹信息框、还要把相机飞到指定坐标。他一开始打算纯前端硬啃 WebGL结果发现客户现场用的是一套成熟的三维地球客户端支持用 C# 做插件式扩展。他翻了两天文档没跑通第一个弹窗最后靠一份二次开发示例代码把链路打通了。这份 TerraExplorer 二次开发 C# 示例代码解决的就是这类问题——它把三维场景的加载、图层控制、对象创建、相机操作、事件响应这些高频动作用 C# 封装成可调用的接口示例。适合两类人一是手里有三维地球客户端、需要做定制功能集的桌面端开发者二是想从零理解三维 GIS 二次开发接口设计思路的 C# 工程师。下面按“能干什么 → 怎么跑 → 怎么改”的顺序拆开讲。2. 技术栈与目录结构先看清这套示例的骨架2.1 技术选型为什么是 C# COM 互操作这套示例的核心技术路线是 C# 通过 COM 互操作调用三维地球客户端暴露的自动化接口。三维地球客户端本身是一个原生桌面程序对外提供了一套基于 COM 的自动化对象模型C# 通过添加引用或动态调用就能拿到场景对象、图层树、相机控制器这些入口。常见做法是在项目里引用对应的互操作程序集或者用Type.GetTypeFromProgID动态创建。示例代码里两种方式都有体现前者适合强类型开发后者适合不想绑定具体版本的场景。// 方式一强类型引用互操作程序集 using TerraExplorerX; // 互操作命名空间 // 方式二动态创建避免版本绑定 Type t Type.GetTypeFromProgID(TerraExplorer.Application); dynamic sg Activator.CreateInstance(t);逻辑说明方式一在编译期就能获得智能提示适合团队协作方式二在运行时才解析适合需要兼容多个客户端版本的场景。参数上ProgID 字符串必须和客户端注册的一致写错会直接抛COMException。2.2 目录结构示例工程里都有什么一份典型的二次开发示例代码目录大致长这样TerraExplorerDemo/ ├── Properties/ │ └── AssemblyInfo.cs ├── References/ │ └── Interop.TerraExplorerX.dll ├── Forms/ │ ├── MainForm.cs // 主窗口承载三维控件 │ └── MainForm.Designer.cs ├── Services/ │ ├── SceneService.cs // 场景加载与保存 │ ├── LayerService.cs // 图层增删改查 │ ├── CameraService.cs // 相机飞行与定位 │ └── ObjectService.cs // 三维对象创建与管理 ├── Helpers/ │ ├── ComHelper.cs // COM 对象释放与异常封装 │ └── CoordHelper.cs // 坐标转换工具 ├── Program.cs └── App.config这个结构把“场景、图层、相机、对象”四类操作拆成独立服务类主窗口只负责 UI 和事件绑定。好处是二次开发时想改哪块就改哪块不用在一个几千行的 Form 里翻来翻去。2.3 核心对象模型从 Application 到具体对象三维地球客户端的自动化对象模型是树状的。顶层是 Application往下是 Project、Scene、Layer、Object。示例代码里最常用的几个入口对象作用典型方法Application客户端进程入口OpenProject、SaveProjectScene三维场景FlyTo、GetCameraLayer图层AddLayer、RemoveLayerObject三维对象CreatePoint、CreatePolyline理解这棵树之后写代码就是“拿到入口、逐层往下、调方法”。示例里每个服务类都对应树上的一个节点职责清晰。3. 跑起来从环境准备到第一个三维弹窗3.1 环境准备客户端、SDK 与开发工具要跑通这套示例本机需要装三维地球客户端并且客户端版本要和示例引用的互操作程序集匹配。开发工具用 Visual Studio目标框架建议 .NET Framework 4.7.2 以上因为 COM 互操作在 .NET Core 上虽然能用但配置起来更折腾。常见做法是先在客户端里手动打开一个工程确认能正常显示三维场景再跑示例代码。如果客户端本身都起不来代码再对也没用。# 检查客户端是否注册了 COM 组件 # 在 PowerShell 里执行 New-Object -ComObject TerraExplorer.Application如果这条命令不报错说明 COM 注册正常。报Retrieving the COM class factory错误通常是客户端没装好或者位数不匹配32 位 vs 64 位。3.2 第一个可运行示例加载工程并飞到指定坐标下面这段代码是示例里最基础的入口作用是启动客户端、打开一个工程、把相机飞到指定经纬度和高度。using System; using TerraExplorerX; namespace TerraExplorerDemo { class Program { static void Main(string[] args) { // 创建客户端实例 var sg new SGWorld(); // 打开已有工程路径按实际修改 sg.Project.Open(C:\Projects\demo.fly); // 飞到指定坐标经度116.39纬度39.9高度5000米 sg.Navigate.FlyTo( sg.Creator.CreatePosition( 116.39, 39.9, 5000, AltitudeTypeCode.ATC_TERRAIN_RELATIVE, 0, 0, 0, 0 ), ActionCode.AC_FLY ); Console.WriteLine(已飞到目标位置); Console.ReadLine(); } } }逻辑说明SGWorld是顶层入口Project.Open加载工程文件Creator.CreatePosition构造一个位置对象Navigate.FlyTo执行飞行。参数里AltitudeTypeCode.ATC_TERRAIN_RELATIVE表示高度相对于地形ActionCode.AC_FLY表示平滑飞行而不是瞬移。参数怎么改经纬度换成目标点高度单位是米飞行速度可以通过Navigate.FlyTo的重载或者设置Navigate.Speed调整。如果飞行没反应先检查工程是否加载成功再看坐标是否在工程范围内。3.3 创建三维对象点、线、面的最小闭环跑通飞行之后下一步是在场景里创建对象。示例里的ObjectService封装了创建点、线、面的方法。// 创建一个点对象 var point sg.Creator.CreatePoint( 116.39, 39.9, 100, // 经纬度和高度 sg.Creator.CreateColor(255, 0, 0, 128), // 红色半透明 5, // 点大小 myPoint, // 对象ID 示例点, // 显示名称 // 描述 ); // 创建一条折线 var polyline sg.Creator.CreatePolyline( sg.Creator.CreateGeometryFromArray( new double[] { 116.39, 39.9, 100, 116.40, 39.91, 100 } ), sg.Creator.CreateColor(0, 255, 0, 200), 3, // 线宽 myLine, 示例线, );逻辑说明CreatePoint返回一个对象引用后续可以通过这个引用修改属性或删除。CreateGeometryFromArray接收一个扁平数组每三个数一组表示一个点的经纬度和高度。颜色用 RGBA 四通道最后一个参数是透明度。参数怎么改点大小、线宽、颜色都是直接改数值。对象 ID 要唯一重复会覆盖或者报错。如果对象创建了但看不见检查高度是否在地形以下或者图层是否被隐藏。3.4 事件响应点击对象弹信息框三维场景里点击对象弹框是最常见的交互。示例里通过绑定OnLButtonClick事件实现。// 绑定鼠标左键点击事件 sg.OnLButtonClick (objectId, x, y) { if (!string.IsNullOrEmpty(objectId)) { // 根据对象ID获取对象信息 var obj sg.ProjectTree.GetObject(objectId); string name obj.Name; // 弹出信息框 sg.Window.ShowMessage( $点击了对象{name}\nID{objectId}, 对象信息, MessageBoxButton.OK, MessageBoxIcon.Information ); } };逻辑说明事件回调里拿到objectId通过ProjectTree.GetObject反查对象再取属性。ShowMessage是客户端封装的弹窗方法也可以用 WinForms 的MessageBox替代。参数怎么改事件类型可以换成OnRButtonClick、OnMouseMove等。弹窗内容可以拼接更多属性比如坐标、图层名。如果事件不触发检查是否在创建对象之后才绑定事件以及对象是否设置了可拾取属性。4. 避坑与排查那些第一次跑必踩的坑4.1 坑一COM 对象没释放客户端进程残留现象程序关闭后任务管理器里还有三维地球客户端的进程再次运行时报“对象被占用”。原因COM 对象没有显式释放.NET 的垃圾回收不会立即回收 COM 引用。解决在finally块里调用Marshal.ReleaseComObject或者用using模式封装。示例里的ComHelper就是干这个的。try { var sg new SGWorld(); // ... 业务代码 } finally { if (sg ! null) Marshal.ReleaseComObject(sg); }4.2 坑二32 位与 64 位不匹配现象编译通过运行时报BadImageFormatException或Class not registered。原因客户端是 32 位但项目编译成了 64 位或者反过来。解决在项目属性里把目标平台改成和客户端一致。常见做法是先用Task Manager看客户端进程有没有*32后缀有就是 32 位。4.3 坑三工程路径含中文或空格导致加载失败现象Project.Open返回失败但路径确实存在。原因部分版本的 COM 接口对非 ASCII 路径支持不好。解决把工程文件放到纯英文无空格的路径下或者先用短路径转换。// 获取短路径 [DllImport(kernel32.dll, CharSet CharSet.Auto)] static extern int GetShortPathName(string longPath, StringBuilder shortPath, int bufferSize);4.4 坑四事件绑定后不触发现象代码里绑定了点击事件但点击对象没反应。原因事件绑定时机不对或者对象没有设置可拾取。解决确保在对象创建之后绑定事件并且创建对象时设置了正确的拾取属性。有些版本需要在ProjectTree里刷新一下。4.5 坑五坐标系统不匹配导致对象偏移现象创建的点不在预期位置偏移几百米甚至几公里。原因工程用的坐标系和传入的经纬度不匹配比如工程是投影坐标系代码传的是地理坐标系。解决先用sg.CoordSys查询工程坐标系必要时做转换。示例里的CoordHelper提供了常见转换方法。5. 二次开发改造从示例到生产项目的几个关键动作5.1 把服务类抽成接口方便替换和测试示例里的SceneService、LayerService都是具体类直接依赖 COM 对象。生产项目里建议抽成接口COM 实现只做一层薄封装。public interface ISceneService { void LoadProject(string path); void FlyTo(double lon, double lat, double height); void SaveProject(); } public class ComSceneService : ISceneService { private readonly SGWorld _sg; public ComSceneService(SGWorld sg) { _sg sg; } public void LoadProject(string path) { _sg.Project.Open(path); } public void FlyTo(double lon, double lat, double height) { _sg.Navigate.FlyTo( _sg.Creator.CreatePosition(lon, lat, height, AltitudeTypeCode.ATC_TERRAIN_RELATIVE, 0, 0, 0, 0), ActionCode.AC_FLY ); } public void SaveProject() { _sg.Project.Save(); } }这样做的价值单元测试可以注入一个假的ISceneService不用启动客户端就能测业务逻辑。换客户端版本时只需要改ComSceneService一个类。5.2 用配置文件管理工程路径和常用坐标示例里路径和坐标是硬编码的生产项目里应该放到配置文件。!-- App.config -- appSettings add keyProjectPath valueC:\Projects\demo.fly/ add keyHomeLon value116.39/ add keyHomeLat value39.9/ add keyHomeHeight value5000/ /appSettings读取时用ConfigurationManager.AppSettings[ProjectPath]。好处是换现场不用重新编译改配置文件就行。5.3 异常处理把 COM 异常翻译成业务异常COM 调用失败时抛的是COMException错误码是一串数字直接抛给用户看不懂。建议在服务层捕获并翻译。public void LoadProject(string path) { try { _sg.Project.Open(path); } catch (COMException ex) { // 常见错误码翻译 switch (ex.ErrorCode) { case -2147221164: // 类未注册 throw new InvalidOperationException(三维客户端未正确安装或未注册, ex); case -2147024894: // 文件未找到 throw new FileNotFoundException($工程文件不存在{path}, ex); default: throw new InvalidOperationException($加载工程失败错误码{ex.ErrorCode}, ex); } } }这样上层调用者只需要处理InvalidOperationException和FileNotFoundException不用关心 COM 细节。5.4 验证改造是否成功的三个检查点改完之后怎么确认没改坏我一般走这三步第一跑一遍示例里的基础流程加载工程、飞行、创建对象、点击弹框确认主链路通。第二把配置文件里的路径改成一个不存在的路径确认异常提示是“工程文件不存在”而不是一串错误码。第三用单元测试注入假的服务实现确认业务逻辑不依赖客户端也能跑。[Test] public void FlyTo_ShouldCallNavigate() { var mock new MockISceneService(); var controller new SceneController(mock.Object); controller.GoHome(); mock.Verify(s s.FlyTo(116.39, 39.9, 5000), Times.Once); }这个测试不需要启动三维客户端跑起来很快适合放在 CI 里。从那以后我每次接三维二次开发的项目都强制先把示例代码跑通一遍再动手改。因为 COM 互操作这东西环境问题比代码问题多得多示例跑通了说明环境没问题后面出问题就能定位到代码上。希望帮到你。本文还有配套的精品资源点击获取