Building a custom geoprocessing function tool (Calculate Area)
GPCalculateArea\CalculateAreaFunction.cs
// Copyright 2007 ESRI
//
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
//
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
//
// See the use restrictions.
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geoprocessing;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.DataSourcesFile;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.ADF.CATIDs;
namespace GPCalculateArea
{
public class CalculateAreaFunction : IGPFunction
{
// Local members
private string m_ToolName = "CalculateArea"; //Function Name
private string m_metadatafile = "CalculateArea_area.xml";
private IArray m_Parameters; // Array of Parameters
private IGPUtilities m_GPUtilities; // GPUtilities object
public CalculateAreaFunction()
{
m_GPUtilities = new GPUtilitiesClass();
}
#region IGPFunction Members
// Set the name of the function tool.
// This name appears when executing the tool at the command line or in scripting.
// This name should be unique to each toolbox and must not contain spaces.
public string Name
{
get { return m_ToolName; }
}
// Set the function tool Display Name as seen in ArcToolbox.
public string DisplayName
{
get { return "Calculate Area"; }
}
// This is the location where the parameters to the Function Tool are defined.
// This property returns an IArray of parameter objects (IGPParameter).
// These objects define the characteristics of the input and output parameters.
public IArray ParameterInfo
{
get
{
//Array to the hold the parameters
IArray parameters = new ArrayClass();
//Input DataType is GPFeatureLayerType
IGPParameterEdit inputParameter = new GPParameterClass();
inputParameter.DataType = new GPFeatureLayerTypeClass();
// Default Value object is GPFeatureLayer
inputParameter.Value = new GPFeatureLayerClass();
// Set Input Parameter properties
inputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionInput;
inputParameter.DisplayName = "Input Features";
inputParameter.Name = "input_features";
inputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;
parameters.Add(inputParameter);
// Area field parameter
inputParameter = new GPParameterClass();
inputParameter.DataType = new GPStringTypeClass();
// Value object is GPString
IGPString gpStringValue = new GPStringClass();
gpStringValue.Value = "Area";
inputParameter.Value = (IGPValue)gpStringValue;
// Set field name parameter properties
inputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionInput;
inputParameter.DisplayName = "Area Field Name";
inputParameter.Name = "field_name";
inputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeRequired; ;
parameters.Add(inputParameter);
// Output parameter (Derived) and data type is DEFeatureClass
IGPParameterEdit outputParameter = new GPParameterClass();
outputParameter.DataType = new GPFeatureLayerTypeClass();
// Value object is DEFeatureClass
outputParameter.Value = new GPFeatureLayerClass();
// Set output parameter properties
outputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionOutput;
outputParameter.DisplayName = "Output FeatureClass";
outputParameter.Name = "out_featureclass";
outputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeDerived;
parameters.Add(outputParameter);
return parameters;
}
}
// Validate: This will validate each parameter and return messages.
// This method will check that a given set of parameter values are of the
// appropriate number, DataType, and Value.
public IGPMessages Validate(IArray paramvalues, bool updateValues, IGPEnvironmentManager envMgr)
{
if (m_Parameters == null)
m_Parameters = ParameterInfo;
// Call InternalValidate (Basic Validation). Are all the required parameters supplied?
// Are the Values to the parameters the correct data type?
IGPMessages validateMsgs = m_GPUtilities.InternalValidate(m_Parameters, paramvalues, updateValues, true, envMgr);
// Check for error messages
IGPMessage msg = (IGPMessage)validateMsgs;
if (msg.IsError())
return validateMsgs;
// Clone the input parameter value
IGPParameter parameter = (IGPParameter)paramvalues.get_Element(0);
IGPValue parameterValue = m_GPUtilities.UnpackGPValue(parameter);
IClone parameterClone = (IClone)parameterValue;
IGPValue outputValue = (IGPValue)parameterClone.Clone();
IDETable inputTable = m_GPUtilities.DecodeDETable(outputValue);
// Add the Area field to the Output Value
parameter = (IGPParameter)paramvalues.get_Element(1);
string field = parameter.Value.GetAsText();
if (inputTable != null)
{
IField areaField = m_GPUtilities.FindField(outputValue, field);
if (areaField == null)
{
IFieldsEdit fieldsEdit = (IFieldsEdit)inputTable.Fields;
IFieldEdit fieldEdit = new FieldClass();
fieldEdit.Name_2 = field;
fieldEdit.Type_2 = esriFieldType.esriFieldTypeDouble;
fieldsEdit.AddField(fieldEdit);
inputTable.Fields = fieldsEdit;
}
parameter = (IGPParameter)paramvalues.get_Element(2);
m_GPUtilities.PackGPValue(outputValue, parameter);
}
return validateMsgs;
}
// Execute: Execute the function given the array of the parameters
public void Execute(IArray paramvalues, ITrackCancel trackcancel, IGPEnvironmentManager envMgr, IGPMessages message)
{
// Call InternalValidate
IGPMessages validateMessages = m_GPUtilities.InternalValidate(m_Parameters, paramvalues, false, false, envMgr);
// Check for error messages
IGPMessage gpmessage = (IGPMessage)validateMessages;
if (!gpmessage.IsError())
{
// Get the first Input Parameter
IGPParameter parameter = (IGPParameter)paramvalues.get_Element(0);
// UnPackGPValue. This ensures you get the value either form the dataelement or GpVariable (modelbuilder)
IGPValue parameterValue = m_GPUtilities.UnpackGPValue(parameter);
// Open Input Feature Class
IFeatureClass inputFeatureClass;
IQueryFilter qf;
m_GPUtilities.DecodeFeatureLayer(parameterValue, out inputFeatureClass, out qf);
if (inputFeatureClass == null)
{
message.AddError(2, "Could not open input dataset.");
return;
}
// Add the field if it does not exist.
int indexA;
parameter = (IGPParameter)paramvalues.get_Element(1);
string field = parameter.Value.GetAsText();
indexA = inputFeatureClass.FindField(field);
if (indexA < 0)
{
IFieldEdit fieldEdit = new FieldClass();
fieldEdit.Type_2 = esriFieldType.esriFieldTypeDouble;
fieldEdit.Name_2 = field;
inputFeatureClass.AddField(fieldEdit);
}
// Create an Update Cursor
indexA = inputFeatureClass.FindField(field);
IFeatureCursor updateCursor = inputFeatureClass.Update(null, false);
IFeature updateFeature = updateCursor.NextFeature();
IGeometry geometry;
IArea area;
double dArea;
while (updateFeature != null)
{
geometry = updateFeature.Shape;
area = (IArea)geometry;
dArea = area.Area;
updateFeature.set_Value(indexA, dArea);
updateCursor.UpdateFeature(updateFeature);
updateFeature.Store();
updateFeature = updateCursor.NextFeature();
}
}
}
// This is the function name object for the Geoprocessing Function Tool.
// This name object is created and returned by the Function Factory.
// The Function Factory must first be created before implementing this property.
public IName FullName
{
get
{
// Add CalculateArea.FullName getter implementation
IGPFunctionFactory functionFactory = new CalculateAreaFunctionFactory();
return (IName)functionFactory.GetFunctionName(m_ToolName);
}
}
// This is used to set a custom renderer for the output of the Function Tool.
public object GetRenderer(IGPParameter pParam)
{
return null;
}
// This is the unique context identifier in a [MAP] file (.h).
// ESRI Knowledge Base article #27680 provides more information about creating a [MAP] file.
public int HelpContext
{
get { return 0; }
}
// This is the path to a .chm file which is used to describe and explain the function and its operation.
public string HelpFile
{
get { return ""; }
}
// This is used to return whether the function tool is licensed to execute.
public bool IsLicensed()
{
return true;
}
// This is the name of the (.xml) file containing the default metadata for this function tool.
// The metadata file is used to supply the parameter descriptions in the help panel in the dialog.
// If no (.chm) file is provided, the help is based on the metadata file.
// ESRI Knowledge Base article #27000 provides more information about creating a metadata file.
public string MetadataFile
{
get { return m_metadatafile; }
}
// This is the class id used to override the default dialog for a tool.
// By default, the Toolbox will create a dialog based upon the parameters returned
// by the ParameterInfo property.
public UID DialogCLSID
{
get { return null; }
}
#endregion
}
//////////////////////////////
// Function Factory Class
////////////////////////////
[
Guid("2554BFC7-94F9-4d28-B3FE-14D17599B35A"),
ComVisible(true)
]
public class CalculateAreaFunctionFactory : IGPFunctionFactory
{
// Register the Function Factory with the ESRI Geoprocessor Function Factory Component Category.
#region "Component Category Registration"
[ComRegisterFunction()]
static void Reg(string regKey)
{
GPFunctionFactories.Register(regKey);
}
[ComUnregisterFunction()]
static void Unreg(string regKey)
{
GPFunctionFactories.Unregister(regKey);
}
#endregion
// Utility Function added to create the function names.
private IGPFunctionName CreateGPFunctionNames(long index)
{
IGPFunctionName functionName = new GPFunctionNameClass();
IGPName name;
switch (index)
{
case (0):
name = (IGPName)functionName;
name.Category = "AreaCalculation";
name.Description = "Calculate Area for FeatureClass";
name.DisplayName = "Calculate Area";
name.Name = "CalculateArea";
name.Factory = (IGPFunctionFactory)this;
break;
}
return functionName;
}
// Implementation of the Function Factory
#region IGPFunctionFactory Members
// This is the name of the function factory.
// This is used when generating the Toolbox containing the function tools of the factory.
public string Name
{
get { return "AreaCalculation"; }
}
// This is the alias name of the factory.
public string Alias
{
get { return "area"; }
}
// This is the class id of the factory.
public UID CLSID
{
get
{
UID id = new UIDClass();
id.Value = this.GetType().GUID.ToString("B");
return id;
}
}
// This method will create and return a function object based upon the input name.
public IGPFunction GetFunction(string Name)
{
switch (Name)
{
case ("CalculateArea"):
IGPFunction gpFunction = new CalculateAreaFunction();
return gpFunction;
}
return null;
}
// This method will create and return a function name object based upon the input name.
public IGPName GetFunctionName(string Name)
{
IGPName gpName = new GPFunctionNameClass();
switch (Name)
{
case ("CalculateArea"):
return (IGPName)CreateGPFunctionNames(0);
}
return null;
}
// This method will create and return an enumeration of function names that the factory supports.
public IEnumGPName GetFunctionNames()
{
IArray nameArray = new EnumGPNameClass();
nameArray.Add(CreateGPFunctionNames(0));
return (IEnumGPName)nameArray;
}
// This method will create and return an enumeration of GPEnvironment objects.
// If tools published by this function factory required new environment settings,
//then you would define the additional environment settings here.
// This would be similar to how parameters are defined.
public IEnumGPEnvironment GetFunctionEnvironments()
{
return null;
}
#endregion
}
}