ArcGIS Engine環境下創建自定義的ArcToolbox Geoprocessing工具


      在上一篇日志中介紹了自己通過幾何的方法合並斷開的線要素的ArcGIS插件式的應用程序。但是后來考慮到插件式的程序的配置和使用比較繁瑣,也沒有比較好的錯誤處理機制,於是我就把之前的程序封裝成一個類似於ArcGIS中ArcToolBox中的批處理工具一樣的程序,有輸入文件的選擇和輸出文件的選擇,類似於下面這個工具界面:

 

     首先是查了一下ESRI的幫助文檔ArcObjects Help for .NET,上面有關於如何創建自定義的Geoprocessing工具的幾篇文章,介紹的不是很全面,但是可以知道創建工具的基本步驟和流程以及如何調試。下面創建自定義工具的基本步驟:

Creating a custom geoprocessing function tool 

There are, of course, scenarios for which you must create your own tool using ArcObjects. This requires implementing the IGPFunction2 and IGPFunctionFactory interfaces. The  Building a custom geoprocessing function tool topic provides detailed information and examples. The following steps summarize the process:
  1. In a .NET application, create two classes (one will implement IGPFunction2 and the other will implement IGPFunctionFactory). You can create two separate files or keep them in a single file.
  2. Set the parameter properties (parameter type, direction, and acceptable values) in the ParameterInfo property of IGPFunction2.
  3. Leave the Validation method of IGPFunction2 as an empty stub—the geoprocessing framework automatically performs the validation.
  4. The Execute method of IGPFunction is the most important part of your tool. This is where you write your code to implement the specific algorithm.
  5. Implement IGPFunctionFactory. This makes your custom function tool accessible to users.

     第一步需要創建兩個類,分別實現IGPFunction2IGPFunctionFactory接口。在實現IGPFunction2 接口的類中定義Tool的目錄、名稱、輸入參數、輸出參數、進度條和Execute函數等參數。需要注意的是如果您的工具是有Output FeatureClass,那就需要在除了IArray ParameterInfo中設置輸入、輸出參數以外,還需在Execute函數中創建新的要素類作為輸出要素類,並為其配置幾何信息和字段信息:

 1 parameter = (IGPParameter)paramvalues.get_Element(1);
 2             parameterValue = m_GPUtilities.UnpackGPValue(parameter);
 3             Geoprocessor gp = new Geoprocessor();
 4             // Create the new Output Polyline Feature Class
 5             CreateFeatureclass cfc = new CreateFeatureclass();       
 6     
 7             //根據用戶在output featureclass中命名的name創建新的輸出要素
 8             IName name = m_GPUtilities.CreateFeatureClassName(parameterValue.GetAsText());
 9             IDatasetName dsName = name as IDatasetName;
10             IFeatureClassName fcName = dsName as IFeatureClassName;
11             IFeatureDatasetName fdsName = fcName.FeatureDatasetName as IFeatureDatasetName;
12 
13             // Check if output is in a FeatureDataset or not. Set the output path parameter for CreateFeatureClass tool.
14             if (fdsName != null)
15             {
16                 cfc.out_path = fdsName;
17             }
18             else
19             {
20                 cfc.out_path = dsName.WorkspaceName.PathName;
21             }
22             // Set the output Coordinate System for CreateFeatureClass tool.
23             IGPEnvironment env = envMgr.FindEnvironment("outputCoordinateSystem");
24             // Same as Input
25             if (env.Value.IsEmpty())
26             {
27                 //IGeoDataset ds = inputFeatureClass as IGeoDataset;
28                 //cfc.spatial_reference = ds.SpatialReference as ISpatialReference3;
29             }
30             // Use the evnviroment setting
31             else
32             {
33                 IGPCoordinateSystem cs = env.Value as IGPCoordinateSystem;
34                 cfc.spatial_reference = cs.SpatialReference as ISpatialReference3;
35             }
36             // Remaing properties for Create Feature Class Tool
37             cfc.out_name = dsName.Name;
38             cfc.geometry_type = "POLYLINE";
39             gp.Execute(cfc, null);

      在配置好輸入輸出參數信息后,就可在Execute()函數中加上您自己的ArcEngine程序了,如果是WinForm或者ArcGIS插件式的AE程序,只需要修改參數,使程序的輸入參數讀取到的是在Tool中輸入的input featureclass,輸出參數是Tool中的output featureclass就可以了,最后程序的處理結果過自動加載到Arcmap地圖窗口中進行顯示。

      在程序寫完后,需要進行調試。這里進行調試和一般的ArcEngine程序不同,程序運行時不會直接命中VS中設置的斷點,需要進行相關的配置才能進行正常的調試。在ESRI的幫助文檔中有詳細的介紹:

How to debug a function tool running in the background

Summary
This topic shows how to debug a function tool in Visual Studio in a background geoprocessing environment.

Debugging a function tool in background processing

When geoprocessing tools are run in ArcGIS applications, the user has the option to run them in a background process (personal server) instead of running them in ArcMap. For more information, see  Foreground and background processing in the ArcGIS Desktop Help system.
When debugging a tool, attaching Visual Studio to the application (for example, ArcMap) will not trigger execution and validation break points since the code is not running in the application.
To debug tools, you can disable background processing or attach your debugger to the background process. To disable background processing, select Options under the application's Geoprocessing menu and clear the Run in background check box.

Debugging in the background

Complete the following steps to debug the tool in a background process:
  1. Start Visual Studio and open your tool project.
  2. When you run or build the .NET solution, it creates a dynamic-link library (DLL) that must be registered by the ESRIRegAsm utility. You can add a post-build command to automate the registration process. In the Project properties Build Events section, type the command on the post-build event command line. The following is one example of a post-build command to register with ArcGIS Engine:
"$(ProgramFiles)\Common Files\ArcGIS\bin\esriRegAsm.exe" $(TargetPath) /p:Engine /v:10.1 /s 
If debugging on ArcGIS for Desktop then set product to Desktop as /p:Desktop instead of /p:Engine
  1. Also, in the Project properties Debug section, set ArcMap in the Start external program option by browsing to the ArcMap.exe location. The default location is <Install Directory>\bin\ArcMap.exe.
  2. Set a breakpoint as necessary for debugging.
  3. Click Debug and click Start Debugging (or press F5) to run the project. The ArcMap application starts.
  4. From the ArcMap menu, click Geoprocessing > Geoprocessing Options. Confirm that Enabled is checked to ensure that background processing is enabled if you want to debug in the background, then click OK on the dialog box. This starts the background processes (if not already started).
  5. In Visual Studio, click Tools, then click Attach to Process. See the following screen shot:
  6. When you click Attach to Process, the Attach to Process dialog box appears. Under the Available Processes section, there will be two of the background processes (RuntimeLocalServer.exe). Select both of the processes and click Attach. See the following screen shot:



  7. In ArcMap, right-click the toolbox and select Add, then click Tool to add the function tool to a custom toolbox. The Add Tool dialog box appears. On the Add Tool dialog box, you can see your function tool under a toolbox named after the category you set in your code. See the following screen shot:
  8. Add the data to ArcMap (if not already added), and run your tool after populating the parameters.
  9. At the appropriate execution point, the debugging stops at the breakpoint.

      按照以上步驟,附加上進程並注冊了工具后,就可以設置斷點進行調試了。不過需要注意的是這種進行調試的方法不太穩定——在完成一次調試后,重新打開VS運行程序后,在附加進程的對話框中再也找不到RuntimelocalServer.exe的兩個進程,沒有辦法繼續進行調試。剛開始自己也沒有很好的解決辦法,后來打電話到ESRI技術支持中心,他們那邊的工程師在自己的電腦上調試這類程序也是這種情況說是一個bug,也沒有很好的解決辦法,只是說讓我在winForm中把程序調通再改。后來就是嘗試各種方法,一次在程序運行中嘗試附加進程,結果終於看到了RuntimelocalServer.exe!!!

      也就是說,遇到程序剛開始不能附加指定進程的情況,可以在已經在程序執行對話框這中配置好輸入、輸出參數后(程序已經在執行Execute()函數),再次在VS工具選項中嘗試附加該進程,這時候就會正常命中斷點!

      最后程序調通后,就是Tool的注冊和調用了。Tool的注冊有兩種方法,第一種是在VS中執行程序,Tool會自動注冊到ArcGIS Desktop中;另一種方式是在程序編譯后在程序目錄的Debug文件夾找到.dll動態鏈接庫文件,然后雙擊該文件,選擇Desktop完成Tool在ArcGIS上的注冊。

    然后在ArcCatalog的自定義工具箱中找到Tool目錄和對應工具,雙擊工具即可運行程序。我的Merge Disconnect Line工具運行后的參數配置界面如下:

 在配置好輸入參數和輸出參數后,點擊OK執行程序。

------------------------------------------------------------------------------------------------------------------------

本程序的主要代碼:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Text;
  4 using System.Runtime.InteropServices;
  5 using System.Windows.Forms;
  6 using ESRI.ArcGIS.ADF.CATIDs;
  7 using ESRI.ArcGIS.DataManagementTools;
  8 using ESRI.ArcGIS.esriSystem;
  9 using ESRI.ArcGIS.Geodatabase;
 10 using ESRI.ArcGIS.Geometry;
 11 using ESRI.ArcGIS.Geoprocessing;
 12 using ESRI.ArcGIS.Geoprocessor;
 13 
 14 namespace GPMergeDisconnectLine
 15 {     
 16     /************************************************************************************************/
 17     //定義GP工具
 22     public class MergeDisjunctLineFunction : IGPFunction
 23     {
 24         //工具的名稱
 25         private string m_ToolName = "MergeDisconnectLine";
 26         private string m_metadatafile = "MergeDisconnectLine.xml";
 27         private IArray m_Parameters;             // Array of Parameters
 28         private IGPUtilities m_GPUtilities;      
 29 
 30         public MergeDisjunctLineFunction()
 31         {
 32             m_GPUtilities = new GPUtilitiesClass();
 33         }
 34 
 35         public string Name
 36         {
 37             get { return m_ToolName; }
 38         }
 39 
 40         // Set the function tool Display Name as seen in ArcToolbox.
 41         public string DisplayName
 42         {
 43             get { return "Merge Disconnect Line"; }
 44         }
 45 
 47         public void Execute(IArray paramvalues, ITrackCancel trackcancel, IGPEnvironmentManager envMgr, IGPMessages message)
 48         {
 49             //輸入參數
 50             IGPParameter parameter = (IGPParameter)paramvalues.get_Element(0);
 51             IGPValue parameterValue = m_GPUtilities.UnpackGPValue(parameter);
 52             // Open Input Dataset
 53             IFeatureClass inputFeatureClass = null;
 54             IQueryFilter qf = null;
 55             IGPRecordSet gprs = null;
 56             IRecordSet2 rs2 = null;
 57 
 58             IFeatureClass outputFeatureClass = null;
 60             int sumCount = 0;
 61             if (parameterValue.DataType is IDEGeoDatasetType)
 62             {
 63                 m_GPUtilities.DecodeFeatureLayer(parameterValue, out inputFeatureClass, out qf);
 64                 if (inputFeatureClass == null)
 65                 {
 66                     message.AddError(2, "Could not open input dataset.");
 67                     return;
 68                 }
 70                 sumCount = inputFeatureClass.FeatureCount(null);
 71             }
 72             else if (parameterValue.DataType is IGPFeatureRecordSetLayerType)
 73             {
 74                 gprs = parameterValue as IGPRecordSet;
 75                 rs2 = gprs.RecordSet as IRecordSet2;
 76                 //SearchCursor = rs2.get_Cursor(false);
 77                 sumCount = rs2.Table.RowCount(null);
 78             }
 79 
 80             /*Create FeatureClass  輸出參數!!!*/
 84             parameter = (IGPParameter)paramvalues.get_Element(1);
 85             parameterValue = m_GPUtilities.UnpackGPValue(parameter);
 86             Geoprocessor gp = new Geoprocessor();
 87             // Create the new Output Polyline Feature Class
 88             CreateFeatureclass cfc = new CreateFeatureclass();       
 89     
 90             //根據用戶在output featureclass中命名的name創建新的輸出要素
 91             IName name = m_GPUtilities.CreateFeatureClassName(parameterValue.GetAsText());
 92             IDatasetName dsName = name as IDatasetName;
 93             IFeatureClassName fcName = dsName as IFeatureClassName;
 94             IFeatureDatasetName fdsName = fcName.FeatureDatasetName as IFeatureDatasetName;
 95 
 96             // Check if output is in a FeatureDataset or not. Set the output path parameter for CreateFeatureClass tool.
 97             if (fdsName != null)
 98             {
 99                 cfc.out_path = fdsName;
100             }
101             else
102             {
103                 cfc.out_path = dsName.WorkspaceName.PathName;
104             }
105             // Set the output Coordinate System for CreateFeatureClass tool.
106             IGPEnvironment env = envMgr.FindEnvironment("outputCoordinateSystem");
107             // Same as Input
108             if (env.Value.IsEmpty())
109             {
110                 //IGeoDataset ds = inputFeatureClass as IGeoDataset;
111                 //cfc.spatial_reference = ds.SpatialReference as ISpatialReference3;
112             }
113             // Use the evnviroment setting
114             else
115             {
116                 IGPCoordinateSystem cs = env.Value as IGPCoordinateSystem;
117                 cfc.spatial_reference = cs.SpatialReference as ISpatialReference3;
118             }
119             // Remaing properties for Create Feature Class Tool
120             cfc.out_name = dsName.Name;
121             cfc.geometry_type = "POLYLINE";
122             gp.Execute(cfc, null);
124 
125             outputFeatureClass = m_GPUtilities.OpenFeatureClassFromString(parameterValue.GetAsText());
129             //Set the properties of the Step Progressor
130             IStepProgressor pStepPro = (IStepProgressor)trackcancel;
131             pStepPro.MinRange = 0;
132             pStepPro.MaxRange = 6;
133             pStepPro.StepValue = (1);
134             pStepPro.Message = "Merge disjunct polyline is in processing";
135             pStepPro.Position = 0;
136             pStepPro.Show();
137 
138             //合並操作開始
139             MergeOperation mOpetation = new MergeOperation();
140             pStepPro.Step();
141             List<IFeature> allPolylineList = mOpetation.getAllPolyline(inputFeatureClass);
142             pStepPro.Step();
143             List<IPoint> allNodePointList = mOpetation.GetNodePtsListByLine(allPolylineList);
144             pStepPro.Step();
145             List<IPoint> distinctNodePointList = mOpetation.GetDistinctNodePtsList(allNodePointList);
146             pStepPro.Step();
147             List<IFeature> unionLineList = mOpetation.MergeLineListOperate(allPolylineList, distinctNodePointList, inputFeatureClass);
148             pStepPro.Step();
149             mOpetation.AddField(inputFeatureClass, outputFeatureClass);
150             pStepPro.Step();
151             mOpetation.WriteUnionLineToFile(unionLineList, outputFeatureClass);
152             pStepPro.Step();
154             System.Runtime.InteropServices.Marshal.ReleaseComObject(outputFeatureClass);
156             //合並操作結束
157             pStepPro.Hide();
158         }
159 
160         // This is the location where the parameters to the Function Tool are defined. 
161         // This property returns an IArray of parameter objects (IGPParameter). 
162         // These objects define the characteristics of the input and output parameters. 
163         public IArray ParameterInfo
164         {                 
165             get 
166             {
167                 //Array to the hold the parameters    
168                 IArray parameters = new ArrayClass();
169 
170                 IGPParameterEdit3 inputParameter = new GPParameterClass();
171                 inputParameter.DataType = new GPFeatureLayerTypeClass();
172                 inputParameter.Value = new GPFeatureLayerClass();
173 
174                 // Set Input Parameter properties
175                 inputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionInput;
176                 inputParameter.DisplayName = "Input Features";
177                 inputParameter.Name = "input_features";
178                 inputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;
179                 parameters.Add(inputParameter);
180 
198                 // Output parameter (Derived) and data type is DEFeatureClass
199                 IGPParameterEdit3 outputParameter = new GPParameterClass();
200                 outputParameter.DataType = new DEFeatureClassTypeClass();
201 
202                 // Value object is DEFeatureClass
203                 outputParameter.Value = new DEFeatureClassClass();
204 
205                 // Set output parameter properties
206                 outputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionOutput;
207                 outputParameter.DisplayName = "Output FeatureClass";
208                 outputParameter.Name = "out_featureclass";
209                 outputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;
210 
211                 // Create a new schema object - schema means the structure or design of the feature class (field information, geometry information, extent)
212                 IGPFeatureSchema outputSchema = new GPFeatureSchemaClass();
213                 IGPSchema schema = (IGPSchema)outputSchema;
214 
215                 // Clone the schema from the dependency. 
216                 //This means update the output with the same schema as the input feature class (the dependency).                                
217                 schema.CloneDependency = true;
218 
219                 // Set the schema on the output because this tool will add an additional field.
220                 outputParameter.Schema = outputSchema as IGPSchema;
221                 outputParameter.AddDependency("input_features");
222                 parameters.Add(outputParameter);
223 
224                 return parameters;
225             }
226         }
227 
228         //驗證合法性
229         public IGPMessages Validate(IArray paramvalues, bool updateValues, IGPEnvironmentManager envMgr)
230         {
231             if (m_Parameters == null)
232                 m_Parameters = ParameterInfo;
233             if (updateValues)
234             {
235                 //UpdateParameters(paramvalues, envMgr);
236             }
237             //// Call InternalValidate (Basic Validation). Are all the required parameters supplied?
238             //// Are the Values to the parameters the correct data type?
239             IGPMessages validateMsgs = m_GPUtilities.InternalValidate(m_Parameters, paramvalues, updateValues, true, envMgr);
240             
241             //UpdateMessages(paramvalues, envMgr, validateMsgs);
242             return validateMsgs;
243         }
244 
245         //更新參數,有些工具需要設置好一個參數后,才能設置下一個參數,例如需要選擇一個矢量數據之后,
246         //才能選擇數據中的字段,這樣的工作可在該代碼中定義,如何定義還需要查看幫助和示例  
247         public void UpdateParameters(IArray paramvalues, IGPEnvironmentManager pEnvMgr)
248         {
249             //m_Parameters = paramvalues;
250             // Retrieve the input parameter value
251             //IGPValue parameterValue = m_GPUtilities.UnpackGPValue(m_Parameters.get_Element(0));
252         }
253 
254         
280         // This is the function name object for the Geoprocessing Function Tool. 
281         // This name object is created and returned by the Function Factory.
282         // The Function Factory must first be created before implementing this property.
283         public IName FullName
284         {
285             get
286             {     
287                 IGPFunctionFactory functionFactory = new MergeDisjunctLineFactory();
288                 return (IName)functionFactory.GetFunctionName(m_ToolName);
289             }
290         }
291 
292         // This is used to set a custom renderer for the output of the Function Tool.
293         public object GetRenderer(IGPParameter pParam)
294         {
295             return null;
296         }
297 
298         //幫助的上下文標識 返回0即可 
299         public int HelpContext
300         {
301             get { return 0; }
302         }
303 
304         // This is the path to a .chm file which is used to describe and explain the function and its operation. 
305         public string HelpFile
306         {
307             get { return ""; }
308         }
309 
310         // This is used to return whether the function tool is licensed to execute.
312         public bool IsLicensed()
313         {
314             IAoInitialize aoi = new AoInitializeClass();
315             ILicenseInformation licInfo = (ILicenseInformation)aoi;
316             string licName = licInfo.GetLicenseProductName(aoi.InitializedProduct());
317             if (licName == "Advanced")
318             {
319                 return true;
320             }
321             else
322             {
323                 return false;
324             }           
325         }
326 
327         //元數據文件 這個返回空字符串也可以 
328         public string MetadataFile
329         {
330             get
331             {
332                 string filePath;
333                 filePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
334                 filePath = System.IO.Path.Combine(filePath, m_metadatafile);
335                 return filePath;
336             }
337         }
338 
339         public UID DialogCLSID
340         {
341             // DO NOT USE. INTERNAL USE ONLY.
342             get { return null; }
343         }
344     }
345 
346 
347     /*IGPFunctionFactory*************************************************************************************************/
348 
349     [Guid("526de91e-3fe5-4a46-a7e2-4d1dc3cdb5db"), ComVisible(true)]
350 
351     public class MergeDisjunctLineFactory : IGPFunctionFactory
352     {
353         // Register the Function Factory with the ESRI Geoprocessor Function Factory Component Category.
354 
355         #region "Component Category Registration"
356 
357         [ComRegisterFunction()]
358         private static void Reg(string regKey)
359         {
360             GPFunctionFactories.Register(regKey);
361         }
362 
363         [ComUnregisterFunction()]
364         private static void Unreg(string regKey)
365         {
366             GPFunctionFactories.Unregister(regKey);
367         }
368 
369         #endregion
370 
371         // Utility Function added to create the function names.
372         private IGPFunctionName CreateGPFunctionNames(long index)
373         {
374             IGPFunctionName functionName = new GPFunctionNameClass();
375             functionName.MinimumProduct = esriProductCode.esriProductCodeAdvanced;
376             IGPName name;
377 
378             switch (index)
379             {
380                     //工具箱中只有一個工具
381                 case (0):
382                     name = (IGPName) functionName;
383                     name.Category = "DisconnectlineMerge";
384                     name.Description = "Merge a disconnect line list to a continuous line";
385                     name.DisplayName = "Merge Disconnectline";
386                     name.Name = "MergeDisconnectLine";
387                     name.Factory = (IGPFunctionFactory) this;
388                     break;
389             }
390 
391             return functionName;
392         }
393 
394         // Implementation of the Function Factory
395         // This is the name of the function factory. 
396         // This is used when generating the Toolbox containing the function tools of the factory.
397         public string Name
398         {
399             get { return "DisconnectlineMerge"; }
400         }
401 
402         // This is the alias name of the factory.
403         public string Alias
404         {
405             get { return "lineMerge"; }
406         }
407 
408         // This is the class id of the factory. 
409         public UID CLSID
410         {
411             get
412             {
413                 UID id = new UIDClass();
414                 id.Value = this.GetType().GUID.ToString("B");
415                 return id;
416             }
417         }
418 
419         // This method will create and return a function object based upon the input name.
420         public IGPFunction GetFunction(string Name)
421         {
422             switch (Name)
423             {
424                 case ("MergeDisconnectLine"):
425                     IGPFunction gpFunction = new MergeDisjunctLineFunction();
426                     return gpFunction;
427             }
428 
429             return null;
430         }
431 
432         // This method will create and return a function name object based upon the input name.
433         public IGPName GetFunctionName(string Name)
434         {
435             IGPName gpName = new GPFunctionNameClass();
436 
437             switch (Name)
438             {
439                 case ("MergeDisconnectLine"):
440                     return (IGPName) CreateGPFunctionNames(0);
441 
442             }
443             return null;
444         }
445 
446         // This method will create and return an enumeration of function names that the factory supports.
447         public IEnumGPName GetFunctionNames()
448         {
449             IArray nameArray = new EnumGPNameClass();
450             nameArray.Add(CreateGPFunctionNames(0));
451             return (IEnumGPName) nameArray;
452         }
453 
454         public IEnumGPEnvironment GetFunctionEnvironments()
455         {
456             return null;
457         }
458     }
459 }

 本文是自己這幾天做ArcGIS GP工具的一些心得體會,部分參考ESRI開發者中心文檔。文章和代碼如有不足之處,請和我聯系,感謝!(郵箱:1312210561@qq.com)

-----------------------------------------------------------------------------------------------------

 本文系作者GISQZC原創文章,歡迎轉載,但必須注明出處,否則將追究相關法律責任!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM