// Copyright 2010 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 at <your ArcGIS install location>/DeveloperKit10.0/userestrictions.txt. // 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 : IGPFunction2 { private string m_ToolName = "CalculateArea"; //Function Name //Local members 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 IGPParameterEdit3 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 IGPParameterEdit3 outputParameter = new GPParameterClass(); outputParameter.DataType = new GPFeatureLayerTypeClass(); // Value object is DEFeatureClass outputParameter.Value = new DEFeatureClassClass(); // Set output parameter properties outputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionOutput; outputParameter.DisplayName = "Output FeatureClass"; outputParameter.Name = "out_featureclass"; outputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeDerived; // Create a new schema object - schema means the structure or design of the feature class (field information, geometry information, extent) IGPFeatureSchema outputSchema = new GPFeatureSchemaClass(); IGPSchema schema = (IGPSchema)outputSchema; // Clone the schema from the dependency. //This means update the output with the same schema as the input feature class (the dependency). schema.CloneDependency = true; // Set the schema on the output because this tool will add an additional field. outputParameter.Schema = outputSchema as IGPSchema; outputParameter.AddDependency("input_features"); parameters.Add(outputParameter); return parameters; } } // Validate: // - Validate is an IGPFunction method, and we need to implement it in case there // is legacy code that queries for the IGPFunction interface instead of the IGPFunction2 // interface. // - This Validate code is boilerplate - copy and insert into any IGPFunction2 code.. // - This is the calling sequence that the gp framework now uses when it QI's for IGPFunction2.. public IGPMessages Validate(IArray paramvalues, bool updateValues, IGPEnvironmentManager envMgr) { if (m_Parameters == null) m_Parameters = ParameterInfo; // Call UpdateParameters(). // Only Call if updatevalues is true. if (updateValues == true) { UpdateParameters(paramvalues, envMgr); } // 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); // Call UpdateMessages(); UpdateMessages(paramvalues, envMgr, validateMsgs); // Return the messages return validateMsgs; } // This method will update the output parameter value with the additional area field. public void UpdateParameters(IArray paramvalues, IGPEnvironmentManager pEnvMgr) { m_Parameters = paramvalues; // Retrieve the input parameter value IGPValue parameterValue = m_GPUtilities.UnpackGPValue(m_Parameters.get_Element(0)); // Get the derived output feature class schema and empty the additional fields. This will ensure you don't get duplicate entries. IGPParameter3 derivedFeatures = (IGPParameter3)paramvalues.get_Element(2); IGPFeatureSchema schema = (IGPFeatureSchema)derivedFeatures.Schema; schema.AdditionalFields = null; // If we have an input value, create a new field based on the field name the user entered. if (parameterValue.IsEmpty() == false) { IGPParameter3 fieldNameParameter = (IGPParameter3)paramvalues.get_Element(1); string fieldName = fieldNameParameter.Value.GetAsText(); // Check if the user's input field already exists IField areaField = m_GPUtilities.FindField(parameterValue, fieldName); if (areaField == null) { IFieldsEdit fieldsEdit = new FieldsClass(); IFieldEdit fieldEdit = new FieldClass(); fieldEdit.Name_2 = fieldName; fieldEdit.Type_2 = esriFieldType.esriFieldTypeDouble; fieldsEdit.AddField(fieldEdit); // Add an additional field for the area values to the derived output. IFields fields = fieldsEdit as IFields; schema.AdditionalFields = fields; } } } // Called after returning from the update parameters routine. // You can examine the messages created from internal validation and change them if desired. public void UpdateMessages(IArray paramvalues, IGPEnvironmentManager pEnvMgr, IGPMessages Messages) { // Check for error messages IGPMessage msg = (IGPMessage)Messages; if (msg.IsError()) return; // 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 the Input Dataset - Use DecodeFeatureLayer as the input might be a layer file or a feature layer from ArcMap. IFeatureClass inputFeatureClass; IQueryFilter qf; m_GPUtilities.DecodeFeatureLayer(parameterValue, out inputFeatureClass, out qf); IGPParameter3 fieldParameter = (IGPParameter3)paramvalues.get_Element(1); string fieldName = fieldParameter.Value.GetAsText(); // Check if the field already exists and provide a warning. int indexA = inputFeatureClass.FindField(fieldName); if (indexA > 0) { Messages.ReplaceWarning(1, "Field already exists. It will be overwritten."); } return; } // Execute: Execute the function given the array of the parameters public void Execute(IArray paramvalues, ITrackCancel trackcancel, IGPEnvironmentManager envMgr, IGPMessages message) { // 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 Dataset 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); } int featcount = inputFeatureClass.FeatureCount(null); //Set the properties of the Step Progressor IStepProgressor pStepPro = (IStepProgressor)trackcancel; pStepPro.MinRange = 0; pStepPro.MaxRange = featcount; pStepPro.StepValue = (1); pStepPro.Message = "Calculating Area"; pStepPro.Position = 0; pStepPro.Show(); // Create an Update Cursor indexA = inputFeatureClass.FindField(field); IFeatureCursor updateCursor = inputFeatureClass.Update(qf, 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(); pStepPro.Step(); } pStepPro.Hide(); // Release the update cursor to remove the lock on the input data. System.Runtime.InteropServices.Marshal.ReleaseComObject(updateCursor); } // 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() { IAoInitialize aoi = new AoInitializeClass(); ILicenseInformation licInfo = (ILicenseInformation)aoi; string licName = licInfo.GetLicenseProductName(aoi.InitializedProduct()); if (licName == "ArcInfo") { return true; } else return false; } // 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; } } // By default, the Toolbox will create a dialog based upon the parameters returned // by the ParameterInfo property. public UID DialogCLSID { // DO NOT USE. INTERNAL USE ONLY. get { return null; } } #endregion } ////////////////////////////// // Function Factory Class //////////////////////////// [ Guid("2554BFC7-94F9-4d28-B3FE-14D17599B35A"), ComVisible(true) ] public class CalculateAreaFunctionFactory : IGPFunctionFactory { private const string m_ToolName = "ylArea"; //Function Name // 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(); functionName.MinimumProduct = esriProductCode.esriProductCodeProfessional; IGPName name; switch (index) { case (0): name = (IGPName)functionName; name.Category = "AreaCalculation"; name.Description = "Calculate Area for FeatureClass"; name.DisplayName = "Calculate Area"; name.Name = m_ToolName; 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 m_ToolName; } } // 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 (m_ToolName): 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 (m_ToolName): 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 } }