1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Runtime.InteropServices;
5 using ESRI.ArcGIS.Geodatabase;
6 using ESRI.ArcGIS.Geometry;
7 using ESRI.ArcGIS.Geoprocessing;
8 using ESRI.ArcGIS.esriSystem;
9 using ESRI.ArcGIS.DataSourcesFile;
10 using ESRI.ArcGIS.DataSourcesGDB;
11 using ESRI.ArcGIS.ADF.CATIDs;
12
13 namespace GPCalculateArea
14 {
15
16 public class CalculateAreaFunction : IGPFunction2
17 {
18 private string m_ToolName = "CalculateArea"; //Function Name
19 //Local members
20
21 private string m_metadatafile = "CalculateArea_area.xml";
22 private IArray m_Parameters; // Array of Parameters
23 private IGPUtilities m_GPUtilities; // GPUtilities object
24
25 public CalculateAreaFunction()
26 {
27 m_GPUtilities = new GPUtilitiesClass();
28 }
29
30 #region IGPFunction Members
31
32 // Set the name of the function tool.
33 // This name appears when executing the tool at the command line or in scripting.
34 // This name should be unique to each toolbox and must not contain spaces.
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 "Calculate Area"; }
44 }
45
46 // This is the location where the parameters to the Function Tool are defined.
47 // This property returns an IArray of parameter objects (IGPParameter).
48 // These objects define the characteristics of the input and output parameters.
49 public IArray ParameterInfo
50 {
51 get
52 {
53 //Array to the hold the parameters
54 IArray parameters = new ArrayClass();
55
56 //Input DataType is GPFeatureLayerType
57 IGPParameterEdit3 inputParameter = new GPParameterClass();
58 inputParameter.DataType = new GPFeatureLayerTypeClass();
59
60 // Default Value object is GPFeatureLayer
61 inputParameter.Value = new GPFeatureLayerClass();
62
63 // Set Input Parameter properties
64 inputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionInput;
65 inputParameter.DisplayName = "Input Features";
66 inputParameter.Name = "input_features";
67 inputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;
68 parameters.Add(inputParameter);
69
70 // Area field parameter
71 inputParameter = new GPParameterClass();
72 inputParameter.DataType = new GPStringTypeClass();
73
74 // Value object is GPString
75 IGPString gpStringValue = new GPStringClass();
76 gpStringValue.Value = "Area";
77 inputParameter.Value = (IGPValue)gpStringValue;
78
79 // Set field name parameter properties
80 inputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionInput;
81 inputParameter.DisplayName = "Area Field Name";
82 inputParameter.Name = "field_name";
83 inputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;
84 parameters.Add(inputParameter);
85
86 // Output parameter (Derived) and data type is DEFeatureClass
87 IGPParameterEdit3 outputParameter = new GPParameterClass();
88 outputParameter.DataType = new GPFeatureLayerTypeClass();
89
90 // Value object is DEFeatureClass
91 outputParameter.Value = new DEFeatureClassClass();
92
93 // Set output parameter properties
94 outputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionOutput;
95 outputParameter.DisplayName = "Output FeatureClass";
96 outputParameter.Name = "out_featureclass";
97 outputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeDerived;
98
99 // Create a new schema object - schema means the structure or design of the feature class (field information, geometry information, extent)
100 IGPFeatureSchema outputSchema = new GPFeatureSchemaClass();
101 IGPSchema schema = (IGPSchema)outputSchema;
102
103 // Clone the schema from the dependency.
104 //This means update the output with the same schema as the input feature class (the dependency).
105 schema.CloneDependency = true;
106
107 // Set the schema on the output because this tool will add an additional field.
108 outputParameter.Schema = outputSchema as IGPSchema;
109 outputParameter.AddDependency("input_features");
110 parameters.Add(outputParameter);
111
112 return parameters;
113 }
114 }
115
116 // Validate:
117 // - Validate is an IGPFunction method, and we need to implement it in case there
118 // is legacy code that queries for the IGPFunction interface instead of the IGPFunction2
119 // interface.
120 // - This Validate code is boilerplate - copy and insert into any IGPFunction2 code..
121 // - This is the calling sequence that the gp framework now uses when it QI's for IGPFunction2..
122 public IGPMessages Validate(IArray paramvalues, bool updateValues, IGPEnvironmentManager envMgr)
123 {
124 if (m_Parameters == null)
125 m_Parameters = ParameterInfo;
126
127 // Call UpdateParameters().
128 // Only Call if updatevalues is true.
129 if (updateValues == true)
130 {
131 UpdateParameters(paramvalues, envMgr);
132 }
133
134 // Call InternalValidate (Basic Validation). Are all the required parameters supplied?
135 // Are the Values to the parameters the correct data type?
136 IGPMessages validateMsgs = m_GPUtilities.InternalValidate(m_Parameters, paramvalues, updateValues, true, envMgr);
137
138 // Call UpdateMessages();
139 UpdateMessages(paramvalues, envMgr, validateMsgs);
140
141 // Return the messages
142 return validateMsgs;
143 }
144
145 // This method will update the output parameter value with the additional area field.
146 public void UpdateParameters(IArray paramvalues, IGPEnvironmentManager pEnvMgr)
147 {
148 m_Parameters = paramvalues;
149
150 // Retrieve the input parameter value
151 IGPValue parameterValue = m_GPUtilities.UnpackGPValue(m_Parameters.get_Element(0));
152
153 // Get the derived output feature class schema and empty the additional fields. This will ensure you don't get duplicate entries.
154 IGPParameter3 derivedFeatures = (IGPParameter3)paramvalues.get_Element(2);
155 IGPFeatureSchema schema = (IGPFeatureSchema)derivedFeatures.Schema;
156 schema.AdditionalFields = null;
157
158 // If we have an input value, create a new field based on the field name the user entered.
159 if (parameterValue.IsEmpty() == false)
160 {
161 IGPParameter3 fieldNameParameter = (IGPParameter3)paramvalues.get_Element(1);
162 string fieldName = fieldNameParameter.Value.GetAsText();
163
164 // Check if the user's input field already exists
165 IField areaField = m_GPUtilities.FindField(parameterValue, fieldName);
166 if (areaField == null)
167 {
168 IFieldsEdit fieldsEdit = new FieldsClass();
169 IFieldEdit fieldEdit = new FieldClass();
170 fieldEdit.Name_2 = fieldName;
171 fieldEdit.Type_2 = esriFieldType.esriFieldTypeDouble;
172 fieldsEdit.AddField(fieldEdit);
173
174 // Add an additional field for the area values to the derived output.
175 IFields fields = fieldsEdit as IFields;
176 schema.AdditionalFields = fields;
177 }
178
179 }
180 }
181
182 // Called after returning from the update parameters routine.
183 // You can examine the messages created from internal validation and change them if desired.
184 public void UpdateMessages(IArray paramvalues, IGPEnvironmentManager pEnvMgr, IGPMessages Messages)
185 {
186 // Check for error messages
187 IGPMessage msg = (IGPMessage)Messages;
188 if (msg.IsError())
189 return;
190
191 // Get the first Input Parameter
192 IGPParameter parameter = (IGPParameter)paramvalues.get_Element(0);
193
194 // UnPackGPValue. This ensures you get the value either form the dataelement or GpVariable (ModelBuilder)
195 IGPValue parameterValue = m_GPUtilities.UnpackGPValue(parameter);
196
197 // Open the Input Dataset - Use DecodeFeatureLayer as the input might be a layer file or a feature layer from ArcMap.
198 IFeatureClass inputFeatureClass;
199 IQueryFilter qf;
200 m_GPUtilities.DecodeFeatureLayer(parameterValue, out inputFeatureClass, out qf);
201
202 IGPParameter3 fieldParameter = (IGPParameter3)paramvalues.get_Element(1);
203 string fieldName = fieldParameter.Value.GetAsText();
204
205 // Check if the field already exists and provide a warning.
206 int indexA = inputFeatureClass.FindField(fieldName);
207 if (indexA > 0)
208 {
209 Messages.ReplaceWarning(1, "Field already exists. It will be overwritten.");
210 }
211
212 return;
213 }
214
215 // Execute: Execute the function given the array of the parameters
216 public void Execute(IArray paramvalues, ITrackCancel trackcancel, IGPEnvironmentManager envMgr, IGPMessages message)
217 {
218
219 // Get the first Input Parameter
220 IGPParameter parameter = (IGPParameter)paramvalues.get_Element(0);
221
222 // UnPackGPValue. This ensures you get the value either form the dataelement or GpVariable (ModelBuilder)
223 IGPValue parameterValue = m_GPUtilities.UnpackGPValue(parameter);
224
225 // Open Input Dataset
226 IFeatureClass inputFeatureClass;
227 IQueryFilter qf;
228 m_GPUtilities.DecodeFeatureLayer(parameterValue, out inputFeatureClass, out qf);
229
230 if (inputFeatureClass == null)
231 {
232 message.AddError(2, "Could not open input dataset.");
233 return;
234 }
235
236 // Add the field if it does not exist.
237 int indexA;
238
239 parameter = (IGPParameter)paramvalues.get_Element(1);
240 string field = parameter.Value.GetAsText();
241
242
243 indexA = inputFeatureClass.FindField(field);
244 if (indexA < 0)
245 {
246 IFieldEdit fieldEdit = new FieldClass();
247 fieldEdit.Type_2 = esriFieldType.esriFieldTypeDouble;
248 fieldEdit.Name_2 = field;
249 inputFeatureClass.AddField(fieldEdit);
250 }
251
252 int featcount = inputFeatureClass.FeatureCount(null);
253
254 //Set the properties of the Step Progressor
255 IStepProgressor pStepPro = (IStepProgressor)trackcancel;
256 pStepPro.MinRange = 0;
257 pStepPro.MaxRange = featcount;
258 pStepPro.StepValue = (1);
259 pStepPro.Message = "Calculating Area";
260 pStepPro.Position = 0;
261 pStepPro.Show();
262
263 // Create an Update Cursor
264 indexA = inputFeatureClass.FindField(field);
265 IFeatureCursor updateCursor = inputFeatureClass.Update(qf, false);
266 IFeature updateFeature = updateCursor.NextFeature();
267 IGeometry geometry;
268 IArea area;
269 double dArea;
270
271 while (updateFeature != null)
272 {
273 geometry = updateFeature.Shape;
274 area = (IArea)geometry;
275 dArea = area.Area;
276 updateFeature.set_Value(indexA, dArea);
277 updateCursor.UpdateFeature(updateFeature);
278 updateFeature.Store();
279 updateFeature = updateCursor.NextFeature();
280 pStepPro.Step();
281 }
282
283 pStepPro.Hide();
284
285 // Release the update cursor to remove the lock on the input data.
286 System.Runtime.InteropServices.Marshal.ReleaseComObject(updateCursor);
287 }
288
289 // This is the function name object for the Geoprocessing Function Tool.
290 // This name object is created and returned by the Function Factory.
291 // The Function Factory must first be created before implementing this property.
292 public IName FullName
293 {
294 get
295 {
296 // Add CalculateArea.FullName getter implementation
297 IGPFunctionFactory functionFactory = new CalculateAreaFunctionFactory();
298 return (IName)functionFactory.GetFunctionName(m_ToolName);
299 }
300 }
301
302 // This is used to set a custom renderer for the output of the Function Tool.
303 public object GetRenderer(IGPParameter pParam)
304 {
305 return null;
306 }
307
308 // This is the unique context identifier in a [MAP] file (.h).
309 // ESRI Knowledge Base article #27680 provides more information about creating a [MAP] file.
310 public int HelpContext
311 {
312 get { return 0; }
313 }
314
315 // This is the path to a .chm file which is used to describe and explain the function and its operation.
316 public string HelpFile
317 {
318 get { return ""; }
319 }
320
321 // This is used to return whether the function tool is licensed to execute.
322 public bool IsLicensed()
323 {
324 IAoInitialize aoi = new AoInitializeClass();
325 ILicenseInformation licInfo = (ILicenseInformation)aoi;
326
327 string licName = licInfo.GetLicenseProductName(aoi.InitializedProduct());
328
329 if (licName == "ArcInfo")
330 {
331 return true;
332 }
333 else
334 return false;
335 }
336
337 // This is the name of the (.xml) file containing the default metadata for this function tool.
338 // The metadata file is used to supply the parameter descriptions in the help panel in the dialog.
339 // If no (.chm) file is provided, the help is based on the metadata file.
340 // ESRI Knowledge Base article #27000 provides more information about creating a metadata file.
341 public string MetadataFile
342 {
343 get { return m_metadatafile; }
344 }
345
346 // By default, the Toolbox will create a dialog based upon the parameters returned
347 // by the ParameterInfo property.
348 public UID DialogCLSID
349 {
350 // DO NOT USE. INTERNAL USE ONLY.
351 get { return null; }
352 }
353
354 #endregion
355 }
356
357 //////////////////////////////
358 // Function Factory Class
359 ////////////////////////////
360 [
361 Guid("2554BFC7-94F9-4d28-B3FE-14D17599B35A"),
362 ComVisible(true)
363 ]
364 public class CalculateAreaFunctionFactory : IGPFunctionFactory
365 {
366 private const string m_ToolName = "ylArea"; //Function Name
367 // Register the Function Factory with the ESRI Geoprocessor Function Factory Component Category.
368 #region "Component Category Registration"
369 [ComRegisterFunction()]
370 static void Reg(string regKey)
371 {
372
373 GPFunctionFactories.Register(regKey);
374 }
375
376 [ComUnregisterFunction()]
377 static void Unreg(string regKey)
378 {
379 GPFunctionFactories.Unregister(regKey);
380 }
381 #endregion
382
383 // Utility Function added to create the function names.
384 private IGPFunctionName CreateGPFunctionNames(long index)
385 {
386 IGPFunctionName functionName = new GPFunctionNameClass();
387 functionName.MinimumProduct = esriProductCode.esriProductCodeProfessional;
388 IGPName name;
389
390 switch (index)
391 {
392 case (0):
393 name = (IGPName)functionName;
394 name.Category = "AreaCalculation";
395 name.Description = "Calculate Area for FeatureClass";
396 name.DisplayName = "Calculate Area";
397 name.Name = m_ToolName;
398 name.Factory = (IGPFunctionFactory)this;
399 break;
400 }
401
402 return functionName;
403 }
404
405 // Implementation of the Function Factory
406 #region IGPFunctionFactory Members
407
408 // This is the name of the function factory.
409 // This is used when generating the Toolbox containing the function tools of the factory.
410 public string Name
411 {
412 get { return m_ToolName; }
413 }
414
415 // This is the alias name of the factory.
416 public string Alias
417 {
418 get { return "area"; }
419 }
420
421 // This is the class id of the factory.
422 public UID CLSID
423 {
424 get
425 {
426 UID id = new UIDClass();
427 id.Value = this.GetType().GUID.ToString("B");
428 return id;
429 }
430 }
431
432 // This method will create and return a function object based upon the input name.
433 public IGPFunction GetFunction(string Name)
434 {
435 switch (Name)
436 {
437 case (m_ToolName):
438 IGPFunction gpFunction = new CalculateAreaFunction();
439 return gpFunction;
440 }
441
442 return null;
443 }
444
445 // This method will create and return a function name object based upon the input name.
446 public IGPName GetFunctionName(string Name)
447 {
448 IGPName gpName = new GPFunctionNameClass();
449
450 switch (Name)
451 {
452 case (m_ToolName):
453 return (IGPName)CreateGPFunctionNames(0);
454
455 }
456 return null;
457 }
458
459 // This method will create and return an enumeration of function names that the factory supports.
460 public IEnumGPName GetFunctionNames()
461 {
462 IArray nameArray = new EnumGPNameClass();
463 nameArray.Add(CreateGPFunctionNames(0));
464 return (IEnumGPName)nameArray;
465 }
466
467 // This method will create and return an enumeration of GPEnvironment objects.
468 // If tools published by this function factory required new environment settings,
469 //then you would define the additional environment settings here.
470 // This would be similar to how parameters are defined.
471 public IEnumGPEnvironment GetFunctionEnvironments()
472 {
473 return null;
474 }
475
476
477 #endregion
478 }
479
480 }