ArcObjects SDK開發 010 FeatureLayer

1、FeatureLayer的結構

FeatureLayer是我們開發的時候用的最多的API之一,其實現的接口以及關聯的其他API也非常多。下面我們就用一張圖來整體看下FeatureLayer有哪些常用的功能。

image1.png

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選項卡。矢量圖層屬性對話框如下圖所示。

image2.png

2、IFeatureClass接口

IFeatureLayer的FeatureClass屬性返回的是IFeatureClass類型,這就是我們的矢量圖層實際指向的數據源。我們用代碼打開一個Shape文件,獲得的就是一個IFeatureClass對象。FeatureLayer的FeatureClass屬性對應的屬性標籤如下圖所示。

image3.png

如果我們打開的是一個Shape文件,通過上圖以及參考SDK的API,能夠直觀的看出可以獲得數據的空間範圍、數據類型、文件路徑、幾何體類型、空間參考等信息。

FeatureClass,我們可以理解爲有一個幾何體字段的二維數據表。二維數據表字段、有數據行,FeatureClass中的字段定義是IField,數據行定位爲IFeature。因爲包含一個幾何體字段,所有就區分幾何體類型,是Point、Polyline或者Polygon。所示IField的類型就多了一個幾何體類型,定義爲esriFieldType. esriFieldTypeGeometry。IFeature也有一個屬性,名爲Shape,返回該數據行存儲的幾何體。

image4.png

3、IFeatureRenderer接口

通過IGeoFeatureLayer的Renderer屬性可以獲得地圖的渲染對象IFeatureRenderer,IFeatureRender對應了矢量圖層選項卡中的Symbology,如下圖所示。

image5.png

該選項卡中做的爲矢量圖層可使用的渲染類型,有的爲選中的渲染類型的屬性信息。矢量圖層支持哪些渲染方式,可展開左側樹結構查看,也可以在ArcObject SDK的幫助中查看有哪些類繼承實現了IFeatureRenderer接口,兩者是可以對應起來的。

image6.png

以最簡單的SimpleRenderer爲例,其對應的是渲染界面上的Single symbol項,這點在幫助裏面也有說明。

image7.png

其主接口爲ISimpleRenderer,其定義基本上也和ArcMap上的UI是對應起來的,如下圖所示。

截圖.png

4、IAnnotateLayerProperties接口

通過IGeoFeatureLayer接口的AnnotationProperties屬性可以獲取IAnnotateLayerPropertiesCollection接口,該接口是IAnnotateLayerProperties的集合,包含多個IAnnotateLayerProperties接口實例。這點我們也可以在ArcMap的Label標籤頁中驗證,矢量圖層在Label的時候,可以設置多種Label規則。如下圖所示。

image9.png

實現IAnnotateLayerProperties接口的類有兩個,我們常用的是 LabelEngineLayerProperties,而LabelEngineLayerProperties又繼承了ILabelEngineLayerProperties、ILabelEngineLayerProperties2等接口。這些接口定義的信息,基本上就能把Label選項卡中的內容對應上了。

5、IFeatureSelection接口

FeatureLayer實現繼承了IFeatureSelection接口,該接口定義的內容可以Selection選項卡里找到。

image11.png

IFeatureSelection的SelectFeatures可以通過設置查詢條件來選擇或者反選要素。查詢條件既可以設置爲屬性查詢條件,也可以設置爲空間查詢條件。

image12.png

IQueryFilter接口定義入下。

image13.png

esriSelectionResultEnum枚舉的定義如下。

image14.png

6、Search函數

IFeatureLayer和IFeatureClass都有Search函數,兩者有什麼區別呢?我們先看IFeatureLayer的Search函數。我們打開IFeatureLayer. Search函數的幫助,如下圖所示。

image15.png

下面備註中的文字大概是以下意思。

如果該圖層定義了查詢集,有就是說在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函數類似,只是其在原始數據的基礎上查詢,和圖層的設置沒有關係。

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條提交依次,這個數可以根據實際情況修改。

8、IFeatureClass.Update函數

該函數用看來批量更新要素。和Insert函數類似,如果我們只是操作一個要素,可以在獲取IFeature之後,修改其屬性值,調用最後調用IFeature的Store函數即可。但如果要批量更新,則建議採用IFeatureClass.Update函數,效率會比較高。調用Update函數的代碼基本上都是一樣的,使用的時候參考下面的模板即可。

int myLevelFieldIndex = myFeatureClass.FindField("Level");
int myValueFieldIndex = myFeatureClass.FindField("Value");
IFeatureCursor myFeatureCursor = myFeatureClass.Update(null, true);
IFeature myFeature = myFeatureCursor.NextFeature();
while (myFeature != null)
{
    double myValue = Convert.ToDouble(myFeature.Value[myValueFieldIndex]);
    myFeature.Value[myLevelFieldIndex] = this.GetLevel(myValue);
    myFeatureCursor.UpdateFeature(myFeature);
    myFeature = myFeatureCursor.NextFeature();
}
ComReleaser.ReleaseCOMObject(myFeatureCursor);

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,可以去驗證下,但我感覺應該是支持的。

10、IField接口和IFieldInfo接口

獲取字段有兩種方式,一是通過FeatureLayer繼承實現了ILayerFields接口獲取字段,另外一種是通過矢量數據源IFeatureClass的Fields屬性獲取IFields接口獲取字段信息。

我們先看下IFields接口,如下圖所示。

image16.png

可以通過索引或字段名稱獲取具體的字段信息,返回是IField接口。我們再看下ILayerFields接口的定義。

image17.png

我們看到除了返回IField外,還可以返回IFieldInfo,這兩個有什麼區別呢?IField是數據源中字段的定義,IFieldInfo是圖層對字段定義的擴展。我們看下IFieldInfo的定義,如下圖所示。

image18.png

在IField的基礎上擴展了字段別名,按照字符串返回某個要素該字段的值,屬性表顯示的時候該字段是否高亮顯示,數字顯示格式、是否只讀、字段是否按照比率顯示,是否可見。我們常用的主要有別名、是否可見等屬性。這些信息對應了ArcMap矢量屬性對話框中的Fields選顯卡,如下圖所示。

image19.png

右側字段詳細信息分爲兩組,上面可設置的部分爲IFieldInfo的信息,下面只讀的信息爲IField信息。

11、字段操作

對字段相關的操作主要是添加和刪除。在Arcobjects中很少更新字段,基本上都是添加新字段,把舊字段的值設置到新字段中,刪除舊字段。

添加字段可以實例化一個IField對象,然後通過IFeatureClass的AddField函數添加字段。添加的時候需要注意,數據源不要被其他應用佔用,否則會發生鎖定錯誤。

var myFeatureClass = ShapeFileHelper.OpenShapeFile(pContourPolygonFile);
IField myField = new Field();
IFieldEdit myFieldEdit = myField as IFieldEdit;
myFieldEdit.Name_2 = "Level";
myFieldEdit.Type_2 = esriFieldType.esriFieldTypeInteger;
myFeatureClass.AddField(myField);

刪除字段可調用IFeatureClass的DeleteField函數,調用的時候,還是需要注意,數據源不要被其他應用佔用,否則會發生鎖定錯誤。

12、打開Shape文件

打開Shape文件後,我們就可以獲得IFeatureClas。打開Shape文件的代碼比較固定,使用下面的代碼打開即可。

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));

13、創建Shape文件

創建Shape文件代碼模式也很固定,即使是往gbd或者企業sde數據庫中創建矢量數據的時候,我一般也喜歡先在一個臨時目錄下創建shape文件,然後調用Arctoolbox裏面的工具,把這個數據拷貝到目標工作空間中。主要還是因爲直接創建shape文件更簡單,更穩定,而且不用考慮針對那麼多數據源再分別寫代碼。

創建一個Shape文件的代碼如下。

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);
}
//創建shapefile 
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對象,然後設置渲染樣式,即可添加到地圖上展示。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章