GPCalculateArea\CalculateAreaFunction.cs
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
    }

}