Map-Layer部分介绍

1.从Mxd文件说起

1.1.Mxd文件介绍

ArcGIS的地图文件为.mxd扩展名。Mxd文件的是有版本的,和ArcGIS的版本对应。可以在ArcMap中的File-Save A Copy,保存一个地图拷贝的时候选择Mxd文件的版本,支持的版本如下图所示。

 

因为我是用ArcMap 10.8截的图,所以这还需要再加一个ArcMap 10.8 Document。一个地图文件包含一个布局对象,一个多或者地图对象以及其他一些附属信息。地图文件不直接存储例如.shp、.tif等实际数据,只是存储一些图层信息以及图层对这些数据的引用。

高版本的ArcMap可以打开同版本和低版本的地图文件。

1.2.使用ArcMap打开mxd文件

下图是ArcMap自带的一个典型的mxd文件打开的效果。如下图所示。

 

当前模式为布局模式,即激活的是PageLayout,一共包含了三个地图,其中激活的地图为第一个Conterminous United States。

切换到数据模式,ArcMap显示的是当前激活的地图,如下图所示。

 

从ArcMap展示上,我们可以看出,一个地图文件,包含了一个布局对象,即PageLayout。包含了多个地图对象,即Map。每个Map都可以作为一个元素在PageLayout上显示,并可以设置自己的显示比例尺,不必统一。针对每个地图,可以添加其关联的比例尺,图例、指北针、网格等智能元素。也可以在布局视图上添加点、线、面以及文字等元素。

ArcMap中,布局和数据视图,同一时刻只能激活一个。而且当数据视图被激活的时候,显示的地图为布局视图中的活动地图。

每个Map都包含了多个图层,  可以为这些图层设置渲染、标注等信息,但并不存储数据本身。我们可以在Map上添加点、线、面以及文字等非数据元素。

1.3.使用ArcObject SDK打开mxd文件

在ArcObjects SDK中,我们一般使用IMapDocument接口打开mxd文件。在帮助中,查看到的IMapDocument的结构如下图所示。

 

有几个比较重要的属性。

ActiveView,当布局视图激活的时候,该属性返回的是IPageLayout,当数据视图激活的时候,返回的是布局视图下活动的地图,即IMap。

MapCount表示当前地图文件有几个地图,通过Map[Index]可以获取指定的地图对象。

PageLayout即地图文件包含的布局对象,返回类型为IPageLayout。

IMap和IPageLayout的实现类都继承了IActiveView,即实际开发的时候,IMap和IPageLayout接口类型的变量都可以转换成IActiveView接口。

IMapDocument、IActiveView、IMap和IPageLayout接口之间的关系可与用下图表示。

 

2.Map-Layer主干结构

一个mxd文件可以包含多个地图,但我们常用的大部分都是包含一个地图。一个地图可以包含多个图层组和图层,而图层指向的则是实际数据。图层可以控制数据是否显示、显示样式、最大最小显示比例尺等。Map可以设置自己的空间参考,在渲染的时候,各图层会投影到地图的空间参考下显示。

我们可以通过打开mxd获取map,从而获取图层对象。也可以通过ArcObjects SDK打开各类数据源,实例化图层。图层也可以单独保存成lyr文件,和mxd文件一样,只是保存了图层信息,并不保存图层指向的数据。

Map-Layer这个部分我认为是ArcObjects SDK中最核心、最重要的部分。我们在开发的时候,经常就是围绕这块的内容展开。从Map-Layer这两个点可以引出一大批重要的接口,例如IFeatureClass、IFeatureSelection、IFeatureCursor、IField、IFeature、IFeatureRenderer、IRaster、IRasterRenderer等,这些都是非常重要且使用频率非常高的接口。Map-Layer引申出来的关系网如下图所示。

 

 

 

3.Map

对于Map的话,我们需要了解以下内容。

1、一个mxd文件是可以包含多个地图文件的,但只能有一个处于激活状态,也就是在MapControl控件中,只能显示一个Map。

2、Map中包含的图层的空间参考可以各种各样,Map也可以设置自己的空间参考,显示的时候,会把图层的转换到Map的空间参考上显示。

3、 如果地图是地理空间参考的话,ArcGIS会默认以Plate_Carree投影显示。

打开ArcGIS自带的地图USA.mxd文件,如下图所示。

 

从图层树以及布局模式下的MapFrame可以看出,当前一共有三个地图,Conterminous United States、Hawaii和Alaska,分别对应布局模式下的三个MapFrame,每个MapFrame都有自己的显示比例以及比例尺元素。

有时候我们在做中国地图的时候,为了省事,南海那部分就用一张图片代替,放到地图的右小角。但把南海部分也做成一个Map,使用两个MapFarme是最规范的做法。

4.FeatureLayer

4.1.FeatureLayer的结构

FeatureLayer是我们开发的时候用的最多的API之一,其实现的接口以及关联的其他API也非常多。下面我们就用一张图来整体看下FeatureLayer有哪些常用的功能。

 

 

 

FeatureLayer类继承实现了非常多的接口。每个接口主要负责什么功能呢?我们可以参考每个接口定义属性和函数,还有一个更直观的方法,就是最找ArcMap软件。

IFeatureLayer接口的FeatureClass属性主要对应着矢量图层属性对话框中的Source选项卡 。IGeoFeatureLayer的Renderer属性对应Symbology选项卡,AnnotationProperties属性对应着Labels选项卡。IFeatureSelection接口对应Selection选项卡。ILayerFields接口对应着Fields选项卡。ILayerGeneralProperties接口对应General选项卡。IFeatureLayerDefinition接口对应Definition Query选项卡。IHTMLPopupInfo以及相似名字的几个接口对应着HTML Popup选项卡。IRelationshipClassCollection接口对应Joins&Relates选项卡。矢量图层属性对话框如下图所示。

 

4.2.IFeatureClass接口

IFeatureLayer的FeatureClass属性返回的是IFeatureClass类型,这就是我们的矢量图层实际指向的数据源。我们用代码打开一个Shape文件,获得的就是一个IFeatureClass对象。FeatureLayer的FeatureClass属性对应的属性标签如下图所示。

 

如果我们打开的是一个Shape文件,通过上图以及参考SDK的API,能够直观的看出可以获得数据的空间范围、数据类型、文件路径、几何体类型、空间参考等信息。

FeatureClass,我们可以理解为有一个几何体字段的二维数据表。二维数据表字段、有数据行,FeatureClass中的字段定义是IField,数据行定位为IFeature。因为包含一个几何体字段,所有就区分几何体类型,是Point、Polyline或者Polygon。所示IField的类型就多了一个几何体类型,定义为esriFieldType. esriFieldTypeGeometry。IFeature也有一个属性,名为Shape,返回该数据行存储的几何体。

 

4.3.IFeatureRenderer接口

通过IGeoFeatureLayer的Renderer属性可以获得地图的渲染对象IFeatureRenderer,IFeatureRender对应了矢量图层选项卡中的Symbology,如下图所示。

 

该选项卡中做的为矢量图层可使用的渲染类型,有的为选中的渲染类型的属性信息。矢量图层支持哪些渲染方式,可展开左侧树结构查看,也可以在ArcObject SDK的帮助中查看有哪些类继承实现了IFeatureRenderer接口,两者是可以对应起来的。

 

以最简单的SimpleRenderer为例,其对应的是渲染界面上的Single symbol项,这点在帮助里面也有说明。

 

其主接口为ISimpleRenderer,其定义基本上也和ArcMap上的UI是对应起来的,如下图所示。

 

 

4.4.IAnnotateLayerProperties接口

通过IGeoFeatureLayer接口的AnnotationProperties属性可以获取IAnnotateLayerPropertiesCollection接口,该接口是IAnnotateLayerProperties的集合,包含多个IAnnotateLayerProperties接口实例。这点我们也可以在ArcMap的Label标签页中验证,矢量图层在Label的时候,可以设置多种Label规则。如下图所示。

 

实现IAnnotateLayerProperties接口的类有两个,我们常用的是 LabelEngineLayerProperties,而LabelEngineLayerProperties又继承了ILabelEngineLayerProperties、ILabelEngineLayerProperties2等接口。这些接口定义的信息,基本上就能把Label选项卡中的内容对应上了。

 

 

 

4.5.IFeatureSelection接口

FeatureLayer实现继承了IFeatureSelection接口,该接口定义的内容可以Selection选项卡里找到。

 

IFeatureSelection的SelectFeatures可以通过设置查询条件来选择或者反选要素。查询条件既可以设置为属性查询条件,也可以设置为空间查询条件。

 

IQueryFilter接口定义入下。

 

esriSelectionResultEnum枚举的定义如下。

 

4.6.Search函数

IFeatureLayer和IFeatureClass都有Search函数,两者有什么区别呢?我们先看IFeatureLayer的Search函数。我们打开IFeatureLayer. Search函数的帮助,如下图所示。

 

下面备注中的文字大概是以下意思。

如果该图层定义了查询集,有就是说在IFeatureLayerDefinition接口的DefinitionExpression属性(ArcMap的Definition Query标签页)定义了查询条件,那么IFeatureLayer的Search函数就会在该查询的基础上进行查询。如果该图层使用Join连接了某个图层或者属性表,但查询的字段有该连接对象的字段,那么请调用IGeoFeatureLayer.SearchDisplayFeatures函数。

IFeatureLayer.Search函数返回的游标,也就是IFeatureCursor接口,不能用来更新要素,如果想更新要素,请使用IFeatureClass.Update函数。

回收游标第二个参数设置为true,否则设置为false。一般我们调用IFeatureLayer.Search函数后,返回的是IFeatureCursor,我们称为要素游标,通过该游标可以遍历查找结果。一般遍历方法如下所示。

IFeatureCursor myFeatureCursor = myFeatureLayer.Search(myQueryFilter, false);

IFeature myFeature = myFeatureCursor.NextFeature();

while (myFeature != null)

{

    myFeature = myFeatureCursor.NextFeature();

}

ComReleaser.ReleaseCOMObject(myFeatureCursor);

如果传false,FeatureCursor.NextFeature之后,上一个IFeature还可以使用,如果是true,则就不能用了。传true会更节约内存,但你要把你想取的信息全部都取出来。

IFeatureClass的Search函数与IFeatureLayer的Search函数类似,只是其在原始数据的基础上查询,和图层的设置没有关系。

4.7.IFeatureClass.Insert函数

该函数用来批量添加要素。如果我们添加一个要素,可以调用IFeatureClass. CreateFeature函数,得到一个IFeature实例,然后对其赋值,最后调用IFeature的Store函数即可。但如果我们要批量添加多个要素,就要调用IFeatureClass.Insert函数,得到要素添加游标,在该游标上添加要素,最后一起提交即可。调用Insert函数的代码基本上都是一样的,使用的时候参考下面的模板即可。

IFeatureBuffer myFeatureBuffer = myFeatureClass.CreateFeatureBuffer();

IFeatureCursor myFeatureCursor = myFeatureClass.Insert(true);

for (int i = 0; i < myXList.Count; i++)

{

    var myPoint = new PointClass

    {

        X = myXList[i],

        Y = myYList[i]

    };

    myFeatureBuffer.Shape = myPoint;

    myFeatureBuffer.Value[2] = i;

    myFeatureBuffer.Value[3] = 0;

    myFeatureCursor.InsertFeature(myFeatureBuffer);

    if (i % 1000 == 0)

    {

        myFeatureCursor.Flush();

    }

}

if (this.InputDataTable.Rows.Count % 1000 > 0)

{

    myFeatureCursor.Flush();

}

ComReleaser.ReleaseCOMObject(myFeatureCursor);

FeatureCursor.Flush()是提交函数,如果添加的要素太多,最后一次性提交,会导致运行太慢哪,把进度条卡住,所以我一般会每1000条提交依次,这个数可以根据实际情况修改。

4.8.IFeatureClass.Update函数

该函数用看来批量更新要素。和Insert函数类似,如果我们只是操作一个要素,可以在获取IFeature之后,修改其属性值,调用最后调用IFeature的Store函数即可。但如果要批量更新,则建议采用IFeatureClass.Update函数,效率会比较高。调用Update函数的代码基本上都是一样的,使用的时候参考下面的模板即可。

int myValueFieldIndex = myFeatureClass.FindField(“Value”);

var myFeatureCursor = myFeatureClass.Update(null, true);

var myFeature = myFeatureCursor.NextFeature();

while (myFeature != null)

{

    //更新要素

    myFeature = myFeatureCursor.NextFeature();

}

myFeatureCursor.Flush();

ComReleaser.ReleaseCOMObject(myFeatureCursor);

4.9.ITable.DeleteSearchedRows函数

该函数用来批量删除要素。如果要删除单个要素,可以调用得到的IFeature.Delete函数。如果批量删除或者根据某个条件来删除要素,则可以调用ITable.DeleteSearchedRows函数。FeatureClass是继承实现了ITable接口的,所以我们把IFeature接口转换成ITable接口,调用该函数即可。一般调用代码如下。

ITable myTabel = this.PointFeatureLayer.FeatureClass as ITable;

IQueryFilter myQueryFilter = new QueryFilterClass

{

    WhereClause = “RowIndex=” + pRowIndex.ToString()

};

myTabel.DeleteSearchedRows(myQueryFilter);

ITable.DeleteSearchedRows函数的参数为IQueryFilter,除了QueryFilter类外,SpatialFilter类也继承了该接口。但DeleteSearchedRows函数又是在ITable接口中定义的,那这个函数是不是支持SpatialFilter,可以去验证下,但我感觉应该是支持的。

4.10.IField接口和IFieldInfo接口

获取字段有两种方式,一是通过FeatureLayer继承实现了ILayerFields接口获取字段,另外一种是通过矢量数据源IFeatureClass的Fields属性获取IFields接口获取字段信息。

我们先看下IFields接口,如下图所示。

 

可以通过索引或字段名称获取具体的字段信息,返回是IField接口。我们再看下ILayerFields接口的定义。

 

我们看到除了返回IField外,还可以返回IFieldInfo,这两个有什么区别呢?IField是数据源中字段的定义,IFieldInfo是图层对字段定义的扩展。我们看下IFieldInfo的定义,如下图所示。

 

在IField的基础上扩展了字段别名,按照字符串返回某个要素该字段的值,属性表显示的时候该字段是否高亮显示,数字显示格式、是否只读、字段是否按照比率显示,是否可见。我们常用的主要有别名、是否可见等属性。这些信息对应了ArcMap矢量属性对话框中的Fields选显卡,如下图所示。

 

右侧字段详细信息分为两组,上面可设置的部分为IFieldInfo的信息,下面只读的信息为IField信息。

4.11.字段操作

对字段相关的操作主要是添加和删除。在Arcobjects中很少更新字段,基本上都是添加新字段,把旧字段的值设置到新字段中,删除旧字段。

添加字段可以实例化一个IField对象,然后通过IFeatureClass的AddField函数添加字段。添加的时候需要注意,数据源不要被其他应用占用,否则会发生锁定错误。

IField myField = new Field();

IFieldEdit myFieldEdit = myField as IFieldEdit;

myFieldEdit.Name_2 = “Level”;

myFieldEdit.Type_2 = esriFieldType.esriFieldTypeInteger;

myFeatureClass.AddField(myField);

删除字段可调用IFeatureClass的DeleteField函数,调用的时候,还是需要注意,数据源不要被其他应用占用,否则会发生锁定错误。

4.12.打开Shape文件

打开Shape文件后,我们就可以获得IFeatureClas。打开Shape文件的代码比较固定,使用下面的代码打开即可。

public static IFeatureClass OpenShapeFile(string pShapeFilePath)

{

    var myType = Type.GetTypeFromProgID(“esriDataSourcesFile.ShapefileWorkspaceFactory”);

    var myObject = Activator.CreateInstance(myType);

    var myWorkspaceFactory = myObject as IWorkspaceFactory;

    var myFeatureWorkspace = myWorkspaceFactory.OpenFromFile(System.IO.Path.GetDirectoryName(pShapeFilePath), 0) as IFeatureWorkspace;

    return myFeatureWorkspace.OpenFeatureClass(System.IO.Path.GetFileNameWithoutExtension(pShapeFilePath));

}

4.13.创建Shape文件

创建Shape文件代码模式也很固定,即使是往gbd或者企业sde数据库中创建矢量数据的时候,我一般也喜欢先在一个临时目录下创建shape文件,然后调用Arctoolbox里面的工具,把这个数据拷贝到目标工作空间中。主要还是因为直接创建shape文件更简单,更稳定,而且不用考虑针对那么多数据源再分别写代码。

创建一个Shape文件的代码如下。

public static IFeatureClass CreateShapeFile(string pShapeFilePath, List<IField> pFieldList)

{

    string myFolderPath = System.IO.Path.GetDirectoryName(pShapeFilePath);

    if (System.IO.Directory.Exists(myFolderPath) == false)

    {

        System.IO.Directory.CreateDirectory(myFolderPath);

    }

    string myFileName = System.IO.Path.GetFileName(pShapeFilePath);

    var myType = Type.GetTypeFromProgID(“esriDataSourcesFile.ShapefileWorkspaceFactory”);

    object myObject = Activator.CreateInstance(myType);

    var myWorkspaceFactory = myObject as IWorkspaceFactory;

    var myFeatureWorkspace = myWorkspaceFactory.OpenFromFile(myFolderPath, 0) as IFeatureWorkspace;

    var myFields = new FieldsClass();

    var myFieldsEdit = myFields as IFieldsEdit;

    foreach (IField myField in pFieldList)

    {

        myFieldsEdit.AddField(myField);

    }

    IFeatureClass myFeatureClass = null;

    try

    {

        myFeatureClass = myFeatureWorkspace.CreateFeatureClass(myFileName, myFields, null, null, esriFeatureType.esriFTSimple, “shape”, “”);

    }

    finally

    {

        ComReleaser.ReleaseCOMObject(myWorkspaceFactory);

    }

    return myFeatureClass;

}

创建完之后,得到IFeatureClass,如果不需要,就可以把该对象释放掉。如果需要添加要素,则可以通过调用其Insert函数,批量添加要素。如果想添加到地图上,则实例化一个FeatureLayer,把该对象赋值给FeatureLayer的FeatureClass对象,然后设置渲染样式,即可添加到地图上展示。

5.  RasterLayer

5.1.RasterLayer的结构

图层的话,除了FeatureLayer外,用的最多的就是RasterLayer了。较FeatureLayer而言,RasterLayer比较简单,这点可以从栅格图层的属性对话框中可以看出。

 

其中General选项卡对应着RasterLayer继承实现的ILayerGeneralProperties接口,Source选项卡对应IRasterLayer的Ratser属性,Display选项卡对应着ILayerEffects接口,Symbology选项卡对应着IRasterLayer的Renderer属性。

5.2.IRaster接口

IRasterLayer的Raster属性返回的是IRaster接口类型。其指的是实际的栅格数据源,我们一般存储为tif或者img文件。Raster类实现了IRaster接口,同时Raster还继承了以下接口。

 

其中通过IRaster可以分块读取栅格数据的像元信息,并可以设置重采样方式。IRaster2接口提供了一些像素与地图坐标相互转换的函数。IRasterBandCollection接口可以读取栅格数据包含的波段信息。IRasterEdit接口提供了修改栅格数据像元值的功能。IRasterProps提供了栅格数据的属性信息。ISaveAs接口提供了栅格数据的保存功能。

不过我们一般很少用IRaser接口去修改栅格数据,主要还是靠调用ArcToolbox里面的工具来处理。

5.3.IRasterRenderer接口

该接口为栅格图层渲染接口,通过RasterLayer的Renderer可以获取或设置。继承该接口的类如下图所示。

 

其中我们常用的有RasterClassifyColorRampRenderer,分级别渲染,例如显示某个区域内的人口密度数据的栅格数据文件,就可以按照不同的颜色进行分段显示。

RasterUniqueValueRenderer,唯一值渲染,例如土地分类数据,就可以把耕地、林地、草地、城市用地等按照不同的颜色显示。

RasterStretchColorRampRenderer,拉伸渲染,一般我们显示Dem数据的时候都会使用这种渲染方式。

每种渲染方式如何设置,都可以参考ArcMap中的参数设置界面以及SDK帮助。

5.4.打开栅格数据

我们常用的栅格数据主要有tif和img格式。打开栅格数据有多种方法,我习惯用RasterLayer的CreateFromFilePath函数打开栅格数据,这这种方式比较简单。代码如下。

var myRasterLayer = new RasterLayerClass();

myRasterLayer.CreateFromFilePath(this.DEMFilePath);

5.5.创建栅格数据文件

我们可以使用IWorkSpace来创建栅格数据文件。

public static IRasterDataset CreateRasterDataset(string Path, string FileName)

{

    try

    {

        IRasterWorkspace2 rasterWs = OpenRasterWorkspace(Path);

        ISpatialReference sr = new UnknownCoordinateSystemClass();

        IPoint origin = new PointClass();

        origin.PutCoords(15.0, 15.0);

        int width = 100;

        int height = 100;

        double xCell = 30;

        double yCell = 30;

        int NumBand = 1;

        IRasterDataset rasterDataset = rasterWs.CreateRasterDataset(FileName, “TIFF”,

                     origin, width, height, xCell, yCell, NumBand, rstPixelType.PT_UCHAR, sr,

            true);

        IRasterBandCollection rasterBands = (IRasterBandCollection)rasterDataset;

        IRasterBand rasterBand;

        IRasterProps rasterProps;

        rasterBand = rasterBands.Item(0);

        rasterProps = (IRasterProps)rasterBand;

        rasterProps.NoDataValue = 255;

        IRaster raster = rasterDataset.CreateFullRaster();

        IPnt blocksize = new PntClass();

        blocksize.SetCoords(width, height);

        IPixelBlock3 pixelblock = raster.CreatePixelBlock(blocksize)as IPixelBlock3;

        //Populate some pixel values to the pixel block.

        System.Array pixels;

        pixels = (System.Array)pixelblock.get_PixelData(0);

        for (int i = 0; i < width; i++)

            for (int j = 0; j < height; j++)

                if (i == j)

                    pixels.SetValue(Convert.ToByte(255), i, j);

                else

                    pixels.SetValue(Convert.ToByte((i * j) / 255), i, j);

        pixelblock.set_PixelData(0, (System.Array)pixels);

        IPnt upperLeft = new PntClass();

        upperLeft.SetCoords(0, 0);

        IRasterEdit rasterEdit = (IRasterEdit)raster;

        rasterEdit.Write(upperLeft, (IPixelBlock)pixelblock);

        System.Runtime.InteropServices.Marshal.ReleaseComObject(rasterEdit);

        return rasterDataset;

    }

    catch (Exception ex)

    {

        System.Diagnostics.Debug.WriteLine(ex.Message);

        return null;

    }

}

public static IRasterWorkspace2 OpenRasterWorkspace(string PathName)

{

    //This function opens a raster workspace.

    try

    {

        IWorkspaceFactory workspaceFact = new RasterWorkspaceFactoryClass();

        return workspaceFact.OpenFromFile(PathName, 0)as IRasterWorkspace2;

    }

    catch (Exception ex)

    {

        System.Diagnostics.Debug.WriteLine(ex.Message);

        return null;

    }

}

我们一般创建的时候,会调用ArcToolbox中的工具去创建。

 

原文地址:http://www.cnblogs.com/mytudousi/p/16871183.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性