ArcGIS Engine 10 开发手册


内部文档,请勿外传 ArcGIS Engine 10 开发手册 ESRI 中国(北京)有限公司 2011 年 9 月 内部文档,请勿外传 版权声明 本文档版权为 ESRI 中国(北京)有限公司所有。未经本公司书 面许可,任何单位和个人不得以任何形式摘抄、复制本文档的部分或 全部,并以任何形式传播。 内部文档,请勿外传 制定及修订记录 版本 完成日期 编写/修订纪要 编写者 备 注 V0.1 2011.3.4 文 档 目 录 结 构 林雪淋/ 刘宇 V0.2 2011.4.8 完 善 控 件 介 绍和空间数 据库的介绍 刘宇 V0.3 2011.5.18 完 善 栅 格 数 据介绍 刘宇 V0.4 2011.6.25 完 善 符 号 化 介绍 刘宇 V0.5 2011.7.18 完 善 网 络 分 析功能 刘宇 V0.6 201.8.26 完 善 参 考 系 的介绍 刘宇 V0.7 2011.9.3 完 善 几 何 对 象的介绍 刘宇 内部文档,请勿外传 目 录 1 一. ArcGIS 介绍 .................................................................................................. 2 2 二.和 ArcGIS Engine 开发相关的 C#知识 .................................................... 10 3 三.使用控件创建第一个桌面应用程序 ........................................................ 22 4 四.空间数据库 ................................................................................................ 71 5 五.几何对象和空间参考 .............................................................................. 121 6 六.矢量数据空间分析 .................................................................................. 154 7 七.符号化 ...................................................................................................... 169 8 八.栅格数据分析 .......................................................................................... 202 9 九.编辑 .......................................................................................................... 237 10 十.地图输出 .................................................................................................. 260 11 十一.ArcGIS Engine 实战................................................................................ 263 12 十二.安装部署 ................................................................................................. 303 内部文档,请勿外传 1 一. ArcGIS 介绍 ArcGIS 软件架构 1.1 ArcGIS 是 ESRI 在全面整合了 GIS 与数据库、软件工程、人工智能、网络技术及其它多方面的计算机 主流技术之后,成功地推出了代表 GIS 最高技术水平的全系列 GIS 产品。ArcGIS 是一个全面的,可伸缩的 GIS 平台,为用户构建一个完善的 GIS 系统提供完整的解决方案。ArcGIS 的基本体系能够让用户在任何需 要的地方部署 GIS 功能和业务逻辑,无论是在桌面、服务器、还是在野外: 桌面 GIS(ArcGIS Desktop)—ArcGIS 桌面 GIS 软件产品是用来编辑、设计、共享、管理和发布地理信息 和概念。ArcGIS 桌面可伸缩的产品结构,从 ArcReader,向上扩展到 ArcView、ArcEditor 和 ArcInfo。目前 ArcInfo 被公认为是功能最强大的 GIS 产品。通过一系列的可选的软件扩展模块,ArcGIS Desktop 产品的能 力还可以进一步得到扩展。 嵌入式 GIS(Embedded GIS)—ArcGIS Engine 是一个完整的嵌入式 GIS 组件库和工具包,开发者能用它创 建一个新的、或扩展原有的可定制的桌面应用程序。使用 ArcGIS Engine,开发者能将 GIS 功能嵌入到已有 的应用程序中,如基于工业标准的产品以及一些商业应用,也可以创建自定义的应用程序,为组织机构中 内部文档,请勿外传 的众多用户提供 GIS 功能。 服务器 GIS(Server GIS)—ArcGIS Server、ArcIMS 和 ArcSDE 用于创建和管理基于服务的 GIS 应用程序, 在大型机构和互联网上众多用户之间共享地理信息。ArcGIS Server 是一个中心应用服务器,它包含一个可 共享的 GIS 软件对象库,能在企业和 Web 计算框架中建立服务器端的 GIS 应用。ArcIMS 是通过开放的 Internet 协议发布地图、数据和元数据的可伸缩的网络地图服务器。ArcSDE 是在各种关系型数据库管理系 统中管理地理信息的高级空间数据服务器。 移动 GIS(Mobile GIS)—ArcPad,支持 GPS 的无线移动设备,越来越多地应用在野外数据采集和信息访 问中。ArcGIS桌面和 ArcGIS Engine 可以运行在便携式电脑或平板电脑上,用户可以在野外进行数据采集、 分析和乃至制定决策。 ArcGIS Engine 介绍 1.2 ArcGIS Engine 是一组完备的并且打包的嵌入式 GIS 组件库和工具库,开发人员可用来创建新的或扩 展已有的桌面应用程序。使用 ArcGIS Engine,开发人员可以将 GIS 功能嵌入到已有的应用软件中,如自 定义行业专用产品;或嵌入到商业生产应用软件中,如 Mirosoftf Word 和 Excel;还可以创建集中式自定义 应用软件,并将其发送给机构内的多个用户。 ArcGIS Engine 由两个产品组成:构建软件所用的开发工具包以及使已完成的应用程序能够运行的可 再发布的 Runtime(运行时环境)。ArcGIS Engine 开发工具包是一个基于组件的软件开发产品,可用于构 建自定义 GIS 和制图应用软件。它并不是一个终端用户产品,而是软件开发人员的工具包,适于为 Windows、 UNIX 或 Linux 用户构建基础制图和综合动态 GIS 应用软件。ArcGIS Engine Runtime 是一个使终端用户 软件能够运行的核心 ArcObjects 组件产品,并且将被安装在每一台运行 ArcGISEngine 应用程序的计算机 上。  ArcGIS Engine 是基于 COM 技术的可嵌入的组件库和工具包,ArcGIS Engine 可以帮助我们很轻松的构 建自定义应用程序. 内部文档,请勿外传  使用 ArcGIS Engine,开发人员可以将 GIS 功能嵌入到已有的应用软件中,如自定义行业专用产品; 或嵌入到商业生产应用软件中,如 Mirosoftf Word 和 Excel;还可以创建集中式自定义应用软件,并 将其发送给机构内的多个用户。 ArcGIS Engine 由两个产品组成:  面向开发人员的软件开发包(ArcGIS Engine Developer kit)  面向最终用户的运行时(ArcGIS Engine Runtime) ArcGIS Engine 开发工具包是一个基于组件的软件开发产品,可用于构建自定义 GIS 和制图应用软件。 它并不是一个终端用户产品,而是软件开发人员的工具包,支持四种开发环境(C++, COM, .NET, 以及 Java),适于为 Windows、UNIX 或 Linux 用户构建基础制图和综合动态 GIS 应用软件。 ArcGIS Engine Runtime 是一个使终端用户软件能够运行的核心 ArcObjects 组件产品,并且将被安装 在每一台运行 ArcGIS Engine 应用程序的计算机上。 ArcGIS Engine 的逻辑体系结构 : ◆ Base Services 包含了 ArcGIS Engine 中最核心的 ArcObjects 组件,几乎所有的 GIS 组件需要调用它们,如 Geometry 和 Display 等。 ◆ Data Access 包含了访问矢量或栅格数据的 GeoDatabase 所有的接口和类组件。 ◆ Map Presentation 包含了 GIS 应用程序用于数据显示、数据符号化、要素标注和专题图制作等需要的接口和类组件。 ◆ Develper Components 包含了进行快速开发所需要的全部可视化控件,如 MapControl、PageLayoutControl、SceneControl、 GlobeControl、TOCControl、ToolbarControl、SymbologyControl 和 LicensenControl 控件等,除了这些,该 库还包括大量可以有 ToolbarControl 调用的内置 commands、tools、Menus,它们可以极大地简化二次开发 工作。 ◆ Extensions 在图中我们可看出,ArcGIS Engine 的开发体系是一条纵线,功能丰富,层次清晰。最上层的 Extensions 内部文档,请勿外传 包含了许多高级开发功能,如 GeoDatabase Update、空间分析、三维分析、网络分析、Schematics 逻辑示意 图以及数据互操作等。ArcGIS Engine 标准版并不包含这些 ArcObjects 许可,他们只能作为扩展存在,需要 特定的 License 才能运行。 注意: ArcGIS Engine 运行时有多种版本级别,从标准版本一直到企业版本。标准的 ArcGIS Engine 运行时提 供所有 ArcGIS 应用程序的核心功能。这个级别的 ArcGIS Engine 运行时可以操作几种不同的栅格和矢量格 式、进行地图表达和创建以及通过执行各种空间或属性查询查找要素。这个级别的 ArcGIS Engine 运行时 还可以进行基本数据创建、编辑 Shapefile 和简单的个人地理数据库(Personal Geodatabase)及 GIS 分析但 是如果遇到企业级数据库(ArcGIS SDE)数据库的编辑以及复杂数据模型的创建(网络,拓扑)就需要 Enterprise GeodatabaseUpdate 许可.ArcGIS Engine 运行时的标准许可相当于 ArcGIS 桌面 View 级别的功能,而 Enterprise GeodatabaseUpdate 许可相当于 ArcGIS 桌面 Editor 级别的功能. 内部文档,请勿外传 ArcGIS Engine 中的类库 1.3 ArcGIS Engine 开发中,为了更好的管理这些 COM 对象,ESRI 将这些 COM 对象放在不同的组件库中, 而他们被物理的防盗 bin 目录下的 dll 中,而逻辑上被分散到不同的命名空间中,下面我们详细对一些类库进 行介绍: Version Version 库是 ArcGIS 10 新出来的一个类库,该类库包含了将独立应用程序绑定到特定的 ArcGIS 系列 产品的函数和方法,该类库是在运行 Engine 的应用程序的时候 System System 库是 ArcGIS 架构中最底层的库。该库包含了暴露组成 ArcGIS 的其它库所使用的服务的组件。 System 库中定义了许多接口,它们可以由开发者来实现。AoInitializer 对象在 System 中定义;所有开发 者必须使用该对象在使用 Engine 功能的应用程序中初始化和 uninitialize ArcGIS Engine。开发者不扩展该 库,但可以通过实现其中的接口来扩展 ArcGIS 系统。 SystemUI SystemUI 库中包含了可在 ArcGIS Engine 中扩展的用户界面组件的接口定义,包括 ICommand、ITool 和 IToolControl 接口。开发者使用这些接口来扩展 UI 组件。该库所包含的对象是 utility 对象,开发者可 用于简化某些用户界面的开发。开发者不扩展该库,但可以通过实现其中的接口来扩展 ArcGIS 系统。 Geometry Geometry 库处理存储在特征类(feature classes)或其它图形要素(graphical elements)中的特征的 geometry 或 shape。大多数用户交互的基本几何对象有 Point 、MultiPoint 、Polyline 和 Polygon 。除了这些顶层的 实体,还有作为 Polylines 和 Polygons 构建模块的几何体(geometries)。这些是组成几何体的基元(primitives)。 它们是 Segments、Paths 和 Rings。Polylines 和 Polygons 由形成一条 Path 的依次相连的 Segments 组成。 一个 Segment 包含两个不同的点,起点和终点,和一个定义从起点到终点的曲线的要素类型。这种 segments 有 CircularArc、Line、EllipticArc 和 BezierCurve。所有的几何对象都可以有与它们顶点相关的 Z、M 和 IDs。 内部文档,请勿外传 基本的几何对象都支持几何操作,如 Buffer 和 Clip。开发者不可以扩展几何基元。GIS 中的实体是指现实 世界中的特征;这些现实世界中的特征的位置由具有空间参考的几何体来定义。投影和地理坐标系统的空 间参考对象都包含在 Geometry 库中。开发者可以通过在空间参考间添加新的空间参考和投影来扩展空间 参考系统。 Display Display 库包含了用于 GIS 数据显示的对象。除了负责实际图像输出的主要显示对象,该库中还包含 了表示颜色和符号的对象,这些颜色和符号用于控制显示上所绘制实体的属性。库中也包含了为用户在与 显示交互时提供可视化反馈的对象。开发者大都通过类似于 Map 或 PageLayout 对象提供的视图与显示交 互。该库的所有部分都可以被扩展,常被扩展的有符号、颜色和显示反馈(display feedbacks)。 Output Output 库被用于创建图形输出到设备,如打印机、绘图仪和硬拷贝格式,如增强型图元文件(enhanced metafiles)和栅格影像格式(JPG、BMP 等)。开发者使用该库和 ArcGIS 系统其它部分中的对象来创建图形 输出。通常这些是 Display 和 Carto 库中的对象。开发者可以扩展 output 库用于定制的设备和输出格式。 GeoDatabase GeoDatabase 库提供了用于 geodatabase 的编程 API。Geodatabase 是一个构建在标准工业关系和对象 数据库技术基础上的地理数据储存库。库中的对象为 ArcGIS 支持的所有数据源提供了统一的编程模型。 GeoDatabase 库定义了许多由架构中较高层次数据源提供者实现的接口。开发者可以扩展 geodatabase 来支 持特殊的数据对象(Features、Classes 等)类型。此外,还可以使用 PlugInDataSource 对象添加自定义的矢量 数据源。geodatabase 支持的 native 数据类型不可以被扩展。 DataSourcesFile DataSourcesFile 库包含用于基于文件数据源的 GeoDatabase API 的实现。这些基于文件的数据源包括 shapefile、coverage、TIN、CAD、SDC、ArcGIS StreetMap™和 VPF。开发者不能扩展 DataSourcesFile 库。 DataSourcesGDB DataSourcesGDB 库包含了用于数据库数据源的 GeoDatabase API 的实现。这些数据源包括 Microsoft Access 和 SDE®软件支持的 RDBMSs。开发者不能扩展 DataSourcesGDB 库。 DataSourceOleDB 内部文档,请勿外传 DataSourcesOleDB 库包含用于 Microsoft OLE DB 数据源的 GeoDatabase API 的实现。该库只有在 Microsoft Windows 操作系统上才可以使用。这些数据源包括任何 OLE DB 支持的数据提供者和文本文件 工作空间。开发者不能扩展 DataSourcesOleDB 库。 DataSourceRaster DataSourcesRaster 库包含了用于 Raster 数据源的 GeoDatabase API 的实现。这些数据源包括 SDE 软 件支持的 RDBMSs,和所有支持的 RDO 栅格文件格式。当需要支持新的栅格 格式时,开发者不扩展该库,而是扩展 RDO。开发者不扩展 DataSourcesRaste 库。 GeoDatabaseDistributed GeoDatabaseDistributed 库通过提供数据导入、导出 geodatabase 的工具来支持到企业 geodatabase 的分 布式访问。开发者不扩展 GeoDatabaseDistributed 库。 Carto Carto 库支持地图的创建和显示;这些地图可以包含一幅地图或具有多幅地图和相关旁注的页中的数 据。PageLayout 对象是宿主一幅或多幅地图和相关旁注(指北针、图例、比例尺条等)的容器。Map 对象是 图层的容器。Map 对象有操作地图中所有图层的属性:空间参考、地图比例尺等,还有操作地图图层的方 法。有多种不同类型的图层可以被加载到地图中。不同数据源通常有一个相关图层负责在地图上显示数据; 矢量特征由 FeatureLayer 对象处理,栅格数据由 RasterLayer 对象处理,TIN 数据由 TinLayer 对象处理等。 若需要,图层可以为它们的相关数据处理所有的绘制操作。但更常见的是图层拥有一个相关的 Renderer 对 象。Renderer 对象的属性控制数据在地图中怎样显示。Renderers 一般使用 Display 库中的 symbols 进行 实际绘图;renderer 只是匹配指定的符号与要绘制的实体的属性。一个 Map 和一个 PageLayout 可以包含 要素(elements)。要素利用几何体来定义它在地图或页面上的位置,以及控制要素显示的行为。有用于基本 形状、文本标注、复杂旁注等的要素。Carto 库也包含对地图注记和动态标注的支持。 Location Location 库包含支持地理编码和与 route 事件一起工作的对象。可通过 full 控件的 finegrained 对象访 问地理编码功能,或 GeocodeServer 对象提供了一个简化的 API。开发者可以创建他们自己的地理编码对 象。线性参考功能提供了添加事件到线性特征的对象,并使用多种绘制方法渲染这些事件。开发者可以扩 展线性参考功能。 内部文档,请勿外传 NetworkAnalysis NetworkAnalysis 库提供的对象在网络加载到 geodatabase 中时,使用网络数据和对象 populating a geodatabase 来分析网络。开发者可以扩展该库来支持定制的网络跟踪。该库是用于 utility 网络的——gas lines、electricity supply lines 等。 Controls 开发者使用 Controls 库来构建或扩展具有 ArcGIS 功能的应用程序。ArcGIS Controls 通过封装 ArcObjects 和提供一个 coarser-grained API,简化了开发过程。尽管控件封装了 fine grained ArcObjects,但 它们并不限制访问这些 ArcObjects。MapControl 和 PageLayoutControl 分别封装了 Carto 库中的 Map 和 PageLayout 对象。ReaderControl 封装了 Map 和 PageLayout 对象,并在使用该控件时提供了简化的 API。 如果地图出版商拥有授权许可,那么开发者可 以访问 Map 和 PageLayout 控件的类似方式访问内部对象。该库也包含实现内容列表的 TOCControl 和 宿主与一个合适控件一起工作的命令和工具的 ToolbarControl GeoAnalyst GeoAnalyst 库包含支持核心空间分析功能的对象。这些功能在 ArcGIS Spatial Analyst 和 ArcGIS 3D Analyst™库中使用。开发者可以通过创建一个新的栅格操作类型来扩展该库。要求有 ArcGIS Spatial Analyst 或 3D Analyst 许可才能使用该库中的对象。 3DAnalyst 3DAnalyst 库包含在三维场景中使用的对象,它们的工作方式类似于 Carto 库中包含的对象在二维地 图中工作。Scene 对象是库中的主要对象之一,因为它与 Map 对象类似,是数据的容器。Camera 和 Target 对象根据特征相对于观察者的位置,指定场景的视图。一个场景(scene)包含一个或多个图层;这些图层指 定了场景中的数据和数据怎样绘制。 3DAnalyst 库拥有一个开发控件和一组和该控件协同工作的命令和工具。该控件可以与 Controls 库中 的对象联合使用。除了创建命令和工具,开发者对该库的扩展较少。使用该库中的对象需要 3D Analyst 许 可。 GlobeCore GlobeCore 库包含与 globe 数据一起工作的对象,工作方式类似于 Carto 库中包含的对象在二维地图 内部文档,请勿外传 中工作。Globe 对象是库中的主要对象之一,因为它与 Map 对象类似,是数据的容器。GlobeCamera 对象 根据 globe 相对于观察者的位置,指定了 Globe 视图。Globe 可以有一个或多个图层;这些图层指定了 Globe 上的数据和数据怎样绘制。 介绍完这些,让我们有个感性上的认识,在以后找某个对象的时候,知道去哪个 dll 中找。 2 二.和 ArcGIS Engine 开发相关的 C#知识 接口编程 2.1 使用 ArcGIS Engine,也就意味着使用里面的接口,那么什么是接口呢?在面向对象的编程中,我们都 会接触到这个玩意儿,我们知道面向对象有三大特性 封装,继承,多态,相信很多人在学习多态的时候, 会接触接口。接口可以用这样一句话来描述“接口就是包含一系列不被实现的方法.而把这些方法的实现交 给继承它的类.”,这句话看起来很晦涩,没关系,我们通过下面一个例子说明 我定义了一个接口 IPeople using System; using System.Collections.Generic; using System.Text; namespace InterfaceTest { interface IPeople { void gender(); } } 这个接口里面有一个性别的方法,这个只对方法进行了定义,而方法内却没有内容,也就是说,通过 这个接口的这个 gender 方法,我们不能知道它到底干什么,但是要知道这个 gender 到底是干什么用的,那 么就要看实现了这个方法的类。同理我定义两个类,分别实现这个接口。 内部文档,请勿外传 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace InterfaceTest { class Boy:IPeople { public void gender() { Console.WriteLine("I‟m a boy."); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace InterfaceTest { class Girl:IPeople { public void gender() { Console.WriteLine("I‟m a girl."); } } } 通过上面的两个类,我们就很清楚的看到这个方法的作用了,我们也可以看到一个接口可以被多个类 内部文档,请勿外传 实现。 我们运行一下这个,看一下效果 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace InterfaceTest { class Program { static void Main(string[] args) { IPeople Person; // 声明接口变量 Person = new Boy(); // 实例化,接口变量中存放对象的引用 Person.gender(); // 这个调用的是 Boy 中的 gender 方法 Person = new Girl(); // 实例化,接口变量中存放对象的引用 Person.gender(); // 这个调用的是 Gril 中的 gender 方法 Console.ReadLine(); } } } 运行结果如下: 内部文档,请勿外传 2.1.1 C#接口的例子 2.1.2 快速掌握 OMD 我们已经知道使用 ArcGIS Engine 开发,也就意味着我们要和接口打交道,ArcGIS Engine 中提供的接 口和类加起来估计上万,但是用过 ArcGIS Engine 的人,知道这个数字不为过。ArcGIS Engine 为了帮助我 们方便的使用这些,将这些分散在不同的类库中,为了便于我们阅读,ArcGIS Engine 提供了一系列的对象 模型图,也就是 OMD (Object model diagrams) 对象模型图表。下面图示中,便是贯穿本节的对象模型 图的钥匙。 这些符号是基于 UML 画图工具创建的,UML 符号是面向对象分析和设计的工业图样标准。对象模型 图中提供的信息非常多,是对象浏览器中信息的重要补充。Visual Basic,或者其它的开发环境,都会列出 所有的类和成员,但不会指明这些类之间的关系。所以,对象模型图是非常有利于读者对 ArcInfo 组件的 理解的!在装了 ArcGIS Engine 后,可以在安装目录下找到很多使用 UML 来描述 ArcInfo 组件的 pdf,这些 pdf 详细的描述了 ArcObjects,并描述你能够创建的数据模型,以下详细说明。 1. 类和对象 内部文档,请勿外传 在 UML 图中有三种类型的类:抽象类(abstract class)、组件类(CO class)与普通类(instantiable class)。 抽象类:不能创建或实例化,从来没有一个抽象类的实例用于定义子类的公共接口,创建实例的任务由其 子类完成。子类继承其定义的接口。 OMD 符号为:二维的内部有阴影的矩形。 普通类:不能创建,从别的对象获得实例。 OMD 符号为:3D 矩形内部没有阴影。 组件类:可以直接创建实例的类,在 C#中,用 New 关键字。 OMD 符号为:带阴影的 3D 矩形符号。 2. 关联 在抽象类、可创建类和可实例化类之间,有几种存在的关联(或称关系)。 联系(association)便描述了类之间的关联。在两端的类中可以定义多重性( Multiplicity)关联。 在这张图上,一个业主能有拥有一块或多块宗地;同样地,一块宗地可以被一个或多个业主所共有。 多重性关联就是限制对象类与其它对象关联的数目关系。以下是用于多重性关联的符号: 1 —— 一个并且只有一个,这种多样性是可选的;如果不标明,则默认为“1” 0..1 —— 零个或一个 M..N —— 从M 到N(正整数) *或者0„ * —— 从零到任意正整数 1„ * —— 从一到任意正整数 在这张图上,我们知道一个对象可能和多个对象有联系 类继承(type inheritance)定义了专门的类,它们拥有超类的属性和方法,并且同时也有自身的属性和 方法。 内部文档,请勿外传 上图说明primary line 和secondary line 是line 的一种类型。 实例化(Instantiation)指定一个类的对象有这样的方法,它能够创建另外一个类的对象。 pole 对象有一个方法能够创建 transformer 对象。 聚合(Aggregation)是一种不对称的关联方式,在这种方式下一个类的对象被认为是一个“整体”,而 另一个类的对象被认为是“部件”。 一个 transformer bank 正好有 3 个 transformer 。在这个图中 transformer 能和一个 transformer bank 相关联, 但当 transformer bank 移除以后,transformer 依然能够存在。 组成(Composition)是一种更为强壮的聚合方式,此种方式下,“整体”对象控制着“部分”对象的生存 时间。 一个 Pole 包含一个或多个 Crossarm。在这个图中当 pole 被移除后,Crossarm 就不能再使用了。因为 Pole 控制着 Crossarm 的生存时间。 内部文档,请勿外传 在 OMD 图中,我们不仅仅能看到类之间的相互关系,还可以得到属性的一些信息,如下图: 属性和方法: 属性:哑铃状的图标,Read(左侧的实心哑铃)和 write(右侧的实心哑铃) 属性除了我们常说的类型,还有一种属性,这个属性本身就是一个对象,符号,空心的哑铃 方法:指向左侧的箭头。 ArcGIS Engline 组件库的每一个组件中定义有不同的类,类下面定义了不同接口,接口中包含不同的 内部文档,请勿外传 属性和方法。类之间有类型继承(Typelnheritance)关系,接口之间有互相调用(Querylnterface)及相互继承 (Interfacelnheritance)关系。 1.1 类与对象在面向对象编程中,类和对象是两个非常重要的概念,可以这么说类就是创建对象的蓝 本,而对象是指具有属性和动作的实体,它封装了一个客观实体的属性与行为。ArcObjcets 中有三类 class, 分别是抽象类(AbstractClass)、组件类(CoClass)和普通类(Class)。抽象类的主要目的是为它的子类定 义公共接口,一个抽象类将把它的部分或全部实现延迟到子类中,因此,一个抽象类不能被实例化。一个 组件类对象可以被直接创建,普通类对象虽然不能直接创建,但它可以可以作为其它类的一个属性或者从 其它类的实例化来创建。 1.2 接口和类接口定义了一组方法和属性,在 ArcObjects 中接口名称都以”I”开始,如 IMap, Ilayer 等。类实现了接口中的方法。一个类可以有多个接口,如 FeatureLayerClass 类有 IFeatureLayer, IFeatureSelection 等不同接口,而一个接口也可被多个类所拥有,如 CadFeatureLayer 类和 FeatureLayer 类都 有 IFeatureLayer 接口。接口定义了能做什么,而定义了该怎么做(The interfacedefines what an object can do, and the class defines how it is done.79 页 Explroing ArcObjects V9.0),在 AO 开发的时候,和对象间的通信是 通过接口完成的,而不是我们在一些其他面向对象语言如(Java)中和对象的通信是通过对象完成的. 1.3 接口查询(QueryInterface) 一个类可以有多个接口,声明了接口变量并且指向一个对象的时候,这 个变量只能使用该接口内的方法和属性,而不能访问其他接口中的方法和属性,如: Dim pMap as IMap Set pMap = New Map pMap.Clear „这里会产生错误此时的 pMap 只能使用 IMap 接口中定义的方法和属性,比如 获取图层的个数,添加图层等,但是不能清空视图上的内容(因为这个方法是在 IActiveView 中定义的) QueryInterface(QI)很方便的让我们在一个类的不同接口间进行切换: Dim pView as IActiveView set pView= pMap „QI 现在 mView 就可以使用 IActiveView 中定义的方法了. 1.4 类类型继承类型继承是指类之间的接口类型的继承,而不是继承其实现。继承过来的接口只是名 称相同,具体的实现则不同。比如 ShpfileWorkspaceFactry 和 AccessWorkspaceFactry 都继承 WorkspaceFactry, 而他们的打开(OpenFromFile)方法却不一样,ShpfileWorkspaceFactry 的(OpenFromFile)方法需要一个 文件目录位置作为参数,而 AccessWorkspaceFactry 的(OpenFromFile)方法需要一个数据库(mdb)位置 作为参数。1.5 接口继承如 ImapFrame 接口和 IMapSurroundFrame 接口继承于 IFrameElement 接口,则父类 接口 IFrameElement 所具有的方法和属性对派生接口 ImapFrame 和 IMapSurroundFrame 都有效。 内部文档,请勿外传 快速理解 QI 2.2 2.2.1 C#+QI 的例子 COM 中,和我们打交道的是接口,也就是说类对我们是隐形的,那么我们要做开发,要使用这些功能,我 们只能通过接口,通过接口暴露出来的方法,COM 是一种服务器端/客户端架构,服务器端定义了操作的方 法,客户端通过接口调用这些方法。下面的这幅图很能说明 COM 的结构: 言归正传,说这篇博文的主题 QI,还记得上一篇我强调的,一个接口可以被多个类实现,而 QI 要解决的 就是一个类实现多个接口的问题。在 COM 中,接口定义了方法,类实现了接口中定义的方法,而一个接口 只能使用自己内部定义的方法,而不能越界,就好比一个班级一样,这个班级内有班长,有学习委员,有 体育委员,每位干部各司其职,每一位干部负责自己职权范围之内的事情,各位干部相互协作,解决班级 内的事情,这班级内的每一位干部就相当于一个接口,而这个班级就相当于实现了这些接口的类。当这个 班级的一些事情需要班长处理的时候,我们就执行班长这个接口中定义的方法,当需要学习委员处理的话, 我们再将执行权交给学习委员这个接口,这也就是接口之间的互相访问(Query Interface),通过这个我 们大体上对 QI 有了感性上的认识,下面我们通过实例演示一下。 我定义了两个接口 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace QITest { interface IFavoriteFood { 内部文档,请勿外传 void Food(); } } using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace QITest { interface IVoice { void Voice(); } } 然后定义了一个Cat的类实现这里面的方法 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace QITest { class Cat:IFavoriteFood ,IVoice 内部文档,请勿外传 { public void Food() { Console.WriteLine("我喜欢的食物是老鼠."); } public void Voice() { Console.WriteLine("喵,喵,喵..."); } } } 这个Cat类的功能就是实现两个接口的方法,猫最喜欢的事物是老鼠, 而他的声音是“喵,喵,喵”。 运行下 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace QITest { class Program 内部文档,请勿外传 { static void Main(string[] args) { IVoice pVoice = new Cat(); pVoice.Voice();//只能调用IVoice中定义的方法 // pVoice.Food();这个就会报错,因为 IVoice 接口中没有这个 方法的定义 IFavoriteFood pFavoriteFood = pVoice as IFavoriteFood; pFavoriteFood.Food();//只能调用IFavoriteFood定义的方法 Console.ReadLine(); } } } 效果如下: 内部文档,请勿外传 3 三.使用控件创建第一个桌面应用程序 1) 新建一个 Windows 窗体应用程序(文件--新建--项目--Windos 窗体应用程序) 2)在点了确定之后,在 VS 的工具箱中找到到和 ArcGIS Engine 相关的控件 其中,AxMapControl 就是 Map 地图控件,AxPageLayouControl 是布局地图控件, AxTOCControl 是目录控件,AxToolbarControl 是 GIS 工具栏控件,AxSceneControl 是 Scene 三维场景 控件,axGlobeControl 是 Globe 控件,AxLicenseControl 是许可控件,AxSymbologyControl 是符号选择器 内部文档,请勿外传 控件,AxArcReaderControl 是 ArcReader 控件,AxArcReaderGlobeControl 是 ArcReaderGlobe 控件。 AxLicenseControl 是许可控件,一般 GIS系统中都必须添加,否则无法使用。将刚才的窗体的名称改为 Engine. 3)然后添加 Toc, Map, Toolbar 控件。许可控件是首选的,否则无法使用 添加完这几个控件后,VS 会自动为我们添加一些引用 4)添加地图文档(Map 控件-右键-属性) 内部文档,请勿外传 5)Toolbar 控件和 Toc 控件与 Map 控件的关联(设置 Buddy 属性) 内部文档,请勿外传 6)在 Toolbar 中进行设置(Toolbar 控件-右键-属性-Itmes 选项) 7)添加工具命令 在 Toolbar control 上右键—属性在 items 选项卡中添加几个工具命令 内部文档,请勿外传 8)运行我们的应用程序,发现下面的错误 出现这个问题的原因是因为 ArcGIS 10 发生了变化,将下面的语句添加到使用 AO 对象的前面: ESRI.ArcGIS.RuntimeManager.Bind(ESRI.ArcGIS.ProductCode.Engine); 如我放在 Main 函数中 内部文档,请勿外传 利用刚才添加的打开文档的命令打开一个新的文档: 内部文档,请勿外传 也可利用上面的放大,缩小工具进行简单的操作了。 在上面,当我们将这些空间拖到窗体中的时候,会自动加载一些 dll,我们知道 ArcGIS Engine 编程, 也就意味着和 COM 对象接触。 代码加载 Mxd 文档 3.1 用代码添加 Mxd 文档,用到 AxMapControl.LoadMxFile(sFilePath),我们只要将 Mxd 文档的路径传给这 个方法即可。我们添加一个按钮: 内部文档,请勿外传 在这个按钮的单击事件中填写如下代码: OpenFileDialog OpenMXD = new OpenFileDialog(); OpenMXD.Title = "打开地图"; OpenMXD.InitialDirectory = "E:"; OpenMXD.Filter ="Map Documents (*.mxd)|*.mxd"; if (OpenMXD.ShowDialog() == DialogResult.OK) { string MxdPath = OpenMXD.FileName; axMapControl1.LoadMxFile(MxdPath); } 控件的 LoadMxFile 是用来加载地图文档的 我们将刚才写的代码封装成一个函数 public string OpenMxd() { string MxdPath = ""; OpenFileDialog OpenMXD = new OpenFileDialog(); OpenMXD.Title = "打开地图"; 内部文档,请勿外传 OpenMXD.InitialDirectory = "E:"; OpenMXD.Filter = "Map Documents (*.mxd)|*.mxd"; if (OpenMXD.ShowDialog() == DialogResult.OK) { MxdPath = OpenMXD.FileName; } return MxdPath; } 添加 Shape 文件 3.2 AxMapControl 还包含下面三个方法用来添加不同类型的数据 这些方法的使用和上面的相似,方法中的参数可能不同,对于 AddShapeFile 来说,它需要两个参数, 第一个参数是 shp 文件名所在的目录,第二个是文件名,因为打开 shp 文件要两个参数,所以构造一个数 组,用来返回相应的参数 代码如下: public string[] OpenShapeFile() { string[] ShpFile = new string[2]; OpenFileDialog OpenShpFile = new OpenFileDialog(); OpenShpFile.Title = "打开Shape文件"; OpenShpFile.InitialDirectory = "E:"; OpenShpFile.Filter = "Shape文件(*.shp)|*.shp"; 内部文档,请勿外传 if (OpenShpFile.ShowDialog() == DialogResult.OK) { string ShapPath = OpenShpFile.FileName; //利用"\\"将文件路径分成两部分 int Position = ShapPath.LastIndexOf("\\"); string FilePath = ShapPath.Substring(0,Position); string ShpName = ShapPath.Substring(Position+1); ShpFile[0] = FilePath; ShpFile[1] = ShpName; } return ShpFile; } 庖丁解牛识控件 3.3 ArcGIS Engine 中提供了很多的控件,这些控件和 ArcMap 中的一些东西有对应关系的: 内部文档,请勿外传 内部文档,请勿外传 ArcGIS Engine 中的 MapControl 控件和 PageLayoutControl 控件分别对应于 ArcMap 中的数据视图和布 局视图,MapControl 控件主要用于空间数据的显示和分析,它封装了地图对象,而 PageLayoutControl 控件 是用于地图的修饰和整理,可以用来生成专题图等,它封装了 PageLayout 对象。 TOCControl 控件和 ToolbarControl 控件分别对应 ArcMap 中的 Table of Contents 控件和工具条控件,这 两个控件都有一个 buddy 属性,这两个控件需要和一个伙伴空间协同工作,伙伴控件可以是 MapControl, PageLayoutContro,SceneControl 或者 globeControl 控件。 IMapDocument pMapDocument = new MapDocumentClass(); if (pMapDocument.get_IsMapDocument(path)) { pMapDocument.Open(path, null); IMap pMap; 内部文档,请勿外传 for (int i = 0; i <= pMapDocument.MapCount - 1; i++) { pMap = pMapDocument.get_Map(i); Console.WriteLine(pMap.Name); IEnumLayer pEnumLayer = pMap.get_Layers(null, true); pEnumLayer.Reset(); ILayer pLayer = pEnumLayer.Next(); while (pLayer != null) { Console.WriteLine(pLayer.Name); pLayer = pEnumLayer.Next(); } } } 3.3.1 第一个控件 MapControl MapControl 对应于 ArcMap 中的数据视图,它封装了 Map 对象,并提供了额外的属性,方法,事件等。 在 ArcGIS Engine 的帮助文件中,我们可以看到 MapControl 主要实现了如下接口: 内部文档,请勿外传 在前面的例子中,我们已经看到如何使用 MapControl 加载 Mxd 文档和 Shp 文件等。下面我们通过实 现鹰眼图这个功能来进一步学习 MapControl 控件。在实现鹰眼图之前,我们需要接口有更深入的了解。 3.3.1.1 变主动为被动-出接口(OutBound interface) COM 编程类似客户端和服务器端的两层结构,COM 所建立的是一个软件模块与另一个软件模块之间 的链接, 当这种链接建立之后, 模块之间就可以通过被称之为 Interface“接口 ”的机制来进行通信。在绝大 部分情况下, 客户应用程序与组件的通信过程是单向的, 客户创建组件对象, 然后客户通过接口调用对象 所提供的功能, 在适当的时候再把对象释放掉。在这种交互过程中, 客户总是主动的, 而组件总是处于被动 状态, 通过自身暴露给客户的接口监听客户的请求, 一旦接收到客户的请求便做出反应,这些反应的“幕后 “,也就是代码是被屏蔽掉的,我们是看不到这些接口内的方法是如何实现的。这样的接口称为入接口 InBound Interface,但是对于一个全面交互过程来说, 这样的单向通信往往是不能满足实际的需要, 组件对 象也要主动与客户进行通信, 因此, 与入接口相对应, 对象也可以提供出接口 OutBound interface,对象通过 这些出接口与客户进行通信。之所以把这些接口称为出接口, 其原因在于这些接口并不由 COM 服务器端 的对象实现, 而是由客户程序自己来实现, 客户实现这些接口, 服务器端调用此接口的成员函数, 即调用了 客户自定义的函数, 这时组件对象变成了客户端的客户。也就是说出接口的实现是由我们自己实现,而被 服务器 调用, 这样的接口,我们往往称之为事件接口,这些接口里面定义了一些如 OnMouseUp , OnMouseMove 等函数,当相应事件发生的时候,由服务器去执行这个事件里面的内容。 内部文档,请勿外传 3.3.1.2 鹰眼图的实现 鹰眼图的实现用到控件如下:: 控件名称 控件类型 备注 axMapControl1 主图 axMapControl2 鸟瞰图 axToolbarControl1 axTOCControl1 分析:鹰眼图的操作主要由以下几个动作,当在一个控件中移动一幅图的时候另一控件中的图也发生 变化,当在主控件中重新加载一幅图的时候,另外一个控件的图也发生相应的变化,同时我们在鸟瞰的控 件中加入一红色边框,注意这个其实是一个面,只是填充的颜色是透明的而已。通过分析我们知道,我们 要添加两个 MapControl 控件,名字分别是 axMapControl1 和 axMapControl2,其中 axMapControl1 为主图, 而 axMapControl1 为鸟瞰图。 对于名称为 axMapControl1 的 MapControl 控件,只需要在 axMapControl1 的 OnExtentUpdated 和 OnMapReplaced 中分别添加以下代码: private void axMapControl1_OnExtentUpdated(object sender, IMapControlEvents2_OnExtentUpdatedEvent e) { // 得到新范围 IEnvelope pEnvelope = (IEnvelope)e.newEnvelope; IGraphicsContainer pGraphicsContainer = axMapControl2.Map as IGraphicsContainer; IActiveView pActiveView = pGraphicsContainer as IActiveView; //在绘制前,清除axMapControl2中的任何图形元素 pGraphicsContainer.DeleteAllElements(); 内部文档,请勿外传 IRectangleElement pRectangleEle = new RectangleElementClass(); IElement pElement = pRectangleEle as IElement; pElement.Geometry = pEnvelope; //设置鹰眼图中的红线框 IRgbColor pColor = new RgbColorClass(); pColor.Red = 255; pColor.Green = 0; pColor.Blue = 0; pColor.Transparency = 255; //产生一个线符号对象 ILineSymbol pOutline = new SimpleLineSymbolClass(); pOutline.Width = 3; pOutline.Color = pColor; //设置颜色属性 pColor = new RgbColorClass(); pColor.Red = 255; pColor.Green = 0; pColor.Blue = 0; pColor.Transparency = 0; //设置填充符号的属性 IFillSymbol pFillSymbol = new SimpleFillSymbolClass(); 内部文档,请勿外传 pFillSymbol.Color = pColor; pFillSymbol.Outline = pOutline; IFillShapeElement pFillShapeEle = pElement as IFillShapeElement; pFillShapeEle.Symbol = pFillSymbol; pGraphicsContainer.AddElement((IElement)pFillShapeEle, 0); pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null); } private void axMapControl1_OnMapReplaced(object sender, IMapControlEvents2_OnMapReplacedEvent e) { if (axMapControl1.LayerCount > 0) { axMapControl2.Map = new MapClass(); for (int i = 0; i <= axMapControl1.Map.LayerCount - 1; i++) { axMapControl2.AddLayer(axMapControl1.get_Layer(i)); } axMapControl2.Extent = axMapControl1.Extent; axMapControl2.Refresh(); } } 对于名称为 axMapControl2 的 MapControl 控件,只需要在 axMapControl2 的 OnMouseMove 和 OnMouseDown 中分别添加以下代码: private void axMapControl2_OnMouseMove(object sender, IMapControlEvents2_OnMouseMoveEvent e) { 内部文档,请勿外传 if (e.button == 1) { IPoint pPoint = new PointClass(); pPoint.PutCoords(e.mapX, e.mapY); axMapControl1.CenterAt(pPoint); axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null); } } private void axMapControl2_OnMouseDown(object sender, IMapControlEvents2_OnMouseDownEvent e) { if (axMapControl2.Map.LayerCount > 0) { if (e.button == 1) { IPoint pPoint = new PointClass(); pPoint.PutCoords(e.mapX, e.mapY); axMapControl1.CenterAt(pPoint); axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null); } else if (e.button == 2) { IEnvelope pEnv = axMapControl2.TrackRectangle(); axMapControl1.Extent = pEnv; axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null); 内部文档,请勿外传 } } } 运行后的效果如下: 3.3.2 TOCContro 控件 TOCControl 控件使用的是用伙伴控件中的数据地图,它控制图层是否在伙伴控件空显示以及和伙伴控 件在符号上保持一致,TOCControl 为用户提供了一个交互式的环境,如果 TOCControl 控件的伙伴控件是 MapControl 控件,当我们将 TOCControl 控件中图层删掉是,MapControl 控件中相应的图层也会被删掉。 3.3.2.1 显示属性表的信息 我们知道 ArcMap 中的 Table of Contents 有 很 多 功 能 , 如 下 图 : 内部文档,请勿外传 而 ArcGIS Engine 提供的 TOCControl 控件几乎没有提供,那么这些都是需要自己开发的,在这里我做一个 显示属性表的功能。 分析:要显示某一个图层的属性表,首先要将这个图层选中,然后在另外一个 Form 中将选中的这个图层 的属性信息进行显示。 添加一个上下文菜单,添加一个新的 Form 窗体,在这个新的窗体上添加 GridView 控件,并在 TOCControl 控件的 OnMouseDown 事件下添加如下代码(pGlobalFeatureLayer 是我定义的一个全局变量): private void axTOCControl1_OnMouseDown(object sender, ESRI.ArcGIS.Controls.ITOCControlEvents_OnMouseDownEvent e) { if (axMapControl1.LayerCount > 0) { esriTOCControlItem pItem = new esriTOCControlItem(); pGlobalFeatureLayer = new FeatureLayerClass(); IBasicMap pBasicMap = new MapClass(); object pOther = new object(); 内部文档,请勿外传 object pIndex = new object(); axTOCControl1.HitTest(e.x, e.y, ref pItem, ref pBasicMap, ref pGlobalFeatureLayer, ref pOther, ref pIndex); } if (e.button == 2) { context.Show(axTOCControl1, e.x, e.y); } } 在上下文菜单的打开属性表的 Click 事件中添加如下代码: private void 打开属性表ToolStripMenuItem_Click(object sender, EventArgs e) { FormTable Ft = new FormTable(pGlobalFeatureLayer as IFeatureLayer); Ft.Show(); } 在新的窗体中添加一个将属性表显示到 GridView 控件中的函数,如下: public void Itable2Dtable() { IFields pFields; pFields = pFeatureLayer.FeatureClass.Fields; dtGridView.ColumnCount = pFields.FieldCount; for (int i = 0; i < pFields.FieldCount;i++ ) { string fldName = pFields.get_Field(i).Name; dtGridView.Columns[i].Name = fldName; dtGridView.Columns[i].ValueType = System.Type.GetType(ParseFieldType(pFields.get_Field(i).Type)); } IFeatureCursor pFeatureCursor; 内部文档,请勿外传 pFeatureCursor = pFeatureLayer.FeatureClass.Search(null, false); IFeature pFeature; pFeature = pFeatureCursor.NextFeature(); while (pFeature != null) { string[] fldValue = new string[pFields.FieldCount]; for (int i = 0; i < pFields.FieldCount; i++) { string fldName; fldName = pFields.get_Field(i).Name; if (fldName==pFeatureLayer .FeatureClass .ShapeFieldName) { fldValue[i] = Convert.ToString(pFeature.Shape.GeometryType); } else fldValue[i] = Convert.ToString(pFeature.get_Value(i)); } dtGridView.Rows.Add(fldValue); pFeature = pFeatureCursor.NextFeature(); } } 运行后,效果如下: 内部文档,请勿外传 3.3.3 命令和工具的宿主控件 ToolBarControl 控件 在 ToolBarControl 控件中,我们通过 ToolBarControl 控件的属性页面添加了一些如打开文档,平移,放大等 功能,在 ArcGIS Engine 中我们将宿主在 ToolBarControl 控件中的内容分为三类“命令,工具,工具控件“ 命令,就是当用户单击时所产生的操作,比如说,我们要打开一个地图文档,我们只需要在 ToolBarControl 控件上添加打开地图文档这个操作,然后用鼠标点击。 工具,存在着交互这个操作。当我们在 ToolBarControl 控件上使用一个工具的时候,我们需要通过两步操 作:(1)单击这个工具,(2)使用这个工具更相应的控件做交互操作。 如果我们点击了平移这个操作,那么这个时候我们还要用鼠标和地图进行平移等交互,那和谁交互呢,我 们知道 ToolBarControl 有一个 buddy 属,这个就体现了在 budy 属性上。ToolbarControl 会将伙伴控件的 CurrentTool 属性设置为我们用鼠标点击的工具,然后伙伴控件就等着和我们的工具交互。 工具控件,这通常是用户界面组件,如 ToolBarControl 控件上的列表框和组合框。 其实在 ToolBarControl 控件中还可以宿主工具条菜单(ToolbarMenu),工具条菜单表示单击命令项的一个 垂直列表。用户必须选择工具条菜单上的一个命令项,或单击工具条菜单之外的地方使其消失。工具条菜 内部文档,请勿外传 单只能驻留命令项(不允许驻留工具或者工具控件) 3.3.3.1 命令和工具 在 ArcGIS Engine 中,命令是实现了 ICommand 接口,在 ArcGIS Engin 的开发帮助中,我们可以看到 ICommand 接口以下成员: 当用户单击这个命令时会导致 ICommand.OnClick 方法被调用,并执行某种操作。 要将一个命令宿主到 ToolBarControl 控件上,有以下三种方法:  使用 UID  使用 progID  使用 ICommand UID pUID = new UIDClass(); pUID.Value = "esriControls.ControlsUndoCommand"; axToolbarControl1.AddItem(pUID, -1, 0, false, -1, esriCommandStyles.esriCommandStyleIconOnly); axEditorToolbar.AddItem("esriControls.ControlsUndoCommand", 0, -1, true, 0, esriCommandStyles.esriCommandStyleIconOnly); 内部文档,请勿外传 ICommand pUndo = new ControlsUndoCommandClass(); axEditorToolbar.AddItem(pUndo, 0, -1, false, 0, esriCommandStyles.esriCommandStyleIconOnly); ICommand 接口是可以被扩展的,也就是说这个接口对我们是开放的,只要我们将 ICommand 接口中成员 实现,因为这个格式是固定的,Esri 提供了相应的模板 我们知道宿主在 ToolBarControl 上的命令操作的对象是 ToolBarControl 的伙伴控件,但是,这个命令怎么和 这个伙伴控件联系起来了,注意到 ICommand 接口中有一个 ICommand.OnCreate 方法,这个方法有一个参 数 hook。 [C #] p ub lic void On Cre at e ( object hook ); 这个 hook 是一个 object 对象。也就是说这儿命令和那个控件协作,要看这个 hook 传入的是那种控件。 当命令对象宿主到 ToolBarControl 控件上后就会立即调用 ICommand.OnCreate 方法,同时会将 ToolBarControl 控件传递给 hook 这个参数,以便命令能和 ToolBarControl 控件的伙伴控件协作。一般要在 这个方法里面要测试这个 hook 是不是有效,也就是说这个命能不能和这个 hook 协作,要看这个命令支不 支持这样的操作,比如说我们要打开一个地图文档,我们知道打开地图文档这个命令是可以和 MapControl, PageLayoutControl 控件协作的,如果我们传进去的是 TOCContro 控件,那么这个命令就会失效,这些话看 内部文档,请勿外传 起来很费解,我们通过一个代码来好好体会这些话。 我们自己定义一个打开地图文档的命令,利用 Esri 提供的命令模板 选择和命令对象协作的控件 using System; 内部文档,请勿外传 using System.Drawing; using System.Runtime.InteropServices; using ESRI.ArcGIS.ADF.BaseClasses; using ESRI.ArcGIS.ADF.CATIDs; using ESRI.ArcGIS.Controls; using ESRI.ArcGIS.Carto; using System.Windows.Forms; namespace EngineApplication { /// /// Summary description for OpenMxdCommand. /// [Guid("c142fea5-2e8e-4f68-95e1-9cad4a6a290e")] [ClassInterface(ClassInterfaceType.None)] [ProgId("CalculateContourTask.OpenMxdCommand")] public sealed class OpenMxdCommand : BaseCommand { #region COM Registration Function(s) [ComRegisterFunction()] [ComVisible(false)] static void RegisterFunction(Type registerType) { // Required for ArcGIS Component Category Registrar support ArcGISCategoryRegistration(registerType); // // TODO: Add any COM registration code here // } [ComUnregisterFunction()] [ComVisible(false)] static void UnregisterFunction(Type registerType) { // Required for ArcGIS Component Category Registrar support ArcGISCategoryUnregistration(registerType); // // TODO: Add any COM unregistration code here // } #region ArcGIS Component Category Registrar generated code 内部文档,请勿外传 /// /// Required method for ArcGIS Component Category registration - /// Do not modify the contents of this method with the code editor. /// private static void ArcGISCategoryRegistration(Type registerType) { string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID); ControlsCommands.Register(regKey); } /// /// Required method for ArcGIS Component Category unregistration - /// Do not modify the contents of this method with the code editor. /// private static void ArcGISCategoryUnregistration(Type registerType) { string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID); ControlsCommands.Unregister(regKey); } #endregion #endregion IMapControl2 pMapControl; public OpenMxdCommand() { // // TODO: Define values for the public properties // base.m_category = "打开地图文档"; //localizable text base.m_caption = "打开地图文档"; //localizable text base.m_message = "打开地图文档"; //localizable text base.m_toolTip = "打开地图文档"; //localizable text base.m_name = " 打 开 地 图 文 档 "; //unique id, non-localizable (e.g. "MyCategory_MyCommand") try { // 内部文档,请勿外传 // TODO: change bitmap name if necessary // string bitmapResourceName = GetType().Name + ".bmp"; base.m_bitmap = new Bitmap(GetType(), bitmapResourceName); } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap"); } } #region Overridden Class Methods /// /// Occurs when this command is created /// /// Instance of the application public override void OnCreate(object hook) { if (hook == null) return; //在这里对hook进行判断 if (hook is IToolbarControl) { IToolbarControl pToolBar= hook as IToolbarControl ; pMapControl = pToolBar.Buddy as IMapControl2; } else if(hook is IMapControl2 ) { pMapControl = hook as IMapControl2; } // TODO: Add other initialization code } /// /// Occurs when this command is clicked /// public override void OnClick() 内部文档,请勿外传 { // TODO: Add OpenMxdCommand.OnClick implementation //launch a new OpenFile dialog OpenFileDialog pOpenFileDialog = new OpenFileDialog(); pOpenFileDialog.Filter = "Map Documents (*.mxd)|*.mxd"; pOpenFileDialog.Multiselect = false; pOpenFileDialog.Title = "Open Map Document"; if (pOpenFileDialog.ShowDialog() == DialogResult.OK) { string docName = pOpenFileDialog.FileName; IMapDocument pMapDoc = new MapDocumentClass(); if (pMapDoc.get_IsPresent(docName) && !pMapDoc.get_IsPasswordProtected(docName)) { pMapControl.LoadMxFile(pOpenFileDialog.FileName, null, null); // set the first map as the active view pMapControl.ActiveView.Refresh(); pMapDoc.Close(); } } } #endregion } } 通过下面两句将我们自定义的打开地图文档的命令宿主到 ToolBarControl 上 OpenMxdCommand pMxdCommand = new OpenMxdCommand(); axToolbarControl1.AddItem(pMxdCommand, -1, 0, false, -1, esriCommandStyles.esriCommandStyleIconOnly); 效果如下: 内部文档,请勿外传 我们可以对上述代码添加断点,发现当程序执行到 axToolbarControl1.AddItem(pMxdCommand, -1, 0, false, -1, esriCommandStyles.esriCommandStyleIconOnly);的时候,OnCreate 函数被执行,进而对 hook 参数判断,通过进一步跟踪,发现程序执行到 if (hook is IToolbarControl) { IToolbarControl pToolBar= hook as IToolbarControl ; pMapControl = pToolBar.Buddy as IMapControl2; } 说明程序将 ToolbarControl 控件传入进去。 而工具是实现了 Itool 和 ICommand 这两个接口,从 ITool 的接口成员中我们就不难看出 工具和命令的区别: 内部文档,请勿外传 ITool 接口的成员几乎都是和交互相关的一些事件. 上述我们都是将命令或者工具宿主到了 ToolBarControl 控件上,能不能脱离 ToolBarControl 控件呢?答 案是可以的。 3.3.4 脱离 ToolBarControl 控件的命令和工具 我们在 Form 中添加 MenueStrip 控件,并添加以下几个功能,然后分别写入相应的代码 private void 文件ToolStripMenuItem_Click(object sender, EventArgs e) { ICommand pMxd = new ControlsOpenDocCommandClass(); pMxd.OnCreate(axMapControl1.Object); pMxd.OnClick(); } private void 查询ToolStripMenuItem_Click(object sender, EventArgs e) { ICommand pAddData = new ControlsAddDataCommandClass(); pAddData.OnCreate(axMapControl1.Object); 内部文档,请勿外传 pAddData.OnClick(); } private void 空间分析ToolStripMenuItem_Click(object sender, EventArgs e) { ICommand pZoomIn = new ControlsMapZoomInToolClass(); pZoomIn.OnCreate(axMapControl1.Object); axMapControl1.CurrentTool = pZoomIn as ITool; } private void 缩小ToolStripMenuItem_Click(object sender, EventArgs e) { ICommand pZoomOut = new ControlsMapZoomOutToolClass(); pZoomOut.OnCreate(axMapControl1.Object); axMapControl1.CurrentTool = pZoomOut as ITool; } 通过运行程序,发现和 toolbarcontrol 的效果一样 3.3.5 另一个视图控件 PageLayoutControl 在 ArcMap 中除了在介绍 MapControl 控件的时候提到的数据视图,还有另外一种视图,就是布局视图 (Layout),PageLlayoutControl 控件则对应了 ArcMap 中的布局视图。PageLayoutControl 控件主要用于制图, 它可以方便的操作各种元素对象,以产生一副精美的地图对象出来。该控件中封装了一个 PageLayout 对象, 这个对象用于控制布局视图中的对象的属性。 3.3.5.1 MapControl 控件与 PageLayout 控件的联动 分析:要实现这两个控件的联动,我们首先回顾下 ArcMap 中的 内部文档,请勿外传 情景,两个控件的联动不仅仅是简单的切换,在切换的时候还因该保留各自控件上的一些状态,比如 说当我们在 MapControl 上有一个放大操作时,当我们没有将这个放大操作取消而切换到 PageLayout 控件, 在 PageLayout 控件上做了一些操作后,又切换到 MapControl 控件,我们应该还应该能进行放大操作而不 用重新使用放大这个工具。 通过分析我们可以得到下面几点: 当切换两个控件的时候,地图的同步 各自控件上激活的工具或者命令的保留 当存在 TOC 控件和 ToolBar 控件的时候,切换了地图控件和布局控件,那么这两个控件的伙伴控件也 应发生变化。 在 Form 中添加 TabControl 控件,分别将地图控件和布局控件放置到里面,如下图所示: 我们在 NET 中定义一个类,这个类用来实现这两个功能,类的名称是 ControlsSynchronizer 内部文档,请勿外传 /// /// 在构造函数中传入地图控件和布局控件 /// /// /// public ControlsSynchronizer(IMapControl3 _MapControl, IPageLayoutControl2 _PageLayoutControl) : this() { //assign the class members pMapControl = _MapControl; pPageLayoutControl = _PageLayoutControl; } /// /// 激活地图控件并销毁布局控件 /// public void ActivateMap() { try { if (pPageLayoutControl == null || pMapControl == null) throw new Exception("ControlsSynchronizer::ActivateMap:\r\nEither MapControl or PageLayoutControl are not initialized!"); //cache the current tool of the PageLayoutControl if (pPageLayoutControl.CurrentTool != null) pPageLayoutActiveTool = pPageLayoutControl.CurrentTool; //deactivate the PagleLayout pPageLayoutControl.ActiveView.Deactivate(); //activate the MapControl pMapControl.ActiveView.Activate(pMapControl.hWnd); //assign the last active tool that has been used on the MapControl back as the active tool if (pMapActiveTool != null) pMapControl.CurrentTool = pMapActiveTool; pIsMapControlactive = true; //on each of the framework controls, set the Buddy control to the MapControl 内部文档,请勿外传 this.SetBuddies(pMapControl.Object); } catch (Exception ex) { throw new Exception(string.Format("ControlsSynchronizer::ActivateMap:\r\n{0}", ex.Message)); } } /// /// 激活布局控件并销毁地图控件 /// public void ActivatePageLayout() { try { if (pPageLayoutControl == null || pMapControl == null) throw new Exception("ControlsSynchronizer::ActivatePageLayout:\r\nEither MapControl or PageLayoutControl are not initialized!"); //cache the current tool of the MapControl if (pMapControl.CurrentTool != null) pMapActiveTool = pMapControl.CurrentTool; //deactivate the MapControl pMapControl.ActiveView.Deactivate(); //activate the PageLayoutControl pPageLayoutControl.ActiveView.Activate(pPageLayoutControl.hWnd); //assign the last active tool that has been used on the PageLayoutControl back as the active tool if (pPageLayoutActiveTool != null) pPageLayoutControl.CurrentTool = pPageLayoutActiveTool; pIsMapControlactive = false; //on each of the framework controls, set the Buddy control to the PageLayoutControl this.SetBuddies(pPageLayoutControl.Object); } catch (Exception ex) 内部文档,请勿外传 { throw new Exception(string.Format("ControlsSynchronizer::ActivatePageLayout:\r\n{0}", ex.Message)); } } /// /// 当激活的控件发生变化时,IToolbarControl控件和ITOCControl控件的伙伴控件也应发生变化 /// /// the active control private void SetBuddies(object _buddy) { try { if (_buddy == null) throw new Exception("ControlsSynchronizer::SetBuddies:\r\nTarget Buddy Control is not initialized!"); foreach (object obj in pFrameworkControls) { if (obj is IToolbarControl) { ((IToolbarControl)obj).SetBuddyControl(_buddy); } else if (obj is ITOCControl) { ((ITOCControl)obj).SetBuddyControl(_buddy); } } } catch (Exception ex) { throw new Exception(string.Format("ControlsSynchronizer::SetBuddies:\r\n{0}", ex.Message)); } } 内部文档,请勿外传 /// /// 如果地图发生了变化,那么地图控件和布局控件的地图也应发生变化 /// /// public void ReplaceMap(IMap _NewMap) { if (_NewMap == null) throw new Exception("ControlsSynchronizer::ReplaceMap:\r\nNew map for replacement is not initialized!"); if (pPageLayoutControl == null || pMapControl == null) throw new Exception("ControlsSynchronizer::ReplaceMap:\r\nEither MapControl or PageLayoutControl are not initialized!"); //create a new instance of IMaps collection which is needed by the PageLayout IMaps pMaps = new Maps(); //add the new map to the Maps collection pMaps.Add(_NewMap); bool bIsMapActive = pIsMapControlactive; //call replace map on the PageLayout in order to replace the focus map //we must call ActivatePageLayout, since it is the control we call 'ReplaceMaps' this.ActivatePageLayout(); pPageLayoutControl.PageLayout.ReplaceMaps(pMaps); //assign the new map to the MapControl pMapControl.Map = _NewMap; //reset the active tools pPageLayoutActiveTool = null; pMapActiveTool = null; //make sure that the last active control is activated if (bIsMapActive) { this.ActivateMap(); pMapControl.ActiveView.Refresh(); } else { this.ActivatePageLayout(); 内部文档,请勿外传 pPageLayoutControl.ActiveView.Refresh(); } } /// /// 当运行应用程序的时候,即便没有加载地图,则创建一个空的地图,让这两个控件和这个地图绑 定在一起,这样就能保持一致 /// /// true if the MapControl supposed to be activated first public void BindControls(bool _ActivateMapFirst) { if (pPageLayoutControl == null || pMapControl == null) throw new Exception("ControlsSynchronizer::BindControls:\r\nEither MapControl or PageLayoutControl are not initialized!"); //创建一个地图实例 IMap pNewMap = new MapClass(); pNewMap.Name = "Map"; //其中Maps为我们创建的一个类,表示的是地图的集合 IMaps pMaps = new Maps(); //add the new Map instance to the Maps collection pMaps.Add(pNewMap); //call replace map on the PageLayout in order to replace the focus map pPageLayoutControl.PageLayout.ReplaceMaps(pMaps); //assign the new map to the MapControl pMapControl.Map = pNewMap; //reset the active tools pPageLayoutActiveTool = null; pMapActiveTool = null; //make sure that the last active control is activated if (_ActivateMapFirst) this.ActivateMap(); else this.ActivatePageLayout(); } 内部文档,请勿外传 还记得我们上次创建的那个打开地图文档的命令?在打开地图的时候,我们只不过是将这个地图付给 了 Map 控件,这样的话布局控件是得不到图的,因此应该对 OnClick 改动下,在这个里面将 map 控件和布 局控件同步起来 public override void OnClick() { //launch a new OpenFile dialog OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "Map Documents (*.mxd)|*.mxd"; dlg.Multiselect = false; dlg.Title = "Open Map Document"; if (dlg.ShowDialog() == DialogResult.OK) { string docName = dlg.FileName; IMapDocument pMapDoc = new MapDocumentClass(); if (pMapDoc.get_IsPresent(docName) && !pMapDoc.get_IsPasswordProtected(docName)) { pMapDoc.Open(docName, string.Empty); // set the first map as the active view IMap map = pMapDoc.get_Map(0); pMapDoc.SetActiveView((IActiveView)map); pControlsSynchronizer.PageLayoutControl.PageLayout = pMapDoc.PageLayout; pControlsSynchronizer.ReplaceMap(map); pMapDoc.Close(); m_sDocumentPath = docName; } } } 在ControlsSynchronizer类中使用的Maps类的代码如下: namespace EngineApplication { 内部文档,请勿外传 /// /// Implementation of interface IMaps which is eventually a collection of Maps /// public class Maps : IMaps, IDisposable { //class member - using internally an ArrayList to manage the Maps collection private ArrayList pArray = null; #region class constructor public Maps() { pArray = new ArrayList(); } #endregion #region IDisposable Members /// /// Dispose the collection /// public void Dispose() { if (pArray != null) { pArray.Clear(); pArray = null; } } #endregion #region IMaps Members /// /// Remove the Map at the given index /// /// public void RemoveAt(int Index) { if (Index > pArray.Count || Index < 0) throw new Exception("Maps::RemoveAt:\r\nIndex is out of range!"); pArray.RemoveAt(Index); } 内部文档,请勿外传 /// /// Reset the Maps array /// public void Reset() { pArray.Clear(); } /// /// Get the number of Maps in the collection /// public int Count { get { return pArray.Count; } } public IMap get_Item(int Index) { if (Index > pArray.Count || Index < 0) throw new Exception("Maps::get_Item:\r\nIndex is out of range!"); return pArray[Index] as IMap; } /// /// Remove the instance of the given Map /// /// public void Remove(IMap Map) { pArray.Remove(Map); } /// /// Create a new Map, add it to the collection and return it to the caller /// /// public IMap Create() { 内部文档,请勿外传 IMap newMap = new MapClass(); pArray.Add(newMap); return newMap; } /// /// Add the given Map to the collection /// /// public void Add(IMap Map) { if (Map == null) throw new Exception("Maps::Add:\r\nNew Map is mot initialized!"); pArray.Add(Map); } #endregion } } 内部文档,请勿外传 3.3.5.2 第一个对象 Map 地图是 GIS 中的重要概念,也是 GIS 的应用中的成果,GIS 的很多成 果都是用地图来说话的,当我们打开 ArcMap 程序后,首先看到的是数 据视图(ArcMap 有两种视图,数据视图和布局视图)骂我们看到的 这个数据视图其实就是一个 Map 对象。 在 ArcMap 中,可以显示在 Map 中的数据有两大类,也就是地理数 据和图形元素,空间数据是 GIS 分析制图的数据源,保存在地理数据 库库或者 Shp 文件中,图形元素也是一种可以在 Map 上显示的对象。 他们两个的共同特征是拥有一个 geomtry 属性。 地图对象是地图数据的容器,它由图层和图形数据组成。Map 对象实现了众 多的接口,如下: 内部文档,请勿外传 3.3.5.3 IMap 接口 该接口主要用于管理 Map 对象中的图层对象,要素选择集和空间参 考等对象,IMap 接口也往往是我们一个任务的起点。使用 IMap 接口, 我们可以获取这个 Map 对象中的图层的个数,可以添加图层,删除 图层,还可以利用 IMap 实现查询的高亮显示。IMap 接口提供了两个 和查询相关的方法 IMap.SelectByShape 方法和 IMap.SelectFeature 方 法。 IMap.SelectFeature 方法可以将在 Map 中获得的一个要素放到这个要 素的图层选择集中,这个方法有一个很重要的用途就是这个方法能使 内部文档,请勿外传 获取的要素高亮显示 使用 IMap.SelectByShape 方法对地图中的要素进行查询 [C #] p ub lic void SelectByShape ( IGeometry Shape, ISelectionEnvironment env, bool justOne ); IMap.SelectByShape 方法将会在 Map 中的所有图层中进行查询,这 个方法需要三个参数,第一个参数是一个 Geomtry 对 象 , IMap.SelectByShape 在查询的时候凡是和我们输入进去的这个几何 对象相交的要素都会查询出来,但是这有一个前提就是每一个要素图 层的 Selectable 属性是 true。 第二个参数 ISelectionEnvironment,ISelectionEnvironment 参数控制着构造选择集的结果和显示的 方法等。 第三个参数是一个 bool 值。 我们在 MapControl’控件中画一个多边形的面,查找出和我们这个多边形面相交的所有图层中的要素 IGeometry pGeo =axMapControl1.TrackPolygon(); axMapControl1.Map.SelectByShape(pGeo, null, false); axMapControl1.ActiveView.Refresh(); 效果如下: 内部文档,请勿外传 使用 IMap.SelectFeature 方法对地图中的要素进行查询 [C #] p ub lic void SelectFeature ( ILayer Layer, IFeature Feature ); void SearchHightlight(IMap _pMap,IFeatureLayer _pFeatureLayer, IQueryFilter _pQuery, bool _Bool) 内部文档,请勿外传 { IFeatureCursor pFtCursor = _pFeatureLayer.Search(_pQuery, _Bool); IFeature pFt = pFtCursor.NextFeature(); while (pFt != null) { _pMap.SelectFeature(_pFeatureLayer as ILayer, pFt); pFt = pFtCursor.NextFeature(); } } 效果如下: 3.3.5.4 IGraphicsContainer 接口 在介绍 Map 对象的时候,我们说过 Map 不仅仅可以显示空间数据, 还可以对一些元素进行显示,Map 对象通过 IGraphicsContainer 接口 来管理这些元素对象。 内部文档,请勿外传 3.3.5.5 IActiveView 接口 IActiveView 接口定义了 Map 对象的数据显示功能,这个接口管理着 绘制图形的显示范围。在 ArcMap 中,我们已经知道有两种视图数据 视图和布局视图,而在 ArcGIS Engine 中有两个对象实现了这个接口 Map 和 PageLayout。 IActiveView 接口有一个很重要的方法 PartialRefresh,该方法可以让 视图对象使用不同的方式来局部刷新以便重新绘制。 3.3.5.6 ILayer ILayer 接口是被图层(Layer)对象实现的,图层对象是用来在地图 中显示空间信息,注意,图层不含有空间数据,它只是获取数据的一 个引用层而已。图层对象是一个抽象对象,它定义了所有图层的公共 方法和属性,它拥有很多子类,如 IFeatureLayer,IGeoFeatureLayer, ICadFeatureLayer,IRasterLayer。图层相当于要素的载体,当用 ArcMap 将要素类打开后就成为了图层。 内部文档,请勿外传 3.3.5.7 IFeatureLayer IFeatureLayer 接口用于管理要素类(Featureclass)图层的信息 。 IFeatureLayer 有一个非常重要的方法 IFeatureLayer.Search,这个方 法用于对要素图层中符合要求的数据进行查询。 3.3.5.8 IGeoFeaturelayer 该接口主要用于控制要素图层中宇地理相关的内容,如要素类着色和 渲染,以及空间范围等。IGeoFeatureLayer 有一个很重要的属性 IGeoFeatureLayer.Renderer,使用这个属性,我们就可以对空间数据 符号化。 4 四.空间数据库 空间数据库介绍 4.1 4.1.1 Geodatabase 介绍 Geodatabase 是 ESRI 在 ArcInfo8 中引入的一种全新的面向对象的空间数据模型,在物理级别上空间数据 库分为三种不同的存储形式,即个人数据库,文件数据库,以及面向企业的 SDE 数据库,个人数据库依赖 于微软的 ACCESS 数据库,也只能在 windows 平台上运行,除此之外个人数据库有容量的限制,最大存储量 不能超过 2GB,文件数据库以二进制方式管理空间数据,单张表可以存储 1TB,可以通过关键字进行配置,是 容量可以扩充到 256TB,从这个数据存储层面来说,文件数据库的容量是无限的,而且可以在多个平台上运 行,如 linux,unix,但是它和个人数据库有一个相同点,就是不能多人同时编辑,而 Sde 数据库除了多 内部文档,请勿外传 人同时编辑数据之外,还提供了一些其他高级功能,如同步复制,历史归档等,同时 SDE 数据库也可以运 行在多个平台上,通过 SDE 将空间数据存储在目前流行的关系型数据库中,目前 SDE 支持5 中数据库(oracle, sql server,db2, infomix,postgresql);在逻辑上,空间数据库采用统一的框架,为管理空间数据提 供了统一的模式。 Geodatabase 是一种面向对象的数据模型,在此模型中,它不仅管理和存储了空间数据,还定义了空间实 体之间的相互关系,如空间中的实体可以表示为具有性质、行为等。 Geodatabase 还支持表达具有不同类型特征的对象,包括简单的物体、地理要素(具有空间信息的对 象)、网络要素(与其他要素有几何关系的对象)、拓扑相关要素、注记要素以及其他更专业的特征类型。该 模型还允许定义对象之间的关系和规则,从而保持地物对象间相关性和拓扑性的完整。 4.1.2 Geodatabase 统一的存储框架 Geodatabase 以层次结构的数据对象来组织地理数据。这些数据对象存储在要素类(Feature Classes)、对 象类(0bject classes)和数据集(Feature datasets)中。Object Class 可以理解为是一个在 Geodatabase 中储存非空 间数据的表。而 Feature class 是具有相同几何类型和属性结构的要素(Feature)的集合。 要素数据集(Feature datasets)是共用同一空间参考要素类的集合。要素类(Feature Class)储存可以在要素 数据集(Feature datasets)内部组织简单要素,也可以独立于要素数据集(Feature datasets)。独立于要素数据集 (Feature datasets)的简单的要素类(Feature Class)称为独立要素类(Feature class)。存储拓扑要素( Feature )的要 素类必须在要素数据集(Feature dataset)内,以确保一个共同的空间参考。 注意表的地位和要素数据集是等同的,也就是说,表是不能存储 在要素数据集中。 Geodatabase 的基本体系结构包括要素数据集、栅格数据集、TIN 数据集、独立的对象类、独立的要素 类、独立的关系类和属性域等,如下图: 内部文档,请勿外传 在上图中我们可以看到最顶级的工作空间。数据库在 ArcGIS Engine 中被抽象为一个工作空间(Worksapce), 下面的 OMD 图描述了空间数据库的结构: 内部文档,请勿外传 IWorkSpace 接口介绍 4.2 IWorkspace 接口提供访问工作空间的通用属性和方法,如它的连接属性,以及包含的数据集的方法。 内部文档,请勿外传 如何打开一个数据库 4.3 要打开一个数据库,也就意味着我们要得到那个工作空间,而工作空间是一个普通类,也就意味着我 们只能从其他类来得到这个工作空间,这个类就是工作空间工厂(WorkspaceFactory),而这个类又是一个 抽象类,也就意味着我们只能使用它的子类来实例化一个对象,WorkspaceFactory 有众多的子类,我们可 以从 OMD 图中获得 在这里说明下,shapefile 是 Esri 早期的空间数据格式,以文件管理, shapefile 文件所在的文件夹也被抽象为一个 workspace,相应的要的 到 shapefile,就要用到啥喷 shapefileworkspacefactory 内部文档,请勿外传 IWorkSpaceFactory 是 Geodatabase 的入口,它定义了数据库的通用属性,比如打开,创建等,我们在 ArcGIS Engine 的帮助中可以详细的得到它的信息,如下图: 打开数据库有两种方式,从上面的图表中也可以看出 OpenFromFile 方法和 Open 方法,这两者的区别在于 方法中的参数不同,其中 Open 方法需要一个 IPropertySet 对象,这个方法我们经常在打开 SDE 数据库中 使用,注意(这个方法同样可以用来打开个人数据库,文件数据库)。 4.3.1 打开个人数据库 public IWorkspace GetMDBWorkspace(String _pGDBName) { IWorkspaceFactory pWsFac = new AccessWorkspaceFactoryClass(); IWorkspace pWs = pWsFac.OpenFromFile(_pGDBName,0); return pWs; } 内部文档,请勿外传 4.3.2 打开文件数据库 public IWorkspace GetFGDBWorkspace(String _pGDBName) { IWorkspaceFactory pWsFac = new FileGDBWorkspaceFactoryClass(); IWorkspace pWs = pWsFac.OpenFromFile(_pGDBName, 0); return pWs; } 4.3.3 打开 SDE 数据库 打开 SDE 数据库我们使用的是 Open 方法,要用这个方法,我们就要对 IPropertySet 对象设置,要打开 SDE 数据库,我们要获取 SDE 数据库的服务器地址,数据库实例,数据库,用户,密码等参数。而 IPropertySet 就好比一个 Key-Value 的对象,用来帮组我们设置这些,然后传到 Open 方法中。 public IWorkspace GetSDEWorkspace(String _pServerIP, String _pInstance, String _pUser, String _pPassword, String _pDatabase, String _pVersion) { ESRI.ArcGIS.esriSystem.IPropertySet pPropertySet = new ESRI.ArcGIS.esriSystem.PropertySetClass(); pPropertySet.SetProperty("SERVER", _pServerIP); pPropertySet.SetProperty("INSTANCE", _pInstance); pPropertySet.SetProperty("DATABASE", _pDatabase); pPropertySet.SetProperty("USER", _pUser); pPropertySet.SetProperty("PASSWORD", _pPassword); pPropertySet.SetProperty("VERSION", _pVersion); ESRI.ArcGIS.Geodatabase.IWorkspaceFactory2 workspaceFactory; workspaceFactory = (ESRI.ArcGIS.Geodatabase.IWorkspaceFactory2)new ESRI.ArcGIS.DataSourcesGDB.SdeWorkspaceFactoryClass(); return workspaceFactory.Open(pPropertySet, 0); } 如何获取数据库中的所有要素类 4.4 4.4.1 深入理解数据集 在 Geodatabase 中,要素类可以直接存储在数据库中,也可以存储在数据集中。数据集(Dataset)是一 内部文档,请勿外传 个代表了 Workspace 中所谓数据集合的抽象类,它是一个集合,但是在理解数据集对象的时候,要用广义 的概念来看待,不要从我们在数据库里存储的那个物理结构去理解,否则会走入误区,因为在设计数据库 的时候,我们可以在数据集里面存储相关的要素类。那么我们在编程的时候就可以这么想,要获取数据库 中的某一个要素类,要先获取数据集,然后获取要素类,其实通过工作空间 IFeatureWorkspace.OpenFeatureClass 就可以,可以把数据库比作文件夹,数据集比作子文件夹,但是在数据 库中的表,要素类,是没有重复的,不像 Windows 上的文件夹,子文件夹里面可以有名称重复的文件,刚 才说到用广义的意义,应该知道所有放在工作空间的对象都是一种数据集对象,也就是说 Table,FeatureClass 等都是数据集,也就是说数据集中的数据可以是一个字段,一行记录,一张表等。Workspace 其实也是一 种数据集,它也继承了 IDataset 这个接口。在数据库中一切对象都可以看做是数据集,不管是要素类,还 是表,或者栅格数据,那么怎么区分我们得到的到底是表,还是要素类?IDataset 有一个很重要的属性 IDataset.Type,通过这个属性我们就可以判断,IDataset.Type 的这个属性是一个枚举类型的常量,如下图: 4.4.2 获取数据库中的要素类 在 ArcGIS Engine 中,要得到某一个类,首要要获取工作空间,然后进入工作空间再得到相应的东西, 我们定义一个函数用来获取个人数据库的路径 public string WsPath() { string WsFileName=""; 内部文档,请勿外传 OpenFileDialog OpenFile = new OpenFileDialog(); OpenFile.Filter = "个人数据库(MDB)|*.mdb"; DialogResult DialogR = OpenFile.ShowDialog(); if (DialogR == DialogResult.Cancel) { } else { WsFileName = OpenFile.FileName; } return WsFileName; } 要获取要素类,首先要进入 private void button2_Click(object sender, EventArgs e) { string WsName = WsPath(); if (WsName != "") { IWorkspaceFactory pWsFt = new AccessWorkspaceFactoryClass(); IWorkspace pWs = pWsFt.OpenFromFile(WsName, 0); IEnumDataset pEDataset = pWs.get_Datasets(esriDatasetType.esriDTAny); IDataset pDataset = pEDataset.Next(); while (pDataset != null) { if (pDataset.Type ==esriDatasetType.esriDTFeatureClass) { FeatureClassBox.Items.Add(pDataset.Name); } //如果是数据集 else if (pDataset.Type == esriDatasetType.esriDTFeatureDataset) 内部文档,请勿外传 { IEnumDataset pESubDataset = pDataset.Subsets; IDataset pSubDataset = pESubDataset.Next(); while (pSubDataset != null) { FeatureClassBox.Items.Add(pSubDataset.Name); pSubDataset = pESubDataset.Next(); } } pDataset = pEDataset.Next(); } } FeatureClassBox.Text = FeatureClassBox.Items[0].ToString(); } 效果如下: 4.4.3 判断要素是否被编辑 IDatasetEdit.IsBeingEdited 内部文档,请勿外传 峰回路转打开数据库的另一种方式 4.5 4.5.1.1 IName(名称对象)介绍 数据集对象可以分为两大类,一种是 Table,我们无法将 Table 存储在要素数据集中(可以尝试下), 一种是 Geodataset,这个是要素类的容器。数据集对象有一个很重要的属性,就是这个 Fullname,用这个 可以返回和数据集相关的名称对象,而这个名称对象有一个很重要的方法 Open(),这个可以获取和这个 名称对象相关的对象(内存中的),Open()方法的返回值是 object,所以用 Open 方法的时候,我们必须 心里清楚,自己到底是要得到那个对象,然后 QI 到我们要的对象上。 IName 对象是一个代表性对象。通过使用 IName 对象,可以访问它所代表的对象的一些基本属性,而 不用将整个对象调入内存。我们用 IWorkspace获得一个 Workspace,那可是会调入内存的,而 IWorkspaceName 则不会,除非你用了 IWorkspaceName.open.在我看来,那些继承 IName 的接口,在数据转换和叠加分析的 时候经常要用到这个。 IName 是一个抽象类,拥有很多子类,借助它的子类 IWorkspaceName 也可以打开数据库。打开一个 数据库,我们要指定它的类型,是个人数据库,还是文件数据库。 IWorkspaceName 的 IWorkspaceName.WorkspaceFactoryProgID 属性用于完成这一操作,这个属性是一个枚举的常量类型  esriDataSourcesGDB.AccessWorkspaceFactory  esriDataSourcesFile.ArcInfoWorkspaceFactory  esriDataSourcesFile.CadWorkspaceFactory  esriDataSourcesGDB.FileGDBWorkspaceFactory  esriDataSourcesOleDB.OLEDBWorkspaceFactory  esriDataSourcesFile.PCCoverageWorkspaceFactory  esriDataSourcesRaster.RasterWorkspaceFactory  esriDataSourcesGDB.SdeWorkspaceFactory  esriDataSourcesFile.ShapefileWorkspaceFactory  esriDataSourcesOleDB.TextFileWorkspaceFactory  esriDataSourcesFile.TinWorkspaceFactory  esriDataSourcesFile.VpfWorkspaceFactory public IWorkspace Get_Workspace(string _pWorkspacePath) 内部文档,请勿外传 { IWorkspaceName pWorkspaceName = new WorkspaceNameClass(); pWorkspaceName.WorkspaceFactoryProgID = "esriDataSourcesGDB.AccessWorkspaceFactory"; pWorkspaceName.PathName = _pWorkspacePath; IName pName = pWorkspaceName as IName; IWorkspace pWorkspace = pName.Open() as IWorkspace; return pWorkspace; } 工作空间(WorkSpace)在逻辑上是一个包含空间数据集和非空间数据集的容器,我们往日所说的要素类, 栅格数据集,表等都存储在这个工作空间中。工作空间提供了访问内部空间和非空间数据的方法啊。工作 空间实现了众多的接口,比如 IWorkSpace,IFeatureWorkspace 等。我们在这里对这几个接口坐下介绍。 如何删除要素类 4.6 要想删除一个要素类,那么必须先得到这个,在得到这个要素类的时候,我们要学习一个新的接口 IFeatureWorkspace。 4.6.1 IFeatureWorkspace 接口介绍 这个接口主要是用于管理基于矢量数据的,如表,,要素类,要素数据集等。他的主要方法和属性如下 内部文档,请勿外传 上图方法中的 OpenDataset,OpenTable,OpenFeatureClass 都是要传入一个相应的名称,如要打开一个名 称为 PointTest 的要素类,只需要在 OpenFeatureClass 中传入这个要素类的名称,代码如下: IWorkspaceFactory pWsFt = new AccessWorkspaceFactoryClass(); IWorkspace pWs = pWsFt.OpenFromFile(WsName, 0); IFeatureWorkspace pFWs = pWs as IFeatureWorkspace; IFeatureClass pFClass = pFWs.OpenFeatureClass("PointTest"); 如果是在 ArcMap 中,我们会切换到 Catalog 中然后进入相应的数据库,然后删除相应的要素类,这种操作 会让我们想到 FeatureClas 这个对象会提供删除的方法,其实不然,这个删除的方法是定义在 Dataset 这 个对象中。 private void button1_Click(object sender, EventArgs e) { string WsName = WsPath(); if( WsName !="") { IWorkspaceFactory pWsFt = new AccessWorkspaceFactoryClass(); IWorkspace pWs = pWsFt.OpenFromFile(WsName, 0); IFeatureWorkspace pFWs = pWs as IFeatureWorkspace; IFeatureClass pFClass = pFWs.OpenFeatureClass("PointTest"); IDataset pDatset = pFClass as IDataset; pDatset.Delete(); } } 删除前 内部文档,请勿外传 删除后 如何创建一个要素数据类 4.7 创建要素类用到了 IFeatureWorkspace.CreateFeatureClass 方法,在这个方法中有众多的参数,为了满足 这些参数,我们要学习和了解下面的接口. 4.7.1 IField,IFieldEdit,IFields,IFieldsEditI,GeometryDef, IGeometryDefEdit 接口 字段对应表中的一列,一个要素类必须有至少 2 个字段,而多个字段的集合就构成了字段集,在要素类中, 有一个特殊的字段,描述了空间对象,我们称之为几何字段,其中 GeometryDef 是用来设计几何字段的。 这个几何字段定义了要素类的类型,比如说我们要在 Catalog 创建一个点要素类,那么我们必须指定他的类 型为 Point,如下图: 内部文档,请勿外传 而上面这 6 个接口,其实是三类,以 Edit 结尾的接口是可写的,也就是说对字段,字段集合,以及几何字 段的编辑都是通过后者完成的。空间数据的一个重要属性就是参考系,参考系也是在 GeometryDef 中定义 的。 注意 在 NET 中,会遇到以“_2”结尾的属性,这些属性是可写的。 //定义一个几何字段,类型为点类型 ISpatialReference pSpatialReference = axMapControl1.ActiveView.FocusMap.SpatialReference; IGeometryDefEdit pGeoDef = new GeometryDefClass(); IGeometryDefEdit pGeoDefEdit = pGeoDef as IGeometryDefEdit; pGeoDefEdit.GeometryType_2 = esriGeometryType.esriGeometryPoint; pGeoDefEdit.SpatialReference_2 = pSpatialReference; //定义一个字段集合对象 IFields pFields = new FieldsClass(); IFieldsEdit pFieldsEdit = (IFieldsEdit)pFields; //定义单个的字段 IField pField = new FieldClass(); IFieldEdit pFieldEdit = (IFieldEdit)pField; pFieldEdit.Name_2 = "SHAPE"; pFieldEdit.Type_2 = esriFieldType.esriFieldTypeGeometry; pFieldsEdit.AddField(pField); pFieldEdit.GeometryDef_2 = pGeoDef; 内部文档,请勿外传 //定义单个的字段,并添加到字段集合中 pField = new FieldClass(); pFieldEdit = (IFieldEdit)pField; pFieldEdit.Name_2 = "STCD"; pFieldEdit.Type_2 = esriFieldType.esriFieldTypeString; pFieldsEdit.AddField(pField); //定义单个的字段,并添加到字段集合中 pField = new FieldClass(); pFieldEdit = (IFieldEdit)pField; pFieldEdit.Name_2 = "SLM10"; pFieldEdit.Type_2 = esriFieldType.esriFieldTypeString; pFieldsEdit.AddField(pField); //定义单个的字段,并添加到字段集合中 pField = new FieldClass(); pFieldEdit = (IFieldEdit)pField; pFieldEdit.Name_2 = "SLM20"; pFieldEdit.Type_2 = esriFieldType.esriFieldTypeString; pFieldsEdit.AddField(pField); //定义单个的字段,并添加到字段集合中 pField = new FieldClass(); pFieldEdit = (IFieldEdit)pField; pFieldEdit.Name_2 = "SLM40"; pFieldEdit.Type_2 = esriFieldType.esriFieldTypeString; pFieldsEdit.AddField(pField); IWorkspaceFactory pFtWsFct = new AccessWorkspaceFactory(); IFeatureWorkspace pWs = pFtWsFct.OpenFromFile(@"E:\arcgis\Engine\s.mdb", 0) as IFeatureWorkspace; IFeatureClass pFtClass = pWs.CreateFeatureClass("Test", pFields, null, null, esriFeatureType.esriFTSimple, "SHAPE", null) 结果如下: 内部文档,请勿外传 如何改变字段的别名 4.8 public void ChangeFieldAliasName(ITable pTable, string pOriFieldName, string pDesFieldName) { IClassSchemaEdit pClassSchemaEdit = (IClassSchemaEdit)pTable; //给对象加上锁 ISchemaLock pSchemaLock = (ISchemaLock)pTable; pSchemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock); if (pTable.FindField(pOriFieldName) != -1) { pClassSchemaEdit.AlterFieldAliasName(pOriFieldName, pDesFieldName); pSchemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock); } else { return; } } 内部文档,请勿外传 域 子类 void CreateDomain(IWorkspace pWorkspace) { IWorkspaceDomains pWorkspaceDomains = (IWorkspaceDomains)pWorkspace; ICodedValueDomain pCodedValueDomain = new CodedValueDomainClass(); pCodedValueDomain.AddCode("RES", "Residential"); pCodedValueDomain.AddCode("COM", "Commercial"); pCodedValueDomain.AddCode("IND", "Industrial"); IDomain pDomain = (IDomain)pCodedValueDomain; pDomain.Name = "Building Types"; pDomain.FieldType = esriFieldType.esriFieldTypeString; pDomain.SplitPolicy = esriSplitPolicyType.esriSPTDuplicate; pDomain.MergePolicy = esriMergePolicyType.esriMPTDefaultValue; pWorkspaceDomains.AddDomain(pDomain); } public void AssignDomainToFieldWithSubtypes(IFeatureClass pFeatureClass) { IDataset pDataset = (IDataset)pFeatureClass; IWorkspace pWorkspace = pDataset.Workspace; 内部文档,请勿外传 IWorkspaceDomains pWorkspaceDomains = (IWorkspaceDomains)pWorkspace; IDomain pDistributionDiamDomain = pWorkspaceDomains.get_DomainByName("DistDiam"); ISubtypes pSubtypes = (ISubtypes)pFeatureClass; pSubtypes.set_Domain(1, "SIZE_ONE", pDistributionDiamDomain); } public void AddPipeSubtypes(IFeatureClass pFeatureClass) { ISubtypes pSubtypes = (ISubtypes)pFeatureClass; pSubtypes.SubtypeFieldName = "PipeType"; pSubtypes.AddSubtype(1, "Primary"); pSubtypes.AddSubtype(2, "Secondary"); pSubtypes.DefaultSubtypeCode = 1; } 栅格数据管理 4.9 4.9.1 栅格数据介绍 在空间数据库中,Esri 对栅格数据提供了三种模型,栅格数据集,栅格目录,以及 ArcGIS 10 中新推 出的镶嵌数据集。 栅格数据集也就是我们经常所得 jpg,tif 文件等,ArcGIS 将这些栅格数据抽象为 RasterDataset,栅格数 据集就代表了磁盘中的一个文件,它由一个或多个波段组成。在使用栅格数据集的时候,栅格数据会被转 换成 IMG 文件存储在数据库中。 我们可以对栅格数据集进行一些操作,如改变空间参考,建立影像金字塔等。栅格目录,正如其名字 一样是一个目录,跟书的目录相似,它记录了一个或者多个栅格数据集,每一个栅格数据集都作为一条记 内部文档,请勿外传 录存储在栅格目录中。栅格目录对栅格数据集的管理有两种方式,托管和非托管.托管方式的时候,栅格数 据是存储在数据库中,非托管的时候,栅格目录记录了栅格数据集的路径,也就是栅格数据并没有存储在数 据库中。当我们删除一条记录的时候,对我们的栅格数据没有任何影响。 镶嵌数据集可以说是栅格数据集和栅格目录的混合技术,它的存储方式和栅格目录类似,但是在使用 的时候和普通的栅格数据集是一样的,镶嵌数据集用于管理和发布海量多分辨率,多传感器影像,对栅格 数据提供了动态镶嵌和实时处理的功能。 对文件地理数据库、个人地理数据库和 ArcSDE 地理数据库中的栅格存储加以比较 栅格存 储特征 文件地理数据库 个人地理数据库 ArcSDE 地理数 据库 大小限 制 每个栅格数据集或栅格 目录的 大小限 制为 1 TB 每个地理数据库限制为 2 千兆字节 (GB)(此值为表的大小限制,而不是 栅格数据集的大小限制。) 无限制;取决 于 DBMS 限制 栅格数 据集文 件格式 文件地理数据库栅格数 据集 ERDAS IMAGINE、JPEG 或 JPEG 2000 ArcSDE 栅格 数据集 存储  栅格数据集:托管  Mosaic 数据集:非 托管  栅格目录:托管或 非托管  作为属性的栅格: 托管或非托管  栅格数据集:托管  Mosaic 数据集:非托管  栅格目录:托管或非托管  作为属性的栅格:托管或非托管  托管  镶嵌数据 集:非托 管 存储在文件系统中 存储在 Microsoft Access 中 存储在 RDBMS 中 压缩 LZ77、JPEG、JPEG 2000 或无 LZ77、JPEG、JPEG 2000 或无 LZ77、JPEG、 JPEG 2000 或 无 金字塔 支持部分构建金字塔 重新构建整个金字塔 支持部分构建 金字塔 镶嵌 可以在镶嵌时追加栅格 每次镶嵌至栅格数据集时都将重写 可以在镶嵌时 内部文档,请勿外传 数据集 一个新的数据集 追加栅格数据 集 更新 允许增量更新 允许增量更新 用户数 单个用户和较小的工作 组;多位读取者和一位 写入者 单个用户和较小的工作组;多位读取 者和一位写入者 多用户;许多 用户和许多写 入者 4.9.2 打开栅格数据 要打开一个栅格数据,这个有点类似我们打开 FeatureClass 一样,先要获取工作空间,只不过我们过于要素 类的时候需要 IFeatureWorkspace,而栅格数据则需要 IRasterWorkspace,示例如下: IRasterWorkspace GetRasterWorkspace(string pWsName) { try { IWorkspaceFactory pWorkFact = new RasterWorkspaceFactoryClass(); return pWorkFact.OpenFromFile(pWsName, 0) as IRasterWorkspace; } catch (Exception ex) 内部文档,请勿外传 { return null; } } IRasterDataset OpenFileRasterDataset(string pFolderName, string pFileName) { IRasterWorkspace pRasterWorkspace = GetRasterWorkspace(pFolderName); IRasterDataset pRasterDataset = pRasterWorkspace.OpenRasterDataset(pFileName); return pRasterDataset; } 注意当访问的栅格数据是存在 SDE 中,文件数据库中或者个人数据库中,应该使用 IRasterWorkspaceEx 接口 IRasterWorkspace 与 IRasterWorkspaceEx 的区别 1) IRasterWorkspace 主要是用来读取以文件格式存储在本地的栅格数据 2) IRasteWorkspaceEx 接口主要是用来读取 GeoDatabase 中的栅格数据集和栅格目录,如下示例: IRasterDataset OpenGDBRasterDataset(IRasterWorkspaceEx pRasterWorkspaceEx, string pDatasetName) { //打开存放在数据库中的栅格数据 return pRasterWorkspaceEx.OpenRasterDataset(pDatasetName); } 4.9.3 打开栅格目录中的一个数据 IRasterDataset GetRasterCatalogItem(IRasterCatalog pCatalog, int pObjectID) { //栅格目录继承了IFeatureClass IFeatureClass pFeatureClass = (IFeatureClass)pCatalog; IRasterCatalogItem pRasterCatalogItem = (IRasterCatalogItem)pFeatureClass.GetFeature(pObjectID); return pRasterCatalogItem.RasterDataset; 内部文档,请勿外传 } 4.9.4 创建栅格数据集 public IRasterDataset CreateRasterDataset(string pRasterFolderPath, string pFileName,string pRasterType,ISpatialReference pSpr ) { IRasterWorkspace2 pRasterWs = GetRasterWorkspace(pRasterFolderPath) as IRasterWorkspace2; IPoint pPoint = new PointClass(); pPoint.PutCoords(15.0, 15.0); int pWidth = 300; int pHeight = 300; double xCell = 30; double yCell = 30; int NumBand = 1; IRasterDataset pRasterDataset = pRasterWs.CreateRasterDataset(pFileName, pRasterType, pPoint, pWidth, pHeight, xCell, yCell, NumBand, rstPixelType.PT_UCHAR, pSpr, true); IRasterBandCollection pRasterBands = (IRasterBandCollection)pRasterDataset; IRasterBand pRasterBand = pRasterBands.Item(0); IRasterProps pRasterProps = (IRasterProps)pRasterBand; pRasterProps.NoDataValue = 255; IRaster pRaster = pRasterDataset.CreateDefaultRaster(); IPnt pPnt = new PntClass(); pPnt.SetCoords(30, 30); IRaster2 pRaster2 = pRaster as IRaster2; 内部文档,请勿外传 IRasterEdit pRasterEdit = (IRasterEdit)pRaster2; IRasterCursor pRasterCursor = pRaster2.CreateCursorEx(pPnt); do { IPixelBlock3 pPixelblock = pRasterCursor.PixelBlock as IPixelBlock3; System.Array pixels = (System.Array)pPixelblock.get_PixelData(0); for (int i = 0; i < pPixelblock.Width; i++) for (int j = 0; j < pPixelblock.Height; j++) if (i == j) pixels.SetValue(Convert.ToByte(255), i, j); else pixels.SetValue(Convert.ToByte((i * j + 30) / 255), i, j); pPixelblock.set_PixelData(0, (System.Array)pixels); IPnt pUpperLeft = pRasterCursor.TopLeft; pRasterEdit.Write(pUpperLeft, (IPixelBlock)pPixelblock); } while (pRasterCursor.Next()); System.Runtime.InteropServices.Marshal.ReleaseComObject(pRasterEdit); return pRasterDataset; } 内部文档,请勿外传 4.9.5 打开镶嵌数据集 IMosaicDataset GetMosaicDataset(string pFGDBPath,string pMDame) { IWorkspaceFactory pWorkspaceFactory = new FileGDBWorkspaceFactoryClass(); IWorkspace pFgdbWorkspace = pWorkspaceFactory.OpenFromFile(pFGDBPath, 0); IMosaicWorkspaceExtensionHelper pMosaicExentionHelper = new MosaicWorkspaceExtensionHelperClass(); IMosaicWorkspaceExtension pMosaicExtention = pMosaicExentionHelper.FindExtension(pFgdbWorkspace); return pMosaicExtention.OpenMosaicDataset(pMDame); } 内部文档,请勿外传 4.9.6 创建一个镶嵌数据集 /// /// 创建镶嵌数据集 /// /// /// /// /// IMosaicDataset CreateMosaicDataset(string pFGDBPath, string pMDame, ISpatialReference pSrs ) { IWorkspaceFactory pWorkspaceFactory = new FileGDBWorkspaceFactory(); IWorkspace pFgdbWorkspace = pWorkspaceFactory.OpenFromFile(pFGDBPath, 0); ICreateMosaicDatasetParameters pCreationPars = new CreateMosaicDatasetParametersClass(); pCreationPars.BandCount = 3; pCreationPars.PixelType = rstPixelType.PT_UCHAR; IMosaicWorkspaceExtensionHelper pMosaicExentionHelper = new MosaicWorkspaceExtensionHelperClass(); IMosaicWorkspaceExtension pMosaicExtention = pMosaicExentionHelper.FindExtension(pFgdbWorkspace); return pMosaicExtention.CreateMosaicDataset(pMDame, pSrs, pCreationPars, ""); } 和查询相关的对象和接口 4.10 查询在 GIS 领域应该是一个很频繁的操作,在 GIS 中除了具有属性查询(和其他关系型数据库的查询类似), 还提供了空间查询。在介绍查询的时候,让我们先了解下面的对象。 内部文档,请勿外传 4.10.1 Table 对象 Table 是不含有空间信息的一张二维表,它主要实现了 ITable 接口。在这张二维表中,每一行称之为 Row (IRow),ITable 接口 定义了对这张二维表行的插入,更新,查询,以及删除等操作。 独立表(standalone table):就是一个单独的不含空间信息的表也就是只能在 ArcMap 中 Table of Contents 的 Source 选项卡中看到的。 4.10.2 对象类 对象类是在 Table 的基础上扩展起来的,因此在外观上来看,它也是一个二维表,也是用来存储非空间数 据,它与 Table 的区别在于它的一行是一个 Object(对象),尽管在形式上也是一条记录,但它是具有属 性和行为的一个对象,而非简单的记录了。 4.10.3 FeatureClass 对象 要素类是存储在工作空间中的一种数据组织方式,要素类是在对象类的基础上的进一步扩展,包含了现实 世界中的空间实体。要素类由要素组成(Feature),要素对应要素类中的一行,要素相当于空间对象 (Geometry )+相应的属性信息。IFeatureClass 定义了对要素的查询,更新,删除等操作。关于 ROW,Table, 对象类,Feature,要素类,我们可以从下面的图上得到启示: FeatureClass 对 象 实 现 了 IFeatureClass 接 口 , IFeatureClass 对 查 询 定 义 了 两 个 方 法 内部文档,请勿外传 IFeatureClass.Search 和 IFeatureClass.Select。 Search 方法需要传入两个参数,一个是过滤器;另外一个是布尔值,用于说明放回的要素游标是否被回收, 一般的,如果仅仅是为了读取数据,那么这个参数应该是 true,如果要对选择出来的要素更新,那么这个 参数应该设置为 false。那么这两个参数到底有什么意义,我们从下面的代码中看一下。 4.10.3.1 false 和 true 参数的差别 我们定义一个 Search 函数,通过传 false 和 true 来对这两个参数进行说明,代码如下: void Search(IFeatureClass _pFeatureClass,bool _Bool) { IFeature pFt1, pFt2; IFeatureCursor pFtCursor; if (_Bool == false) { pFtCursor = _pFeatureClass.Search(null, _Bool); pFt1 = pFtCursor.NextFeature(); while (pFt1 != null) { pFt2 = pFtCursor.NextFeature(); if (pFt1 == pFt2) { MessageBox.Show("Recycling 参数是 false"); } pFt1 = pFtCursor.NextFeature(); } } else { pFtCursor = _pFeatureClass.Search(null, _Bool); pFt1 = pFtCursor.NextFeature(); while (pFt1 != null) { pFt2 = pFtCursor.NextFeature(); 内部文档,请勿外传 if (pFt1 == pFt2) { MessageBox.Show("Recycling 参数是true"); } pFt1 = pFtCursor.NextFeature(); } } } 当 recycling 为 true 的时候,我们会看到程序执行到 MessageBox.Show("Recycling 参数是 true"),如 下图: 等号成立,说明了当为 true,程序返回的是同一个 Feature 的引用,查询后的要素共享同一内存,说名 Next 之后前一个游标所占的内存被回收了,当为 false 的时候,等号不成立。说明系统给每一个要素分配了一个 游标。 4.10.3.2 Search 和 Select 方法的比较 Search 返回游标,Select 返回选择集 游标:必须遍历游标才能得到所有的结果,不必太关注内存 选择集: 查询后既可得到,但是通常只保留 OID 字段,数据量打的 内部文档,请勿外传 时候要考虑内存压力 4.10.3.3Cursor 和 FeatureCursor 对象 Cursor 中文‘游标“,它本质上是一个指向数据的指针,自身并不包含数据。游标有三类,查询游标,插 入游标和更新游标,每一中游标都是又其相应的方法得到,如查询游标是由 ITable.Search 方法得到。游 标是 GIS 中使用频率很高的,凡是和数据的查询,更新,删除等都跟他有关。ICursor 定义了对游标的操 作。当我们通过 ITable.Search 对数据进行查询,要获取具体 Row 的信息的时候,要通过 ICursor.NextRow 方法向前遍历,游标是不能后退的。游标是和 Table 相对应的。IFeatureCursor 继承了 ICursor, IFeatureCursor 是和要素类相对应。 4.10.3.4QueryFilter 与 SpatialFilter 对象 在 ArcGIS Engine 中进行查询或者选择,都需要传给一个查找条件,或者过滤条件,这个条件就相当于一 般的 SQL 语句中的 Where 语句,如 Select * from 用户 where 性别=’女’;我们知道 GIS 不仅仅有属性 查询,还有一般关系型数据库不具有的空间查询。而 QueryFilter 对象和 SpatialFilter 对象分别对应了 ArcGIS Engine 中的属性查询和空间查询。 IQueryFilter 被两个类实现 QueryFilterClass 和 SpatialFilterClass,前者是针对属性查询的,后者是 针对空间查询的。 在介绍下这些对象后,我们现在来做一个高亮显示的查询操作。 4.10.4 IFeatureSelection 接口 IFeatureSelection 接口负责管理一个图层中的要素选择集的方法和属性。 IFeatureSelection 接口的 Add 方法可以把本图层中的一个要素添加到图层的选择集中;SelectFeatures 方法则利用过滤器对象将符合条 件的要素放入到图层的选择集中。使用 IFeatureSelection 接口可以实现要素的高亮显示。在 ArcGIS Engine 中有很多类实现了这个接口,如下图: 内部文档,请勿外传 4.10.4.1使用 IFeatureSelection 接口高亮显示 在介绍 IMap 接口那一节,我们用 IMap 的 IMap.SelectFeature 方法实现了对查询的要素高亮显示,现在我 们用 IFeatureSelection 接口实现查询高亮显示 IMap pMap = axMapControl1.Map; IFeatureLayer pFeaturelayer = GetLayer(pMap, "Roads") as IFeatureLayer; IFeatureSelection pFeatureSelection = pFeaturelayer as IFeatureSelection; IQueryFilter pQuery = new QueryFilterClass(); pQuery.WhereClause = "TYPE=" +"'paved'"; pFeatureSelection.SelectFeatures(pQuery,esriSelectionResultEnum.esriSelectionResultNew,false ); axMapControl1.ActiveView.Refresh(); 内部文档,请勿外传 其中 GetLayer 函数是我们写的一个根据图层的名称获取图层的方法,代码如下图: private ILayer GetLayer(IMap pMap, string LayerName) { IEnumLayer pEnunLayer; pEnunLayer = pMap.get_Layers(null, false); pEnunLayer.Reset(); ILayer pRetureLayer; pRetureLayer = pEnunLayer.Next(); while (pRetureLayer != null) { if (pRetureLayer.Name == LayerName) { break; } pRetureLayer = pEnunLayer.Next(); } return pRetureLayer; } 内部文档,请勿外传 4.10.5 空间查询 空间查询可以说是两步过滤,属性过滤和空间过滤,既然有空间过滤, 空 间 查 询 体 现 在 ISpatialFilter.Geometry 和 ISpatialFilter.SpatialRel 上,那么这个接口的 Geometry 和 spatialrel 属 性是必须的。众所周知 ArcMap 的空间查询是非常丰富的,而 ArcGIS Engine 可以毫无保留的实现 ArcMap 所能提供的全部空间查询,对于 ArcGIS Engine 来说这些空间查询只是 ISpatialFilter 的参数,而这些参 数都是常量,如下表所示: 示例:查询矩形范围内的点要素: public ESRI.ArcGIS.Geodatabase.IFeatureCursor GetAllFeaturesFromPointSearchInGeoFeatureLayer(ESRI.ArcGIS.Geometry.IEnvelope pEnvelope, IPoint pPoint, IFeatureClass pFeatureClass) { if (pPoint == null || pFeatureClass == null) { return null; } //ITopologicalOperator pTopo = pPoint as ITopologicalOperator; //IGeometry pGeo = pTopo.Buffer(pSearchTolerance); System.String pShapeFieldName = pFeatureClass.ShapeFieldName; ESRI.ArcGIS.Geodatabase.ISpatialFilter pSpatialFilter = new ESRI.ArcGIS.Geodatabase.SpatialFilterClass(); pSpatialFilter.Geometry = pEnvelope; pSpatialFilter.SpatialRel = ESRI.ArcGIS.Geodatabase.esriSpatialRelEnum.esriSpatialRelEnvelopeIntersects; pSpatialFilter.GeometryField = pShapeFieldName; 内部文档,请勿外传 ESRI.ArcGIS.Geodatabase.IFeatureCursor pFeatureCursor = pFeatureClass.Search(pSpatialFilter, false); return pFeatureCursor; } IMap map = this.axMapControl1.Map; ; ISelection selection = map.FeatureSelection; IEnumFeatureSetup pEnumFeatureSetup = (IEnumFeatureSetup)selection; pEnumFeatureSetup.AllFields=true; IEnumFeature pEnumFeature = (IEnumFeature)selection; IFeature feature = pEnumFeature.Next(); while (feature != null) { string a = Convert.ToString(feature.get_Value(feature.Fields.FindField("dfd"))); feature = pEnumFeature.Next(); } 附件功能 4.11 附件功能是 ArcGIS 10 的一个新功能,ArcGIS 10 引入了要素类附件,这样就能够灵活地管理与要素相关 的附加信息。我们可以向单个要素添加文件作为附件,它们可以是图像、PDF、文本文档或任意其他文件类 型。例如,如果用某个要素表示建筑物,则可以使用附件来添加多张从不同角度拍摄的建筑物照片,以及 包含建筑物契约和税务信息的 PDF 文件。 附件与超链接类似,但允许多个文件与一个要素相关联、将关联的文件存储在地理数据库中并以更多方式 访问这些文件。可通过识别 窗口、属性 窗口(编辑时)、属性表窗口以及 HTML 弹出窗口来查看这些附 件。 内部文档,请勿外传 4.11.1 和附件相关的接口 ITableAttachments 该接口用于控制一个要素类的附件,为一个要素类开启附件功能等,因此该接口被要素类(FeatureClass 实现) IAttachmentManager 从该接口的字面意思就可以看出,该接口是用来管理附件的,相当于一个关系类,通过该接口将附件添加 到和要素相关联的那个关系类中 IAttachment 该接口表示一个附件,附件对象实现。 IMemoryBlobStream ArcGIS 实际上是将附件以二进制存入到数据库中的,该接口用于控制附件的读取 实例:开启附件功能,并添加一个附件。 private void CreateAttachTable(IFeatureClass pFeatureClass,int pID,string pFilePath,string pFileType) { //要素表是否有附件表,数据库只能是10版本的 ITableAttachments pTableAtt = pFeatureClass as ITableAttachments; if (pTableAtt.HasAttachments == false) { pTableAtt.AddAttachments(); } //获取附件管理器 IAttachmentManager pAttachmentManager = pTableAtt.AttachmentManager; //用二进制流读取数据 IMemoryBlobStream pMemoryBlobStream = new MemoryBlobStreamClass(); pMemoryBlobStream.LoadFromFile(pFilePath); //创建一个附件 IAttachment pAttachment = new AttachmentClass(); pAttachment.ContentType=pFileType; pAttachment.Name = System.IO.Path.GetFileName(pFilePath); pAttachment.Data = pMemoryBlobStream; 内部文档,请勿外传 //添加到表中 pAttachmentManager.AddAttachment(pID, pAttachment); } 综合例子空间查询和创建 Table 4.12 内部文档,请勿外传 创建符合要求的表 /// /// 输出结果为一个张表,这张表有3个字段,其中面ID为面要素数据的FID /// 个数用于记录这个面包含的点的个数 /// /// /// /// public ITable CreateTable(string _TablePath, string _TableName) { IWorkspaceFactory pWks = new ShapefileWorkspaceFactoryClass(); IFeatureWorkspace pFwk = pWks.OpenFromFile(_TablePath, 0) as IFeatureWorkspace; //用于记录面中的ID; IField pFieldID = new FieldClass(); IFieldEdit pFieldIID = pFieldID as IFieldEdit; pFieldIID.Type_2 = esriFieldType.esriFieldTypeInteger; pFieldIID.Name_2 = "面ID"; //用于记录个数的; IField pFieldCount = new FieldClass(); IFieldEdit pFieldICount = pFieldCount as IFieldEdit; 内部文档,请勿外传 pFieldICount.Type_2 = esriFieldType.esriFieldTypeInteger; pFieldICount.Name_2 = "个数"; //用于添加表中的必要字段 ESRI.ArcGIS.Geodatabase.IObjectClassDescription objectClassDescription = new ESRI.ArcGIS.Geodatabase.ObjectClassDescriptionClass(); IFields pTableFields = objectClassDescription.RequiredFields; IFieldsEdit pTableFieldsEdit = pTableFields as IFieldsEdit; pTableFieldsEdit.AddField(pFieldID); pTableFieldsEdit.AddField(pFieldCount); ITable pTable = pFwk.CreateTable(_TableName, pTableFields, null, null, ""); return pTable; } /// /// 第一个参数为面数据,第二个参数为点数据,第三个为输出的表 /// /// /// /// public void StatisticPointCount(IFeatureClass _pPolygonFClass, IFeatureClass _pPointFClass, ITable _pTable) { IFeatureCursor pPolyCursor = _pPolygonFClass.Search(null, false); IFeature pPolyFeature = pPolyCursor.NextFeature(); while (pPolyFeature != null) { IGeometry pPolGeo = pPolyFeature.Shape; int Count = 0; 内部文档,请勿外传 ISpatialFilter spatialFilter = new SpatialFilterClass(); spatialFilter.Geometry = pPolGeo; spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelContains; IFeatureCursor pPointCur = _pPointFClass.Search(spatialFilter, false); if (pPointCur != null) { IFeature pPointFeature = pPointCur.NextFeature(); while (pPointFeature != null) { pPointFeature = pPointCur.NextFeature(); Count++; } } if (Count != 0) { IRow pRow = _pTable.CreateRow(); pRow.set_Value(1, pPolyFeature.get_Value(0)); pRow.set_Value(2, Count); pRow.Store(); } pPolyFeature = pPolyCursor.NextFeature(); } } 效果如下: 内部文档,请勿外传 上面这个例子只是用了空间过滤,没有用到属性过滤,我们将上面的代码稍微改动下,加上一句 结果对照: 内部文档,请勿外传 数据转换 4.13 数据转换主要涉及复制和转换数据出入 Geodatabases 的对象。 两个主要的数据转换对象是 FeatureDataConverter 和 GeoDBDataTransfer。 ArcCatalog 用户应该熟悉 FeatureDataConverter;导入(import)功能广泛使用该 coclass。 ArcCatalog 用户也应该熟悉 GeoDBDataTransfer;在 Geodatabases 之间复制数据集的 copy/paste 功能使用该 class。 还有其它一些对象和接口支持 FeatureDataConverter 和 GeoDBDataTransfer,并执行以下 功能: 使用 IFieldChecker 检查字段名称中的潜在问题。 检查使用 IEnumInvalidObject 的转换过程中被拒绝的数据。 使终端用户了解 IFeatureProgress /// /// /// /// /// /// /// 内部文档,请勿外传 /// /// public void ConvertFeatureClass(IWorkspaceFactory _pSWorkspaceFactory,String _pSWs,string _pSName, IWorkspaceFactory _pTWorkspaceFactory ,String _pTWs,string _pTName ) { // Open the source and target workspaces. IWorkspace pSWorkspace = _pSWorkspaceFactory.OpenFromFile(_pSWs, 0); IWorkspace pTWorkspace = _pTWorkspaceFactory.OpenFromFile(_pTWs, 0); IFeatureWorkspace pFtWs = pSWorkspace as IFeatureWorkspace; IFeatureClass pSourceFeatureClass = pFtWs.OpenFeatureClass(_pSName); IDataset pSDataset = pSourceFeatureClass as IDataset; IFeatureClassName pSourceFeatureClassName = pSDataset.FullName as IFeatureClassName; IDataset pTDataset = (IDataset)pTWorkspace; IName pTDatasetName = pTDataset.FullName; IWorkspaceName pTargetWorkspaceName = (IWorkspaceName)pTDatasetName; IFeatureClassName pTargetFeatureClassName = new FeatureClassNameClass(); IDatasetName pTargetDatasetName = (IDatasetName)pTargetFeatureClassName; pTargetDatasetName.Name = _pTName; pTargetDatasetName.WorkspaceName = pTargetWorkspaceName; // 创建字段检查对象 IFieldChecker pFieldChecker = new FieldCheckerClass(); IFields sourceFields = pSourceFeatureClass.Fields; IFields pTargetFields = null; 内部文档,请勿外传 IEnumFieldError pEnumFieldError = null; pFieldChecker.InputWorkspace = pSWorkspace; pFieldChecker.ValidateWorkspace = pTWorkspace; // 验证字段 pFieldChecker.Validate(sourceFields, out pEnumFieldError, out pTargetFields); if (pEnumFieldError != null) { // Handle the errors in a way appropriate to your application. Console.WriteLine("Errors were encountered during field validation."); } String pShapeFieldName = pSourceFeatureClass.ShapeFieldName; int pFieldIndex = pSourceFeatureClass.FindField(pShapeFieldName); IField pShapeField = sourceFields.get_Field(pFieldIndex); IGeometryDef pTargetGeometryDef = pShapeField.GeometryDef; // 创建要素转换对象 IFeatureDataConverter pFDConverter = new FeatureDataConverterClass(); IEnumInvalidObject pEnumInvalidObject = pFDConverter.ConvertFeatureClass (pSourceFeatureClassName, null, null, pTargetFeatureClassName, pTargetGeometryDef, pTargetFields, "", 1000, 0); // Check for errors. IInvalidObjectInfo pInvalidInfo = null; pEnumInvalidObject.Reset(); while ((pInvalidInfo = pEnumInvalidObject.Next()) != null) { // Handle the errors in a way appropriate to the application. Console.WriteLine("Errors occurred for the following feature: {0}", pInvalidInfo.InvalidObjectID); } } 内部文档,请勿外传 IWorkspaceFactory pSwf = new AccessWorkspaceFactoryClass(); IWorkspaceFactory pDwf = new AccessWorkspaceFactoryClass(); ConvertFeatureClass(pSwf,"E:\\s.mdb","s",pDwf,"E:\\d.mdb","d"); Querylayer 4.14 查询图层是通过 SQL 查询定义的图层或独立表。通过查询图层可将空间信息和非空间信息都存储在 DBMS 中,从而使这些信息可以轻松地整合到 ArcMap 中的各 GIS 项目。由于查询图层将通过 SQL 对数据库表 和视图进行直接查询,所以查询图层所使用的空间信息不需要位于地理数据库中。 在 ArcMap 中进行操作时,可以通过定义 SQL 查询来创建查询图层。然后针对数据库中的表和视图运行查 询,并将结果集以图层或独立表的形式(取决于查询本身)添加到 ArcMap 中。 每次在 ArcMap 中显示或使用该图层时都将执行该查询。这样,无需生成数据的副本或快照便可显示最新 信息,这尤其适用于处理频繁更改的动态信息。查询图层功能适用于 ArcGIS 支持的所有 DBMS。 查询图层允许 ArcMap 整合地理数据库和 DBMS 中的数据。因此,无论信息存储的位置和方式如何,查询 图层都可以快速地将空间信息和非空间信息整合到 GIS 项目。 使用查询图层快速浏览 4.15  通过对 DBMS 中的表和视图定义查询,ArcMap 用户可将“查询图层”添加到地图。  查询图层类似于任何其他要素图层或单独表,所以这些图层可用于作为地理处理工具的输入来 显示数据,或使用开发人员 API 通过编程方式进行访问。  创建“查询图层”后,它可另存为图层文件 (.lyr) 或用于创建图层包 (.lpk)。这样可以很容易 地与其他应用程序、地图文档和其他用户共享“查询图层”。 ArcMap 中的所有图层都需要唯一标识符。因此,查询图层也必须含有唯一标识符。 通常,唯一标识符字段属于 ObjectID 属性,地理数据库中所有对象均应具有该属性。但是,由于 查询图层也可以使用未存储在地理数据库中的数据创建,因此各查询图层的字段集中未必都具有 ObjectID 字段。因此,有必要指定将哪个字段或哪组字段用于在 ArcGIS 中生成唯一标识符。 默认情况下,ArcGIS 会在验证时将在结果集中找到的第一个非空字段设置为唯一标识符字段。该值通常为 内部文档,请勿外传 适宜用作唯一标识符字段的值,但您也可以通过在唯一标识符字段列表中选择其他字段来更改此属性。 仅某些字段类型可用作唯一标识符。这些字段类型包括整型、字符串、GUID 和日期。如果指定的是单个整 型字段,则 ArcGIS 将只会直接使用该字段中的值识别从查询图层返回的所有要素和行。但是,如果将单 个字符串字段或一组字段用作唯一标识符,则 ArcGIS 必须将这些唯一值映射为一个整数。在系统需要使 用 ObjectID 属性的任何时候(例如,创建地图选择内容或打开属性表时)均可在 ArcGIS 中完成此操作。 由于唯一标识符字段中的值是识别 ArcGIS 中行或要素对象的唯一值,因此,该字段中的值必须始终唯一 且不可为空。您必须确保此字段中的值满足此要求。ArcGIS 并不强制要求查询图层的唯一标识符字段中的 值必须唯一。但如果遇到不唯一的值,ArcGIS 中某些元素的行为将无法预测。 您可以在唯一标识符列表中选择和取消选择字段。如果选择了多个字段,则这些字段中的值将作为键用于 生成唯一整数值,生成字段的名称将始终为 ESRI_OID,除非已存在具有该名称的字段。 在 ArcGIS Engine 中要使用查询图层,我们要了解一个接口 ISqlWorkspace,从这个接口的名称也容易看 出,这个接口可以和 SQL 打交道,这正是 QueryLayer 的一个特点。在帮助文件中我们可以获得 ISqlWorkspace 的详细信息,如下图: 内部文档,请勿外传 内部文档,请勿外传 内部文档,请勿外传 内部文档,请勿外传 ISqlWorkspace.GetTables()返回 IStringArray 类型的变量,用这 个方法我们可以获取数据库中所有的表的名称。 ISqlWorkspace.OpenQueryCursor()这个方法通过传入一个过滤语 句,返回一个游标; ISqlWorkspace.OpenQueryClass()返回通过过滤条件返回 ITable 类型的对象。 ISqlWorkspace 被 SqlWorkspaceClass 实现,而 SqlWorkspaceClass 同时实现了 IWorkspace 接口。那也就意味着 ISqlWorkspace 的使用 和 IWorksapce 的使用是类似的,我们可以按照以下步骤来执行一个 QueryLayer。 1) 获取 SqlWorkspaceFactory 2) 获取 SqlWorkspace 3) 构造查询语句 4) 执行查询 5) 获取结果 public IFeatureLayer OracleQueryLayer() { // 创建SqlWorkspaceFactory的对象 Type pFactoryType = 内部文档,请勿外传 Type.GetTypeFromProgID("esriDataSourcesGDB.SqlWorkspaceFactory"); IWorkspaceFactory pWorkspaceFactory = (IWorkspaceFactory)Activator.CreateInstance(pFactoryType); // 构造连接数据库的参数 IPropertySet pConnectionProps = new PropertySetClass(); pConnectionProps.SetProperty("dbclient", "Oracle11g"); pConnectionProps.SetProperty("serverinstance", "esri"); pConnectionProps.SetProperty("authentication_mode", "DBMS"); pConnectionProps.SetProperty("user", "scott"); pConnectionProps.SetProperty("password", "arcgis"); // 打开工作空间 IWorkspace workspace = pWorkspaceFactory.Open(pConnectionProps, 0); ISqlWorkspace pSQLWorkspace = workspace as ISqlWorkspace; //获取数据库中的所有表的名称 IStringArray pStringArray= pSQLWorkspace.GetTables(); for (int i = 0; i < pStringArray.Count; i++) { MessageBox.Show(pStringArray.get_Element(i)); } // 构造过滤条件 SELECT * FROM PointQueryLayer IQueryDescription queryDescription = pSQLWorkspace.GetQueryDescription("SELECT * FROM PointQueryLayer"); ITable pTable = pSQLWorkspace.OpenQueryClass("QueryLayerTest", queryDescription); IFeatureLayer pFeatureLayer = new FeatureLayerClass(); pFeatureLayer.FeatureClass = pTable as IFeatureClass; return pFeatureLayer; 内部文档,请勿外传 } 5 五.几何对象和空间参考 Geometry 是 GIS 中使用最为广泛的对象集之一,用户在创建、删除、编辑和进行地理分析的时候,就是处 理一个包含几何形体的矢量对象;除了显示要素意外,控件对象选择,要素符号化,标注要素,编辑要素 都需要 Geometry 参与。在 ArcGIS Engine 中,几个对象都有严格的定义,比如我们所说的直线,多断线等, 于此同时 ArcGIS Engine 提供了而一个几何对象的模型图,如下: 内部文档,请勿外传 而在 ArcGIS Engine 中和这个模型对应的对象如下: 在这上面的模型图中,位于最上面的 Geomtry 是一个抽象的对象,因而在使用它的时候需要由其子类完成 实例,在 ArcGIS Engine 中 Geometry 类实现 IGometry 接口,而 IGeometry 接口定义了所有几何对象通用 的属性和方法,不如投影,获取空间参考等,IGeometry 接口的方法和属性如下: 内部文档,请勿外传 其中 IGeometry.Dimension 属性获取几何对象的拓扑唯度,如返回 0 就表示该几何对象为点对象或者多点 多线,1 表示该对象为多线,具体的可参看下图: IGeometry.Envelope 返回一个 IEnvelope 对象, Envelope 是所有几何对象的外接矩形,用于表示几何对 象的最小边框,所有的几何对象都有一个 Envelope 对象,IEnvelope 是 Envelope 对象的主要接口,通过 它可以获取几何对象的 XMax,XMin,YMax,YMin,Height,Width 属性,下图为不同几何对象的 Envelope: 内部文档,请勿外传 IGeometry.SpatialReference 用于返回该几何对象的空间参考信息。 IGeometry.Project 方法用于对该几何对象做参参考系的转换。 点和多点 5.1 世界的本质是物质,对于 GIS 来说,点就应该是矢量数据的本质,点生线,线生面„„,如此组合,构 成了 GIS 世界中的矢量空间。 Point 几何对象 Point 是一个 0 维的几何图形,具有 X,Y 坐标值,以及一些可选的属性:如高程值(Z 值),度量值(M 值) M 这个属性在线性参考和动态分段中经常用到和 ID 号,点对象用于描述精确定位的对象,例如一个电话亭 在一个城市的精确位置,如下图: 内部文档,请勿外传 同时,点对象还可以有 Z 和 M 两个可选属性,。以下代码演示如何创建一个 Point 对象: /// /// 获取点 /// /// /// /// private IPoint ConstructPoint(double x, double y) { IPoint pPoint = new PointClass(); pPoint.PutCoords(x, y); return pPoint; } MultiPoint 几何对象 MultiPoint 对象是一系列无序的点的群集,这些点具有相同的属性信息。例如可以用一个点集来表示整个 城市天然气调压站。如下图所示:一个 Multipoint 对象由6个 Point 对象组成。 以下代码片段演示如何构建 Multipoint 对象: private object pMissing = Type.Missing; 内部文档,请勿外传 public IGeometry GetMultipointGeometry() { const double MultipointPointCount = 25; IPointCollection pPointCollection = new MultipointClass(); for (int i = 0; i < MultipointPointCount; i++) { pPointCollection.AddPoint(GetPoint(), ref pMissing, ref pMissing); } return pPointCollection as IGeometry; } private IPoint GetPoint() { const double Min = -10; const double Max = 10; Random pRandom = new Random(); double x = Min + (Max - Min) * pRandom.NextDouble(); double y = Min + (Max - Min) * pRandom.NextDouble(); return ConstructPoint(x, y); } Segment,Path,Ring 和 Polyline 对象 5.2 Segment 几何对象 Segment 对象是一个有起点和终点的“线“,也就是说 Segement 只有两个点,至于两点之间的线是直 的,还是曲的,需要其余的参数定义。所以 Segment 是由起点,终点和参数三个方面决定的。Segment 有 4 个子类,它的 4 个子类(直线,圆弧,椭圆弧,贝赛尔曲线)如下图: ISegment 有两个很有用的方法如下图: 内部文档,请勿外传 这两个方法用于将该 Segment 进行分割成小的 Segement。 Path 几何对象 Path 是连续的 Segment 的集合,除了路径的第一个 Segment 和最后一个 Segment 外其余的 Segment 的起始 点都是前一个 Segment 的终止点,即 Path 对象的中的 Segment 不能出现分离,Path 可以是任意数的 Segment 子类的组合。 该 Path 对象有很多我们经常用到的方法,如平滑曲线,对曲线抽稀等操作,如下图: 内部文档,请勿外传 Ring 几何对象 Ring 是一个封闭的 Path 即起始和终止点有相同的坐标值,它有内部和外部属性。 Polyline 几何对象 Polyline 对象是由一个或多个相连或者不相连的 path 对象的有序集合,通常用来代表线状地物如道路, 河流,管线等等。该对象在 ArcGIS Engine 中 的 模 型 图 如 下 : 内部文档,请勿外传 在这个模型中,我们看到某些几何对象可以组合产生新的几何形体,如 polyline 又 path 构成,path 又可 以由 segement 组成,但是这并不意味着用户必须按照这种层次去构造 polyline,实际上 Point 集合直接构 成 Polyline,组成 Polyline 的这些路径既可以是连续的,也可以是不连续的,如下图: 内部文档,请勿外传 Polyline 是有序 path 组成的集合,可以拥有 M、Z 和 ID 属性值,Polyline 对象的 IPointCollection 接口 包含了所有的节点信息,IGeometryCollection 接口可以获取 polyline 的 paths,ISegmentCollection 接 口可以获取 polyline 的 segments。 一个 Polyline 对象必须满足以下准则: 1.组成 Polyline 对象的所有 Path 对象必须是有效的。 2.组成 Polyline 对象的所有 Path 对象不能重合,相交或自相交。 3.组成 Polyline 对象的多个 Path 对象可以连接与某一点,也可以分离。 4.Path 对象的长度不能为 0. IPolyline 是 Polyline 类的主要接口,IPolyline 的 Reshape 方法可以使用一个 Path 对象为一个 Polyline 对象整形,IPolyline 的 SimplifyNetwork 方法用于简化网络。 Polyline 对象可以使用 IGeometryCollection 接口添加 Path 对象的方法来创建,使用该接口需注意 以下情况: 1.每一个 Path 对象必须是有效的,或使用 IPath::Simplify 方法后有效。 2.由于 Polyline 是 Path 对象的有序集合,所以添加 Path 对象时必须注意顺序和方向。 3.为了保证 Polyline 是有效的,可以创建完 Polyline 对象后使用 ITopologicalOperator 接口的 Simplify 方法。 内部文档,请勿外传 下面代码片段演示了一个 Polyline 的构成: private object pMissing = Type.Missing; public IGeometry GetPolylineGeometry() { const double PathCount = 3; const double PathVertexCount = 3; IGeometryCollection pGeometryCollection = new PolylineClass(); for (int i = 0; i < PathCount; i++) { IPointCollection pPointCollection = new PathClass(); for (int j = 0; j < PathVertexCount; j++) { pPointCollection.AddPoint(GetPoint(), ref pMissing, ref pMissing); } pGeometryCollection.AddGeometry(pPointCollection as IGeometry, ref pMissing, ref pMissing); } return pGeometryCollection as IGeometry; } private IPoint GetPoint() { const double Min = -10; const double Max = 10; Random random = new Random(); double x = Min + (Max - Min) * random.NextDouble(); double y = Min + (Max - Min) * random.NextDouble(); return ConstructPoint(x, y); } Segment,Path,Ring 和 Polyline 的区别 在这四者当中 Segment 是最小的单位具体的构成路线可以分为两个条: 内部文档,请勿外传 Segment-Path-Ring(封闭的 Path) Segment-Path-Polyline 我们可以这样说 Segment 是 Path,只不过是这个 Path 由一个 Segment 组成,Ring 也是一种 Path,只不 过是一个起点和终点重合的 Path,至于 Polyline 那就很明显了,他们的区别可以从下图看出: Polygone 对象 5.3 Polylgon 对象是由一个或多个 Ring 对象的有序集合,它可以是由单个 Ring 对象构成,也可以使用多个 Ring 组成。Polygon 通常用来代表有面积的多边形矢量对象,如行政区,建筑物等。Polygone 的组成结构 可以看下图: 内部文档,请勿外传 从这个图上可以看出 Polygon 是由 Rings 构成,而 Ring 又是又 Segment 构成,但是这并不意味着用 户必须按照这种层次去构造 Polygon,实际上用 Point 的集合可以构成 Polygon 如下代码: /// /// 通过点构造面 /// /// /// public IPolygon CreatePolygonByPoints(IPointCollection pPointCollection) { IGeometryBridge2 pGeometryBridge2 = new GeometryEnvironmentClass(); IPointCollection4 pPolygon = new PolygonClass(); WKSPoint[] pWKSPoint = new WKSPoint[pPointCollection.PointCount]; for (int i = 0; i < pPointCollection.PointCount; i++) { pWKSPoint[i].X = pPointCollection.get_Point(i).X; 内部文档,请勿外传 pWKSPoint[i].Y = pPointCollection.get_Point(i).Y; } pGeometryBridge2.SetWKSPoints(pPolygon, ref pWKSPoint); IPolygon pPoly= pPolygon as IPolygon; pPoly.close(); return pPoly; } 组成 Polygon 的是 Ring,其中 Ring 可以分为 Outer Ring(外环)和 Inner Ring(内环)之分。外环和内 环都是有方向的,它们的区别是外环的方向是顺时针的,内环的方向是逆时针。如下图: Polygon 对象实现了一个接口 IArea,而该接口用来对 Poylgon 的中心,重心,以及面积进行访问,下面 片段用来获取 Polygoe 的面积: IArea pArea = pPolygon as IArea; Double S= pArea. Area Curve 对象几何对象 5.4 除去 Point,MultiPoint 和 Envelope 外,其他所有的几何体都可以看做是 Curve( 曲线)。 Line,Polyline,Polygon,CircularArc,BezierCurve,EllipticArc 和 CircularArc 都是曲线的一种,它们 都实现了 ICurve 接口。 ICurve 接口的 Length 属性用于返回一个 Curve 对象的长度。 ICurve 接口的 FromPoint 和 ToPoint 属性可以获得 Curve 对象的起止点。 内部文档,请勿外传 ICurve 接口的 Reverseorientation 方法可以改变一个 Curve 对象的节点次序即调动 Curve 对象的起 始点和终止点互相调换。 ICurve 接口的 IsClosed 属性则可以判断一个 Curve 对象起始点和终止点是否在一个位置上。 ICurve 接口的 GetSubcurve 方法可以复制一条 Curve 对象的特定部分,例如一条 10 千米公路的 Curve 对象,获取 2-5 千米处的公路的曲线代码片段如下所示: //QI 到 ICurve 接口 ICurve pCurve = pPolyline as ICurve; //创建一个 Polyline 对象 ICurve pNewCurve = new PolylineClass(); bool btrue= true; //获取-5 千米间的曲线对象 pCurve.GetSubcurve(2, 5, btrue, out pNewCurve); 此外 ICurve 的 QueryTangent 和 QueryNormal 方法分别用于获取 Curve 对象上某一点的曲线的切线 和法线。 5.4.1.1 平头缓冲 private IPolygon FlatBuffer(IPolyline pLline1, double pBufferDis) { object o = System.Type.Missing; //分别对输入的线平移两次(正方向和负方向) IConstructCurve pCurve1 = new PolylineClass(); pCurve1.ConstructOffset(pLline1, pBufferDis, ref o, ref o); IPointCollection pCol = pCurve1 as IPointCollection; IConstructCurve pCurve2 = new PolylineClass(); pCurve2.ConstructOffset(pLline1, -1 * pBufferDis, ref o, ref o); //把第二次平移的线的所有节点翻转 IPolyline pline2 = pCurve2 as IPolyline; pline2.ReverseOrientation(); //把第二条的所有节点放到第一条线的IPointCollection里面 IPointCollection pCol2 = pline2 as IPointCollection; pCol.AddPointCollection(pCol2); //用面去初始化一个IPointCollection IPointCollection pPointCol = new PolygonClass(); pPointCol.AddPointCollection(pCol); //把IPointCollection转换为面 IPolygon pPolygon = pPointCol as IPolygon; //简化节点次序 内部文档,请勿外传 pPolygon.SimplifyPreserveFromTo(); return pPolygon; } Multipatch 几何对象 5.5 Multipatch 几何对象用于描述 3D 图形,可以由 TriangleStrip, TriangleFan, Triangle 和 ring 对 象组合构成组成。Multipatch 可以通过多种方式创建,一种是通过导入外部 3D 格式数据文件(3D Studio Max .3ds files, OpenFlight .flt files, COLLADA .dae files, Sketchup .skp files, VRML .wrl files), 另外 ArcGIS Engine 提供了多种创建 Multipatch 几何对象的方法: 如果创建没有贴图纹理,没有法向,没有组成部分信息的 Multipatch 时,只需创建好组成的 Multipatch 的各个部分即可,然后通过 MultiPatch 的 IGeometryCollection 接口添加各个组成部分即可。 如 果 要 为 Multipatch 每个组成部分添加纹理信息,法向信息,属性信息就必须使用 GeneralMultiPatchCreator 对象来创建,通过其 IGeneralMultiPatchInfo 接口来为 MultiPatch 各个组成 部分定义法向,材质,属性信息。通过 IGeneralMultiPatchInfo 接口可以获取这些 MultiPatch 的各个组 成部分的信息。 通过 IConstructMultiPatch 接口和 IExtrude 接口操作 GeometryEnvironment 对象可以通过拉伸 Polyline 对象(拉伸为墙)和 Polygon 对象(拉伸为多面体)来创建 MultiPatch. 通过访问 3D 符号库,获取 3DSymbol 来渲染点,把三维符号放置在点的位置从而生成 Multipatch. Geometry 集合接口 5.6 通过前边对于具体的 Geometry 对象的介绍可知,除了 Point 对象之外,其他几何对象都是通过其他 几何对象集合构建而成。如 MultiPoint 对象是点的集合,Path 对象是 Segment 对象的集合,Polyline 对 象是 Path 对象的集合,Polygon 对象是 Ring 对象的集合,Multipatch 对象是 Triangle Strip 和 Trangle Fan, Trangle,Ring 对象的集合。 ArcGIS Engine 提供了三个主要的几何图形集合接口用于对几何对象的操作, 分别是 IPointCollection,ISegmentCollection 和 IGeometryCollection,这些接口揭示出 ArcGIS Engine 的几何 模型的实质——它们是一种组合构成的模式,这种组合并不一定按照严格的层次结构组织。 在前面介绍一些几何对象的时候,也给大家演示了部分使用功能,这三个接口在程序开发中经常使 用到,接下来简单阐述以下这三个接口的使用方法。 IGeometryCollection 接口 被 Polygon,Polyline, Multipoint, Multipatch, Trangle,T rangle Strip,Trangle Fan 和 GeometryBag 所实现。IGeometryCollection 接口提供的方法可以让开发者对一个几 何对象的组成元素即子对象进行添加,改变和移除。例如: 组成 Polyline 对象的子对象是 Path 对象。 组成 Polygon 对象的子对象是 Ring 对象。 内部文档,请勿外传 组成 Multipoint 对象的子对象是 Point 对象。 组成 MultiPatch 对象的子对象是 TrangleFan TrangleStrip,Triangle 或 Ring 对象。 组成 GeometryBag 对象的是任何类型的几何体对象,实际上 GeometryBag 是一个可以容纳任何类型 几何对象的容器。 IGeometryCollection、这个接口是具有相同类型的几何对象的集合,该接口的的 Geometry 属性可 以通过一个索引值返回一个组成该几何对象的某个子对象,而 GeometryCount 返回组成该几何对象的子对 象的数目。 IGeometry 的 AddGeometry 和 AddGeometries 方法都用于向一个几何对象添加子对象,它们的区别是 前者一次只能添加一个几何对象,而后者可以一次添加一个几何对象数组。除此之外,AddGeometry 方法 可以将子对象添加到几何的指定索引值的位置,而 AddGeometries 方法将子对象数组添加到集合的最后。 在使用 AddGeometry 方法添加子对象到 Polygon 对象的过程中,如果子对象即 Ring 出现覆盖现象, 那么多边形就没有封闭或出现了包含关系,那么这个 Polygon 就 不是 简 单 Polygon , 因此 通 过 IGometryCollection 来创建一个 Polygon 时,需要使用 ITopologicalOperator 的 Simplify 方法保证其有 效性。 IGeometryCollection 接口 通过 IGeometryCollection 创建一个 Polygon 对象的代码片段如下: private IPolygon ConstructorPolygon(List pRingList) { try { IGeometryCollection pGCollection = new PolygonClass(); object o = Type.Missing; for (int i = 0; i < pRingList.Count; i++) { //通过IGeometryCollection接口的AddGeometry方法向Polygon对象中添加Ring子 对象 pGCollection.AddGeometry(pRingList[i], ref o, ref o); } //QI至ITopologicalOperator ITopologicalOperator pTopological = pGCollection as ITopologicalOperator; //执行Simplify操作 pTopological.Simplify(); IPolygon pPolygon = pGCollection as IPolygon; //返回Polygon对象 return pPolygon; 内部文档,请勿外传 } catch (Exception Err) { return null; } } private IPolygon MergePolygons(IPolygon firstPolygon, IPolygon SecondPolygon) { try { //创建一个Polygon对象 IGeometryCollection pGCollection1 = new PolygonClass(); IGeometryCollection pGCollection2 = firstPolygon as IGeometryCollection; IGeometryCollection pGCollection3 = SecondPolygon as IGeometryCollection; //添加firstPolygon pGCollection1.AddGeometryCollection(pGCollection2); //添加SecondPolygon pGCollection1.AddGeometryCollection(pGCollection3); //QI至ITopologicalOperator ITopologicalOperator pTopological = pGCollection1 as ITopologicalOperator; //执行Simplify操作 pTopological.Simplify(); IPolygon pPolygon = pGCollection1 as IPolygon; //返回Polygon对象 return pPolygon; } catch (Exception Err) { return null; } } ISegmentCollection 接口 ISegmentCollection 接口被 Path,Ring,Polyline 和 Polygon 四个类所实现,它们被称作是 Segment 集合对象,使用这个接口可以处理组成 Segment 集 合对 象 中 的 每 一 个 子 Segment 对象。使用 ISegmentCollection 接 口 可 以 为 一 个 Segment 集合对象添加,插入,删除 Segment 子对象。 ISegmentCollection 接口 SetCircle 和 SetRectangle 方法提供了一种简单不需要添加 Segment 的情况下 构建一个完成的 Path,Ring,Polyline 和 Polygon 的方法。 IPointCollection 接口 内部文档,请勿外传 IPointCollection 可以被多个几何对象类所实现,这些对象都是由多个点构成如: Mullipoint,Path,Ring,Polyline,Polygon,TriangleFan,TrangleStrip,Trangle,Multipatch 等,它们都 可以称作 PointCollection 对象,通过 IPointCollection 接口定义的方法可以获取,添加,插入,查询, 移除几何对象中的某个顶点。同以上两个接口一样它也定义了操作一个点集合对象的方法,例如通过 AddPoint 方法可以向 PointCollection 对象中的特定索引位添加一个点对象,如果不指定位置,则添加到 最后。通过 IPointCollection 的 Point 属性通过顶点索引可以得到某一顶点。 在 Geometry 模型中的几何对象分为两种类型,一类是用来直接构建要素类的称为高级几何对象,一 类用来构建高级几何对象相对低一级的几何对象成为构建几何对象如下表所示: 几何对象名称 所属类别 构成子几何对象 用于创建和编辑的接口 Polyline 高级 Path IGeometryCollection, IPointCollection Polygon 高级 Ring IGeometryCollection, IPointCollection MultiPoint 高级 Point IGeometryCollection, IPointCollection MultiPatch 高级 TrangleFan,Ring TrangleStrip ,Trangle, IGeometryCollection, IPointCollection Ring 低级 Segment ISegmentCollection, IPointCollection Path 低级 Segment ISegmentCollection, IPointCollection Segment 低级 Point IPoint,ILine, ICurve TriangleFan 低级 Point IGeometryCollection, IPointCollection TriangleStrip 低级 Point IGeometryCollection, IPointCollection Triangle 低级 Point IGeometryCollection, IPointCollection Point 高级/低级 无 IPoint 5.6.1 示例代码 5.6.1.1 等距离打断线 private IEnumGeometry MakeMultiPoints(IPolyline pGeometry, int inPoints) { 内部文档,请勿外传 IConstructGeometryCollection pConGeoCollection = new GeometryBagClass(); pConGeoCollection.ConstructDivideEqual(pGeometry, inPoints, esriConstructDivideEnum.esriDivideIntoPolylines); IEnumGeometry pEnumGeometry = pConGeoCollection as IEnumGeometry; return pEnumGeometry; } 5.6.2 IGeometryBag vs. IGeometryCollection GeometryBag 是支持 IGeometry 接口的几何对象引用的集合,任何几何对象都可以通过 IGeometryCollection 接口添加到 GeometryBag 中,但是在使用拓扑操作的时候,需要注意不同类型的几 何类型可能会有相互不兼容的情况。在向 GeometryBag 中添加几何对象的时候,GeometryBag 对象需要指 定空间参考,添加到其中的几何对象均拥有和 GeometryBag 对象一样的空间参考。在 GIS 中,矢量数据模 型是地理数据的重要表现形式,而 Esri 提供了多种方式对矢量数据进行管理,shpfile,coverage 以及 Geodatabase。在 Geodatabase 中,一个要素类的一条记录都有一个 Shape 字段,而这个 Shape 字段就存储 了我们的空间形体。这个空间形体用来精确的描述现实世界中的空间对象,我们知道 ArcGIS 中提供了众多 的空间分析工具,而这些分析就是对这个 Shape 进行操作的。 空间参考 5.7 空间参考(Spatial Reference)是 GIS 数据的骨骼框架,能够将我们的数据定位到相应的位置,为 地图中的每一点提供准确的坐标。 在同一个地图上显示的地图数据的空间参考必须是一致的,如果两个图 层的空间参考不一致,往往会导致两幅地图无法正确拼合,因此开发一个 GIS 系统时,为数据选择正确的 空间参考非常重要。 在 ArcGIS 中,每个数据集都具有一个坐标系,该坐标系用于将数据集与通用坐标框架(如地图)内的其 他地理数据图层集成。通过坐标系可在地图中集成数据集,以及执行各种集成的分析操作,例如叠加不同 的源和坐标系中的数据图层。 内部文档,请勿外传 5.7.1 相关知识 5.7.1.1 大地水准面 大地水准面是由静止海水面并向大陆延伸所形成的不规则的封闭曲面。它是重力等位面,即物体沿 该面运动时,重力不做功(如水在这个面上是不会流动的)。因为地球的质量并非在各个点均匀分布,因 此重力的方向也会相应发生变化,所以大地水准面的形状是不规则的,如下图: 与平均海水面相吻合的称为大地水准面 内部文档,请勿外传 5.7.1.2 地球椭球体 由定义可以知大地水准面的形状也是不规则的,仍不能用简单的数学公式表示,为了测量成果的 计算和制图的需要,人们选用一个同大地水准面相近的可以用数学方法来表达的椭球体来代替, 简称地球椭球体,它是一个规则的曲面,是测量和制图的基础,因地球椭球体是人们选定的跟大 地水准面很接近的规则的曲面,所以地球椭球体就可以有多个,地球椭球体是用长半轴、短半轴 和扁率来表示的。下表列出了一些最常见的参考椭球: 椭球名称 长半轴 (米) 短半轴 (m) 扁率的倒数, 克拉克(Clarke) 1866 6 378 206.4 6 356 583.8 294.978 698 2 白塞尔(Bessel)1841 6 377 397.155 6 356 078.965 299.152 843 4 International 1924 6 378 388 6 356 911.9 296.999 362 1 克拉索夫斯基 1940 6 378 245 6 356 863 298.299 738 1 GRS 1980 6 378 137 6 356 752.3141 298.257 222 101 WGS 1984 6 378 137 6 356 752.3142 298.257 223 563 5.7.1.3 基准面 基准面是在特定区域内与地球表面极为吻合的椭球体。椭球体表面上的点与地球表面上的特定位 置相匹配,也就是对椭球体进行定位,该点也被称作基准面的原点。原点的坐标是固定的,所有其 内部文档,请勿外传 他点由其计算获得。 基准面的坐标系原点往往距地心有一定偏移(有的也在地心,如 WGS1984),如西安 80 的基准面和北京 54 的基准面.因为椭球体通过定位以便能更好的拟合不同的地区,所以同一个椭球体可以拟合好几个基准 面.因为原点不同,所以不同的基准面上,同一个点的坐标是不相同的,这点我们应该清楚.下面以华盛 顿州贝灵厄姆市为例来说明。使用 NAD27、NAD83 和 WGS84 以十进制为单位比较贝灵厄姆的坐标。显 而易见,NAD83 和 WGS84 表示的坐标几乎相同,但 NAD27 表示的坐标则大不相同,这是因为所使用 的基准面和旋转椭球体对地球基本形状的表示方式不同。 基准面 经度 纬度 NAD 1927 -122.46690368652 48.7440490722656 NAD 1983 -122.46818353793 48.7438798543649 WGS 1984 -122.46818353793 48.7438798534299 3 个不同基准面时华盛顿州贝灵厄姆市的地理坐标 下图来自 ArcGIS 说明了基准面是基于椭球体的: 5.7.1.4 地图投影 简单的说地图投影就是把地球表面的任意点,利用一定数学法则,转换到地图平面上的理论和方法。 内部文档,请勿外传 5.7.2 两种坐标系 5.7.2.1 地理坐标系 地理坐标系也可称为真实世界的坐标系,是用于确定地物在地球上位置的坐标系,它用经纬度来表示地物 的位置,经度和纬度是从地心到地球表面上某点的测量角,通常以度或百分度为单位来测量该角度。下图 将地球显示为具有经度和纬度值的地球。 地理坐标系 (GCS) 是基于基准面的使用三维球面来定义地球上的位置,GCS 往往被误称为基准 面,而基准面仅是 GCS 的一部分,GCS 包括角度测量单位、本初子午线和基准面。下图来自 ArcGIS 说明了了地理坐标系的组成: 内部文档,请勿外传 5.7.2.2 投影坐标系 投影坐标系是基于地理坐标系的,它使用基于 X,Y 值的坐标系统来描述地球上某个点所处的位置,可以这 样认为投影坐标系=地理坐标系(如:北京 54、西安 80、WGS84)+投影方法(如:高斯-克吕格、Lambert 投 影、Mercator 投影)+线性单位。下图来自 ArcGIS 说明了投影坐标系的组成: 内部文档,请勿外传 5.7.3 ArcGIS Engine 对空间参考的支持 ArcGIS Engine 提供了一系列对象供开发者管理 GIS 系统的坐标系统。对大部分开发者而言了解 ProjectedCoordinateSystem, GeographicCoordinateSystem, SpatialReference Environment 这三个 组件类是非常有必要的,对于高级开发者而言,可能需要自定义坐标系统可以使用这些对象 Projection,Datum,AngularUnit,Spheriod,PrimeMeridian 和 GeoTransformation 等。在 ArcGIS 中除了我 们上面介绍的两种坐标系,还有一种称之为 Unknown 的坐标系,这种坐标系是当我们的数据没有坐标(jpg 等文件)或者坐标文件丢失的时候 ArcMap 不能识别数据的投影信息而赋予的,在 ArcGIS Engine 中下面三 个类分别对应了三个坐标系: 内部文档,请勿外传 利用 ArcGIS Engine 创 建 一 个 坐 标 系 或 者 基准面用的是 SpatialReferenceEnvironmentClass 类,该类实现了 ISpatialReferenceFactory 接口,该接口 定义了创建坐标系,基准面等方法和属性,如下图: 在利用 ISpatialReferenceFactory 创建坐标系的时候往往需要一个 int 类型的参数,这个 int 其实就是这些坐标系的代号,如我们熟悉的 4326 就是 WGS1984,下面为部分截图: 5.7.3.1 同一基准面的坐标转换 对于同一基准面,我们可以肯定一点就是同一位置经纬度坐标是一样的,而不同的就是计算成平面坐标的 时候可能有所不同,因为算法不一样,在这里我只是将经纬度坐标转成平面的坐标。 private IPoint GetpProjectPoint(IPoint pPoint, bool pBool) { ISpatialReferenceFactory pSpatialReferenceEnvironemnt = new SpatialReferenceEnvironment(); ISpatialReference pFromSpatialReference = pSpatialReferenceEnvironemnt.CreateGeographicCoordinateSystem((int)esriSRGeoCS3Type .esriSRGeoCS_Xian1980);//西安80 内部文档,请勿外传 ISpatialReference pToSpatialReference = pSpatialReferenceEnvironemnt.CreateProjectedCoordinateSystem((int)esriSRProjCS4Type .esriSRProjCS_Xian1980_3_Degree_GK_Zone_34);//西安80 if (pBool == true)//球面转平面 { IGeometry pGeo = (IGeometry)pPoint; pGeo.SpatialReference = pFromSpatialReference; pGeo.Project(pToSpatialReference); return pPoint; } else //平面转球面 { IGeometry pGeo = (IGeometry)pPoint; pGeo.SpatialReference = pToSpatialReference; pGeo.Project(pFromSpatialReference); return pPoint; } } 5.7.3.2 不同基准面的坐标转换 通过前面的介绍,我们知道地球上同一位置的坐标在不同的基准面上 是不一样的,而基准面是构成坐标系的一个部分,因为基准面在定位 的时候牵扯到了相对地心的平移或旋转等,所以对于这样的转换我们 无法直接进行,需要一个转换参数,而这些参数也是基于不同的模型 的,常用的有三参数和 7 参数,三参数是比较简单的也是比较容易理 解的,三参数是在两个基准面之间进行了 X,Y,Z 轴的平移,通过下面 的图我们很清楚的看到三参数之间两个基准面的关系: 内部文档,请勿外传 如果知道了这三个平移的参数 外加个基准面上的点, 那么另外一个点的坐标就是 而 7 参数的模型比较复杂,这种复杂的同时让精度大为提高,7 参数 不仅仅考虑了两个基准面之间的平移,还考虑了旋转外加一个比例因 子(椭球体的大小可能不一样),从下面的图我们可以清楚看到这种关 系: 内部文档,请勿外传 对于 7 参数,我们知道了平移三参数 旋转三参数 以及比例因子 外加一个基准面上的坐标就可按照下面 的公式求出另外一个基准面上的坐标: 对于不同基准面之间的转换,ArcGIS Engine 提供了一个用来控制转 换参数的接口 IGeoTransformation,该接口被以下类实现 内部文档,请勿外传 着每一个接口对应了一种转换方法,比如 GeocentricTranslationClass 类就实现了三参数,而 CoordinateFrameTransformationClass 类实现了 7 参数,要实现 3 参数或者 7 参数需要 IGeometry2 或更新接口的 ProjectEx 方法,下面我们用代码实现一个不同基准面之间的坐标转 换。 public void ProjectExExample() { ISpatialReferenceFactory pSpatialReferenceFactory = new SpatialReferenceEnvironmentClass(); // ISpatialReference pFromCustom = pSpatialReferenceFactory.CreateESRISpatialReferenceFromPRJFile(@"E:\arcgis\Engine\z idingyi.prj"); IPoint pFromPoint = new PointClass(); pFromPoint.X = 518950.788; pFromPoint.Y = 4335923.97; IZAware pZAware = pFromPoint as IZAware; pZAware.ZAware = true; pFromPoint.Z = 958.4791; // ((IGeometry)pFromPoint).SpatialReference = pFromCustom; 内部文档,请勿外传 //自定义投影WGS84下的北京6度19带。 ((IGeometry)pFromPoint).SpatialReference = CreateCustomProjectedCoordinateSystem(); //目标投影 IProjectedCoordinateSystem projectedCoordinateSystem = pSpatialReferenceFactory.CreateProjectedCoordinateSystem((int)esriSRProjCS4Type.esr iSRProjCS_Xian1980_GK_Zone_19); //因为目标基准面和原始基准面不在同一个上,所以牵扯到参数转换,我用7参数 转换 ICoordinateFrameTransformation pCoordinateFrameTransformation = new CoordinateFrameTransformationClass(); pCoordinateFrameTransformation.PutParameters(-112.117, 4.530, 21.89, -0.00058702, -0.00476421, 0.00009358, 0.99998006411); pCoordinateFrameTransformation.PutSpatialReferences(CreateCustomProjectedCoordinate System(), projectedCoordinateSystem as ISpatialReference); //投影转换 IGeometry2 pGeometry = pFromPoint as IGeometry2; pGeometry.ProjectEx(projectedCoordinateSystem as ISpatialReference, esriTransformDirection.esriTransformForward, pCoordinateFrameTransformation, false, 0, 0); } private IProjectedCoordinateSystem CreateCustomProjectedCoordinateSystem() { ISpatialReferenceFactory2 pSpatialReferenceFactory = new SpatialReferenceEnvironmentClass(); IProjectionGEN pProjection = pSpatialReferenceFactory.CreateProjection((int) esriSRProjectionType.esriSRProjection_GaussKruger) as IProjectionGEN; IGeographicCoordinateSystem pGeographicCoordinateSystem = pSpatialReferenceFactory.CreateGeographicCoordinateSystem((int)esriSRGeoCSType.esri 内部文档,请勿外传 SRGeoCS_WGS1984); ILinearUnit pUnit = pSpatialReferenceFactory.CreateUnit((int)esriSRUnitType.esriSRUnit_Meter) as ILinearUnit; IParameter[] pParameters = pProjection.GetDefaultParameters(); IProjectedCoordinateSystemEdit pProjectedCoordinateSystemEdit = new ProjectedCoordinateSystemClass(); object pName = "WGS-BeiJing1954"; object pAlias = "WGS-BeiJing1954"; object pAbbreviation = "WGS-BeiJing1954"; object pRemarks = "WGS-BeiJing1954"; object pUsage = "Calculate Meter From lat and lon"; object pGeographicCoordinateSystemObject = pGeographicCoordinateSystem as object; object pUnitObject = pUnit as object; object pProjectionObject = pProjection as object; object pParametersObject = pParameters as object; pProjectedCoordinateSystemEdit.Define(ref pName, ref pAlias, ref pAbbreviation, ref pRemarks, ref pUsage, ref pGeographicCoordinateSystemObject, ref pUnitObject,ref pProjectionObject, ref pParametersObject); IProjectedCoordinateSystem5 pProjectedCoordinateSystem = pProjectedCoordinateSystemEdit as IProjectedCoordinateSystem5; pProjectedCoordinateSystem.FalseEasting = 500000; pProjectedCoordinateSystem.LatitudeOfOrigin = 0; pProjectedCoordinateSystem.set_CentralMeridian(true,111); pProjectedCoordinateSystem.ScaleFactor=1; pProjectedCoordinateSystem.FalseNorthing=0; return pProjectedCoordinateSystem; } 内部文档,请勿外传 6 六.矢量数据空间分析 叠加分析 6.1 叠加分析是将有关主题层组成的数据层面,进行叠加产生一个新数据层面的操作,其结果综合了原来两层 或多层要素所具有的属性,从已有的数据中提取空间隐含的信息。叠加分析不仅包含空间关系的比较,还 包含属性关系的比较。叠加分析可以分为矢量图层的叠加分析和栅格数据的叠加分析,其中矢量的叠加分 析包括交集(Intersect)、裁减(Clip)、合并叠加(Union)以及合并(Merge)等类型。矢量图层叠加 分析需要用到的主要接口是 IBasicGeoProcessor,它提供了以下方法和属性: 在 IBasicGeoProcessor 接口中,它所定义的几个方法的参数都很相似,现在我们以 Intersect 方法为例, 在帮助中,我们可以看到 Intersect 方法包含下面几个参数: p ub lic IFeatureClass Intersect ( ITable inputTable,(第一个要素类) bool useSelectedInput,(是否使用第一个要素中选择的数据) ITable overlayTable,(第二个要素类) bool useSelectedOverlay,(是否使用第二个要素中选择的数据) double Tolerance,(容差) IFeatureClassName outputName(输出要素对象) 内部文档,请勿外传 输出要素对象的类型是 IFeatureClassName,也就是名称对象,关于名称对象,在数据库那一章节有详细 说明,其它几个参数比较容易理解,我们要做Intersect也就是构造这几个参数而已,如果深入理解下,Esri 提供的这个方法,这些参数也正如叠加分析的定义那样,对相关数据的输入,通过叠加分析,构造一个新 的数据从而挖掘潜在信息,下面的一个例子演示了如何进行Intersect操作: public IFeatureClass Intsect(IFeatureClass _pFtClass,IFeatureClass _pFtOverlay,string _FilePath,string _pFileName) { IFeatureClassName pOutPut = new FeatureClassNameClass(); pOutPut.ShapeType = _pFtClass.ShapeType; pOutPut.ShapeFieldName = _pFtClass.ShapeFieldName; pOutPut.FeatureType = esriFeatureType.esriFTSimple; //set output location and feature class name IWorkspaceName pWsN = new WorkspaceNameClass(); pWsN.WorkspaceFactoryProgID = "esriDataSourcesFile.ShapefileWorkspaceFactory"; pWsN.PathName = _FilePath; //也可以用这种方法,IName 和IDataset的用法 /* IWorkspaceFactory pWsFc = new ShapefileWorkspaceFactoryClass(); IWorkspace pWs = pWsFc.OpenFromFile(_FilePath, 0); IDataset pDataset = pWs as IDataset; IWorkspaceName pWsN = pDataset.FullName as IWorkspaceName; */ IDatasetName pDatasetName = pOutPut as IDatasetName; pDatasetName.Name = _pFileName; 内部文档,请勿外传 pDatasetName.WorkspaceName =pWsN; IBasicGeoprocessor pBasicGeo = new BasicGeoprocessorClass(); IFeatureClass pFeatureClass = pBasicGeo.Intersect(_pFtClass as ITable , false, _pFtOverlay as ITable , false, 0.1, pOutPut); return pFeatureClass; } 其中,第一个要素类如下: 第二个要素类如下: 内部文档,请勿外传 Insetsect 之后,结果如下: 关系操作 6.2 GIS中的空间对象除了拥有属性数据之外,他们之间还拥有某种关系,比如说一个点在一个面的内部,两个 对象相交,相等,包含,相接等关系。关系运算符(Relational Operators)比较两个几何体,并返回一个 boolean 来说明所要的关 系是否存在,这些关系都是通过 IRelationalOperator 接口实现的。IRelationalOperatior 接口的方法如 下: 内部文档,请勿外传 IRelationalOperator 接口被面,线等几何要素实现,IRelationalOperator 接口中的方法的参数也非常类 似,参数往往是一个几何对象,以 IRelationalOperator.Contains 方法为例说明: [C #] p ub lic bool Co nta in s ( IGeometry other (另一个几个对象) ); 示例:利用 IRelationalOperator 实现空间包含统计: IFeatureClass pPolygonFClass = GetFeatureClass(@"D:\空间查询\分析用的空间数据", "三级成矿区带 "); IFeatureClass pPointFClass = GetFeatureClass(@"D:\空间查询\分析用的空间数据", " 探矿权点"); ITable pTable = CreateTable(@"D:\空间查询\分析用的空间数据", "Res3"); IFeatureCursor pPolyCursor = pPolygonFClass.Search(null, false); IFeature pPolyFeature = pPolyCursor.NextFeature(); while (pPolyFeature != null) { IGeometry pPolGeo = pPolyFeature.Shape; IRelationalOperator pRel = pPolGeo as IRelationalOperator; int Count = 0; 内部文档,请勿外传 IFeatureCursor pPointCur = pPointFClass.Search(null, false); IFeature pPointFeature = pPointCur.NextFeature(); while (pPointFeature != null) { IGeometry pPointGeo = pPointFeature.Shape; if (pRel.Contains(pPointGeo)) { Count++; } pPointFeature = pPointCur.NextFeature(); } if (Count != 0) { IRow pRow = pTable.CreateRow(); pRow.set_Value(1, pPolyFeature.get_Value(0)); pRow.set_Value(2, Count); pRow.Store(); } pPolyFeature = pPolyCursor.NextFeature(); } 结果和前面的一样 临近操作 6.3 临近操作用于确定一个到多个要素、或两个要素类间的要素邻近性。它经常用来识别和一个要素最近的其 他要素或者两个要素间的最短距离等,在 ArcGIS Engine 中,实现临近分析操作的接口是 IProximityOperator,IProximityOperator 接口只有三个方法,如下: 内部文档,请勿外传 这三个方法主要用于得到两个几何对象之间的距离或得到一个给定点到某个几个对象的最近点之间的距离, 比如我们求一个点到一个多边形的最近的点,就可以用这个接口。下图展示了一个查找最近点的示例: 示例:通过临近分析操作实现 Moran'I 中的邻接矩阵 Moran'I 分为全局和局部两种。 通常情况,先做一个地区的全局 I 指数,全局指数只是告诉我们空间是否出现了集聚或异常值,但并 没有告诉我们在哪里出现。换句话说全局 Moran'I 只回答 Yes 还是 NO;如果全局有自相关出现,接着做局 部自相关; 局部 Moran'I 会告诉我们哪里出现了异常值或者哪里出现了集聚,是一个回答 Where 的工具。在计算 Moran 的时候有一个很关键的步骤就是计算邻接矩阵,借助 IProximityOperator 接口我们可以生成这样一 个矩阵表,代码如下: /// /// 这个字段要是唯一的 /// /// /// /// /// /// private ITable CreateWeightTable(string _FilePath, string _TableName,IFeatureClass 内部文档,请勿外传 _pFeatureClass,string _FieldName) { IWorkspaceFactory pWks = new ShapefileWorkspaceFactoryClass(); IFeatureWorkspace pFwk = pWks.OpenFromFile(_FilePath, 0) as IFeatureWorkspace; //用于添加表中的必要字段 ESRI.ArcGIS.Geodatabase.IObjectClassDescription objectClassDescription = new ESRI.ArcGIS.Geodatabase.ObjectClassDescriptionClass(); IFields pTableFields = objectClassDescription.RequiredFields; IFieldsEdit pTableFieldsEdit = pTableFields as IFieldsEdit; int index = _pFeatureClass.FindField(_FieldName); IField pField = new FieldClass(); IFieldEdit pFieldEdit = pField as IFieldEdit; pFieldEdit.Name_2 = _FieldName; pTableFieldsEdit.AddField(pFieldEdit); pFieldEdit.Type_2 = _pFeatureClass.Fields.get_Field(index).Type; IFeatureCursor pFtCursor = _pFeatureClass.Search(null, false); IFeature pFt = pFtCursor.NextFeature(); while (pFt!= null ) { IField pFieldv = new FieldClass(); IFieldEdit pFieldEditv = pFieldv as IFieldEdit; pFieldEditv.Name_2 = pFt.get_Value(index).ToString(); 内部文档,请勿外传 pFieldEditv.Type_2 = esriFieldType.esriFieldTypeInteger; pTableFieldsEdit.AddField(pFieldEditv); pFt = pFtCursor.NextFeature(); } ITable pTable = pFwk.CreateTable(_TableName, pTableFields, null, null, ""); IFeatureCursor pFtCursor1 = _pFeatureClass.Search(null, false); IFeature pFt1 = pFtCursor1.NextFeature(); while (pFt1 != null) { IRow pRow = pTable.CreateRow(); pRow .set_Value(1,pFt1.get_Value(index)); pRow.Store(); pFt1 = pFtCursor1.NextFeature(); } return pTable; } IFeatureClass pPolygonFClass = GetFeatureClass(@"D:\空间查询\分析用的空间数据", "行政区"); ITable pTable = CreateWeightTable(@"D:\ 空间查询\ 分析用的空间数据", "Weight",pPolygonFClass ,"NAME"); IFeature pFt1, pFt2; IFeatureCursor pFtCur1, pFtCur2; 内部文档,请勿外传 pFtCur1 = pPolygonFClass.Search(null, false); pFt1 = pFtCur1.NextFeature(); ICursor pCursor = pTable.Update(null, false); IRow pRow = pCursor.NextRow(); int j = 0; ///这里是关键,在这里进行计算,这里可以通过计算上三角或者下三角进行优化 while (pFt1 != null) { IProximityOperator pProx = pFt1.Shape as IProximityOperator; pFtCur2 = pPolygonFClass.Search(null, false); pFt2 = pFtCur2.NextFeature(); while (pFt2 != null) { double dis = pProx.ReturnDistance(pFt2.Shape); if(dis ==0) { pRow.set_Value(j + 2, 1); pRow.Store(); } pFt2 = pFtCur2.NextFeature(); j++; } j = 0; pRow = pCursor.NextRow(); pFt1 = pFtCur1.NextFeature(); } 内部文档,请勿外传 拓扑关系操作 6.4 空间拓扑关系是空间分析中的重要部分,各种空间分析的结果都可以通过几何图像之间的拓扑运算实现。 比如:查找距离超市 1000 米内有多少居民。这些居民中有多少潜在顾客。这也是一个典型的缓冲区分析, 实际上就是给超市做了个 1000 米得缓冲区,然后用这个缓冲区和居民数据叠加,进而挖掘潜在顾客。空间 拓扑关系都定义在了 ITopologicalOperator 接口中,从帮助文档中我们可以获得 ITopologicalOperator 的详细信息,如下: ITopologicalOperator 接口提供了基于现有几何体(geometries)之间拓扑关系来构建新几 何体的方法和属性 示例:在地图上通过鼠标点击实现空间缓冲查询,代码如下: IMap pMap = axMapControl1.Map; IActiveView pActView = pMap as IActiveView; IPoint pt = pActView.ScreenDisplay.DisplayTransformation.ToMapPoint(e.x, e.y); ITopologicalOperator pTopo = pt as ITopologicalOperator; 内部文档,请勿外传 IGeometry pGeo = pTopo.Buffer(500); ESRI.ArcGIS.Display.IRgbColor rgbColor = new ESRI.ArcGIS.Display.RgbColorClass(); rgbColor.Red = 255; ESRI.ArcGIS.Display.IColor color = rgbColor; // Implicit Cast ESRI.ArcGIS.Display.ISimpleFillSymbol simpleFillSymbol = new ESRI.ArcGIS.Display.SimpleFillSymbolClass(); simpleFillSymbol.Color = color; ESRI.ArcGIS.Display.ISymbol symbol = simpleFillSymbol as ESRI.ArcGIS.Display.ISymbol; pActView.ScreenDisplay.SetSymbol(symbol); pActView.ScreenDisplay.DrawPolygon(pGeo); pMap.SelectByShape(pGeo, null, false); //闪动1000次 axMapControl1.FlashShape(pGeo, 1000, 2, symbol); axMapControl1.ActiveView.Refresh(); 空间插值 6.5 在 GIS 的地理空间信息采集过程中,我们对某种地理空间现象或特征进行地理空间测量,都是基于一种离 散的样本测量,利用这些有限的采样点数据,而对研究区域内其他未知区域的特征数据进行地理空间信息 内部文档,请勿外传 的推理和估计,从而构建一个连续的地理特征表面分布,我们把这种地理空间推理计算和估计的方法称为 地理空间插值。 地理空间插值方法: 6.6 GIS 中常用的地理空间插值方法主要包括如下几种:  距离加权倒数空间插值法(IDW)  自然临近空间插值法  样条空间插值法  克里格空间插值法  趋势空间插值法 这些地理空间插值方法给我们一种技术手段以供对未知点的推理和估算,其测算结果具有地统计意义,测 算结果与样本空间大小及样本空间的分布直接相关,而且这些插值方法在预测估值的时候都有自己的前提 假设,这个假设也就是每一种插值算法的理论前提。ArcGIS Engine 中 空间插值的方法都定义在 IInter polationOp 接口中,现在这个接口已经到了 IInterpolationOp3,下面就是 IInterpolationOp3 接口中定 义的方法: 和插值相关的接口 6.7 IFeatureClassDescriptor 接口: IFeatureClassDescriptor 接口被FeatureClassDescriptor 对象实现,FeatureClassDescriptor 对象通 过指定一个值字段用来来描述插值的时候所需要的一些信息。 IRasterAnalysisEnvironment 接口: 内部文档,请勿外传 IRasterAnalysisEnvironment 接口定义了插值后生成栅格的大小,范围,Mask 等。 IRasterRadius 接口: 从这个的字面意思就可以看出这个接口是和距离有关的,没错,我们知道一些插值比如 IDW 就是和距离有 关的,而设置距离的一些信息就定义在这个接口中,这个接口被 RasterRadiusClass 类实现。 示例:利用 ArcGIS Engine 实现 IDW 差值,代码如下: public IGeoDataset IDW(IFeatureClass _pFeatureClass, string _pFieldName, double _pDistance, double _pCell, int _pPower) { IGeoDataset Geo = _pFeatureClass as IGeoDataset; object pExtent = Geo.Extent; object o = Type.Missing; IFeatureClassDescriptor pFeatureClassDes = new FeatureClassDescriptorClass(); pFeatureClassDes.Create(_pFeatureClass, null, _pFieldName); IInterpolationOp pInterOp = new RasterInterpolationOpClass(); IRasterAnalysisEnvironment pRasterAEnv = pInterOp as IRasterAnalysisEnvironment; // pRasterAEnv.Mask = Geo; pRasterAEnv.SetExtent(esriRasterEnvSettingEnum.esriRasterEnvValue, ref pExtent, ref o); object pCellSize = _pCell;//可以根据不同的点图层进行设置 pRasterAEnv.SetCellSize(esriRasterEnvSettingEnum.esriRasterEnvValue, ref pCellSize); IRasterRadius pRasterrad = new RasterRadiusClass(); object obj = Type.Missing; 内部文档,请勿外传 pRasterrad.SetFixed(_pDistance, ref obj); object pBar = Type.Missing; IGeoDataset pGeoIDW = pInterOp.IDW(pFeatureClassDes as IGeoDataset, _pPower, pRasterrad, ref pBar); return pGeoIDW; } 开发利器 GP 6.8 Geoprocessing 是 ArcGIS 的一个基础组成部分,它提供了数据分析、数据管理和数据转换等大多数 GIS 用户常用的工具。 GIS 程序通常需要操作和分析地理数据,如将数据集从一种投影转换为另一种投影,要 素添加缓冲区。ArcGIS 10 包括了超过七百个 Geoprocessing 工具来执行这些任务。 在开发一个 ArcGIS Engine 的应用程序的时候,这些 Geoprocessing 工具也经常被用到,为了降低开 发难度和提高开发效率,Esri 在 ArcGIS Engine 9.2 中添加了 GeoProcessor 类,更为可贵的是我们知道 在 ArcMap 中我们可以自定义一些用来解决相关问题的工具,而 Engine 提供的这个类也可以调用我们自定 义的工具。 在使用 GeoProcessor 时,一般需先定义一个 GeoProcessor 对象,Geoprocessor.Geoprocessor 是 简化调用 Geoprocessing 工具任务的主要对象。这个对象是执行 ArcGIS 中任何 Geoprocessing 工具的唯 一访问点,它是一个粗粒度对象,包含了许多属性和方法,在设置完操作类的参数后,则通过 GeoProcessor 的 Excute 函数来执行,Excute 方法中需要一个操作对象作为参数,如:Intersect,Clip 等,具体包含哪 些操作类,可通过 ArcToolBox 和 ESRI 的帮助文档查找。Geoprocessor 对象可以使用任何语言,包括.NET 和 Java 等来进行访问。 示例:利用 GP 实现 Intersect 分析: 在 ArcMap 中要实现 Intersect,我们只需要找到 Intersect 工具,打开,然后设置相关的参数,如下: 内部文档,请勿外传 在 ArcGIS Engine 中,我们也可以利用寥寥数句,完成和这个一样的功能,代码如下: 从这简短的代码可以看出,几乎是零代码就完成了一个 Intersect 操作,但是我还是要强调下,在 ArcGIS Engine 中,由于许可的原因,并不是所有的工具都可以通过这样的操作实现,但是 ArcGIS Desktop Editor 级别的工具在 ArcGIS Engine 的 Geodatabaseupdate 许可中是可以完全实现的,如果要实现 ArcGIS Desktop Info 中的所有功能,那么就要有 ArcGIS Desktop Info 的许可,这一点还请牢记。 7 七.符号化 符号化可以认为是给我们的空间数据披上多彩的衣服,让她更加婀娜多姿。在我们拿到地图的时候,映入 眼帘的是花花绿绿的符号。地图符号是表达空间数据的一种手段,是将现实世界可视化的有力工具,也是 我们沟通地图的语言。他不仅仅表示了空间数据的位置,还表示了空间数据的特征,布局形状等。 内部文档,请勿外传 地图符号话决定了我们的数据最终在我们面前的面目,也是第一印象,就好比一个打扮中女孩给人的第一 感觉如何。 GIS 制图是一门科学,在我国很多高校开设了地图制图学专业,涉及计算机图形学、地图学、电子地图设 计与原理等多门专业课程。随着地理信息系统技术的发展,地图制图与 GIS 的结合越来越紧密,制作一幅 内涵丰富的电子地图已经成为 3S 行业用户工作成果的重要展示方式。 和符号化相关的对象 7.1 颜色对象 7.2 颜色是显示世界中事物最普遍的属性.对于不同的行业来说,他们眼中的颜色不尽相同.我们常见的红绿蓝 等,其实这是一种称之为 RGB 的颜色模型,除了这种模型之外,在印刷行业常用的是一种称之为 CMYK 的颜色 模型,而在遥感图像增强处理的时候我们还知道另外一种颜色模型 HSI 在 ArcGIS Engine 中样色对象实现了 IColor 接口,这个接口被一下对象实现: 从这个表中也可以看出 ArcGIS Enine 中存在五种颜色模型 RGB,HSV,HIS,GARY 和 CMYK. RGB 7.3 RGB 色彩模式使用 RGB 模型为图像中每一个像素的 RGB 分量分配一个 0~255 范围内的强度值。RGB 图像只使 用三种颜色,就可以使它们按照不同的比例混合,在屏幕上重现 16777216 种颜色。 在 RGB 模式下,每种 RGB 成分都可使用从 0(黑色)到 255(白色)的值。 例如,亮红色使用 R 值 255、 G 值 0 和 B 值 0。 当所有三种成分值相等时,产生灰色阴影。 当所有成分的值均为 255 时,结果是纯 内部文档,请勿外传 白色;当该值为 0 时,结果是纯黑色。 7.3.1 HSV 颜 色模型 每一种颜色都是由色相(Hue,简 H),饱和度(Saturation,简 S)和色明度(Value,简 V)所表 示的。HSV 模型对应于 圆柱坐标系中的一个圆锥形子集,圆锥的顶面对应于 V=1。它包含 RGB 模型中的 R=1,G=1,B=1 三个面, 所代表的颜色较亮。色彩 H 由绕 V 轴的旋转角给定。红色对应于 角度 0° ,绿色对应于角度 120°,蓝色 对应于角度 240°。在 HSV 颜色模型中,每一种颜色和它的补色相差 180°。饱和度 S 取值从 0 到 1,所以 圆锥顶面的半径为 1。HSV 颜色模型所代表的颜色域是 CIE 色度图的一个子集,这个 模型中饱和度为百分 之百的颜色,其纯度一般小于百分之百。在圆锥的顶点(即原点)处,V=0,H 和 S 无定义,代表黑色。圆锥 的顶面中心处 S=0,V=1,H 无定义,代表白色。从该点到原点代表亮度渐暗的灰色,即具有不同 灰度的灰 色。对于这些点,S=0,H 的值无定义。可以说,HSV 模型中的 V 轴对应于 RGB 颜色空间中的主对角线。 在 圆锥顶面的圆周上的颜色,V=1,S=1,这种颜色是纯色。HSV 模型对应于画家配色的方法。画家用改变色 浓和 色深的方法从某种纯色获得不同色调的颜色,在一种纯色中加入白色以改变色浓,加入黑色以改变色 深,同时加入不同比例的白色,黑色即可获得各种不同的色调。 7.3.2 HSI 颜 色模型 HSI 色彩空间是从人的视觉系统出发,用色调(Hue)、色饱 HSI 和度(Saturation 或 Chroma)和亮度 (Intensity 或 Brightness)来描述色彩。HSI 色彩空间可以用一 个圆锥空间模型来描述。用这种 描述 HIS 色彩空间的圆锥模型相当复杂,但确能把色调、亮度和色饱和度 的变化情形表现得很清楚。 通常把色调和饱和度通称为色度,用来表示颜色的类别与深浅程度。由于人的 视觉对亮度的敏感 程度远强于对颜色浓淡的敏感程度,为了便于色彩处理和识别,人的视觉系统经常采用 HSI 色彩空间, 它比 RGB 色彩空间更符合人的视觉特性。在图像处理和计算机视觉中大量算法都可在 HSI 色彩空间中 方便地使用,它们可以分开处理而且是相互独立的。因此,在 HSI 色彩空间可以大大简化图像 分析 和处理的工作量。HSI 色彩空间和 RGB 色彩空间只是同一物理量的不同表示法,因而它们之间存在着 内部文档,请勿外传 转换关系。 7.3.3 CMYK 颜 色模型 CMYK(Cyan, Magenta, Yellow)颜色空间应用于印刷工业, CMYK 与 RGB 的关系 印刷业通过青(C)、品(M)、黄(Y)三原色油墨的不同网点面积率的叠印来表现丰富多彩的颜色和阶调,这便 是三原色的 CMY 颜色空间。实际印刷中,一般采用青 (C)、品(M)、黄(Y)、黑(BK)四色印刷,在印刷的中 间调至暗调增加黑版。当红绿蓝三原色被混合时,会产生 白色,但是当混合蓝绿色、紫红色和黄色三原色 时会产生黑色。既然实际用的墨水并不会产生纯正的颜色, 黑色是包括在分开的颜色,而这模型称之为 CMYK。CMYK 颜色空间是和设备或者是印刷过程相关的,则工艺方法、 油墨的特性、纸张的特性等,不同 的条件有不同的印刷结果。所以 CMYK 颜色空间称为与设备有关的表色空间。 而且,CMYK 具有多值性,也 就是说对同一种具有相同绝对色度的颜色,在相同的印刷过程前提下,可以用分种 CMYK 数字组合来表示 和印刷出来。这种特性给颜色管理带来了很多麻烦,同样也给控制带来了很多的灵活性。 在印刷过程中, 必然要经过一个分色的过程,所谓分色就是将计算机中使 用的 RGB 颜色转换成印刷使用的 CMYK 颜色。在 转换过程中存在着两个复杂的问题,其一是这两个颜色模型在表现颜色的范围上不完全一样,RGB 的色域 较大而 CMYK 则较小,因此就要进行色域压缩;其二是这两个颜色都是和具体的设备相关的,颜色本身没有 绝对性。因此就需要通过一个与设备无关的颜色模型来进行转换,即可以通过以上介绍的 XYZ 或 LAB 色空 间来进行转换。 7.3.4 色带 在制图的过程中,有的时候使用的颜色不止一种,比如说我这个专题图需要 200 种颜色。对于这种需求, 不可能让程序员或者制图人员设置 200 种颜色吧?为此 ArcGIS Engine 中提供了颜色带对象。颜色带对象 用来一次产生多种颜色,这给我们制图带来了很大的方便。 颜色带对象实现了 IColorRamp 接口,该接口被 4 个类实现,如下图: iColorRamap 接口定义了这 4 个类的公共方法和属性 内部文档,请勿外传 符号对象 7.4 符号可以用来表示地理对象的某些描述性信息,如我们看到一个房子的符号,就说明了此处表达的是一个 和房子相关的对象。这样符号就作为一种传递空间信息的载体。符号就是用于显示在地图上的修饰要素的 元素对象,作为元素对象的一个属性而存在,符号的形状可以让我们将现实世界中的实体对象加以区分, 比如铁路和公路的符号,这些符号的差异也能让我们了解要素时间属性上的差别和联系。 在 GIS 中我们简单的空间实体抽象为点,线,面三种要素,在 ArcGIS Engine 中对符号也抽象为和前者对 应的 MarkerSymbol,LineSymbol 和 FillSymbol。此外还有两种特殊的符号,一种是用于显示 3D 的 3D Chart, 另一种是用来标注文字的 TextSymbol。 MarkerSymbol 7.5 MarkerSymbol 对象是用于修饰点对象的符号,在 AE 的帮助中,我们可以看到 Markersymbol 有好几种子类, 其中不同的子类产生不同的符号, 但是所有的 MmarkerSymbol 类都实现了 IMarkerSymbol 接口,这个接口 定义了符号的公共属性,如下图: LineSymbol 对象 7.6 从字面意思可以得知这个对象是用来修饰线状要素的,和 MarkerSymbol 对象一样,该对象有好几种子类, 其中每种 LineSymbol 对象都实现了 ILineSymbol 接口,这个接口有两个属性,这也是 LineSymbol 对象的 公共属性,如下图: 内部文档,请勿外传 FillSymbol 对象 7.7 填充符号是针对面状要素类型的,它实现了 IFillSymbol 接口,该接口有两个属性 IFillSymbol.Outline 属性是修饰面状要素的轮廓是一个 ILineSymbol 对象。IFillSymbol 接口是一个抽象 类,在 AE 的帮助中我们可以看到有以下类实现了 IFillSymbol 接口。 专题图制作 7.8 上面对 ArcGIS 中的一些颜色,符号做了详细的介绍,这些都是为了专题图的制作而打基础。 专题图是突 出表示一种或几种自然图或社会经济现象的地图,专题图按照空间分布的点状、线状和面状分布大致有以 下的一些表示方法:定点符号法、线状符号法、质别底色法、等值线法、定位图表法、范围法、点值法、 分值比较法、分区图表法和动线法等。ArcMap 提供了很多制作不同类型的专题图,在 Layer Pproperties 的 Symbol 选项卡中可以看到下图: 内部文档,请勿外传 ArcGIS 中将专题图分为 5 大类。 我在前面介绍图层的时候提到 IGeoFeatureLayer 接口,专题图的制作和这个接口息息相关,该接口有一个 IGeoFeatureLayer.Renderer 属性,该属性返回 IFeatureRenderer,该接口被 FeatureRender 对象实现, 而 FeatureRender 类是一个抽象类,在 AE 的帮助中可以看到下图: 关于制作专题图的 OMD 图可以参看如下: 内部文档,请勿外传 7.8.1 唯一值 ArcMap 中是如何做的: 1. 加载要分类渲染的数据 2. 在图层上右键/properties/Symbolygy 3. 在 Categories 中找到 Unique Values 内部文档,请勿外传 4. 设置唯一值字段,然后 Add All Values,如如下图: 7.8.1.1 和唯一值渲染图相关的接口 IUniqueValueRenderer 该接口被 UniqueValueRenderer 对象实现,用来控制唯一值渲染的主要信息,如字段设置,符号等。 示例:唯一值渲染代码: public UniqueValueRender(AxMapControl pMapcontrol, IFeatureLayer pFtLayer,int pCount, string pFieldName) { IGeoFeatureLayer pGeoFeaturelayer = pFtLayer as IGeoFeatureLayer; IUniqueValueRenderer pUnique = new UniqueValueRendererClass(); pUnique.FieldCount = 1; pUnique.set_Field(0, pFieldName); ISimpleFillSymbol pSimFill = new SimpleFillSymbolClass(); //给颜色 内部文档,请勿外传 IFeatureCursor pFtCursor = pFtLayer.FeatureClass.Search(null, false); IFeature pFt = pFtCursor.NextFeature(); IFillSymbol pFillSymbol1; ////添加第一个符号 //pFillSymbol1 = new SimpleFillSymbolClass(); //pFillSymbol1.Color = GetRGBColor(103, 252, 179) as IColor; ////添加第二个符号 //IFillSymbol pFillSymbol2 = new SimpleFillSymbolClass(); //pFillSymbol2.Color = GetRGBColor(125, 155, 251) as IColor; //创建并设置随机色谱从上面的的图可以看出我们要给每一个值定义一种颜色,我们可以创 建色谱,但是色谱的这些参数 IRandomColorRamp pColorRamp = new RandomColorRampClass(); pColorRamp.StartHue = 0; pColorRamp.MinValue = 20; pColorRamp.MinSaturation = 15; pColorRamp.EndHue = 360; pColorRamp.MaxValue = 100; pColorRamp.MaxSaturation = 30; pColorRamp.Size = pCount ; //pColorRamp.Size = pUniqueValueRenderer.ValueCount; bool ok = true; pColorRamp.CreateRamp(out ok); IEnumColors pEnumRamp = pColorRamp.Colors; 内部文档,请勿外传 //IColor pColor = pEnumRamp.Next(); int pIndex =pFt.Fields.FindField(pFieldName); //因为我只有24条记录,所以改变这些,这些都不会超过255或者为负数.求余 int i = 0; while (pFt != null) { IColor pColor = pEnumRamp.Next(); if(pColor ==null) { pEnumRamp.Reset(); pColor = pEnumRamp.Next(); } //以下注释代码为自定义的两种颜色 ,如果不使用随机的颜色,可以采用这样的 //if (i % 2 == 0) //{ // pUnique.AddValue(Convert.ToString(pFt.get_Value(pIndex)), pFieldName, pFillSymbol1 as ISymbol); //} //else //{ // pUnique.AddValue(Convert.ToString(pFt.get_Value(pIndex)), pFieldName, pFillSymbol2 as ISymbol); //} //i++; pFillSymbol1 = new SimpleFillSymbolClass(); 内部文档,请勿外传 pFillSymbol1.Color = pColor; pUnique.AddValue(Convert.ToString(pFt.get_Value(pIndex)), pFieldName, pFillSymbol1 as ISymbol); pFt = pFtCursor.NextFeature(); // pColor = pEnumRamp.Next(); } pGeoFeaturelayer.Renderer = pUnique as IFeatureRenderer; pMapcontrol.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null); } private IRgbColor GetRGBColor(int R, int G, int B)//子类赋给父类 { IRgbColor pRGB; pRGB = new RgbColorClass(); pRGB.Red = R; pRGB.Green = G; pRGB.Green = B; return pRGB; } } 此图为注释掉的代码,自定义的两种颜色生成生成的专题图: 内部文档,请勿外传 此图为随机颜色生成的专题图: 内部文档,请勿外传 7.8.2 分类图 7.8.2.1 和分类渲染图相关的接口 IClassBreaksRenderer 该接口主要被 ClassBreaksRenderer 对象实现,该接口控制了分类渲染对象的主要信息,如分类的字段, 分类间隔值等。 IBasicHistogram 该接口被 BasicTableHistogram 对象实现,IBasicHistogram 接口的 GetHistogram(out datavalus,out Frenquen)方法用于获取相应数值字段的数据和对应的频数。 IClassifyGEN 该接口用于控制对要素类中的数值字段类型的数据进行统计和分类,在 ArcGIS 分类有等间距,等比和标准 差分类等,而这些对应的对象都实现了 IClassifyGEN 接口,如下图: IClassifyGEN 接口的 Classify 方法用于产生分割线,在分类的时候应该特别注意应该将分割线数据的第 一个值付给分类渲染对象的最小值,如: IClassBreaksRenderer.MinimumBreak = IClassifyGEN.ClassBreaks(0) 将分割线数据的第二个数值付给分类渲染对象的第一个值,如: IClassBreaksRenderer.Breaks(0) = IClassifyGEN.ClassBreaks(1) 这个对应关系可以看下图: 内部文档,请勿外传 示例:分类渲染代码: public class ClassRender { public ClassRender(AxMapControl pMapControl, IFeatureLayer pFtLayer, int ClassCount, string pFieldName) { IGeoFeatureLayer pGeolayer; IActiveView pActiveView; pGeolayer = pFtLayer as IGeoFeatureLayer; pActiveView = pMapControl.ActiveView; //以下是为了统计和分类所需要的对象 ITable pTable; IClassifyGEN pClassify;//C#要作为分类对象。 ITableHistogram pTableHist;//相当于一个统计表 IBasicHistogram pBasicHist;//这个对象有一个很重要的方法 double[] ClassNum; int ClassCountResult;//返回分类个数。 内部文档,请勿外传 IHsvColor pFromColor; IHsvColor pToColor;//用于构建另外一个颜色带对象。 IAlgorithmicColorRamp pAlgo; pTable = pGeolayer as ITable; IMap pMap; pMap = pMapControl.Map; pMap.ReferenceScale = 0; pBasicHist = new BasicTableHistogramClass();//也可以实例化pTableHist pTableHist = pBasicHist as ITableHistogram; pTableHist.Table = pTable; pTableHist.Field = pFieldName; object datavalus; object Frenquen; pBasicHist.GetHistogram(out datavalus,out Frenquen);//获得数据和相应的频数。 pClassify = new EqualIntervalClass(); try { pClassify.Classify(datavalus, Frenquen, ref ClassCount); } catch (Exception e) { MessageBox.Show(e.Message); } // 分类完成 ClassNum = (double[])pClassify.ClassBreaks; ClassCountResult = ClassNum.GetUpperBound(0);//返回分级个数。 内部文档,请勿外传 IClassBreaksRenderer pClassBreak; pClassBreak = new ClassBreaksRendererClass(); pClassBreak.Field = pFieldName; pClassBreak.BreakCount = ClassCountResult; pClassBreak.SortClassesAscending = true; pAlgo = new AlgorithmicColorRampClass(); pAlgo.Algorithm = esriColorRampAlgorithm.esriHSVAlgorithm; pFromColor = Hsv(60, 100, 96); pToColor = Hsv(0, 100, 96); pAlgo.FromColor = pFromColor; pAlgo.ToColor = pToColor; pAlgo.Size = ClassCountResult; bool ok; pAlgo.CreateRamp(out ok); IEnumColors pEnumColor; pEnumColor = pAlgo.Colors; pEnumColor.Reset(); IColor pColor; ISimpleFillSymbol pSimFill; for (int indexColor = 0; indexColor <= ClassCountResult - 1; indexColor++) { pColor = pEnumColor.Next(); 内部文档,请勿外传 pSimFill = new SimpleFillSymbolClass(); pSimFill.Color = pColor; // pSimFill.Color = pRgbColor[indexColor ]; pSimFill.Style = esriSimpleFillStyle.esriSFSSolid; //染色 pClassBreak.set_Symbol(indexColor, pSimFill as ISymbol); pClassBreak.set_Break(indexColor, ClassNum[indexColor + 1]); } pGeolayer.Renderer = pClassBreak as IFeatureRenderer; pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null); } public IHsvColor Hsv(int hue, int saturation, int val) { IHsvColor pHsvC; pHsvC = new HsvColorClass(); pHsvC.Hue = hue; pHsvC.Saturation = saturation; pHsvC.Value = val; return pHsvC; } 内部文档,请勿外传 public IRgbColor ColorRgb(int r, int g, int b) { IRgbColor pRGB; pRGB = new RgbColorClass(); pRGB.Red = r; pRGB.Green = g; pRGB.Blue = b; return pRGB; } } 7.8.3 比例图 7.8.3.1 和比例渲染图相关的接口 IProportionalSymbolRenderer 该接口被 ProportionalSymbolRenderer 对象实现,用来控制唯一值渲染的 内部文档,请勿外传 主要信息,如字段设置,最小值和最大值等。 示例:比例渲染代码: public class ProPortialRender { public ProPortialRender(AxMapControl pMapcontrol, IFeatureLayer pFtLayer, string pFieldName) { IGeoFeatureLayer pGeo = pFtLayer as IGeoFeatureLayer; IProportionalSymbolRenderer pProRender = new ProportionalSymbolRendererClass(); pProRender.Field = pFieldName; pProRender.ValueUnit = esriUnits.esriUnknownUnits; ISimpleMarkerSymbol pMarkerSymbol = new SimpleMarkerSymbolClass(); pMarkerSymbol.Style = esriSimpleMarkerStyle.esriSMSCircle; pMarkerSymbol.Size = 2; pMarkerSymbol.Color = GetRGBColor(255, 0, 0); pProRender.MinSymbol = pMarkerSymbol as ISymbol; IDataStatistics pDataStat = new DataStatisticsClass(); IFeatureCursor pFtCursor = pFtLayer.FeatureClass.Search(null, false); pDataStat.Cursor = pFtCursor as ICursor; pDataStat.Field = pFieldName; pProRender.MinDataValue = pDataStat.Statistics.Minimum; 内部文档,请勿外传 pProRender.MaxDataValue = pDataStat.Statistics.Maximum; IFillSymbol pFillS = new SimpleFillSymbolClass(); pFillS.Color = GetRGBColor(239, 228, 190); ILineSymbol pLineS = new SimpleLineSymbolClass(); pLineS.Width = 2; pFillS.Outline = pLineS; ISimpleFillSymbol pSFillS = pFillS as ISimpleFillSymbol; pSFillS.Color = GetRGBColor(100, 100, 253); pProRender.BackgroundSymbol = pFillS; pGeo.Renderer = pProRender as IFeatureRenderer; pMapcontrol.ActiveView.Refresh(); } public IRgbColor GetRGBColor(int r, int g, int b) { IRgbColor pRGB; pRGB = new RgbColorClass(); pRGB.Red = r; 内部文档,请勿外传 pRGB.Green = g; pRGB.Blue = b; return pRGB; } } 7.8.4 简单图 public class SimpleRender { public SimpleRender(AxMapControl pMapcontrol, IFeatureLayer pFtLayer, String Field) { IGeoFeatureLayer pGeolayer; IActiveView pActiveView; pGeolayer = pFtLayer as IGeoFeatureLayer; 内部文档,请勿外传 pActiveView = pMapcontrol.ActiveView; IFillSymbol pFillSymbol; ILineSymbol pLineSymbol; pFillSymbol = new SimpleFillSymbolClass(); pFillSymbol.Color = GetRGBColor(220, 110, 200); pLineSymbol = new SimpleLineSymbolClass(); pLineSymbol.Color = GetRGBColor(255, 120, 105); pLineSymbol.Width = 2; pFillSymbol.Outline = pLineSymbol; ISimpleRenderer pSimpleRender;//用什么符号渲染 pSimpleRender = new SimpleRendererClass(); pSimpleRender.Symbol = pFillSymbol as ISymbol ; pSimpleRender.Description = "China"; pSimpleRender.Label = "SimpleRender"; ITransparencyRenderer pTrans; pTrans = pSimpleRender as ITransparencyRenderer; pTrans.TransparencyField = Field; pGeolayer.Renderer = pTrans as IFeatureRenderer; pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null); //地理图层的渲染对象是一个要素渲染对象,而这个对象是由一些相关对象组成的。 //属性也是一个对象,说明大对象是由小对象组成的。 内部文档,请勿外传 } private IRgbColor GetRGBColor(int R, int G, int B)//子类赋给父类 { IRgbColor pRGB; pRGB = new RgbColorClass(); pRGB.Red = R; pRGB.Green = G; pRGB.Green = B; return pRGB; } } 7.8.5 饼图 内部文档,请勿外传 public PieRender(AxMapControl pMapcontrol, IFeatureLayer pFtLayer, string pFieldName1, string pFieldName2) { IGeoFeatureLayer pGeoFeaLayer = (IGeoFeatureLayer)pFtLayer; IChartRenderer pChartRenderer = new ChartRendererClass(); // Set up the field to draw charts IRendererFields pRenderFields = (IRendererFields)pChartRenderer; pRenderFields.AddField(pFieldName1, pFieldName1); pRenderFields.AddField(pFieldName2, pFieldName2); IPieChartRenderer pPieChartRender = (IPieChartRenderer)pChartRenderer; //计算最大值部分有待补充//////////////////////////////////// //Calculate the max value of the data field to scale the chart //ICursor pCursor = new CursorClass(); IQueryFilter pQueryFilter = new QueryFilterClass(); //IRowBuffer pRow = new RowBufferClass(); ITable pTable = (ITable)pGeoFeaLayer; pQueryFilter.AddField(pFieldName1); ICursor pCursor = pTable.Search(pQueryFilter, true); IDataStatistics pDataStat = new DataStatisticsClass(); IFeatureCursor pFtCursor = pFtLayer.FeatureClass.Search(null, false); pDataStat.Cursor = pFtCursor as ICursor; pDataStat.Field = pFieldName1; double pMax = pDataStat.Statistics.Maximum; IPieChartSymbol pPiechartSymbol = new PieChartSymbolClass(); IFillSymbol pFillSymbol = new SimpleFillSymbolClass(); IChartSymbol pChartSymbol = (IChartSymbol)pPiechartSymbol; pPiechartSymbol.Clockwise = true; pPiechartSymbol.UseOutline = true; ILineSymbol pOutLine = new SimpleLineSymbolClass(); pOutLine.Color = GetRGBColor(255, 0, 255); pOutLine.Width = 1; pPiechartSymbol.Outline = pOutLine; 内部文档,请勿外传 IMarkerSymbol pMarkerSymbol = (IMarkerSymbol)pPiechartSymbol; //finally pChartSymbol.MaxValue = pMax; pMarkerSymbol.Size = 16; //像符号数组中添加 添加符号 ISymbolArray pSymbolArray = (ISymbolArray)pPiechartSymbol; pFillSymbol.Color = GetRGBColor(213, 212, 252); pFillSymbol.Outline = pOutLine; pSymbolArray.AddSymbol((ISymbol)pFillSymbol); ////////////////////////// pFillSymbol.Color = GetRGBColor(183, 242, 122); pFillSymbol.Outline = pOutLine; pSymbolArray.AddSymbol((ISymbol)pFillSymbol); //set up the background pFillSymbol.Color = GetRGBColor(239, 228, 190); pChartRenderer.BaseSymbol = (ISymbol)pFillSymbol; pChartRenderer.UseOverposter = false; pPieChartRender.MinSize = 1; pPieChartRender.MinValue = pDataStat.Statistics.Minimum; pPieChartRender.FlanneryCompensation = false; pPieChartRender.ProportionalBySum = true; pChartRenderer.ChartSymbol = (IChartSymbol)pPiechartSymbol; pChartRenderer.CreateLegend(); pGeoFeaLayer.Renderer = (IFeatureRenderer)pChartRenderer; pMapcontrol.ActiveView.Refresh(); } public IRgbColor GetRGBColor(int r, int g, int b) { IRgbColor pRGB; pRGB = new RgbColorClass(); pRGB.Red = r; pRGB.Green = g; pRGB.Blue = b; return pRGB; } } 内部文档,请勿外传 内部文档,请勿外传 7.8.6 点状图 和点 public class DotRender { IGeoFeatureLayer pGeoLayer; IDotDensityRenderer pDotDensityRenderer;//渲染对象 IDotDensityFillSymbol pDotDensityFill;//渲染填充符号对象,大对象分解小对象, 独立的可看作对象。 IRendererFields pRendFields;//用那个字段渲染。理解层次关系。 ISymbolArray pSymbolArry; public DotRender(AxMapControl pMapControl, IFeatureLayer pFtLayer, double pValue,string pFieldName) { IActiveView pActiveView; this.pGeoLayer = pFtLayer as IGeoFeatureLayer; pActiveView = pMapControl.ActiveView; pDotDensityRenderer = new DotDensityRendererClass(); pRendFields = pDotDensityRenderer as IRendererFields; pRendFields.AddField(pFieldName, pFieldName); //同一个对象的接口的切换, 很方便的。 this.pDotDensityFill = new DotDensityFillSymbolClass(); pDotDensityFill.DotSize =2.46; pDotDensityFill.Color = GetRGBColor(0, 100, 0); pDotDensityFill.BackgroundColor = GetRGBColor(255, 255, 255); 内部文档,请勿外传 pSymbolArry = pDotDensityFill as ISymbolArray;//难道是密度。 ISimpleMarkerSymbol pSimpleMark; pSimpleMark = new SimpleMarkerSymbolClass(); pSimpleMark.Style = esriSimpleMarkerStyle.esriSMSDiamond; pSimpleMark.Size = 2.46; pSimpleMark.Color = GetRGBColor(235, 190, 89); pSymbolArry.AddSymbol(pSimpleMark as ISymbol); pDotDensityRenderer.DotDensitySymbol = pDotDensityFill; pDotDensityRenderer.DotValue = pValue; pDotDensityRenderer.CreateLegend(); pGeoLayer.Renderer = (IFeatureRenderer)pDotDensityRenderer; pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null); } public IRgbColor GetRGBColor(int r, int g, int b) { IRgbColor pRGB; pRGB = new RgbColorClass(); pRGB.Red = r; pRGB.Green = g; pRGB.Blue = b; return pRGB; 内部文档,请勿外传 } } 7.8.7 柱状图 class BarRender { public BarRender(AxMapControl pMapcontrol, IFeatureLayer pFtLayer, String pFieldName1, string pFieldName2) { IGeoFeatureLayer pGeoFeatureLayer = pFtLayer as IGeoFeatureLayer; pGeoFeatureLayer.ScaleSymbols = true; IFeatureClass pFeatureClass = pFtLayer.FeatureClass; //定义柱状图渲染组建对象 IChartRenderer pChartRenderer = new ChartRendererClass(); 内部文档,请勿外传 //定义渲染字段对象并给字段对象实例化为pChartRenderer IRendererFields pRendererFields; pRendererFields = (IRendererFields)pChartRenderer; //向渲染字段对象中添加字段--- 待补充自定义添加 pRendererFields.AddField(pFieldName1, pFieldName1); pRendererFields.AddField(pFieldName2, pFieldName2); ITable pTable= pGeoFeatureLayer as ITable; int[] pFieldIndecies = new int[2]; pFieldIndecies[0] = pTable.FindField(pFieldName1); pFieldIndecies[1] = pTable.FindField(pFieldName2); IDataStatistics pDataStat = new DataStatisticsClass(); IFeatureCursor pFtCursor = pFtLayer.FeatureClass.Search(null, false); pDataStat.Cursor = pFtCursor as ICursor; pDataStat.Field = pFieldName2; double pMax = pDataStat.Statistics.Maximum; // 定义并设置渲染时用的chart marker symbol IBarChartSymbol pBarChartSymbol = new BarChartSymbolClass(); pBarChartSymbol.Width = 6; IChartSymbol pChartSymbol; pChartSymbol = pBarChartSymbol as IChartSymbol; IMarkerSymbol pMarkerSymbol; pMarkerSymbol = (IMarkerSymbol)pBarChartSymbol; IFillSymbol pFillSymbol; //设置pChartSymbol的最大值 pChartSymbol.MaxValue = pMax; // 设置bars的最大高度 pMarkerSymbol.Size = 80; 内部文档,请勿外传 //下面给每一个bar设置符号 //定义符号数组 ISymbolArray pSymbolArray = (ISymbolArray)pBarChartSymbol; //添加第一个符号 pFillSymbol = new SimpleFillSymbolClass(); pFillSymbol.Color = GetRGBColor(193, 252, 179) as IColor; pSymbolArray.AddSymbol(pFillSymbol as ISymbol); //添加第二个符号 pFillSymbol = new SimpleFillSymbolClass(); pFillSymbol.Color = GetRGBColor(145, 55, 251) as IColor; pSymbolArray.AddSymbol(pFillSymbol as ISymbol); pChartRenderer.ChartSymbol = pChartSymbol as IChartSymbol; //pChartRenderer.Label = "AREA"; pFillSymbol = new SimpleFillSymbolClass(); pFillSymbol.Color = GetRGBColor(239, 228, 190); pChartRenderer.BaseSymbol = (ISymbol)pFillSymbol; pChartRenderer.CreateLegend(); pChartRenderer.UseOverposter = false; //将柱状图渲染对象与渲染图层挂钩 pGeoFeatureLayer.Renderer = (IFeatureRenderer)pChartRenderer; //刷新地图和TOOCotrol IActiveView pActiveView = pMapcontrol.ActiveView as IActiveView; pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null); } public IRgbColor GetRGBColor(int r, int g, int b) { IRgbColor pRGB; 内部文档,请勿外传 pRGB = new RgbColorClass(); pRGB.Red = r; pRGB.Green = g; pRGB.Blue = b; return pRGB; } } //pFillSymbol = new SimpleFillSymbolClass(); //pFillSymbol.Color = GetRGBColor(239, 228, 190); //pChartRenderer.BaseSymbol = (ISymbol)pFillSymbol; 内部文档,请勿外传 8 八.栅格数据分析 栅格数据分析介绍 8.1 GIS 数据一般可分为两种主要的类型:栅格数据和矢量数据。矢量数据由弧、结点、线以及它们之间用以 组成地理空间数据的关联关系来定义。真实要素和真实表面可以表示为存储在 GIS 中的矢量数据。栅格数 据则是栅格单元的矩形矩阵,以行和列的形式表示。每个栅格单元均表示地球表面上一块经过定义的方形 区域,其值在整个栅格单元范围内始终保持不变。表面可以通过栅格数据呈现,数据中的每个栅格单元均 表示实际信息的某个值。该值可以为高程数据、污染程度、地下水位高度等。栅格数据从数学的角度来看 就是一个二维矩阵,那么我们对栅格数据的分析也就可以看成是对这个二维矩阵的数学计算. 栅格数据一般可以存储为 ESRI GRID(由一系列文件组成),TIFF 格式(包括一个 TIF 文件和一个 AUX 文件),IMAGINE Image 格式 在 ArcGIS Engine 中一般调用 ISaveAs 接口来保存栅格数据 一个栅格数据集由一个或者多个波段(RasterBand)的数据组成,一个波段就是一个数据矩阵。对于格 网数据(DEM 数据)和单波段的影像数据,表现为仅仅只有一个波段数据的栅格数据集,而对于多光谱 影像数据则表现为具有多个波段的栅格数据集 栅格目录(RasterCatalog)用于显示某个研究区域内各种相邻的栅格数据,这些相邻的栅格数据没有 经过拼接处理合成一副大的影像图. ArcGIS 10 中新出来了一种栅格数据管理模型,镶嵌数据集,镶嵌数据集可以看做是栅格数据集和栅格目 内部文档,请勿外传 录的混合技术.镶嵌数据集有众多的优点而被众多的人接受,镶嵌数据集用于存储、管理、查看和查询各种 大小的栅格和影像数据集。镶嵌数据集是地理数据库中的数据模型,用于管理一组以目录形式存储并以镶 嵌影像方式查看的栅格数据集(影像)。镶嵌数据集具有高级栅格查询功能和处理函数,还可用作提供影 像服务的源。 8.1.1 两个扩展模块 ArcGIS Desktop 中提供了许多用来处理栅格数据的工具,以便您进行数据管理、转换和变换;但要在分析 操作中使用栅格数据,还需要使用其中一个扩展模块。 ArcGIS 提供了两个扩展模块,用来增强栅格数据的分析功能。它们是  ArcGIS Spatial Analyst - 此扩展模块提供了一整套高级空间建模和分析工具,可用来执行集成的 栅格和矢量分析。  ArcGIS 3D Analyst - 此扩展模块可用于高效地显示、分析和生成表面数据,并且提供了用于进行三 维建模和分析的工具。 利用这两个扩展模块可以创建基于栅格的数据,并对其查询,分析,绘图。在空间分析模块中我们可以采 用的数据包括影像,Grid 以及其他的栅格数据集。 空间分析适用于生成 GRID 表面,还包含强大的表面分析工具,包括能够写复杂的地图代数命令。用户可从 现存数据中得到新的数据,分析空间关系和空间特征,比如距离制图,统计分析等. ArcGIS 3D Analyst 模块适用于 3 维可视化分析,能够对表面数据进行高效率的可视化和分析,ArcGIS 3D Analyst 提供了三维建模的高级 GIS 工具,比如挖填分析,通视分析等。 两种扩展模块都都能够计算坡度、坡向、山体阴影等等. 8.1.2 不得不提的一个类库 GeoAnalyst 类库是一个被拥有 3D 扩展和空间分析扩展所共享的类库,包含的核心的空间分析操作. 该类 库基于对栅格类相关的对象和接口的 Datasourcesraster 类库上.以下为 GeoAnalyst Object 的 OMD 图: 内部文档,请勿外传 8.1.3 辅助(帮助 helper) 对象 栅格分析是一个很复杂的操作,在 ArcGIS Engine 中要使用一个分析,往往是由好多对象相互协作才能完成, 上图的上半部分是由一组对象进行栅格分析的集合。在上面的 OMD 中的下部分存在着一些孤立的对象(通常 称为辅助对象)用于指定分析时候的参数,例如,IDW 插值的时候设置距离阈值等。如下面的 kriging 插 值中就用到了两个辅助对象: public void Kkriging(IFeatureClass pFeatureClass) { 内部文档,请勿外传 // FeatureClassDescriptor 对象用于控制和描述插值的参数 IFeatureClassDescriptor pFDescr = new FeatureClassDescriptorClass(); pFDescr.Create(pFeatureClass, null, "Ozone"); // 栅格半径辅助对象用于控制插值的参数 IRasterRadius pRasRadius = new RasterRadiusClass(); object object_Missing = System.Type.Missing; pRasRadius.SetVariable(12, ref object_Missing); IInterpolationOp pInterpOp = new RasterInterpolationOpClass(); IRaster pRasterOut = (IRaster)pInterpOp.Krige((IGeoDataset)pFDescr, esriGeoAnalysisSemiVariogramEnum.esriGeoAnalysisExponentialSemiVariogram, pRasRadius, false, ref object_Missing); } 8.1.4 栅格分析的通常步骤 1,分析环境设置 2,设置输入参数(输入数据等) 3,执行分析操作 4,使用分析结果 分析环境设置 因为 ArcGIS 10 将 Sptial Analyst 和 3D Analyst 工具条的功能移植到了 Toolbox 中,所以在对分析环境 设置的时候是在打开工具后在下面的 Enviroment Settings 中设置, Enviroment Settings 的界面如下: 内部文档,请勿外传 坐标系统 和矢量数据类似,没有坐标系统的栅格数据是没有太大使用价值的。很多基本的空间分析操作都要求 你的数据指定坐标系统。同时可以指定输出结果的坐标系统。 输出栅格形式 缺省情况下,大多数的空间分析操作生成的栅格是 ArcInfo 的 Grid 格式,生成的 Grid 有临时和永久 两种形式. 设置分析范围 在 Processings Extent 中,可以设置空间分析的范围,可以用户指定坐标范围,一般情况是选择等同 于某个图层的空间范围或者当前显示范围 设置 Cell 大小 在空间分析中,分析结果缺省的 cell 大小为输入数据的最大 cell 的大小,用户可以指定 cell 大小或 者指定输出 cell 等同于输入的某个数据的 cell 大小 . 除了上述设置外,还要对工作目录进行设置和分析 Mask,分析掩码是用来标识分析中操作的部分,所以分 析掩码中的空值单元将被屏蔽掉。 ArcGIS Engine 中提供的和分析环境对应的接口是 IRasterAnalysisEnvironment,该接口的属性和方法如 内部文档,请勿外传 下: 而 ArcGIS Engine 中提供的空间分析的类也几乎全部继承了这个接口,在帮助手册中我们可以看到以下类继 承了 IRasterAnalysisEnvironment 接口: 帮助中的这个图可以和 ArcGIS Desktop 中的以下功能对应: 内部文档,请勿外传 这个对应关系也说明了,用 ArcGIS Engine 我们可以实现和 ArcGIS Desktop 相同的功能。 在 ArcGIS Engine 中和空间分析相关的对象被分到 3 个不同的类库当中,每一个类库包含和空间分析相关的 一些对象和接口,之所以这样划分,是因为许可的模式,一些和空间分析相关的对象在 ArcGIS 的核心产品中, 一些类库(GeoAnalyst library )可用于 3D 分析和空间分析当中,还有一些只能被拥有空间分析的模块使用 (IExtractionOp). 空间分析 8.2 8.2.1 和栅格空间分析的相关接口 栅格数据结构简单、直观,非常利于计算机操作和处理,是 GIS 常用的空间基础数据格式。 基于栅格数据 的空间分析是 GIS 空间分析的基础,也是 ArcGIS 的空间分析的重要组成部分.在 ArcGIS Engine 中对栅格 数据的空间分析提供了众多的接口,现在我们对常用的进行简单介绍。 8.2.1.1 IRasterProps 该接口用来描述通用的栅格数据的属性,如行数,列数等.如下面的代码用来获取栅格数据的高和宽 内部文档,请勿外传 void GetRasterProps(IRaster pRaster) { IRasterProps pRasterPros = pRaster as IRasterProps; int pH = pRasterPros.Height;//3973 int pW = pRasterPros.Width;//5629 } 8.2.1.2 IRasterCursor IRasterCursor 接 口 控 制着 一 个 Raster 的 像 素块 (Pixblock), IRasterCurosr 接口 跟 前 面介 绍 的 IFeatureCursor 一样具有一个 NEXT 方法,用于获取下一个 Pixblock,在默认情况下 IRasterCurosr 将整个 Raster 划分为高为 128 的像素块,宽为整个 raster 的宽, IRasterCurosr 每次读取比前一次低于 128 行的 像素块 接口的属性和方法如下: 为了获取 IRasterCursor,我们要用 IRaster::CreateCursor 或者 IRaster2::CreateCursorEx 方法,而这 两个的区别是前者不需要参数,而是系统默认的,而后者是需要我们制定一个大小(IPnt,看清是 IPnt,不是 IPoint) void GetRasterCursorDefault(IRaster pRaster) { IRasterCursor pRasterCursor = pRaster.CreateCursor(); while (pRasterCursor.Next()) { IPixelBlock pPixBlock = pRasterCursor.PixelBlock; 内部文档,请勿外传 int W = pPixBlock.Width; //这个W也就是整个栅格数据记得宽度 int H = pPixBlock.Height; } } void GetRasterCursorCustom(IRaster pRaster) { IRaster2 pRaster2 = pRaster as IRaster2; IPnt pPnt = new PntClass(); pPnt.X = 256; pPnt.Y = 256; //IRasterCursor pRasterCursor2 = pRaster2.CreateCursorEx(null); //参数null的时候,获取PixBlock大小为1*1 while (pRasterCursor2.Next()) { IPixelBlock pPixBlock = pRasterCursor2.PixelBlock; int W = pPixBlock.Width; int H = pPixBlock.Height; } } 内部文档,请勿外传 8.2.1.3 IPixelBlock 我们知道栅格数据的容量一般很大,怎么样调高效率呢,如果按照数组的方式一个一个像素的读取,将整 个栅格数据集都塞进二维数组也不是不可以,但是这样占的内存很多。ArcGIS 用数据库管理栅格数据的时 候是按照 block(默认是 128*128)将数据存在数据库中.而在 ArcGIS Engine 中,IPixelBlock 接口就提 供了类似的功能 8.2.1.4 IRasterLayerExport IRasterLayerExport 接口提供的栅格数据提取功能有限,只能以矩形范围作为提取范围,如果需要更强大 的栅格数据提供功能,请考虑 IExtractionOp 接口,IExtractionOp 接口提供了多边形,圆,属性,矩形 等几种形式作为提取栅格数据. 8.2.1.5 IRasterDataset 该接口用来描述栅格数据集,栅格数据集可以认为是对栅格数据的抽象,用于代表磁盘上的栅格数据,比 如 jpg,img 等。下例为获取一个栅格数据: IRasterWorkspace pRasterWs = GetRasterWorkspace(@".\data\IDW数据"); IRasterDataset pRasterDataset = pRasterWs.OpenRasterDataset("MOD02HKM.A2010068.0310.005.2010069144441.GEO - 副本.tif"); 8.2.1.6 IRasterBandCollection 栅格数据集代表磁盘上的栅格数据文件,我们知道栅格数据是由一个或多个波段组成,这些波段的集合就 是被 IRasterBandCollection 接口控制,这也说明如果要获取某一个具体的波段,应该考虑该接口。 8.2.1.7 IRaster IRaster 接口被 Raster 对象实现,Raster 对象是一个内存对象,IRaster 接口用于控制对栅格数据像素的 读取,此接口还有一个 ResampleMethod 方法用于控制栅格数据的重采样,该接口所包含的方法如下: 内部文档,请勿外传 8.2.1.8 IRasterLayer 该接口和 Featurelayer 接口类似,是用来承载 Raster 对象的重要接口,该接口也可以用来控制栅格数 据的渲染。以下代码为打开一个栅格数据: private void 打开栅格数据ToolStripMenuItem_Click(object sender, EventArgs e) { IRasterWorkspace pRasterWs = GetRasterWorkspace(@".\data\IDW数据"); IRasterDataset pRasterDataset = pRasterWs.OpenRasterDataset("MOD02HKM.A2010068.0310.005.2010069144441.GEO - 副本.tif"); IRasterLayer pRasterLayer = new RasterLayerClass(); pRasterLayer.CreateFromDataset(pRasterDataset); axMapControl1.Map.AddLayer(pRasterLayer as ILayer); ; } 8.2.1.9 IRasterGeometryProc IRasterGeometryProc 接口被 RasterGeometryProc 对象实现,而 RasterGeometryProc 对象只能操作我们 前面提到的 Raster 对象,因为 Raster 对象是内存对象,那么也就意味着对 Raster 的对象的操作是临时的。 该接口是用来对 Raster 进行处理的,该接口包含众多的对 Raster 进行处理的方法,如可以用一个矩形对 栅格数据进行裁剪,或者对 Raster 进行重采样等,更多方法如下图: 内部文档,请勿外传 关于栅格数据重采样: 栅格数据的重采样主要基于三种方法:最邻近采样(NEAREST),双线性 ILINEAR)和三次卷积采样(CUBIC)。 (1).最邻近采样:它用输入栅格数据中最临近栅格值作为输出值。因此,在重采样后的输出栅格中的 每个栅格值, 都是输入栅格数据中真实存在而未加任何改变的值。这种方法简单易用,计算量小,重采 样的速度最快。 (2).双线性采样:此重采样法取待采样点(x,y)点周围四个邻点,在 y 方向(或 X 方向)内插两次, 再在 x 方向(或 y 方向)内插一次,得到(x,y)点的栅格值。 (3).三次卷积采样:这是进一步提高内插精度的一种方法。它的基本思想是增加邻点来获得最佳插值 函数。取待计算点周围相邻的 16 个点,与双线性采样类似,可先在某一方向上内插,如先在 x 方向上, 每四个值依次内插四次,再根据四次的计算结果在 y 方上内插,最终得到内插结果。 代码示例:采用双线性采样: IRasterGeometryProc rasterGeometryProc = new RasterGeometryProcClass(); rasterGeometryProc.Resample(rstResamplingTypes.RSP_CubicConvolution, newCellSize, clipRaster); 示例,改变栅格数据像素的值 public void ChangeRasterValue(IRasterDataset2 pRasterDatset) { IRaster2 pRaster2 = pRasterDatset.CreateFullRaster() as IRaster2; IPnt pPntBlock = new PntClass(); pPntBlock.X = 128; pPntBlock.Y = 128; IRasterCursor pRasterCursor = pRaster2.CreateCursorEx(pPntBlock); IRasterEdit pRasterEdit = pRaster2 as IRasterEdit; if (pRasterEdit.CanEdit()) { IRasterBandCollection pBands = pRasterDatset as IRasterBandCollection; IPixelBlock3 pPixelblock3 = null; int pBlockwidth = 0; int pBlockheight = 0; System.Array pixels; IPnt pPnt = null; object pValue; 内部文档,请勿外传 long pBandCount = pBands.Count; //获取Nodata //IRasterProps pRasterPro = pRaster2 as IRasterProps; //object pNodata = pRasterPro.NoDataValue; do { pPixelblock3 = pRasterCursor.PixelBlock as IPixelBlock3; pBlockwidth = pPixelblock3.Width; pBlockheight = pPixelblock3.Height; for (int k = 0; k < pBandCount; k++) { pixels = (System.Array)pPixelblock3.get_PixelData(k); for (int i = 0; i < pBlockwidth; i++) { for (int j = 0; j < pBlockheight; j++) { pValue = pixels.GetValue(i, j); if (Convert.ToInt32(pValue) == 0) { pixels.SetValue(Convert.ToByte(50), i, j); } } } pPixelblock3.set_PixelData(k, pixels); } pPnt = pRasterCursor.TopLeft; pRasterEdit.Write(pPnt, (IPixelBlock)pPixelblock3); } while (pRasterCursor.Next()); System.Runtime.InteropServices.Marshal.ReleaseComObject(pRasterEdit); } 内部文档,请勿外传 } 原始影像图: 处理后的影像图: 内部文档,请勿外传 /// /// 分割栅格数据 /// /// /// /// /// public void CreateTilesFromRasterDataset(IRasterDataset pRasterDataset, IWorkspace pOutputWorkspace, int pWidth, int pHeight) { IRasterProps pRasterProps = (IRasterProps)pRasterDataset.CreateDefaultRaster(); double xTileSize = pRasterProps.MeanCellSize().X * pWidth; double yTileSize = pRasterProps.MeanCellSize().Y * pHeight; int xTileCount = (int)Math.Ceiling((double)pRasterProps.Width / pWidth); int yTileCount = (int)Math.Ceiling((double)pRasterProps.Height / pHeight); IEnvelope pExtent = pRasterProps.Extent; IEnvelope pTileExtent = new EnvelopeClass(); ISaveAs pSaveAs = null; for (int i = 0; i < xTileCount; i++) { for (int j = 0; j < yTileCount; j++) { pRasterProps = (IRasterProps)pRasterDataset.CreateDefaultRaster(); pTileExtent.XMin = pExtent.XMin + i * xTileSize; pTileExtent.XMax = pTileExtent.XMin + xTileSize; pTileExtent.YMin = pExtent.YMin + j * yTileSize; pTileExtent.YMax = pTileExtent.YMin + yTileSize; pRasterProps.Height = pHeight; pRasterProps.Width = pWidth; pRasterProps.Extent = pTileExtent; pSaveAs = (ISaveAs)pRasterProps; pSaveAs.SaveAs("tile_" + i + "_" + j + ".tif", pOutputWorkspace, "TIFF"); } 内部文档,请勿外传 } } 重要名词 8.3 8.3.1 什么是表面 3D 表面模型是三维空间中要素(真实或假想)的一种数字表达形式。比如地下天然气矿床以及用于测定地 下水位深度的深井组成的网络,但表面也可以是派生的或假想的。某种特定细菌在每个井中的污染程度就 是派生表面的一个示例。这些污染程度级别也可以绘制成 3D 表面地图。而假想 3D 表面通常在视频游戏 或计算机模拟环境中可以见到。 通常可以使用专门设计的算法来获取或计算 3D 表面,这些算法对点、线或面数据进行采样然后将其转换 为数字 3D 表面。ArcGIS 可以创建和存储三种类型的表面模型:栅格、TIN 和 terrain 数据集(没见过)。 8.3.2 栅格表面 GIS 数据一般可分为两种主要的类型:栅格数据和矢量数据。矢量数据由弧、结点、线以及它们之间用以 组成地理空间数据的关联关系来定义。真实要素和真实表面可以表示为存储在 GIS 中的矢量数据。栅格数 据则是栅格单元的矩形矩阵,以行和列的形式表示。每个栅格单元均表示地球表面上一块经过定义的方形 区域,其值在整个栅格单元范围内始终保持不变。表面可以通过栅格数据呈现,数据中的每个栅格单元均 表示实际信息的某个值。该值可以为高程数据、污染程度、地下水位高度等。 8.3.3 TIN 表面 不规则三角网 (TIN) 以数字方式来表示表面形态。TIN 是基于矢量的数字地理数据的一种形式,通过将一 系列折点(点)组成三角形来构建。通过由一系列边连接各个折点,形成三角网。可通过多种不同的插值 方法形成这些三角形,例如 Delaunay 三角测量法或距离排序法。ArcGIS 支持 Delaunay 三角测量方法。 表面分析 8.4 在栅格表面分析的时候经常会遇到 ISurfaceop 和 Isurface 接口,为此我们对这两个接口做一下介绍,虽然 这两个接口只相差两个字母,但是功能和用法却大不一样. 8.4.1 ISurfaceOp 和 ISurface 的区别 ISurfaceOp 接 口 被 RasterSurfaceOpClass 类 实 现 , 而 RasterSurfaceOpClass 类是在 内部文档,请勿外传 ESRI.ArcGIS.GeoAnalyst 类库中,通过前面的介绍我们知道 ESRI.ArcGIS.GeoAnalyst 类库中的接口和类是 被拥有 3D 扩展模块和空间扩展模块的用户所拥有.在帮助文档中我们可以看到 ISurfaceOp 接口所拥有的属 性和方法如下: 在空间分析模块中我们可以看到下图 这样的比较使得我们知道 ISurfaceOp 接口完全可以实现空间分析下 的 Surface 工具集中的功能而 ISurface 接口被 RasterSurfaceClass 类实 现,而 RasterSurfaceClass 类是在 ESRI.ArcGIS.Analyst3D 类库中,也就 是这个接口只能在 3D 分析中使用,Isurface 接口拥有更多的方法和属 性,以下为部分截图: 内部文档,请勿外传 8.4.2 专属于空间分析的表面分析(邻域分析,单元分析,栅格 提取,代数计算等) 8.4.2.1 邻域分析 public IGeoDataset CreateNeighborhoodOpBlockStatisticsRaster(IGeoDataset GeoDataset) { INeighborhoodOp pNeighborhoodOP = new RasterNeighborhoodOpClass(); IRasterNeighborhood pRasterNeighborhood = new RasterNeighborhoodClass(); pRasterNeighborhood.SetRectangle(3, 3, esriGeoAnalysisUnitsEnum.esriUnitsCells); IGeoDataset pGeoOutput = pNeighborhoodOP.BlockStatistics(GeoDataset,esriGeoAnalysisStatisticsEnum.esriGeoAnalysisStat sMean, pRasterNeighborhood, true); 内部文档,请勿外传 return pGeoOutput; } 8.4.2.2 裁剪 public IGeoDataset CreateExtractOp(IGeoDataset pGeoDataset, ESRI.ArcGIS.Geometry.IPolygon pPolygone) { ESRI.ArcGIS.SpatialAnalyst.IExtractionOp pExtractionOp = new ESRI.ArcGIS.SpatialAnalyst.RasterExtractionOpClass(); ESRI.ArcGIS.Geodatabase.IGeoDataset pGeoOutput = pExtractionOp.Polygon(pGeoDataset, pPolygone, true); return pGeoOutput; } 8.4.2.3 密度分析 密度分析的对象为 RasterDensityOp,而该对象实现了 IDensityOp 接 口,该接口里面定义了密度分析的几种方法,比如点密度,核密度等, 如下图: 内部文档,请勿外传 public IRaster DensityAnalyst(IFeatureClass pFeatureClass,string pFieldName, double pCellSize, double pRadius) { //辅助对象,设置密度分析时候的参数 IFeatureClassDescriptor pFDescr = new FeatureClassDescriptorClass(); pFDescr.Create(pFeatureClass, null, pFieldName); IDensityOp pDensityOp = new RasterDensityOpClass(); //设置环境 IRasterAnalysisEnvironment pEnv = pDensityOp as IRasterAnalysisEnvironment; object object_cellSize = (System.Object)pCellSize; pEnv.SetCellSize(esriRasterEnvSettingEnum.esriRasterEnvValue, ref object_cellSize); System.Double double_radio_dis = pRadius; object object_radio_dis = (System.Object)double_radio_dis; object Missing = Type.Missing; //核函数密度制图方法生成栅格数据 IRaster pRaster = pDensityOp.KernelDensity(pFDescr as IGeoDataset, ref object_radio_dis, ref Missing) as IRaster; return pRaster; } 内部文档,请勿外传 8.4.2.4 IDistanceOp 接口 public IGeoDataset CreateDistanceOpCostPathRaster(IGeoDataset pGeoDataset1, IGeoDataset pGeoDataset2, IGeoDataset pGeoDataset3) { IDistanceOp pDistanceOp = new RasterDistanceOpClass(); IGeoDataset pGeoDatasetOutput = pDistanceOp.CostPath(pGeoDataset1, pGeoDataset2, pGeoDataset3, esriGeoAnalysisPathEnum.esriGeoAnalysisPathForEachZone); return pGeoDatasetOutput; } 8.4.2.5 RasterConvertHelperClass 接口 public void ConvertRaterToLineFeature(string pRasterWs,string pRasterDatasetName,string pShapeFileName) { IRasterDataset pRasterDataset = GetRasterWorkspace(pRasterWs).OpenRasterDataset(pRasterDatasetName); 内部文档,请勿外传 IConversionOp pConversionOp = new RasterConversionOpClass(); IRasterAnalysisEnvironment pEnv = (IRasterAnalysisEnvironment)pConversionOp; IWorkspaceFactory pWorkspaceFactory = new RasterWorkspaceFactoryClass(); IWorkspace pWorkspace = pWorkspaceFactory.OpenFromFile(pRasterWs, 0); pEnv.OutWorkspace = pWorkspace; IWorkspaceFactory pShapeFactory = new ShapefileWorkspaceFactoryClass(); IWorkspace pShapeWS = pShapeFactory.OpenFromFile(pRasterWs, 0); System.Object pDangle = (System.Object)1.0; IGeoDataset pFeatClassOutput = pConversionOp.RasterDataToLineFeatureData((IGeoDataset)pRasterDataset, pShapeWS, pShapeFileName, false, false, ref pDangle); } /// /// 要素转成Grid /// /// /// /// /// /// public IGeoDataset CreateGridFromFeatureClass(IFeatureClass pFeaureClass, String pRasterWorkspaceFolder, double pCellsize ,string pGridName) { IGeoDataset pGeoDataset = (ESRI.ArcGIS.Geodatabase.IGeoDataset)pFeaureClass; // Explicit Cast ISpatialReference pSpatialReference = pGeoDataset.SpatialReference; IConversionOp pConversionOp = new ESRI.ArcGIS.GeoAnalyst.RasterConversionOpClass(); IWorkspaceFactory pWorkspaceFactory = new ESRI.ArcGIS.DataSourcesRaster.RasterWorkspaceFactoryClass(); IWorkspace pWorkspace = pWorkspaceFactory.OpenFromFile(pRasterWorkspaceFolder, 0); 内部文档,请勿外传 IRasterAnalysisEnvironment pAnalysisEnvironment = (ESRI.ArcGIS.GeoAnalyst.IRasterAnalysisEnvironment)pConversionOp; // Explicit Cast pAnalysisEnvironment.OutWorkspace = pWorkspace; ESRI.ArcGIS.Geometry.IEnvelope pEnvelope = new ESRI.ArcGIS.Geometry.EnvelopeClass(); pEnvelope = pGeoDataset.Extent; object pObjectCellSize = (System.Object)pCellsize; pAnalysisEnvironment.SetCellSize(ESRI.ArcGIS.GeoAnalyst.esriRasterEnvSettingEnum.esriRasterE nvValue, ref pObjectCellSize); object object_Envelope = (System.Object)pEnvelope; object object_Missing = Type.Missing; pAnalysisEnvironment.SetExtent(ESRI.ArcGIS.GeoAnalyst.esriRasterEnvSettingEnum.esriRasterEnv Value, ref object_Envelope, ref object_Missing); pAnalysisEnvironment.OutSpatialReference = pSpatialReference; IRasterDataset pRasterDataset = new ESRI.ArcGIS.DataSourcesRaster.RasterDatasetClass(); pRasterDataset = pConversionOp.ToRasterDataset(pGeoDataset, "GRID", pWorkspace, pGridName); IGeoDataset pGeoOutput = (ESRI.ArcGIS.Geodatabase.IGeoDataset)pRasterDataset; return pGeoOutput; } 内部文档,请勿外传 8.4.3 3D 分析 8.4.3.1 获取高程的代码 double GetElevation(IRaster pRaster, IPoint point) { IRasterSurface pRasterSurface = new RasterSurfaceClass(); pRasterSurface.PutRaster(pRaster, 0); ISurface pSurface = pRasterSurface as ISurface; return pSurface.GetElevation(point); } 8.4.3.2 通视分析代码 if(axMapControl1.Map.get_Layer(0)!=null) { IRasterLayer pRasterLayer = axMapControl1.Map.get_Layer(0) as IRasterLayer; IRasterSurface pRasterSurface = new RasterSurfaceClass(); pRasterSurface.PutRaster(pRasterLayer.Raster, 0); ISurface pSurface = pRasterSurface as ISurface; IPolyline pPolyline = axMapControl1.TrackLine() as IPolyline; IPoint pPoint =null ; IPolyline pVPolyline =null; IPolyline pInPolyline= null; object pRef=0.13; bool pBool =true; //获取Dem的高程 double pZ1 = pSurface.GetElevation(pPolyline.FromPoint); double pZ2= pSurface.GetElevation(pPolyline.ToPoint); 内部文档,请勿外传 IPoint pPoint1 = new PointClass(); pPoint1.Z = pZ1; pPoint1.X = pPolyline.FromPoint.X; pPoint1.Y = pPolyline.FromPoint.Y; IPoint pPoint2 = new PointClass(); pPoint2.Z = pZ2; pPoint2.X = pPolyline.ToPoint.X; pPoint2.Y = pPolyline.ToPoint.Y; pSurface.GetLineOfSight(pPoint1, pPoint2, out pPoint, out pVPolyline, out pInPolyline, out pBool, false, false, ref pRef); if (pVPolyline != null) { IElement pLineElementV = new LineElementClass(); pLineElementV.Geometry = pVPolyline; ILineSymbol pLinesymbolV = new SimpleLineSymbolClass(); pLinesymbolV.Width = 2; IRgbColor pColorV = new RgbColorClass(); pColorV.Green =255; pLinesymbolV.Color = pColorV; ILineElement pLineV = pLineElementV as ILineElement; pLineV.Symbol = pLinesymbolV; 内部文档,请勿外传 axMapControl1.ActiveView.GraphicsContainer.AddElement(pLineElementV, 0); } if (pInPolyline != null) { IElement pLineElementIn = new LineElementClass(); pLineElementIn.Geometry = pInPolyline; ILineSymbol pLinesymbolIn = new SimpleLineSymbolClass(); pLinesymbolIn.Width = 2; IRgbColor pColorIn = new RgbColorClass(); pColorIn.Red = 255; pLinesymbolIn.Color = pColorIn; ILineElement pLineIn = pLineElementIn as ILineElement; pLineIn.Symbol = pLinesymbolIn; axMapControl1.ActiveView.GraphicsContainer.AddElement(pLineElementIn, 1); } axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null); 内部文档,请勿外传 8.4.3.3 Tin 表面分析 ITinSurface Interface Tin 的表面分析得益于 ITinSurface 接口,而 ITinSurface 继承了 ISurface 接口,我们知道空间分析和 3D 分析在栅格表面分析有部分重合,但是 Tin 的表面分析只能在 3D 分析下进行.Tin 是一个用来模拟地表的 组件类以便进行相应的分析. 内部文档,请勿外传 打开 Tin public ITinLayer GetTINLayer(string pPath)//打开TIN文件 { ITinWorkspace pTinWorkspace; IWorkspace pWS; IWorkspaceFactory pWSFact = new TinWorkspaceFactoryClass(); ITinLayer pTinLayer = new TinLayerClass(); string pathToWorkspace = System.IO.Path.GetDirectoryName(pPath); string tinName = System.IO.Path.GetFileName(pPath); ITin pTin; pWS = pWSFact.OpenFromFile(pathToWorkspace, 0); pTinWorkspace = pWS as ITinWorkspace; if (pTinWorkspace.get_IsTin(tinName)) { pTin = pTinWorkspace.OpenTin(tinName); pTinLayer.Dataset = pTin; pTinLayer.ClearRenderers(); return pTinLayer; } else { MessageBox.Show("该目录不包含Tin文件"); return null; } } 或者下面的代码 ITinAdvanced2 pTin = new TinClass(); pTin.Init(@"E:\arcgis\Engine\IDW数据\dvtin"); ITinLayer pTinLayer = new TinLayerClass(); 内部文档,请勿外传 pTinLayer.Dataset = pTin; axMapControl1.Map.AddLayer(pTinLayer as ILayer); 创建 Tin private void 创建TinToolStripMenuItem_Click(object sender, EventArgs e) { IFeatureClass pFeatureClass = GetFeatureClass(@"E:\arcgis\Engine\IDW数据", "山东 20100321"); IField pField = pFeatureClass.Fields.get_Field(pFeatureClass.FindField("H")); ITin pTin = CreateTin(pFeatureClass, pField, @"E:\arcgis\Engine\IDW 数据 \TinTest"); ITinLayer pTinLayer = new TinLayerClass(); pTinLayer.Dataset = pTin; axMapControl1.Map.AddLayer(pTinLayer as ILayer); } /// /// 创建Tin /// /// /// /// /// public ITin CreateTin(IFeatureClass pFeatureClass, IField pField, string pPath) { IGeoDataset pGeoDataset = pFeatureClass as IGeoDataset; ITinEdit pTinEdit = new TinClass(); pTinEdit.InitNew(pGeoDataset.Extent); object pObj = Type.Missing; pTinEdit.AddFromFeatureClass(pFeatureClass, null, pField, null, esriTinSurfaceType.esriTinMassPoint, ref pObj); pTinEdit.SaveAs(pPath, ref pObj); pTinEdit.Refresh(); return pTinEdit as ITin; 内部文档,请勿外传 } Tin 生成等高线 private void Tin2Contour(ITin pTin,IFeatureClass pFeatureClass) { ITinSurface pTinSurface = pTin as ITinSurface; //不要启动编辑,因为这个接口会在要素类中添加字段 pTinSurface.Contour(0, 2, pFeatureClass, "Height", 0); } private void 等高线ToolStripMenuItem_Click(object sender, EventArgs e) { ITinLayer pTinLayer = GetTINLayer(@"E:\arcgis\Engine\IDW数据\dvtin"); IFeatureClass pFeatureClass = GetFeatureClass(@"E:\arcgis\Engine\IDW 数据", "TinContour"); Tin2Contour(pTinLayer.Dataset as ITin, pFeatureClass); IFeatureLayer pFeatLayer = new FeatureLayerClass(); pFeatLayer.Name = "等高线"; pFeatLayer.FeatureClass = pFeatureClass; axMapControl1.Map.AddLayer(pFeatLayer as ILayer); 内部文档,请勿外传 } AE 创建泰森多边形 private void 泰森多边形ToolStripMenuItem_Click(object sender, EventArgs e) { ITinLayer pTinLayer = GetTINLayer(@"E:\arcgis\Engine\IDW数据\dvtin"); IFeatureClass pFeatureClass = GetFeatureClass(@"E:\arcgis\Engine\IDW数据", "Vr"); CreateVr(pFeatureClass, pTinLayer.Dataset as ITin); IFeatureLayer pFeatLayer = new FeatureLayerClass(); pFeatLayer.Name = "泰森多边形"; pFeatLayer.FeatureClass = pFeatureClass; 内部文档,请勿外传 axMapControl1.Map.AddLayer(pFeatLayer as ILayer); } /// /// 创建泰森多边形 /// /// /// void CreateVr(IFeatureClass pFeatureClass ,ITin pTin) { ITinNodeCollection pTinColl = pTin as ITinNodeCollection; pTinColl.ConvertToVoronoiRegions(pFeatureClass, null, null, "", ""); } 分析助手 8.5 栅格数据分析异常复杂和庞大,但是 Esri 提供给了我们连个利器 ESRI.ArcGIS.SpatialAnalystTools 和 内部文档,请勿外传 ESRI.ArcGIS.Analyst3Dtools,只要拥有相关的许可,那么这些分析都会变得容易。 波段计算和影像金字塔 8.6 /// /// 波段统计和创建影像金字塔 /// /// public static void CalculateStatsAndPyramids(IRasterDataset pRasterDataset) { IRasterBandCollection pBandColl = (IRasterBandCollection)pRasterDataset; // 波段统计 for (int i = 0; i < pBandColl.Count; i++) { IRasterBand pRasterBand = pBandColl.Item(i); pRasterBand.ComputeStatsAndHist(); } //创建金字塔 IRasterPyramid pRasterPyramids = (IRasterPyramid)pRasterDataset; if (pRasterPyramids.Present == false) { pRasterPyramids.Create(); } } 分级渲染 8.7 public IRasterRenderer ClassifyRenderer(ESRI.ArcGIS.Geodatabase.IRasterDataset pRasterDataset) { try { //Create the classify renderer. IRasterClassifyColorRampRenderer pClassifyRenderer = new RasterClassifyColorRampRendererClass(); IRasterRenderer pRasterRenderer = (IRasterRenderer)pClassifyRenderer; 内部文档,请勿外传 //Set up the renderer properties. IRaster pRaster = pRasterDataset.CreateDefaultRaster(); pRasterRenderer.Raster = pRaster; pClassifyRenderer.ClassCount = 9; pRasterRenderer.Update(); IRgbColor pFromColor = new RgbColorClass(); pFromColor.Red = 255; pFromColor.Green = 0; pFromColor.Blue = 0; IRgbColor pToColor = new RgbColorClass(); pToColor.Red = 0; pToColor.Green = 255; pToColor.Blue = 255; //Set the color ramp for the symbology. IAlgorithmicColorRamp pRamp = new AlgorithmicColorRampClass(); pRamp.Size = 9; pRamp.FromColor = pFromColor; pRamp.ToColor = pToColor; bool pBoolColorRamp; pRamp.CreateRamp(out pBoolColorRamp); //Create the symbol for the classes. IFillSymbol pFillSymbol = new SimpleFillSymbolClass(); for (int i = 0; i < pClassifyRenderer.ClassCount; i++) { pFillSymbol.Color = pRamp.get_Color(i); pClassifyRenderer.set_Symbol(i, (ISymbol)pFillSymbol); pClassifyRenderer.set_Label(i, Convert.ToString(i)); } return pRasterRenderer; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return null; } } 内部文档,请勿外传 渲染 8.8 public IRasterRenderer ColormapRenderer(ESRI.ArcGIS.Geodatabase.IRasterDataset pRasterDataset) { try { IRasterRenderer pColormapRender = new RasterColormapRendererClass(); IRaster pRaster = pRasterDataset.CreateDefaultRaster(); pColormapRender.Raster = pRaster; return pColormapRender; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return null; } } 拉伸 8.9 public IRasterRenderer StretchRenderer(ESRI.ArcGIS.Geodatabase.IRasterDataset pRasterDataset) { try { //Define the from and to colors for the color ramp. IRgbColor pFromColor = new RgbColorClass(); pFromColor.Red = 255; pFromColor.Green = 0; pFromColor.Blue = 0; IRgbColor pToColor = new RgbColorClass(); pToColor.Red = 0; pToColor.Green = 255; pToColor.Blue = 0; //Create the color ramp. IAlgorithmicColorRamp pRamp = new AlgorithmicColorRampClass(); pRamp.Size = 255; pRamp.FromColor = pFromColor; pRamp.ToColor = pToColor; bool createColorRamp; pRamp.CreateRamp(out createColorRamp); 内部文档,请勿外传 //Create a stretch renderer. IRasterStretchColorRampRenderer pStretchRenderer = new RasterStretchColorRampRendererClass(); IRasterRenderer pRasterRenderer = (IRasterRenderer)pStretchRenderer; //Set the renderer properties. IRaster pRaster = pRasterDataset.CreateDefaultRaster(); pRasterRenderer.Raster = pRaster; pRasterRenderer.Update(); pStretchRenderer.BandIndex = 0; pStretchRenderer.ColorRamp = pRamp; //Set the stretch type. IRasterStretch pStretchType = (IRasterStretch)pRasterRenderer; pStretchType.StretchType = esriRasterStretchTypesEnum.esriRasterStretch_StandardDeviations; pStretchType.StandardDeviationsParam = 2; return pRasterRenderer; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return null; } } 9 九.编辑 编辑对于 GIS 来说无处不在,对空间数据的更改,增加和删除都是随数据的编辑,而这个最常用的操作幕 后又是怎么运作的呢?我们从下面进行详细介绍。 9.1.1 编辑是如何进行的? 在 ArcGIS Engine 中编辑操作必须位于一个编辑会话中,且编辑编辑操作不能在其他编辑操作嵌套。下图 显示了使用 IWorkspaceEdit 进行操作的流程。该图不包括使用撤消和重做操作: 内部文档,请勿外传 Geodatabase 编辑是长事务操作,一个编辑会话对应于一个长事务,应用程序在编辑会话中看到的仅是该 应用程序对数据更改所做的变化数据变化,其它同时执行的的更改(若允许)在编辑会话被保存或丢弃之前 是看不到的(SDE 数据库可以多人同时编辑)。 9.1.2 编辑会话 应用程序可以使用地理数据库编辑会话和编辑操作管理数据库事务。编辑会话和编辑操作操作提供了几个 好处: 1,将编辑组合成事务。如果在编辑完成之前发生错误,这个事物就会回滚。 2,可选的由数据库维护“重做”和“撤销“数据库操作堆栈。 编 辑 操作 后停 止, 它被放置在撤消堆栈。应用程序开发人员可以遍历撤消 /重 做堆 栈, 以便 调 用 UndoEditOperation 和 RedoEditOperation。, 3,编辑会话和编辑操作允许出现批量更新,在编辑 SDE 数据库的时候以便提供显着的性能优势。 4,在地理数据库允许多个用户同时并发编辑数据,在编辑会话期间用户不会看到其他用户所做的变化,直 到会话结束的时候才可以。 5,地理数据库保证在一个编辑会话中存在一个唯一的从数据库中检索行对象实例。如果已经在编辑会话中 实例化了该实例,任何访问非回收的对象将返回该对象在内存中的对象实例。 9.1.3 编辑操作 ArcGIS Engine 的编辑相当于数据库中一个事物的操作,我们可以清楚的知道在 ArcMap 中,如果我们要做 内部文档,请勿外传 一个编辑,第一步就是启动编辑,然后操作,最后保存,在我们启动编辑的时候实际上相当于开启了一个 和关系型数据库中对应的一个事物,在我们开启编辑,保存操作,停止编辑都对应了 ArcGIS Engine 中提 供的如下三个方法: ArcGIS Engine 关系型数据库 StartEditOperation Start a transaction StopEditOperation Commit the transaction AbortEditOperation Abort the transaction 下面给出了一个启动编辑会话和编辑操作的简单示例代码,该例子的作用是给数据库中的一个表创建一个 新的行。 public void StartEdit(IWorkspace pWorkspace, ITable pTable) { IWorkspaceEdit pWorkspaceEdit = (IWorkspaceEdit)pWorkspace; //启动编辑会话 pWorkspaceEdit.StartEditing(false); //启动编辑操作 pWorkspaceEdit.StartEditOperation(); IRow pRow = pTable.CreateRow(); pRow.set_Value(2, "练习"); pRow.Store(); //结束编辑操作 pWorkspaceEdit.StopEditOperation(); //结束编辑会话 pWorkspaceEdit.StopEditing(true); } 内部文档,请勿外传 IWorkspaceEdit 9.2 IWorkspaceEdit 接口是 ArcGIS Engine 实现空间数据编辑的重要接口,它让程序启动或者停止一个编辑流 程,在这个编辑流程内,可以对数据库中的数据进行删除,添加,更改。我们所有的对要素或者属性的修 改都可以放到一个会话中,这个会话就相当于 ArcMap 中的 Start Edting,当会话启动后,我们就可以在 图层中对数据进行修改,删除等操作。如果我们已经在使用 IEngineEdito 接口编辑 Geodatabase,那就不 应该使用该接口控制编辑。 IWorkspaceEdit 接口的方法和属性如下: 这个例子中,我创建了 1000 条要素,并结合缓冲将数据写到文件中,并且添加了时间统计,当然数据是我 捏造的,还请原谅,这个花费的时间为 0.978 秒,速度还行。 /// /// 编辑的全局变量 /// /// /// private void button6_Click_1(object sender, EventArgs e) { IWorkspaceFactory pWsF = new ShapefileWorkspaceFactory(); IFeatureWorkspace pFtWs = pWsF.OpenFromFile(@"E:\arcgis\Engine\数据", 0) as IFeatureWorkspace; IFeatureClass pFtClass = pFtWs.OpenFeatureClass("edit"); IFeatureLayer pFt = new FeatureLayerClass(); pFt.FeatureClass = pFtClass; 内部文档,请勿外传 pFt.Name = "画线"; axMapControl1.Map.AddLayer(pFt as ILayer); axMapControl1.Refresh(); //为了进一步说明Idataset,我特意从下面的代码 IDataset pDataset = pFtClass as IDataset; IWorkspace pWs = pDataset.Workspace; pWsEdit = pWs as IWorkspaceEdit; pWsEdit.StartEditing(true); pWsEdit.StartEditOperation(); pBoolStart = pWsEdit.IsBeingEdited(); IFeatureBuffer pFeatureBuffer = pFtClass.CreateFeatureBuffer(); IFeatureCursor pFtCusor = pFtClass.Insert(true); ESRI.ArcGIS.Geometry.IPolyline polyline = new ESRI.ArcGIS.Geometry.PolylineClass(); ESRI.ArcGIS.Geometry.IPoint pPoint = new ESRI.ArcGIS.Geometry.PointClass(); ESRI.ArcGIS.Geometry.IPoint pPoint2 = new ESRI.ArcGIS.Geometry.PointClass(); for (int i = 0; i < 1000; i++) { pPoint.X = 48 + i * 102; pPoint.Y = 65 + i * 10; 内部文档,请勿外传 polyline.FromPoint = pPoint; pPoint2.X = 480 + i * 10; pPoint2.Y = 615 + i * 102; polyline.ToPoint = pPoint2; pFeatureBuffer.Shape = polyline; pFeatureBuffer.set_Value(2, i); object pFeatureOID = pFtCusor.InsertFeature(pFeatureBuffer); } pFtCusor.Flush(); pWsEdit.StopEditing(true); axMapControl1.Refresh(); } 内部文档,请勿外传 IMultiuserWorkspaceEdit 9.3 通过这个接口的字面意思就可以看出这个接口是为多用户编辑设计的,也就是针对 SDE 数据库的。该接口 只有三个方法,如下图: IMultiuserWorkspaceEdit.StartMultiuserEditing 是 SDE 数据库编辑的入口。相当于 IWorksspaceEditor.start().而该方法可开启 SDE 数据库的版本会话和非版本会话,这个区别是因为 IMultiuserWorkspaceEdit.StartMultiuserEditing 这个方法需要一个参数,这个 该方法需要一个参数是枚举类型的如下图所示 内部文档,请勿外传 编辑 SDE 版本的代码示例 public void StartSDEVersion(IWorkspace pWorkspace) { IMultiuserWorkspaceEdit pMWorkspaceEdit = (IMultiuserWorkspaceEdit)pWorkspace; IWorkspaceEdit pWorkspaceEdit = (IWorkspaceEdit)pWorkspace; //启动版本编辑会话 pMWorkspaceEdit.StartMultiuserEditing(esriMultiuserEditSessionMode.esriMESMVersioned); //启动编辑操作 pWorkspaceEdit.StartEditOperation(); //编辑过程... //结束编辑操作 pWorkspaceEdit.StopEditOperation(); //结束编辑会话 pWorkspaceEdit.StopEditing(true); } IEngineEditor 9.4 IEngineEditor 接口被 EngineEditor 对象继承并实现,EngineEditor 接口就相当于 ArcMap 中的编辑工具 条中的 Editor 一样,控制着整个编辑的生命周期(注意,我们可以在 ToolbarConrol 上将Editor 模拟出来, 然后按照 ArcGIS 桌面中的 Start Editing,同时也可以采用代码的方式,但是这两种方式在一个应用程序 中只能选取一个) ArcGIS 桌面提供的编辑工具条 内部文档,请勿外传 ArcGIS Engine 控件模拟的编辑工具条, IEngineEditor 接口的方法和属性如下: IEngineEditor 里面有好多 Task,Task 可以认为是为了执行一个操作而 封装的一个流程,可能有点抽象,我们可以回想一下 ArcMap 中的情 况,当我们在 ArcMAP 中要创建一个新的要素的时候,我们要将 Task 中选择为 Create new feature,然后在 Editor 工具条上结合草图工具实 现对数据的编辑。而且这个 Task 对我们是开放的,我们可以自定义 操作 IEngineEditTask 接口的方法和属性如下: 内部文档,请勿外传 IEngineEditor 接口和 IWorkspaceEdit 的比较 IEngineEditor IWorkspaceEdit Start an edit session StartEditing StartEditing Stop an edit session StopEditing StopEditing Start an edit operation StartOperation StartEditOperation Stop an edit operation StopOperation StopEditOperation Abort an edit operation AbortOperation AbortEditOperation Rollback an edit operation UndoOperation UndoEditOperation Reapply an edit operation RedoOperation RedoEditOperation Commit edits StopEditing(True) StopEditing(True) 内部文档,请勿外传 在上一个例子中,我们对 IWorkspaceEdit 接口做了介绍,通过 IWorkspaceEdit 接口,我们实现了对要素的添加,而在编辑的时候, 我还提到了另外一个接口 IEngineEditor。 IEngineEditor 接口被 EngineEditorClass 实现,而 EngineEditor 对象相当于我们在 ArcMap 中编辑的时候用到的 Editor 工具条,回想 下我们要在一个线图层中添加一条新的要素,我们往往是按照下面的 步骤 1)Start Editing 2)将 Targetlayer 设置为我们要编辑的图层 3)设置 Task 为 Create new Feature 4)然后使用草图工具开始编辑。 其实这 4 个步骤分别对应了四个接口,而这 4 个接口中有 3 个接口都 EngineEditorClass 直接实现,这三个个接口分别是: IEngineEditor IEngineEditLayer IEngineEditSketch 在这里还有一个 ITask 接口,ITask 接口为 IEngineEditor 接口的一 个属性 Task 是一个任务流,ArcGIS 中的这个 Task 就是对一系列操作的封 内部文档,请勿外传 装,也就是用草图工具所做的一系列动(Mousedown,Mousemove 等)。 示例等高线赋值 9.5 以前在网上看过一个用 VB.NET 写的等高线批量赋值(AO),我参考的那个好像是 ”等高线自动标注组件 开发“,那个代码是有问题的,我参考其中的代码,用 C#重新写了(Engine),迁移到 ArcGIS 10 中,现在 跟大家分享,主要代码如下: 9.5.1 主要执行代码: IWorkspaceFactory pWsF = new ShapefileWorkspaceFactory(); IFeatureWorkspace pFtWs = pWsF.OpenFromFile(@"E:\arcgis\Engine\ 数据", 0) as IFeatureWorkspace; IFeatureClass pFClass = pFtWs.OpenFeatureClass("ctour9_Clip"); IFeatureLayer pFtLayer = new FeatureLayerClass(); pFtLayer.Name = "等高线"; pFtLayer.FeatureClass = pFClass; axMapControl1.AddLayer(pFtLayer as ILayer); axMapControl1.Refresh(); pEngineEditor = new EngineEditorClass(); //启动编辑 pEngineEditor.StartEditing(pFtWs as IWorkspace, axMapControl1.Map); pEngineEditor.StartOperation(); //设置目标图层 IEngineEditLayers pEditLayer = pEngineEditor as IEngineEditLayers; 内部文档,请勿外传 pEditLayer.SetTargetLayer(pFtLayer, 0); //设置任务流 pEngineEditor.CurrentTask = new CalculatContourTask() as IEngineEditTask; //启用草图工具 ICommand pSketch = new ControlsEditingSketchToolClass(); pSketch.OnCreate(axMapControl1.Object); axMapControl1.CurrentTool = pSketch as ITool; //结束编辑 if (pEngineEditor.EditState == esriEngineEditState.esriEngineStateEditing) { pEngineEditor.StopEditing(true); } 9.5.2 自定义的等高线赋值类,代码如下: using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using ESRI.ArcGIS.ADF.CATIDs; using ESRI.ArcGIS.Controls; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.esriSystem; using System.Windows.Forms; namespace EngineApplication 内部文档,请勿外传 { public class CalculatContourTask : ESRI.ArcGIS.Controls.IEngineEditTask { #region Private Members IEngineEditor pEngineEditor; IEngineEditSketch pEditSketch; IEngineEditLayers pEditLayer; #endregion IFeatureLayer pFeatureLayer; #region IEngineEditTask Implementations public void Activate(ESRI.ArcGIS.Controls.IEngineEditor pEditor, ESRI.ArcGIS.Controls.IEngineEditTask pOldTask) { if (pEditor == null) return; pEngineEditor = pEditor; pEditSketch = pEngineEditor as IEngineEditSketch; pEditSketch.GeometryType = esriGeometryType.esriGeometryPolyline; pEditLayer = pEditSketch as IEngineEditLayers; //Listen to engine editor events ((IEngineEditEvents_Event)pEditSketch).OnTargetLayerChanged += new IEngineEditEvents_OnTargetLayerChangedEventHandler(OnTargetLayerChanged); ((IEngineEditEvents_Event)pEditSketch).OnCurrentTaskChanged += new IEngineEditEvents_OnCurrentTaskChangedEventHandler(OnCurrentTaskChanged); } public void Deactivate() { pEditSketch.RefreshSketch(); //Stop listening to engine editor events. ((IEngineEditEvents_Event)pEditSketch).OnTargetLayerChanged -= OnTargetLayerChanged; 内部文档,请勿外传 ((IEngineEditEvents_Event)pEditSketch).OnCurrentTaskChanged -= OnCurrentTaskChanged; //Release object references. pEngineEditor = null; pEditSketch = null; pEditLayer = null; } public string GroupName { get { //This property allows groups to be created/used in the EngineEditTaskToolControl treeview. //If an empty string is supplied the task will be appear in an "Other Tasks" group. //In this example the Reshape Polyline_CSharp task will appear in the existing Modify Tasks group. return "Modify Tasks"; } } public string Name { get { return "CalculateContourTask"; //unique edit task name } } public void OnDeleteSketch() { } public void OnFinishSketch() { //get reference to featurelayer being edited pFeatureLayer = pEditLayer.TargetLayer as IFeatureLayer; //get reference to the sketch geometry IGeometry pPolyline = pEditSketch.Geometry; if (pPolyline.IsEmpty == false) { 内部文档,请勿外传 ParaSetting pFormSetting = new ParaSetting(pFeatureLayer.FeatureClass); pFormSetting.ShowDialog(); if (pFormSetting.DialogResult == DialogResult.OK) { pHeightName = pFormSetting.pFieldNames.Text; pHeight = Convert.ToDouble(pFormSetting.dHeight.Text); pInterval = Convert.ToDouble(pFormSetting.dInterval.Text); pFormSetting.Dispose(); pFormSetting = null; IFeatureCursor pFeatureCursor = GetFeatureCursor(pPolyline, pFeatureLayer.FeatureClass); CalculateIntersect(pFeatureCursor, pPolyline); MessageBox.Show("计算完成"); } } //refresh the display IActiveView pActiveView = pEngineEditor.Map as IActiveView; pActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, (object)pFeatureLayer, pActiveView.Extent); } public string UniqueName { get { return "CalculateContourTask"; } } 内部文档,请勿外传 #endregion #region Event Listeners public void OnTargetLayerChanged() { PerformSketchToolEnabledChecks(); } void OnCurrentTaskChanged() { if (pEngineEditor.CurrentTask.Name == "CalculateContourTask") { PerformSketchToolEnabledChecks(); } } #endregion private IFeatureCursor GetFeatureCursor(IGeometry pGeometry, IFeatureClass pFeatureClass) { //空间过虑器的创建 ISpatialFilter pSpatialFilter = new SpatialFilter(); pSpatialFilter.Geometry = pGeometry; //空间过虑器几何体实体 //空间过虑器参照系 //空间过虑器空间数据字段名 pSpatialFilter.GeometryField = pFeatureClass.ShapeFieldName; //空间过虑器空间关系类型 pSpatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects; //相交 IFeatureCursor pFeatureCursor = pFeatureClass.Search(pSpatialFilter, false); return pFeatureCursor; } 内部文档,请勿外传 //起始等高线值 private double pHeight; //等高线间距 private double pInterval; //高程字段名 private string pHeightName; private void CalculateIntersect(IFeatureCursor pFeatureCursor, IGeometry pGeometry) { if (pFeatureCursor == null) { return; } //要素游标 IMultipoint pIntersectionPoints = null; //多点 IPointCollection pPointColl = null; List pFeatureList = new List(); //和直线相交的要素集合 ITopologicalOperator pTopoOperator = pGeometry as ITopologicalOperator; IPointCollection pSketchPointColl = pGeometry as IPointCollection; //所画直线的起点 IPoint pPoint0 = pSketchPointColl.get_Point(0); IFeature pFeature = pFeatureCursor.NextFeature(); pFeatureList.Clear(); while ((pFeature != null)) { //和直线相交的要素集合 pFeatureList.Add(pFeature); // 内部文档,请勿外传 pFeature = pFeatureCursor.NextFeature(); } IPolyline pPolyline = pGeometry as IPolyline; IPoint pPointF = pPolyline.FromPoint; Dictionary pDic = new Dictionary(); // IProximityOperator //此时pFeatureL中的等值线并不是按顺序(空间)排列,需要排序 //求出各交点到直线起点距离 int pCount = pFeatureList.Count; double[] sortArray = new double[pCount]; for (int i = 0; i <= pCount - 1; i++) { try { pFeature = pFeatureList[i]; //求交点: pIntersectionPoints = pTopoOperator.Intersect(pFeature.Shape, esriGeometryDimension.esriGeometry0Dimension) as IMultipoint; pPointColl = pIntersectionPoints as IPointCollection; sortArray[i] = GetDistace(pPointF, pPointColl.get_Point(0)); pDic.Add(GetDistace(pPointF, pPointColl.get_Point(0)), pFeatureList[i]); //距离 //下个要素 pFeature = pFeatureCursor.NextFeature(); } catch (Exception e) { MessageBox.Show(e.ToString()); 内部文档,请勿外传 } } //冒泡法 for (int H = sortArray.Length - 1; H >= 0; H--) { for (int j = 0; j < H; j++) { if (sortArray[j] > sortArray[j + 1]) { double temp = sortArray[j]; sortArray[j] = sortArray[j + 1]; sortArray[j + 1] = temp; } } } int pFieldIndex = pFeatureLayer.FeatureClass.Fields.FindField(pHeightName); Dictionary pDicdis = new Dictionary(); for (int m = 0; m < sortArray.Length; m++) { foreach (KeyValuePair pKey in pDic) { if (sortArray[m] == pKey.Key) { IFeature pFeatureH = pKey.Value; 内部文档,请勿外传 pFeatureH.set_Value(pFieldIndex, pHeight + pInterval * m); pFeatureH.Store(); } } } } /// /// 获取我们画的线和等高线之间的距离 /// /// /// /// private double GetDistace(IPoint pPoint1, IPoint pPoint2) { //pPoint1.X = GetPlan(pPoint1.X); //pPoint1.Y = GetPlan(pPoint1.Y); //pPoint2.X = GetPlan(pPoint2.X); //pPoint2.Y = GetPlan(pPoint2.Y); //return (pPoint1.X - pPoint2.X) * (pPoint1.X - pPoint2.X) + (pPoint1.Y - pPoint2.Y) * (pPoint1.Y - pPoint2.Y); IProximityOperator pProximity = pPoint1 as IProximityOperator; return pProximity.ReturnDistance(pPoint2); } #region private methods private void PerformSketchToolEnabledChecks() { if (pEditLayer == null) return; //Only enable the sketch tool if there is a polyline target layer. if (pEditLayer.TargetLayer.FeatureClass.ShapeType != esriGeometryType.esriGeometryPolyline) { pEditSketch.GeometryType = esriGeometryType.esriGeometryNull; return; } 内部文档,请勿外传 pEditSketch.GeometryType = esriGeometryType.esriGeometryPolyline; } #endregion } } 9.5.3 窗体界面如下,代码如下: public partial class ParaSetting : Form { public ParaSetting(IFeatureClass pFtClass) { InitializeComponent(); this.pFeatureClass = pFtClass; } IFeatureClass pFeatureClass; private void ParaSetting_Load(object sender, EventArgs e) { AddFdName(this.pFeatureClass); } private void AddFdName(IFeatureClass pFeatureClass) { pFieldNames.Items.Clear(); for (int i = 0; i <= pFeatureClass.Fields.FieldCount - 1; i++) { if (pFeatureClass.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeDouble || pFeatureClass.Fields.get_Field(i).Type == esriFieldType.esriFieldTypeInteger) { pFieldNames.Items.Add(pFeatureClass.Fields.get_Field(i).Name); 内部文档,请勿外传 } } } } 内部文档,请勿外传 10 十.地图输出 很多的时候在地图制作完成以后,我们需要将它用不同的格式输出,如 PDF,bmp 等格式,这样的格式方便 我们用户在没有安装 ArcMap 的计算机平台上对地图进行浏览,查看。地图输出可以分为两大类,即栅格数 据和适量数据格式,前者的如 BMP,JPG,而后者的如 PDF,,SVG. IExport 接口作为地图输出的主要接口,被不同的类实现,如下图所示: 这 10 个类都是组件类,可以直接用来实例化,同样,这 10 个类对应了 ArcGIS 所支持的地图输出格式, 同时这 10 个类也可以划分为两大类,即矢量格式和栅格格式。Window 平台的分辨率一般为 96dpi,而这个 也是 ArcGIS 栅格 数据输出的默认分辨率,而对于像 PDF 这样的分辨率,默认为 300dpi。IExport 接口定 义了地图输出的通用方法和属性,如下图: 矢量格式地图输出 10.1 矢量格式文件的输出主要是依靠 IExportVector 接口,该接口被以下 5 个类实 现: 内部文档,请勿外传 示例:输出EMF格式: private void ExportEMF() { IActiveView pActiveView; pActiveView = axPageLayoutControl1.ActiveView; IExport pExport; pExport = new ExportEMFClass(); pExport.ExportFileName = @"E:\arcgis\Engine\ExportEMF.emf"; pExport.Resolution = 300; tagRECT exportRECT; exportRECT = pActiveView.ExportFrame; IEnvelope pPixelBoundsEnv; pPixelBoundsEnv = new EnvelopeClass(); pPixelBoundsEnv.PutCoords(exportRECT.left, exportRECT.top, exportRECT.right, exportRECT.bottom); pExport.PixelBounds = pPixelBoundsEnv; int hDC; hDC = pExport.StartExporting(); pActiveView.Output(hDC, (int)pExport.Resolution, ref exportRECT, null, null); pExport.FinishExporting(); pExport.Cleanup(); } 示例:输出PDF格式: private void ExportPDF() { IActiveView pActiveView; pActiveView = axPageLayoutControl1.ActiveView; IEnvelope pEnv; pEnv = pActiveView.Extent; IExport pExport; pExport = new ExportPDFClass(); pExport.ExportFileName = @"E:\arcgis\Engine\ExportPDF.pdf"; pExport.Resolution = 30; tagRECT exportRECT; exportRECT.top = 0; exportRECT.left = 0; exportRECT.right = (int)pEnv.Width; exportRECT.bottom = (int)pEnv.Height; IEnvelope pPixelBoundsEnv; pPixelBoundsEnv = new EnvelopeClass(); 内部文档,请勿外传 pPixelBoundsEnv.PutCoords(exportRECT.left, exportRECT.bottom, exportRECT.right, exportRECT.top); pExport.PixelBounds = pPixelBoundsEnv; int hDC ; hDC = pExport.StartExporting(); pActiveView.Output(hDC, (int)pExport.Resolution, ref exportRECT, null, null); pExport.FinishExporting(); pExport.Cleanup(); } 栅格格式地图输出 10.2 栅格格式文件的输出主要是依靠 IExportImage 接口,该接口被以下 5 个类实 现: 示例:根据传入的分辨率输出JPG格式: public void CreateJPEGHiResolutionFromActiveView(IActiveView pActiveView,String pFileName, Int32 pScreenResolution, Int32 pOutputResolution) { ESRI.ArcGIS.Output.IExport pExport = new ESRI.ArcGIS.Output.ExportJPEGClass(); pExport.ExportFileName = pFileName; pExport.Resolution = pOutputResolution; ESRI.ArcGIS.Display.tagRECT pExportRECT; pExportRECT.left = 0; pExportRECT.top = 0; pExportRECT.right = pActiveView.ExportFrame.right * (pOutputResolution / pScreenResolution); pExportRECT.bottom = pActiveView.ExportFrame.bottom * (pOutputResolution / pScreenResolution); ESRI.ArcGIS.Geometry.IEnvelope pEnvelope = new ESRI.ArcGIS.Geometry.EnvelopeClass(); pEnvelope.PutCoords(pExportRECT.left, pExportRECT.top, pExportRECT.right, pExportRECT.bottom); pExport.PixelBounds = pEnvelope; System.Int32 hDC = pExport.StartExporting(); pActiveView.Output(hDC, (System.Int16)pExport.Resolution, ref pExportRECT, null, null); pExport.FinishExporting(); 内部文档,请勿外传 pExport.Cleanup(); } 11 十一.ArcGIS Engine 实战 坐标生成点(AddXY 功能) 11.1 野外采集的数据或一些客户提交的数据经常不是 shp 也不是以数据库存储的 FeatureClass,而是含有 X,Y 字段的 Excel 或者 Txt,利用 ArcMap 的 Addxydata 功能我们可以实现 Excel 数据到空间数据的转换,具体 操作是利用 Addxy 功能,设置好相关的参数,这个时候 ArcMap 会生成一个内存图层,借助于 ArcMap 的导 出功能,导成 shp 或者 FeatureClass,在这里我们用 ArcGIS Engine 实现这个功能,要实现这个功能,我 们需要了解下面的几个接口: IXYEvent2FieldsProperties:该接口用来控制生成空间数据的 X,Y 字段信息 IXYEventSourceName:该接口用来生成空间数据 /// /// 模拟Addxy /// /// /// /// public IFeatureClass CreateXYEventSource(ITable pTable, ISpatialReference pSpatialReference) { IXYEvent2FieldsProperties pEvent2FieldsProperties = new XYEvent2FieldsPropertiesClass(); pEvent2FieldsProperties.XFieldName = "X"; pEvent2FieldsProperties.YFieldName = "Y"; IDataset pSourceDataset = (IDataset)pTable; IName sourceName = pSourceDataset.FullName; IXYEventSourceName pEventSourceName = new XYEventSourceNameClass(); pEventSourceName.EventProperties = pEvent2FieldsProperties; pEventSourceName.EventTableName = sourceName; pEventSourceName.SpatialReference = pSpatialReference; 内部文档,请勿外传 IName pName = (IName)pEventSourceName; IXYEventSource pEventSource = (IXYEventSource)pName.Open(); IFeatureClass pFeatureClass = (IFeatureClass)pEventSource; return pFeatureClass; } 排序 11.2 在处理数据时,有时候需要对数据进行升序和降序排序,我们一贯的做法是在 ArcMap 中,在排序的字段上 右键,然后点击排序操作,可是当我们关闭这个数据后,重新打开属性表,发现顺序又变回去了,更甚者, 我们将数据排序之后,然后利用 ArcMap 倒出去,但是结果不是我们想要的,对于这样的问题 ArcGIS Engine 提供了一个 ITableSort 的接口,该接口可以实现我们想要的功能。ITableSort 接口包含以下属性和方法: 示例:利用 ITableSort 对要素类排序: /// /// true 表示升序,false 表示降序 /// /// /// /// void Sort(ITable _pTable, string _FieldName, bool _Bool) { ITableSort pTableSort = new TableSortClass(); pTableSort.Table = _pTable; pTableSort.Fields = _FieldName; pTableSort.set_Ascending(_FieldName, _Bool); pTableSort.Sort(null); 内部文档,请勿外传 ICursor pSortCursor = pTableSort.Rows; IRow pSortRow = pSortCursor.NextRow(); IDataset plSortDataset =_pTable as IDataset; IFeatureWorkspace pFWs = plSortDataset.Workspace as IFeatureWorkspace; ITable plStable = pFWs.CreateTable("NewSort", _pTable.Fields, null, null, null); while (pSortRow != null) { IRow pRow = plStable.CreateRow(); for (int i = 0; i < pRow.Fields.FieldCount; i++) { if (pRow.Fields.get_Field(i).Type != esriFieldType.esriFieldTypeOID) { pRow.set_Value(i, pSortRow.get_Value(i)); } } pRow.Store(); pSortRow = pSortCursor.NextRow(); } } 内部文档,请勿外传 网络分析 11.3 11.3.1 什么是网络(地理网络) 网络是由一系列相互连通的点和线组成,用来描述地理要素(资源)的流动情况,用来模拟城市交通网络, 如连接各个城市的高速公路、连接各家各户的排给水网络等。 11.3.2 网络分析需要解决的问题 1. 路径分析: 最佳路径分析:寻找最佳路径功能主要包括确定两点间的最佳路径和多点间的最佳路径。 2. 服务区域的判定: 目的为在一个网络路径上确定任何位置的服务区域和服务网络,并显示在视图中。在创建服务区的基础上, 可评估该地点的可达性。 3. 查找最邻近设施: 目的为在网络路径上找出距某一位置最近的设施,并设计到达这些设施的最近路线。例如:对一场火 灾来说,最近设施是指最近的消防栓;对一起交通事故来说,它是指离事故现场最近的能够提供急救服务 的医院;而对于一个家庭的日常生活来说,最近设施又是指距住宅最近的零售店或超市。 1. 导航:导航图生成 内部文档,请勿外传 2. 物流配送 3. 爆管分析, 上下游追踪分析 ArcGIS 对于网络分析的支持是非常丰富的我们上面提到的,ArcGIS 全都支持,而在 ArcGIS 10 中又增加 了基于霍夫模型的选址分析。rcGIS 提供了两种网络分析,即基于 Geometric Network 的有向网络和基于 Network Dataset 的无向网络。有向网络分析意味着网络中流动的物质必须按照在 Network 中定义好的规 则前进,运行路径都是事先定义好的,可以被修改,但是不能被物质本身修改,而是被网络的工程师来修 改网络的规则,使通过设置结点的开启状态来改变网络的流动方向;而无向网络分析则意味著用户可以自 由定义在网络中前进的方向,速度以及终点,例如一个卡车司机可以决定在哪条道路上开始行进,在什么 地方停止,采用什么方向。并且还可以给网络设置限定性规则,例如是单行线还是禁行。 它们的区别可以参考下面的表格: Geometric network Network dataset Network features:Edges and junctions Network elements: Edges, junctions, and turns 数据源:GDB feature classes only 数据源:GDB feature classes, shapefiles, or StreetMap data System manages connectivity User controls when connectivity is built Weights based on feature attribute fields More robust attribute (weight) model 存在于:Feature dataset only 存在于:Feature dataset or workspace 单模型 单模型或者多模型 Network tracing functionality Network solver functionality utilities/natural resources modeling transportation modeling 不支持转弯 支持转弯 uses custom features: simple/complex edge features and junctions uses simple features: points and lines 内部文档,请勿外传 11.3.3 Geometric Network 分析 Geometry Network 分析属于有向网络或者定向网络,网络中的流向由源(Source),汇(Sink)以及通达 性决定、网络中流动的资源自身不能决定流向。如水流的路径是预先设定好的,它只能按照预先设定好的 路径进行流通。当然我们可以通过开关阀门来达到改变水流的流向目的,但这属于流通规则的内容。在效 用网络中,水、电、气通过管道和线路输送给消费者,水、电、气被动地由高压向低压输送,不能主观选 择方向。Geometric Network 主要用于模拟现实世界中的水网,电网,煤气网,电话服务等资源网络。 几何网络(有向)网络解决的问题有: A.寻找 连通的/不连通的管线 B.上/下游追踪 C.寻找环路 D.寻找通路 E.爆管分析 Geometric Network 由一组相互连接的 Edge 和 Junctions 组成,并且包含 Connectivity Rules。Geometric Network 必须构建于 Geodatabase 的 Feature Dataset 中,其中的 Feature Class 是作为 Junctions 和 Edge 的数据源。Geometric Network 中包含两种主要的要素:Edges 和 Junctions 在几何网络中,Edge 和 Junctions 是 Topologically Connected to each other:Edge 和 Edge 在 Junctions 处连接,某个 Edge 中流动的要素是通过 Junctions 流动到其它的 Edge。 Geometric 中有两种类型的 Edges: Simple Edges-连着两个 Junctions,Edge 的每一头连接一个 Junction; Complex Edges-通常在端点处至少连接两个 Junctions,而且在 Edge 的中间部分,也可以连接很多 Junctions,例如:主管道上可以连接多个支管道。 Geometric 中有两种类型的 Junctions: User defined Junctions:在构建 Geometric Network 时,根据用户定义的 Point Source 生成的 Junctions; Orphan Junctions:当第一个 Edge Feature Class 添加到 Geometric Network 时,创建了 Simple Junction Feature Class ,被称为 Orphan Junction Feature Class,主要是用于维护网络的完整性。 当用户添加其它的 Junctions Feature 时,该点处的 Orphan Junctions 将被删除;此外当用户删除 Geometric Network 时,则 Orphan Junctions 也被删除。 当创建一个 Geometric Network 时,也创建了一个相应的 Logic Network,用于表现和模型化要素之间 内部文档,请勿外传 的连通关系,实现 Tracing 和 Flow 计算。Logic Network 是由一系列的 Table 组成,并且由 ArcGIS 维 护。当 Geometric Network 被更新或者删除时,Logic Network 会自动更新。 11.3.3.1几何网络中的的相关元素 1, Sources 和 Sinks 网络要素的流动方向是从 Sources 和 Sinks 来计算的,从 Sources 流出,汇于 Sinks。可以在创 建 Geometric Network 时,将 Junctions 设置为 Sources 或者 Sinks 或者都不是,一旦设定为 Source 或者 Sink,则在属性表中添加字段 AncillaryRole 用于记录其类型。, 2, Network Weight 网络可以被设置权重,用于表示网络要素在其中流动的环境,使利用参与网络的 Feature 的属性 来设置网络的 Weight. 3, Enable and Disable Feature Geometric Network 中的 Edge和 Junctions 可以在 Logic Network 中设置为 Enabled 或者 Disabled。 网络的 Enabled 或者 Disabled 状态是由要素属性字段 Enabled 设置的,可以选择的属性为 True, False,当通过简单要素类创建 Geometric Network 时,该字段自动添加为输入要素中,并且缺 省状态下属性值为 True。 4, Connectivity 在现实生活中,Geometric Network 中并不是所有的要素都是可以相互连接的,系统所创建的网 络连通性可能不适合,则用户可以根据自己的需要修改 Connectivity,方法是:在 ArcCatalog 中点击创建的 Geometric Network,在 Properties 中选择 Connectivity 面板,实现 Connectivity 的设置。可以创建的 Connectivity Rule 包括两种:Edge-Junction Connectivity, Edge-Edge Connectivity。 11.3.3.2如何创建一个几何网络 在做几何网络分析的时候,首先要有这个几何网络,那么如何创建一个创建 Geometric Network?前面说 过要熟练的掌握 ArcGIS Engine,如果对于 ArcMap 掌握的很熟悉的话,那对我们学习 ArcGIS Engine 是很 有帮助的,ArcMap 对很多操作提供了界面,而 ArcGIS Engine 提供给我们的是 API,界面上的设置也是响应 内部文档,请勿外传 的 API 的参数,希望从这个例子能加深这种体会。我们看以下 ArcMap 是如何生成一个几何网络的? 我们按照下面的步骤创建一个几何网络: 1,在 Catalog 中打开创建几个网络的工具 2,网络名称 3,参与网络的要素类 4,是否采用 enable 字段 5,指定 source 和 sink 6,创建权重 7,建立几何网络 打开创建几何网络的向导: 网络名称 内部文档,请勿外传 参与网络的要素类 设置 enable 字段 内部文档,请勿外传 设置 sink 和 source 设置权重 内部文档,请勿外传 建立网络: 结果: 示例:用代码来创建几何网络: /// /// 打开个人数据库 /// 内部文档,请勿外传 /// /// public IWorkspace GetWorkspace(String _pGDBName) { IWorkspaceFactory pWsFac = new AccessWorkspaceFactoryClass(); IWorkspace pWs = pWsFac.OpenFromFile(_pGDBName,0); return pWs; } public void CreateGeometricNetwork(IWorkspace _pWorkspace, IFeatureDatasetName _pFeatureDatasetName,String _pGeometricName) { INetworkLoader2 pNetworkLoader = new NetworkLoaderClass(); // 网络的名称 pNetworkLoader.NetworkName = _pGeometricName; // 网络的类型 pNetworkLoader.NetworkType = esriNetworkType.esriNTUtilityNetwork; // Set the containing feature dataset. pNetworkLoader.FeatureDatasetName = (IDatasetName)_pFeatureDatasetName; // 检查要建立几何网络的数据,每一个要素只能参与一个网络 if (pNetworkLoader.CanUseFeatureClass("PrimaryLine") == esriNetworkLoaderFeatureClassCheck.esriNLFCCValid) { pNetworkLoader.AddFeatureClass("PrimaryLine", esriFeatureType.esriFTComplexEdge, null, false); } if (pNetworkLoader.CanUseFeatureClass("Feeder") == esriNetworkLoaderFeatureClassCheck.esriNLFCCValid) { pNetworkLoader.AddFeatureClass("Feeder", esriFeatureType.esriFTSimpleJunction, null, false); } 内部文档,请勿外传 // 我的数据中没有enable字段,所以,用了false,如果用true的话,就要进行相关 的设置 INetworkLoaderProps pNetworkLoaderProps = (INetworkLoaderProps)pNetworkLoader; pNetworkLoader.PreserveEnabledValues = false; // Set the ancillary role field for the Feeder class. String defaultAncillaryRoleFieldName = pNetworkLoaderProps.DefaultAncillaryRoleField; esriNetworkLoaderFieldCheck ancillaryRoleFieldCheck = pNetworkLoader.CheckAncillaryRoleField("Feeder", defaultAncillaryRoleFieldName); switch (ancillaryRoleFieldCheck) { case esriNetworkLoaderFieldCheck.esriNLFCValid: case esriNetworkLoaderFieldCheck.esriNLFCNotFound: pNetworkLoader.PutAncillaryRole("Feeder", esriNetworkClassAncillaryRole.esriNCARSourceSink, defaultAncillaryRoleFieldName); break; default: Console.WriteLine( "The field {0} could not be used as an ancillary role field.", defaultAncillaryRoleFieldName); break; } pNetworkLoader.SnapTolerance = 0.02; // 给几何网络添加权重 pNetworkLoader.AddWeight("Weight", esriWeightType.esriWTDouble, 0); // 将权重和PrimaryLine数据中的SHAPE_Length字段关联 pNetworkLoader.AddWeightAssociation("Weight", "PrimaryLine", "SHAPE_Length"); // 构建网络 内部文档,请勿外传 pNetworkLoader.LoadNetwork(); } IWorkspace pWs = GetWorkspace(@"E:\arcgis\Engine\Geometric.mdb"); IFeatureWorkspace pFtWs = pWs as IFeatureWorkspace; IFeatureDataset pFtDataset = pFtWs.OpenFeatureDataset("work"); IDataset pDataset = pFtDataset as IDataset; IFeatureDatasetName pFtDatasetName = pDataset.FullName as IFeatureDatasetName; CreateGeometricNetwork(pWs, pFtDatasetName, "TestGeometric"); 效果如下: 有了几何网络,接下来就是在这个几何网络上做分析,几何网络能执行的操作都在 ITraceFlowSolverGEN 接口中,该接口的方法如下图: 内部文档,请勿外传 示例:用代码实现几何网络的最短路径分析: public void SolvePath(IMap _pMap, IGeometricNetwork _pGeometricNetwork, string _pWeightName, IPointCollection _pPoints, double _pDist, ref IPolyline _pPolyline, ref double _pPathCost) { try { // 这4个参数其实就是一个定位Element的指标 int intEdgeUserClassID; int intEdgeUserID; int intEdgeUserSubID; int intEdgeID; IPoint pFoundEdgePoint; double dblEdgePercent; ITraceFlowSolverGEN pTraceFlowSolver = new TraceFlowSolverClass() as ITraceFlowSolverGEN; INetSolver pNetSolver = pTraceFlowSolver as INetSolver; //操作是针对逻辑网络的,INetwork是逻辑网络 INetwork pNetwork = _pGeometricNetwork.Network; pNetSolver.SourceNetwork = pNetwork; INetElements pNetElements = pNetwork as INetElements; int pCount = _pPoints.PointCount; //定义一个边线旗数组 IEdgeFlag[] pEdgeFlagList = new EdgeFlagClass[pCount]; 内部文档,请勿外传 IPointToEID pPointToEID = new PointToEIDClass(); pPointToEID.SourceMap = _pMap; pPointToEID.GeometricNetwork = _pGeometricNetwork; pPointToEID.SnapTolerance = _pDist; for (int i = 0; i < pCount; i++) { INetFlag pNetFlag = new EdgeFlagClass() as INetFlag; IPoint pEdgePoint = _pPoints.get_Point(i); //查找输入点的最近的边线 pPointToEID.GetNearestEdge(pEdgePoint, out intEdgeID, out pFoundEdgePoint, out dblEdgePercent); pNetElements.QueryIDs(intEdgeID, esriElementType.esriETEdge, out intEdgeUserClassID, out intEdgeUserID, out intEdgeUserSubID); pNetFlag.UserClassID = intEdgeUserClassID; pNetFlag.UserID = intEdgeUserID; pNetFlag.UserSubID = intEdgeUserSubID; IEdgeFlag pTemp = (IEdgeFlag)(pNetFlag as IEdgeFlag); pEdgeFlagList[i] = pTemp; } pTraceFlowSolver.PutEdgeOrigins(ref pEdgeFlagList); INetSchema pNetSchema = pNetwork as INetSchema; INetWeight pNetWeight = pNetSchema.get_WeightByName(_pWeightName); INetSolverWeightsGEN pNetSolverWeights = pTraceFlowSolver as INetSolverWeightsGEN; pNetSolverWeights.FromToEdgeWeight = pNetWeight;//开始边线的权重 pNetSolverWeights.ToFromEdgeWeight = pNetWeight;//终止边线的权重 内部文档,请勿外传 object[] pRes = new object[pCount - 1]; //通过FindPath得到边线和交汇点的集合 IEnumNetEID pEnumNetEID_Junctions; IEnumNetEID pEnumNetEID_Edges; pTraceFlowSolver.FindPath(esriFlowMethod.esriFMConnected, esriShortestPathObjFn.esriSPObjFnMinSum, out pEnumNetEID_Junctions, out pEnumNetEID_Edges, pCount - 1, ref pRes); //计算元素成本 _pPathCost = 0; for (int i = 0; i < pRes.Length; i++) { double m_Va = (double)pRes[i]; _pPathCost = _pPathCost + m_Va; } IGeometryCollection pNewGeometryColl = _pPolyline as IGeometryCollection;//QI ISpatialReference pSpatialReference = _pMap.SpatialReference; IEIDHelper pEIDHelper = new EIDHelperClass(); pEIDHelper.GeometricNetwork = _pGeometricNetwork; pEIDHelper.OutputSpatialReference = pSpatialReference; pEIDHelper.ReturnGeometries = true; IEnumEIDInfo pEnumEIDInfo = pEIDHelper.CreateEnumEIDInfo(pEnumNetEID_Edges); int Count = pEnumEIDInfo.Count; pEnumEIDInfo.Reset(); for (int i = 0; i < Count; i++) { IEIDInfo pEIDInfo = pEnumEIDInfo.Next(); 内部文档,请勿外传 IGeometry pGeometry = pEIDInfo.Geometry; pNewGeometryColl.AddGeometryCollection(pGeometry as IGeometryCollection); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } 红色的表示计算结果 11.3.4 Network dataset 分析 Nework dataset 分析属于无向网络分析,无向网络分析的的网络是存储在 Network Dataset 中。Network Dataset 由 Feature 要素创建而来,能够用来表现复杂场景,包括 Multimodal 交通网络,同样也可以包含 多个网络属性以模拟网络限制条件和层次结构。流向不确定、流动的资源可以决定流向。如交通系统中流 通介质可以自行决定方向、速度和目的地。 无向解决的问题有: A.最短路径 B.物流输送 C.临近设施分析 内部文档,请勿外传 D.服务区分析 E.选址分析 Network Dataset 包含以下三种类型: (1)Network Dtaset:创建网络的数据源存储于 Personal 或者 Enterprise Geodatabase 中,因为其中 可以存储很多数据源,因此可以构建 Multimodal Network (2)Shapefile-based Network Dataset:是基于 Polyline Shapefile 文件创建的,也可以添加 Shapefile Turn Feature Class,这种 Network Dataset 不能够支持多种 Edge 类型,也不能用于创建 Multimodal Networks (3)ArcGIS Network Analyst 也可以读取 SDC Network Dataset,可以实现网络分析功能,而不能创建 Network Dataset Network Elements 包括三类:Edges,Junctions,Turns。 Connectivity Group 要想定义 ArcGIS Network Analyst 的 Connectivity,首先要定义 Connectivity Group。每一个 Edge Source 只能够被赋予一个 Connectivity Group,而 Junction Source 可以被赋予多种 Connectivity Group。只有 将 Junction 设为两种或者多种 Connectivity Group,才可以去连接不同 Connectivity Group 的 Edge。 Connectivity Group 用于创建 Multimodal Transportation Network。 以下为 Network Dataset 所支持的三种 Connectivity Model: (1)Connecting Edges within a Connectivity Group 可以设置“Endpoint Connectivity”,也可以设置“Any Vertex Connectivity”。第一种方式中,边和 边只能在终点处相交,第二种方式则可以在边的任意位置相交 (2)Connecting Edges through Junctions across Connectivity Group 能够将不同 Connectivity Group 中的 Edge 通过被不同 Connectivity Group 共享的 Junctions 连接。 (3)Elevation Fields 主要用于 Network Dataset 中检查 Line Endpoints 的 Connectivity。每一个 Edge Feature 具备两个字段 用来描述每一个端点的高程。 Network Attribute Network Attribute 主要用于设定网络的流通属性,包括: Name: Usage Type: Unit:Centimeter,Meter 等等 内部文档,请勿外传 Data Types:Boolean,Integer,Float,Double Use by Default: Cost:例如走过某段路需要花费的时间 Descriptors:对某条道路的描述信息,例如道路速度的限制,有多少个红绿灯等。 Restrictions:例如某条线是禁行,或者是单向的 Hierarchy:例如道路的分级 Types of Evaluators used by a network Network 的 Attribute 都需要设定 Value,通常是利用 Evaluators 从 Network Source 中获取属性值。具备 四种 Evaluators: Field Evaluator:利用属性字段的值; Field Expression Evaluator:利用属性字段构建计算表达式; Constant Evaluators:赋予常数; VBscript Evaluators:通过执行 VBScript 代码,主要用于赋予复杂的属性值 每个 Junction Source 和 Turn Source 需要一个 Evaluator,而每个 Edge Source 需要两个-Edge 的每个方 向都需要一个 Evaluator Turns in the Network Dataset Turn 的类型有多种,可以是 Multi Edge Turn,也可以是 U-Turn。在 ArcGIS 中,Turn 是通过 Turn Feature Class 转变而来的,这些 Turn Feature Class 都是 Polyline Feature Class。Turn Feature Class 必须 是与其他 Network 要素位于同一个 Feature Dataset 中,具备相同的空间参考,不参与 Connectivity Groups, 也不具备 Elevation 信息。Turn 至少具备两条 Edge,至多 20 条 Edge。 Setting Directions 支持 Directions 的 Network Dataset 必须至少满足以下要求: 具备 Length 属性,包括 Length 单位; 至少有一个 Edge Source; 在 Edge Source 上至少有一个 Text 字段。 11.3.4.1如何创建一个无向网络 内部文档,请勿外传 同几何网络一样,要做无向网络的分析,首先要有无向网络,那么无向网络是怎么建立的呢?无向网络的 建立相比有向网络稍微复杂点,我们可以按照下面的步骤: 1,打开网络数据集的向导,定义几何网络的名称 2,参与网络数据集的要素类: 内部文档,请勿外传 3,是否使用转向数据集 内部文档,请勿外传 4,连通性设置 内部文档,请勿外传 内部文档,请勿外传 5,是否使用搞成字段来模拟连通性 , 6,权重设置 内部文档,请勿外传 , 7,是否建立行驶方向 内部文档,请勿外传 , 8,结果如下: 示例:代码创建 Networkdataset: /// /// 个人数据库的路径,要素数据集的路径,建立网络的名称,参与网络的要素类 /// /// /// /// /// void CreateNetworkDataset(string _pWsName, string _pDatasetName,string _pNetName, string _pFtName) { IDENetworkDataset pDENetworkDataset = new DENetworkDatasetClass(); pDENetworkDataset.Buildable = true; 内部文档,请勿外传 IWorkspace pWs = GetWorkspace(_pWsName); IFeatureWorkspace pFtWs = pWs as IFeatureWorkspace; IFeatureDataset pFtDataset = pFtWs.OpenFeatureDataset(_pDatasetName); // 定义空间参考,负责会出错 IDEGeoDataset pDEGeoDataset = (IDEGeoDataset)pDENetworkDataset; IGeoDataset pGeoDataset = pFtDataset as IGeoDataset; pDEGeoDataset.Extent = pGeoDataset.Extent; pDEGeoDataset.SpatialReference = pGeoDataset.SpatialReference; // 网络数据集的名称 IDataElement pDataElement = (IDataElement)pDENetworkDataset; pDataElement.Name = _pNetName; // 参加建立网络数据集的要素类 INetworkSource pEdgeNetworkSource = new EdgeFeatureSourceClass(); pEdgeNetworkSource.Name = _pFtName; pEdgeNetworkSource.ElementType = esriNetworkElementType.esriNETEdge; // 要素类的连通性 IEdgeFeatureSource pEdgeFeatureSource = (IEdgeFeatureSource)pEdgeNetworkSource; pEdgeFeatureSource.UsesSubtypes = false; pEdgeFeatureSource.ClassConnectivityGroup = 1; pEdgeFeatureSource.ClassConnectivityPolicy =esriNetworkEdgeConnectivityPolicy.esriNECPEndVertex; 内部文档,请勿外传 //不用转弯数据 pDENetworkDataset.SupportsTurns = false; IArray pSourceArray = new ArrayClass(); pSourceArray.Add(pEdgeNetworkSource); pDENetworkDataset.Sources = pSourceArray; //网络数据集的属性设置 IArray pAttributeArray = new ArrayClass(); // Initialize variables reused when creating attributes: IEvaluatedNetworkAttribute pEvalNetAttr; INetworkAttribute2 pNetAttr2; INetworkFieldEvaluator pNetFieldEval; INetworkConstantEvaluator pNetConstEval; pEvalNetAttr = new EvaluatedNetworkAttributeClass(); pNetAttr2 = (INetworkAttribute2)pEvalNetAttr; pNetAttr2.Name ="Meters"; pNetAttr2.UsageType = esriNetworkAttributeUsageType.esriNAUTCost; pNetAttr2.DataType = esriNetworkAttributeDataType.esriNADTDouble; pNetAttr2.Units = esriNetworkAttributeUnits.esriNAUMeters; pNetAttr2.UseByDefault = false; pNetFieldEval = new NetworkFieldEvaluatorClass(); pNetFieldEval.SetExpression("[METERS]", ""); //方向设置 pEvalNetAttr.set_Evaluator(pEdgeNetworkSource, esriNetworkEdgeDirection.esriNEDAlongDigitized, (INetworkEvaluator)pNetFieldEval); pEvalNetAttr.set_Evaluator(pEdgeNetworkSource, esriNetworkEdgeDirection.esriNEDAgainstDigitized, (INetworkEvaluator) pNetFieldEval); pNetConstEval = new NetworkConstantEvaluatorClass(); pNetConstEval.ConstantValue = 0; 内部文档,请勿外传 pEvalNetAttr.set_DefaultEvaluator(esriNetworkElementType.esriNETEdge, (INetworkEvaluator)pNetConstEval); pEvalNetAttr.set_DefaultEvaluator(esriNetworkElementType.esriNETJunction, (INetworkEvaluator)pNetConstEval); pEvalNetAttr.set_DefaultEvaluator(esriNetworkElementType.esriNETTurn, (INetworkEvaluator)pNetConstEval); // 一个网络数据集可以有多个属性,我只添加了一个 pAttributeArray.Add(pEvalNetAttr); pDENetworkDataset.Attributes = pAttributeArray; // 创建网络数据集,注意在创建几何网络的时候会锁定相应的要素类,因此不要 用ArcMap或者catalog等打开参相应的数据 INetworkDataset pNetworkDataset = Create(pFtDataset, pDENetworkDataset); //建立网络 INetworkBuild pNetworkBuild = (INetworkBuild)pNetworkDataset; pNetworkBuild.BuildNetwork(pGeoDataset.Extent); } /// /// 创建无向网络 /// /// /// /// public INetworkDataset Create(IFeatureDataset _pFeatureDataset, IDENetworkDataset2 _pDENetDataset) { IFeatureDatasetExtensionContainer pFeatureDatasetExtensionContainer = (IFeatureDatasetExtensionContainer) _pFeatureDataset; IFeatureDatasetExtension pFeatureDatasetExtension = pFeatureDatasetExtensionContainer.FindExtension 内部文档,请勿外传 (esriDatasetType.esriDTNetworkDataset); IDatasetContainer2 pDatasetContainer2 = (IDatasetContainer2)pFeatureDatasetExtension; IDEDataset pDENetDataset = (IDEDataset)_pDENetDataset; INetworkDataset pNetworkDataset = (INetworkDataset)pDatasetContainer2.CreateDataset (pDENetDataset); return pNetworkDataset; } /// /// _pFtClass参数为Stops的要素类,_pPointC是用鼠标点的点生成的点的集合,最后 一个参数是捕捉距离 /// /// /// /// /// void NASolve(INAContext _pNaContext,IFeatureClass _pFtClass, IPointCollection _pPointC, double _pDist) { INALocator pNAlocator = _pNaContext.Locator; for (int i = 0; i < _pPointC.PointCount; i++) { IFeature pFt = _pFtClass.CreateFeature(); IRowSubtypes pRowSubtypes = pFt as IRowSubtypes; pRowSubtypes.InitDefaultValues(); pFt.Shape = _pPointC.get_Point(i) as IGeometry; IPoint pPoint = null; INALocation pNalocation = null; pNAlocator.QueryLocationByPoint(_pPointC .get_Point(i),ref pNalocation,ref pPoint,ref _pDist); 内部文档,请勿外传 INALocationObject pNAobject = pFt as INALocationObject; pNAobject.NALocation = pNalocation; int pNameFieldIndex = _pFtClass.FindField("Name"); pFt.set_Value(pNameFieldIndex, pPoint.X.ToString() + "," + pPoint.Y.ToString()); int pStatusFieldIndex = _pFtClass.FindField("Status"); pFt.set_Value(pStatusFieldIndex, esriNAObjectStatus.esriNAObjectStatusOK); int pSequenceFieldIndex = _pFtClass.FindField("Sequence"); pFt.set_Value(_pFtClass.FindField("Sequence"), ((ITable)_pFtClass).RowCount(null)); pFt.Store(); } } /// /// 获取网络数据集 /// /// /// /// /// INetworkDataset GetNetDataset(IFeatureWorkspace _pFeatureWs, string _pDatasetName,string _pNetDatasetName) { ESRI.ArcGIS.Geodatabase.IDatasetContainer3 pDatasetContainer = null; ESRI.ArcGIS.Geodatabase.IFeatureDataset pFeatureDataset = _pFeatureWs.OpenFeatureDataset(_pDatasetName); ESRI.ArcGIS.Geodatabase.IFeatureDatasetExtensionContainer pFeatureDatasetExtensionContainer = pFeatureDataset as 内部文档,请勿外传 ESRI.ArcGIS.Geodatabase.IFeatureDatasetExtensionContainer; // Dynamic Cast ESRI.ArcGIS.Geodatabase.IFeatureDatasetExtension pFeatureDatasetExtension = pFeatureDatasetExtensionContainer.FindExtension(ESRI.ArcGIS.Geodatabase.esriDataset Type.esriDTNetworkDataset); pDatasetContainer = pFeatureDatasetExtension as ESRI.ArcGIS.Geodatabase.IDatasetContainer3; // Dynamic Cast ESRI.ArcGIS.Geodatabase.IDataset pNetWorkDataset = pDatasetContainer.get_DatasetByName(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTN etworkDataset, _pNetDatasetName); return pNetWorkDataset as ESRI.ArcGIS.Geodatabase.INetworkDataset; // } /// /// 加载NetworkDataset到Map中 /// /// /// void loadNet(IMap _pMap,INetworkDataset _pNetworkDataset) { INetworkLayer pNetLayer = new NetworkLayerClass(); pNetLayer.NetworkDataset = _pNetworkDataset; _pMap.AddLayer(pNetLayer as ILayer); } /// /// 获取网络分析上下文,这个接口是网络分析中很重要的一个 /// /// /// /// public INAContext GetSolverContext(INASolver _pNaSolver, INetworkDataset _pNetworkDataset) { //Get the Data Element IDatasetComponent pDataComponent = _pNetworkDataset as IDatasetComponent; IDEDataset pDeDataset = pDataComponent.DataElement; INAContextEdit pContextEdit = _pNaSolver.CreateContext(pDeDataset as 内部文档,请勿外传 IDENetworkDataset, _pNaSolver.Name) as INAContextEdit; //Prepare the context for analysis based upon the current network dataset schema. pContextEdit.Bind(_pNetworkDataset, new GPMessagesClass()); return pContextEdit as INAContext; } /// /// 获取NALayer /// /// /// /// INALayer GetNaLayer(INASolver _pNaSover,INAContext _pNaContext) { return _pNaSover.CreateLayer(_pNaContext); } 分析 IFeatureClass pftclass =pNaContext.NAClasses.get_ItemByName("Stops") as IFeatureClass; NASolve(pNaContext, pftclass, pPointC, 5000); IGPMessages gpMessages = new GPMessagesClass(); bool pBool= pNASolveClass.Solve(pNaContext, gpMessages, null); 内部文档,请勿外传 线性参考 11.4 11.4.1 什么是线性参考 线性参考是使用沿测量的线状要素的相对位置存储地理位置的方法。距离测量值用于定位沿线的事件: 要素上的测量值用于使用各种约定来定位点事件和线事件。以下是一些常见示例: 如下图所示,可以沿线定位点:  沿线测量值为 12 的位置  沿线的测量标记 10 以东 4 个单位 可通过几种方式参照线要素。在上例中  线从测量值 18 处开始,到测量值 26 处结束。  线从测量值 28 处开始并延伸 12 个单位。 内部文档,请勿外传 11.4.2 为什么使用线性参考 使用线性参照的原因很多,以下是两个主要原因:  许多位置以沿线性要素事件的方式记录,例如,使用“沿国道 287 参照英里标记 35 以东 27 米”这样的约定记录交通事故的位置。许多传感器使用沿线(沿管线、道路、河流等)的距离 测量值或时间测量值来记录沿线状要素的条件。  线性参照还用于将多个属性集与线状要素的部分关联,不需要在每次更改属性值时分割(分段) 基本线。例如,大多数道路中心线要素类会在三个或更多路段相交以及路段的名称发生改变时 分段。 用户通常想要记录有关道路的许多其他属性。如果不使用线性参照,可能需要在属性值更改 的每个位置将道路分割成很多小段。这时,可供选择的方法是将这些情况处理为沿道路的线 性参照事件,如下图所示: 11.4.2.1示例:在一个道路网络中利用线性参考找到 ID 为 20000013, 并且长度是 0-25 的区间: IPolyline FindRoutByMeasure(IFeatureClass _pRouteFC, string _pPKName,object _pID, double _pFrom, double _pTo) { IDataset pDataset = (IDataset)_pRouteFC; IName pName = pDataset.FullName; IRouteLocatorName pRouteLocatorName = new RouteMeasureLocatorNameClass(); pRouteLocatorName.RouteFeatureClassName = pName; pRouteLocatorName.RouteIDFieldName = _pPKName; pRouteLocatorName.RouteMeasureUnit = esriUnits.esriFeet; pName = (IName)pRouteLocatorName; IRouteLocator2 pRouteLocator = (IRouteLocator2)pName.Open(); 内部文档,请勿外传 IRouteLocation pRouteLoc = new RouteMeasureLineLocationClass(); pRouteLoc.MeasureUnit = esriUnits.esriFeet; pRouteLoc.RouteID = _pID; IRouteMeasureLineLocation rMLineLoc = (IRouteMeasureLineLocation)pRouteLoc; rMLineLoc.FromMeasure = _pFrom; rMLineLoc.ToMeasure = _pTo; IGeometry pGeo = null; esriLocatingError locError; pRouteLocator.Locate(pRouteLoc , out pGeo, out locError); return pGeo as IPolyline; } IMap pMap = axMapControl1.Map; IFeatureWorkspace pFtWs = GetFGDBWorkspace(@"E:\arcgis\Engine\Rout.gdb") as IFeatureWorkspace ; IFeatureLayer pFeatureLayer = new FeatureLayerClass(); pFeatureLayer.FeatureClass = pFtWs.OpenFeatureClass("routes"); pFeatureLayer.Name = "路径"; axMapControl1.Map.AddLayer(pFeatureLayer as ILayer); axMapControl1.Refresh(); IPolyline pPolyline = FindRoutByMeasure(pFeatureLayer.FeatureClass, "ROUTE1",20000013, 0, 25); IRgbColor pColor = new RgbColorClass(); pColor.Red = 255; IElement pElement = new LineElementClass(); ILineSymbol pLinesymbol = new SimpleLineSymbolClass(); pLinesymbol.Color = pColor as IColor; 内部文档,请勿外传 pLinesymbol.Width = 100; pElement.Geometry = pPolyline as IGeometry; IGraphicsContainer pGrahicsC = pMap as IGraphicsContainer; pGrahicsC.AddElement(pElement, 0); axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null); 11.4.3 什么是动态分段 动态分段是使用线性参照测量系统计算事件表中存储和管理事件的地图位置以及在地图上显示它们 的过程。术语“动态分段”源于每次更改属性值时无需分割(也就是“分段”)线要素的理念,即可以“动 态”定位线段。利用动态分段,可将多组属性与现有线状要素的任意部分相关联,无论其开始或结束 位置为何。可以显示、查询、编辑和分析这些属性,而不会影响基础线状要素的几何。 内部文档,请勿外传 11.4.3.1示例:用代码实现动态分段: IFeatureClass EventTable2FeatureClass(IFeatureClass _pRouteFC, string _pPKName, ITable _pEventTable, string _pFKName, string _pFrom, string _pTo) { IDataset pDataset = (IDataset)_pRouteFC; IName pName = pDataset.FullName; IRouteLocatorName pRouteLocatorName = new RouteMeasureLocatorNameClass(); pRouteLocatorName.RouteFeatureClassName = pName; pRouteLocatorName.RouteIDFieldName = _pPKName; pRouteLocatorName.RouteMeasureUnit = esriUnits.esriFeet; pName = (IName)pRouteLocatorName; IRouteEventProperties2 pRouteProp = new RouteMeasureLinePropertiesClass(); pRouteProp.AddErrorField = true; pRouteProp.EventMeasureUnit = esriUnits.esriFeet; pRouteProp.EventRouteIDFieldName = _pFKName; IRouteMeasureLineProperties rMLineProp = (IRouteMeasureLineProperties)pRouteProp; rMLineProp.FromMeasureFieldName = _pFrom; rMLineProp.ToMeasureFieldName = _pTo; IDataset pDs = (IDataset)_pEventTable; IName pNTableName = pDs.FullName; IRouteEventSourceName pRouteEventSourceName = new RouteEventSourceNameClass(); pRouteEventSourceName.EventTableName = pNTableName; pRouteEventSourceName.EventProperties = (IRouteEventProperties)pRouteProp; pRouteEventSourceName.RouteLocatorName = pRouteLocatorName; pName = (IName)pRouteEventSourceName; 内部文档,请勿外传 IFeatureClass pFeatureClass = (IFeatureClass)pName.Open(); return pFeatureClass; } 内部文档,请勿外传 12 十二.安装部署 1. NetFramwork35sp1, ArcGIS Engine Runtime 单独安装 12.1 ArcGIS Engine 应用程序打包的打包,我们可以采取这样的方案(Runtime 和应用程序分开安装, Runtime 和应用程序一起安装),这里我先介绍分开安装。为了能正确的使用我们开发的应用程序,确保 自己的电脑上装了以下组件: 1)NetFramework35sp1; 2)ArcGIS Engine 10 Runtime(ArcGIS Engine Runtime 已经正确授权)。 采用 Visual Stuido 2008 自带的打包程序打包 步骤如下: 1,在解决资源方案中添加新的项目(我的 Engine 应用程序也存在这个解决发方案中),如下图: 内部文档,请勿外传 2,找到安装和部署的节点,选择安装项目这个模板,并命名为 Setup,如下图: 3,选择 Setup 的文件系统编辑按钮中,然后在应用程序文件夹中右键 选择添加项目-输出,如下图: 内部文档,请勿外传 4,选择主输出,项目为我们的 Engine 应用程序的名称,如下图: 内部文档,请勿外传 5,我们会看到中间的一个窗口中多了很多 Esri 的 dll,还有我们的 exe,在我们应用程序上右键,创 建快捷方式,如下图: 内部文档,请勿外传 6,在 Setup 的引用中,排除 Esri 的 dll,为什么?因为 Runtime 中有这些 dll,所以我们不需要打包 这些,如下图: 内部文档,请勿外传 7,将我们刚才创建的快捷方式,重命名,拖到用户桌面的那个目录中或者程序菜单中,当然可以创建 两个快捷方式,分别拖过去. 8,执行 Setup,我们就可以看到打包后的 msi 和 exe,如下图: 安装就会看到如下界面,如下图: 内部文档,请勿外传 2. 将 NetFramwork35sp1 ,ArcGIS Engine Runtime 一起打12.2 包 1,新建立一个工程,填写这个工程的位置以及工程的名字,如下图所示: 内部文档,请勿外传 2, 新建工程完成后,Installshield 会进入 Project Assistant 界面,这是 Installshield 的安装助 手,它将一些最常用的操作按照“上一步”、“下一步”的方式组织成为一个向导,我们可以利用此向导 快速的完成安装程序制作的全过程。不过此方式过于简单,如果我们想进行复杂一些的操作就做不到了。 所以需要制作专业的安装程序的朋友们还是使用 Installation Designer 方式来制作安装程序,但是偶是 一个菜鸟,就两种方式结合了下。建立工程完成后,会在 Project Assistant 看到 InstallShield 的打包 流程,如下图: 3,下一步,填写应用程序的信息,如下图: 内部文档,请勿外传 4,下一步,设置安装架构(我直接默认),如下图: 5,下一步之后就到了,应用程序的文件,如下图: 内部文档,请勿外传 我们可以添加文件,我没这么做,而是切换到了 Installation Designer 这个中,在左边的 Files and Folders 双击,如下图: 双击了 Files and Folders,会出来几个窗口,其中 Source computer’s folders 就是我们的计算机 上的目录,而下面的那个 Destination computer’s folders 就是安装的目录,将我们的源程序文件夹从 Source computer’s folders 拖到 Destination computer’s folders 的 Application Target Folder 中, 并建立在 Application Target Folder 右键 建立一个 temp 目录,将我们的 ArcGIS Engine 10(这个目录 中含有 ArcGIS Engine Runtime 以及相应的授权文件)拖放到这个 temp 目录中,如下图: 内部文档,请勿外传 建立 temp 目录如下图: 两步操作完成后,我们可以看到下面的结构图: 内部文档,请勿外传 我们有切换的 Project Assistant 面板中,如下: 6,下一步,创建开始菜单和快捷方式,如下图: 内部文档,请勿外传 单击 New 按钮,选择我们的 EXE 文件 将系统给的名字 launch WRE_B.exe 重新命名下,如下图: 内部文档,请勿外传 切换到 Installation Designer 中,找到 Shortcuts,进行进一步的设计,如下图: 内部文档,请勿外传 将上图的 esri 删除掉,重新建立目录和快捷方式,并对右边的 Internal Name 进行改写,如下图: 7,切换到 Project Assistant,然后下一步,如下图: 8,下一步,这一步就让选择一些许可声明之类的(安装过软件的朋友都知道,安装的时候会出现一些 声明条款的,就是这个),我没有这个需要,所以没选这个,如下图: 内部文档,请勿外传 9,下一步,选择语言,我当然选择中文了(说明下,不同的版本不同,2011 的默认语言是英文,2008 的是繁体中文,2010 是中文,想不到,我为了打包这个,用了 3 个版本的软件),如下图: 10,下一步,选择打包的类型,选择第一个(这个流程已经完成,别急着建立,我们的 Net Framework 3.5 SP1 还没打包进去呢),如下图: 内部文档,请勿外传 11,切换到 Installation Designer 面板中,找到 InstallScript,如下图: 内部文档,请勿外传 将这个函数粘贴在这个脚本文件中,函数如下: function OnMoved() string szCommand,szCmdLine,szPath,svResult,LicPath; STRING svLine,svReturnLine,InsertTxt; //////操作文件的变量 NUMBER nvFileHandle; //////////文件句柄 NUMBER nvLineNumber ,nvResult; //////////操作文件的变量 begin szPath=TARGETDIR^"temp"; szCommand = WINSYSDIR^"msiexec.exe"; LongPathToShortPath(szCommand); ////////////////////////安装 AE 运行时 if (FindFile (szPath^"ArcGIS Engine10", "setup.msi", svResult) = 0) then szCmdLine =TARGETDIR^"temp"^"ArcGIS Engine10"^"setup.msi\" /qn"; LongPathToShortPath(szCmdLine); Delay(1); if (LaunchAppAndWait(szCommand ,"/i \""+szCmdLine,WAIT) < 0) then 内部文档,请勿外传 abort; endif; endif; /////////////////////////AE 授权 LicPath=" -Lif \"" +TARGETDIR^"temp"^"ArcGIS Engine10"^"ArcGIS Engine Runtime License.ECP\" -s"; if (FindFile (TARGETDIR^"temp"^"ArcGIS Engine10", "ArcGIS Engine Runtime License.ECP", svResult) = 0) then if(SYSINFO.bIsWow64) then szCmdLine="C:\\Program Files\\Common Files (x86)\\ArcGIS\\bin\\SoftwareAuthorization.exe"; else szCmdLine="C:\\Program Files\\Common Files\\ArcGIS\\bin\\SoftwareAuthorization.exe"; endif; if (LaunchAppAndWait(szCmdLine ,LicPath,WAIT) < 0) then abort; endif; 内部文档,请勿外传 endif; if (ExistsDir(TARGETDIR^"temp")=0 ) then if (DeleteProgramFolder (TARGETDIR^"temp") < 0) then endif ; endif ; end; 12,打包 Net Framework 3.5 sp1,在 Prerequisites 中找到 Net Framework 3.5 sp1,如下图: 在 Net Framework 3.5 sp1 前打钩,出现是否下载的对话框,选择否 (因为我已经有了这个),如下图: 内部文档,请勿外传 在 Net Framework 3.5 sp1 上右键,选择编辑,移除原来的那个文件, 添加上自己机器上的 Net Framework 3.5 sp1,如如下图: 在 Application to Run 的界面中将 Specify the command line for the application 和 Specify the command line for the application when the setup is running in silent mode 中的参数改为:/q 、norestart,如下图: 内部文档,请勿外传 14,在 User Interface 中找到 Dialogue,然后找到 Skin,设置皮肤的 颜色,如下图: 内部文档,请勿外传 设置完之后,建立我们的程序,运行成功后,如下图: 这样我们只需要将这个在客户机上安装即可。 3. 打包中的两个问题 12.3 问题(1):当执行 Setup.exe 的时候,由于我们打包了 NetFramwork3.5 sp1,当 NetFramwork3.5 sp1 装完后,会问我们是否要重新启动电脑,如果点了否,安装程序就中断了,当再次安装 Setup.exe 的时候, 就可以完成。 问题(2):当 Runtime 完成后,发现应用程序不能用,没有授权成功?授权是成功的,因为 ArcGIS Engine 10 的许可机制发生了点变化,所以我们可以通过手动配置:开始-程序-ArcGIS-Administrator(但是不需 要授权文件,只需要点了,授权,然后取消即可)。 因为有以上两个问题,我们可以变通下,比如说手动操作下 Administrator。 内部文档,请勿外传 先说授权的问题,将 ArcGIS Engine 10 的许可的注册文件打包进去,操作如下: 将已经可用 ArcGIS Engine 10 的注册表导出去,然后利用 installshield,操作如下: 在 Installation Designer 面板中,在 System Configuratin 中找到 Registy,然后右键,在 Destination 中找到 Local-Machine,如下图: 右键,找到 Import REG File 内部文档,请勿外传 点了 Import REG File 之后,就看到注册表导入向导,进行设置如下: 内部文档,请勿外传 想一下操作有问题吗?有一个前提条件,就是这个打包程序要开启使用注册表,在 Project Assistant 如下: 4,至于 NetFramwork3.5 sp1,在 Installation Designer 中找 Prerequistes 中将 NetFramwork3.5 sp1 前面的勾去掉,如下图: 5,在 Installation Designer 中,将 NetFramwork3.5 sp1 添加到 Language Independent 中,如下 图: 内部文档,请勿外传 然后写入以下脚本: /////安装 dotnetfx35sp1.exe GetEnvVar("TEMP", tempdir);//得到临时目录 FindAllFiles(tempdir, "dotnetfx35sp1.exe" , tempdir, CONTINUE );//在临时目录下搜索 dotnetfx.exe 文件 if(LaunchAppAndWait(tempdir, "/q /norestart",WAIT)<0) then abort; endif; 整个完整的脚本如下: ,function OnMoved() string szCommand,szCmdLine,szPath,svResult,LicPath,tempdir; STRING svLine,svReturnLine,InsertTxt; //////操作文件的变量 NUMBER nvFileHandle; //////////文件句柄 NUMBER nvLineNumber ,nvResult; //////////操作文件的变量 begin szPath=TARGETDIR^"temp"; szCommand = WINSYSDIR^"msiexec.exe"; LongPathToShortPath(szCommand); 内部文档,请勿外传 /////安装 dotnetfx35sp1.exe GetEnvVar("TEMP", tempdir);//得到临时目录 FindAllFiles(tempdir, "dotnetfx35sp1.exe" , tempdir, CONTINUE );//在临时目录下搜索 dotnetfx.exe 文件 if(LaunchAppAndWait(tempdir, "/q /norestart",WAIT)<0) then abort; endif; ////////////////////////安装 AE 运行时 if (FindFile (szPath^"ArcGIS Engine10", "setup.msi", svResult) = 0) then szCmdLine =TARGETDIR^"temp"^"ArcGIS Engine10"^"setup.msi\" /qn"; LongPathToShortPath(szCmdLine); Delay(1); if (LaunchAppAndWait(szCommand ,"/i \""+szCmdLine,WAIT) < 0) then abort; endif; endif; /////////////////////////AE 授权 LicPath="/Lif \""+TARGETDIR^"temp"^"ArcGIS Engine10"^"ArcGIS Engine Runtime License.asr\" /S"; if (FindFile (TARGETDIR^"temp"^"ArcGIS Engine10", "ArcGIS Engine Runtime License.asr", svResult) = 0) then szCmdLine="C:\\Program Files\\Common Files\\ArcGIS\\bin\\SoftwareAuthorization.exe"; 内部文档,请勿外传 if (LaunchAppAndWait(szCmdLine,LicPath,WAIT) < 0) then abort; endif; endif; LicPath="/Lif \"" +TARGETDIR^"temp"^"ArcGIS Engine10"^"GeodatabaseUpdate.asr\" /S"; if (FindFile (TARGETDIR^"temp"^"ArcGIS Engine10", "GeodatabaseUpdate.asr", svResult) = 0) then szCmdLine="C:\\Program Files\\Common Files\\ArcGIS\\bin\\SoftwareAuthorization.exe"; if (LaunchAppAndWait(szCmdLine ,LicPath,WAIT) < 0) then abort; endif; endif; if (ExistsDir(TARGETDIR^"temp")=0 ) then if (DeleteProgramFolder (TARGETDIR^"temp") < 0) then endif ; endif ; end; 经过测试,大功告成。 内部文档,请勿外传 I 内部文档,请勿外传 1
还剩335页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 4 人已下载

下载pdf

pdf贡献者

sue1385

贡献于2012-09-03

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf