這個例子專業講解基於ArcEngine使用GP開發的過程及遇到的問題。更多GP使用方法:GP使用心得
功能需求:現在外業第一次數據(簡稱調繪.mdb)和第二次數據(簡稱檢查.mdb)有重復。第二次是在第一次的基礎上進行的,即如果調繪.mdb中LCA層有365個要素,檢查時發現錯誤,就刪除了11個錯誤,並新增了43個,共408個,檢查.mdb相對於調繪.mdb實際上有354個重復,現在要將重復的刪除,mdb中包括點、線、面三種類型的要素類。
軟件實現:在ArcGIS里利用工具可以實現,使用按空間位置查詢,找出重復的,然后刪除即可;由於每個mdb中的要素圖層比較多,所以可以使用批處理,批處理時填寫參數可以利用Excel快速進行。但是,mdb較多,路徑各不相同,圖層較多,操作起不比較費時。
程序實現:
1.首先確定用什么工具來實現(先在ArcGIS里做一次):利用空間位置查詢選擇重復的要素,開啟編輯器,刪除!
2.設計界面,我每寫一個功能,都要把界面整理一下,因為我不想讓它很丑就出去見人。這里使用DotNetBar,節省了相當多的時間。
3.因為是直接對數據庫進行操作,所以首先就要遍歷數據中的要素圖層,一般做法是利用GP的ListFeatureClasses方法。
GP.SetEnvironmentValue("workspace", moreDBPath.Trim()); IGpEnumList pGpEnumList = GP.ListFeatureClasses("", "", ""); string strFC = pGpEnumList.Next(); while (strFC != "") { System.Windows.Forms.Application.DoEvents(); Console.WriteLine(strFC); strFC = pGpEnumList.Next(); }選擇遍歷數據庫沒有問題!
4.利用按位置選擇
SelectLayerByLocation SLBL = new SelectLayerByLocation(); SLBL.in_layer = moreDB + "\\" + strFC; SLBL.select_features = referDB + "\\" + strFC; SLBL.overlap_type = "ARE_IDENTICAL_TO"; GPClass.Execute(SLBL); //這里使用了自定義的GPClass類,可以直接使用GPExecute報錯ERROR 000368,去官網幫助一查,竟然沒有368,這是為什么?但從相鄰錯誤信息來看,應該是哪個參數無效!
再去官網看了按空間位置查詢的幫助文檔。它說:輸入必須是要素圖層;不可以是要素類。要素圖層?要素類?以前一直把它們看成是一種意思,於是去查一下了幫助,長知識了。
結果Python的示例代碼,使用了MakeFeatureLayer來創建要素圖層。
//創建要素圖層 MakeFeatureLayer MFL = new MakeFeatureLayer(); MFL.in_features = moreDB + "\\" + strFC; MFL.out_layer = strFC + @"_Lyr"; GPClass.Execute(MFL); //按位置選擇 SelectLayerByLocation SLBL = new SelectLayerByLocation(); SLBL.in_layer = strFC + @"_Lyr"; SLBL.select_features = referDB + "\\" + strFC; SLBL.overlap_type = "ARE_IDENTICAL_TO"; GPClass.Execute(SLBL);GP執行成功,雖然還看不到效果。因為MakeFeatureLayer是臨時圖層,程序結束就沒有了,需要將它導出來:
//復制要素導出CopyFeatures CF = new CopyFeatures(); CF.in_features = strFC + @"_Lyr"; CF.out_feature_class = resultDB + "\\" + strFC; GPClass.Execute(CF);現在的問題是是選擇類型是ARE_IDENTICAL_TO,如果輸入圖層中的要素與某一選擇要素相同(就幾何而言),則會選擇這些要素。生成的mdb是重復那一部分,而需要的結果是不重復的那一部分。在ArcGIS里可以通過切換選擇來操作。於是想,先把它全部選中,然后將重復的移除。代碼:
//創建要素圖層 MakeFeatureLayer MFL = new MakeFeatureLayer(); MFL.in_features = moreDB + "\\" + strFC; MFL.out_layer = strFC + @"_Lyr"; GPClass.Execute(MFL); //選擇所有 SelectLayerByAttribute SLBA = new SelectLayerByAttribute(); SLBA.in_layer_or_view = strFC + @"_Lyr"; GPClass.Execute(SLBA); //按位置選擇(移除) SelectLayerByLocation SLBL = new SelectLayerByLocation(); SLBL.in_layer = strFC + @"_Lyr"; SLBL.select_features = referDB + "\\" + strFC; SLBL.overlap_type = "ARE_IDENTICAL_TO"; SLBL.selection_type = "REMOVE_FROM_SELECTION"; GPClass.Execute(SLBL); //復制要素 CopyFeatures CF = new CopyFeatures(); CF.in_features = strFC + @"_Lyr"; CF.out_feature_class = resultDB + "\\" + strFC; GPClass.Execute(CF);終於實現了。5.為了增加用戶的體驗,重寫在了一個類,並把新建一個線程來處理:
string referDB = ""; string moreDB = ""; string resultDB = ""; ProgressBarX progress = null; public NoRepeatClass(string _referDB, string _moreDB, string _resultDB, ProgressBarX _progress) { referDB = _referDB; moreDB = _moreDB; resultDB = _resultDB; progress = _progress; Thread MyThreadOne = new Thread(new ThreadStart(MainFun)); MyThreadOne.Name = "NoRepeat"; MyThreadOne.IsBackground = true; MyThreadOne.Start(); } private void MainFun() { //主程序代碼 }6.為了讓用戶知道處理的進度,於是添加了進度條,並更新內容為當前處理圖層的名稱。但這里,不能直接在一個線程里設置主線程的控件屬性,於是找了一個函數:
#region 設置控件參數 /// <summary> /// 設置控件參數 /// </summary> /// <param name="oControl">控件</param> /// <param name="propName">參數名稱</param> /// <param name="propValue">參數值</param> delegate void SetControlValueCallback(Control oControl, string propName, object propValue); public static void SetControlPropertyValue(Control oControl, string propName, object propValue) { if (oControl.InvokeRequired) { SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue); oControl.Invoke(d, new object[] { oControl, propName, propValue }); } else { Type t = oControl.GetType(); System.Reflection.PropertyInfo[] props = t.GetProperties(); foreach (System.Reflection.PropertyInfo p in props) { if (p.Name.ToUpper() == propName.ToUpper()) { p.SetValue(oControl, propValue, null); } } } } #endregion目前的效果如下:
7.為了提高操作效率,增加了路徑拖放功能,下面是拖放類:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.IO; namespace GPTools { class DragClass { private Control control; private string fileType = ""; public DragClass(Control _control,string _fileType) { //如果控件為空 if (_control == null) { return; } control = _control; fileType = _fileType; //設置是否可以拖放 control.AllowDrop = true; //定義拖放事件 control.DragEnter += new DragEventHandler(control_DragEnter); ontrol.DragDrop += new DragEventHandler(control_DragDrop); } private void control_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) { e.Effect = DragDropEffects.Copy; } } private void control_DragDrop(object sender, DragEventArgs e) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); foreach (string file in files) { //判斷文件類型 if (Path.GetExtension(file) == fileType) { Console.WriteLine(file); control.Text = file; } } } } }在初始化主程序后實例化拖放類,一旦拖放就會觸發DragEnter事件和DragDrop事件:
DragClass dg = new DragClass(this.txbReferDB, ".mdb"); DragClass dg2 = new DragClass(this.txbMoreDB, ".mdb");8.因為我們只遍歷了檢查.mdb中的圖層,萬一調繪.mdb中沒有對應圖層怎么,所以,我們得檢查,如果沒有則跳過。
//創建要素圖層 //…… //檢查數據 object dt = ""; if (GPClass.GP.Exists(referDB + "\\" + strFC, ref dt)) { //3.選擇所有 //4.按位置選擇(移除) } //復制要素 //……9.為了更准確的統計操作時間,添加了計時功能。效果圖如下:
10.為了讓用戶快速進行執行任務,可以在執行按鈕上添加Enter事件。
private void btnExcute_Enter(object sender, EventArgs e) { Excute(); } private void Excute() { referDBPath = txbReferDB.Text.Trim(); moreDBPath = txbMoreDB.Text.Trim(); resultDBPath = txbResultDB.Text.Trim(); if (referDBPath.Trim() != "" && moreDBPath.Trim() != "" && resultDBPath.Trim() != "") { NoRepeatClass nr = new NoRepeatClass(referDBPath, moreDBPath, resultDBPath, progressBar); } else { MessageBoxEx.EnableGlass = false; MessageBoxEx.Show("警告:數據庫路徑選擇有誤!\n 請檢查數據路徑是否正確。", "提示"); } }
參考:http://bbs.esrichina-bj.cn/esri/viewthread.php?tid=50540