GPPointFileInfo\GPOutputFeatureClass.cs
Creating a non-trival custom GP tool - inputs, outputs, and responding to environment settings
GPPointFileInfo\GPOutputFeatureClass.cs
// Copyright 2008 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 use restrictions at <your ArcGIS install location>/developerkit/userestrictions.txt.
// 

using System;
using System.Collections.Generic;
using System.Collections;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geoprocessing;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;

namespace GPPointFileInfo
{
    public class GPOutputFeatureClass : GPPointFileInfo.IGPOutputFeatureClass
    {
        private static int _parameterIndex;

        private IFeatureClass _featureClass;

        public GPOutputFeatureClass(IParameterValueArray parameterValueArray, IGPInputFolder gpInputFolder, ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference, IOutputHasZ outputHasZ, IESOverwriteOutput esOverwriteOutput, IESConfigKeyword esConfigKeyword, IESScratchWorkspace esScratchWorkspace, IESSpatialGrid1 esSpatialGrid1, IESSpatialGrid2 esSpatialGrid2, IESSpatialGrid3 esSpatialGrid3)
        {
            IGPValue gpValue = parameterValueArray.GetGPValue(_parameterIndex);

            _featureClass = GetFeatureClass(gpValue, gpInputFolder, inputSpatialReference, outputSpatialReference, outputHasZ, esOverwriteOutput, esConfigKeyword, esScratchWorkspace, esSpatialGrid1, esSpatialGrid2, esSpatialGrid3);
        }

        public double MinAvgPtSpc
        {
            get
            {
                return GetMinAvgPtSpc();
            }
        }

        private double GetMinAvgPtSpc()
        {
            const double InvalidMinAvgPtSpc = -1;
            const string FieldName = "AvgPtSpc";

            double minAvgPtSpc;

            int featureCount = _featureClass.FeatureCount(null);

            if (featureCount == 0)
            {
                minAvgPtSpc = InvalidMinAvgPtSpc;
            }
            else
            {
                IFeatureCursor featureCursor = _featureClass.Search(null, false);

                ICursor cursor = featureCursor as ICursor;

                IDataStatistics dataStatistics = new DataStatisticsClass();

                dataStatistics.Field = FieldName;
                dataStatistics.Cursor = cursor;

                IStatisticsResults statisticsResults = dataStatistics.Statistics;

                minAvgPtSpc = statisticsResults.Minimum;
            }

            return minAvgPtSpc;
        }

        public static IGPParameterEdit GetGPParameterEdit()
        {
            IGPParameterEdit gpParameterEdit = new GPParameterClass();

            IGPDataType gpDataType = new DEFeatureClassTypeClass();

            IGPValue gpValue = new DEFeatureClassClass();

            gpParameterEdit.Name = "out_output_feature_class";
            gpParameterEdit.DisplayName = "Output Feature Class";
            gpParameterEdit.DataType = gpDataType;
            gpParameterEdit.Value = gpValue;
            gpParameterEdit.Direction = esriGPParameterDirection.esriGPParameterDirectionOutput;
            gpParameterEdit.ParameterType = esriGPParameterType.esriGPParameterTypeRequired;

            return gpParameterEdit;
        }

        public static void SetGPParameterIndex(int index)
        {
            _parameterIndex = index;
        }

        public IFeatureClass FeatureClass
        {
            get
            {
                return _featureClass;
            }
        }

        private IFeatureClass GetFeatureClass(IGPValue gpValue, IGPInputFolder gpInputFolder, ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference, IOutputHasZ outputHasZ, IESOverwriteOutput esOverwriteOutput, IESConfigKeyword esConfigKeyword, IESScratchWorkspace esScratchWorkspace, IESSpatialGrid1 esSpatialGrid1, IESSpatialGrid2 esSpatialGrid2, IESSpatialGrid3 esSpatialGrid3)
        {
            const esriFeatureType FeatureType = esriFeatureType.esriFTSimple;

            IFeatureClass featureClass;

            string featureClassNameAsText = GetFeatureClassNameAsText(gpValue);

            string featureClassPath = GetFeatureClassPath(gpValue, gpInputFolder, esScratchWorkspace);

            gpValue = GetGPValue(featureClassPath, featureClassNameAsText);

            IGPUtilities gpUtilities = new GPUtilitiesClass();

            if (gpUtilities.Exists(gpValue))
            {
                if (esOverwriteOutput.Value == false)
                {
                    throw new GPException(GPExceptionSeverity.Error, "Invalid Output Feature Class. Output Feature Class Already Exists.");
                }
                else
                {
                    IDataset dataset;

                    try
                    {
                        dataset = gpUtilities.OpenDataset(gpValue);
                    }
                    catch (COMException ex)
                    {
                        throw new GPException(GPExceptionSeverity.Error, "Invalid Output Feature Class. Output Feature Class Cannot Be Opened." + ObjectToString.GetToString(gpValue), ex);
                    }

                    if (!dataset.CanDelete())
                    {
                        throw new GPException(GPExceptionSeverity.Error, "Invalid Output Feature Class. Output Feature Class Cannot Be Deleted. Feature Class May Be Participating In A Controller Dataset: Terrain, Topology, Or Network." + ObjectToString.GetToString(dataset));
                    }
                    else
                    {
                        gpUtilities.Delete(gpValue);
                    }
                }
            }

            IFeatureClassName featureClassName = GetFeatureClassName(gpValue);

            FeatureClassDescriptionClass featureClassDescriptionClass = new FeatureClassDescriptionClass();

            IFeatureClassDescription featureClassDescription = featureClassDescriptionClass as IFeatureClassDescription;

            IObjectClassDescription objectClassDescription = featureClassDescription as IObjectClassDescription;

            IFields fields = GetFields(featureClassNameAsText, objectClassDescription, inputSpatialReference, outputSpatialReference, outputHasZ, esSpatialGrid1, esSpatialGrid2, esSpatialGrid3);

            if (IsFeatureClassInFeatureDataset(featureClassName))
            {
                featureClass = GetFeatureClassFromFeatureDataset(featureClassName, featureClassNameAsText, fields, featureClassDescription, objectClassDescription, FeatureType, esConfigKeyword);
            }
            else
            {
                featureClass = GetFeatureClassFromFeatureWorkspace(featureClassName, featureClassNameAsText, fields, featureClassDescription, objectClassDescription, FeatureType, esConfigKeyword);
            }

            return featureClass;
        }   

        private IFeatureClassName GetFeatureClassName(IGPValue gpValue)
        {
            IFeatureClassName featureClassName;

            IGPUtilities gpUtilities = new GPUtilitiesClass();

            IDataElement dataElement = gpValue as IDataElement;

            IName name = gpUtilities.CreateFeatureClassName(dataElement.CatalogPath);

            featureClassName = name as IFeatureClassName;

            return featureClassName;
        }

        private string GetFeatureClassNameAsText(IGPValue gpValue)
        {
            string featureClassNameAsText;

            IDataElement dataElement = gpValue as IDataElement;

            featureClassNameAsText = dataElement.Name;

            return featureClassNameAsText;
        }

        private string GetFeatureClassPath(IGPValue gpValue, IGPInputFolder gpInputFolder, IESScratchWorkspace esScratchWorkspace)
        {
            string featureClassPath;

            string featureClassPathFromGPValue = GetFeatureClassPath(gpValue);
            string featureClassPathFromScratchWorkspace = GetFeatureClassPath(esScratchWorkspace);
            string featureClassPathFromIGPInputFolder = GetFeatureClassPath(gpInputFolder);

            if (IsBlank(featureClassPathFromGPValue))
            {
                if (IsBlank(featureClassPathFromScratchWorkspace))
                {
                    featureClassPath = featureClassPathFromIGPInputFolder;
                }
                else
                {
                    featureClassPath = featureClassPathFromScratchWorkspace;
                }
            }
            else
            {
                featureClassPath = featureClassPathFromGPValue;
            }

            return featureClassPath;
        }

        private bool IsBlank(string text)
        {
            const string Pattern = "^\\s*$";

            bool isBlank;

            Regex regex = new Regex(Pattern);

            isBlank = regex.IsMatch(text);

            return isBlank;
        }

        private string GetFeatureClassPath(IGPValue gpValue)
        {
            string featureClassPath;

            IDataElement dataElement = gpValue as IDataElement;

            featureClassPath = dataElement.GetPath();

            return featureClassPath;
        }

        private string GetFeatureClassPath(IESScratchWorkspace esScratchWorkspace)
        {
            return esScratchWorkspace.Path;
        }

        private string GetFeatureClassPath(IGPInputFolder gpInputFolder)
        {
            return gpInputFolder.Folder;
        }

        private IGPValue GetGPValue(string featureClassPath, string featureClassNameAsText)
        {
            IGPValue gpValue;

            IGPDataType gpDataType = new DEFeatureClassTypeClass();

            gpValue = gpDataType.CreateValue(featureClassPath + "\\" + featureClassNameAsText);

            return gpValue;
        }

        private IFields GetFields(string featureClassNameAsText, IObjectClassDescription objectClassDescription, ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference, IOutputHasZ outputHasZ, IESSpatialGrid1 esSpatialGrid1, IESSpatialGrid2 esSpatialGrid2, IESSpatialGrid3 esSpatialGrid3)
        {
            IFields fields = new FieldsClass();
            
            IFieldsEdit fieldsEdit = fields as IFieldsEdit;

            IFields requiredFields = GetRequiredFields(featureClassNameAsText, objectClassDescription, inputSpatialReference, outputSpatialReference, outputHasZ, esSpatialGrid1, esSpatialGrid2, esSpatialGrid3);

            for(int i = 0; i < requiredFields.FieldCount; i++)
            {
                IField requiredField = requiredFields.get_Field(i);

                fieldsEdit.AddField(requiredField);
            }

            Dictionary<string, esriFieldType> fieldsDictionary = GetFieldsDictionary();

            IDictionaryEnumerator dictionaryEnumerator = fieldsDictionary.GetEnumerator();

            bool isElement = dictionaryEnumerator.MoveNext();

            while (isElement)
            {
                IField field = GetField(dictionaryEnumerator.Entry);
                    
                fieldsEdit.AddField(field);

                isElement = dictionaryEnumerator.MoveNext();
            }

            return fields;
        }

        private IFields GetRequiredFields(string featureClassNameAsText, IObjectClassDescription objectClassDescription, ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference, IOutputHasZ outputHasZ, IESSpatialGrid1 esSpatialGrid1, IESSpatialGrid2 esSpatialGrid2, IESSpatialGrid3 esSpatialGrid3)
        {
            IFields requiredFields = new FieldsClass();
            
            IFieldsEdit requiredFieldsEdit = requiredFields as IFieldsEdit;

            for (int i = 0; i < objectClassDescription.RequiredFields.FieldCount; i++)
            {
                IField requiredField = objectClassDescription.RequiredFields.get_Field(i);

                if (IsShapeField(requiredField))
                {
                    requiredField = GetShapeField(requiredField, inputSpatialReference, outputSpatialReference, outputHasZ, esSpatialGrid1, esSpatialGrid2, esSpatialGrid3);
                }

                requiredFieldsEdit.AddField(requiredField);
            }

            return requiredFields;
        }

        private bool IsShapeField(IField field)
        {
            const string ShapeFieldName = "SHAPE";

            return (field.Name == ShapeFieldName);
        }

        private IField GetShapeField(IField field, ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference, IOutputHasZ outputHasZ, IESSpatialGrid1 esSpatialGrid1, IESSpatialGrid2 esSpatialGrid2, IESSpatialGrid3 esSpatialGrid3)
        {
            IField shapeField = field;

            IFieldEdit shapeFieldEdit = shapeField as IFieldEdit;

            shapeFieldEdit.GeometryDef_2 = GetGeometryDef(inputSpatialReference, outputSpatialReference, outputHasZ, esSpatialGrid1, esSpatialGrid2, esSpatialGrid3);

            return shapeField;
        }

        private IGeometryDef GetGeometryDef(ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference, IOutputHasZ outputHasZ, IESSpatialGrid1 esSpatialGrid1, IESSpatialGrid2 esSpatialGrid2, IESSpatialGrid3 esSpatialGrid3)
        {
            const bool HasM = false;

            IGeometryDef geometryDef = new GeometryDefClass();

            IGeometryDefEdit geometryDefEdit = geometryDef as IGeometryDefEdit;

            esriGeometryType geometryType = GetGeometryType(outputHasZ);

            geometryDefEdit.GeometryType_2 = geometryType;
            geometryDefEdit.HasZ_2 = outputHasZ.HasZ;
            geometryDefEdit.HasM_2 = HasM;
            geometryDefEdit.SpatialReference_2 = GetSpatialReference(inputSpatialReference, outputSpatialReference);
            geometryDefEdit.GridCount_2 = 3;

            try
            {
                geometryDefEdit.set_GridSize(0, esSpatialGrid1.Value);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Error, "Invalid Output Feature Class. Unable To Set Grid Size On Geometry Def Edit. index = 0" + ObjectToString.GetNewline() + "esSpatialGrid1: " + ObjectToString.GetToString(esSpatialGrid1), ex);
            }

            try
            {
                geometryDefEdit.set_GridSize(1, esSpatialGrid2.Value);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Error, "Invalid Output Feature Class. Unable To Set Grid Size On Geometry Def Edit. index = 1" + ObjectToString.GetNewline() + "esSpatialGrid2: " + ObjectToString.GetToString(esSpatialGrid2), ex);
            }

            try
            {
                geometryDefEdit.set_GridSize(2, esSpatialGrid3.Value);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Error, "Invalid Output Feature Class. Unable To Set Grid Size On Geometry Def Edit. index = 2" + ObjectToString.GetNewline() + "esSpatialGrid3: " + ObjectToString.GetToString(esSpatialGrid3), ex);
            }

            return geometryDef;
        }

        private ESRI.ArcGIS.Geometry.ISpatialReference GetSpatialReference(ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference)
        {
            return ((!inputSpatialReference.IsUnknown) && (outputSpatialReference.IsUnknown)) ? inputSpatialReference.SpatialReference : outputSpatialReference.SpatialReference;
        }

        private esriGeometryType GetGeometryType(IOutputHasZ outputHasZ)
        {
            return outputHasZ.HasZ ? esriGeometryType.esriGeometryMultiPatch : esriGeometryType.esriGeometryPolygon;
        }

        private Dictionary<string, esriFieldType> GetFieldsDictionary()
        {
            Dictionary<string, esriFieldType> fieldsDictionary = new Dictionary<string, esriFieldType>();

            fieldsDictionary.Add("FileName", esriFieldType.esriFieldTypeString);
            fieldsDictionary.Add("PointCount", esriFieldType.esriFieldTypeDouble);
            fieldsDictionary.Add("AvgPtSpc", esriFieldType.esriFieldTypeDouble);
            fieldsDictionary.Add("XMin", esriFieldType.esriFieldTypeDouble);
            fieldsDictionary.Add("XMax", esriFieldType.esriFieldTypeDouble);
            fieldsDictionary.Add("YMin", esriFieldType.esriFieldTypeDouble);
            fieldsDictionary.Add("YMax", esriFieldType.esriFieldTypeDouble);
            fieldsDictionary.Add("ZMin", esriFieldType.esriFieldTypeDouble);
            fieldsDictionary.Add("ZMax", esriFieldType.esriFieldTypeDouble);

            return fieldsDictionary;
        }

        private IField GetField(DictionaryEntry dictionaryEntry)
        {
            IField field = new FieldClass();

            IFieldEdit fieldEdit = field as IFieldEdit;

            fieldEdit.Name_2 = dictionaryEntry.Key as string;
            fieldEdit.Type_2 = (esriFieldType)dictionaryEntry.Value;

            return field;
        }

        private bool IsFeatureClassInFeatureDataset(IFeatureClassName featureClassName)
        {
            return (featureClassName.FeatureDatasetName != null);
        }

        private IFeatureClass GetFeatureClassFromFeatureDataset(IFeatureClassName featureClassName, string featureClassNameAsText, IFields fields, IFeatureClassDescription featureClassDescription, IObjectClassDescription objectClassDescription, esriFeatureType featureType, IESConfigKeyword esConfigKeyword)
        {
            IFeatureClass featureClass;

            IDatasetName datasetName = featureClassName.FeatureDatasetName;

            IName name = datasetName as IName;

            IFields fixedFields = GetFixedFieldsFromFeatureDataset(name, fields);

            object openedName = name.Open();

            IFeatureDataset featureDataset = openedName as IFeatureDataset;

            try
            {
                featureClass = featureDataset.CreateFeatureClass(featureClassNameAsText, fixedFields, objectClassDescription.InstanceCLSID, objectClassDescription.ClassExtensionCLSID, featureType, featureClassDescription.ShapeFieldName, esConfigKeyword.Value);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Error, "Invalid Output Feature Class. Unable To Create Feature Class In Feature Dataset." + ObjectToString.GetNewline() + "featureDataset: " + ObjectToString.GetToString(featureDataset) + "featureClassNameAsText: " + ObjectToString.GetToString(featureClassNameAsText) + "fixedFields: " + ObjectToString.GetToString(fixedFields) + "objectClassDescription: " + ObjectToString.GetToString(objectClassDescription) + "featureType: " + ObjectToString.GetToString(featureType) + "featureClassDescription: " + ObjectToString.GetToString(featureClassDescription) + "esConfigKeyword: " + ObjectToString.GetToString(esConfigKeyword), ex);
            }

            return featureClass;
        }

        private IFields GetFixedFieldsFromFeatureDataset(IName name, IFields fields)
        {
            IFields fixedFields;

            object openedName = name.Open();

            IFeatureDataset featureDataset = openedName as IFeatureDataset;

            IWorkspace workspace = featureDataset.Workspace;

            IFieldChecker fieldChecker = new FieldCheckerClass();

            fieldChecker.ValidateWorkspace = workspace;

            IEnumFieldError enumFieldError;

            fieldChecker.Validate(fields, out enumFieldError, out fixedFields);

            return fixedFields;
        }

        private IFeatureClass GetFeatureClassFromFeatureWorkspace(IFeatureClassName featureClassName, string featureClassNameAsText, IFields fields, IFeatureClassDescription featureClassDescription, IObjectClassDescription objectClassDescription, esriFeatureType featureType, IESConfigKeyword esConfigKeyword)
        {
            IFeatureClass featureClass;

            IDatasetName datasetName = featureClassName as IDatasetName;

            IWorkspaceName workspaceName = datasetName.WorkspaceName;

            IName name = workspaceName as IName;

            IFields fixedFields = GetFixedFieldsFromFeatureWorkspace(name, fields);

            object openedName = name.Open();

            IWorkspace workspace = openedName as IWorkspace;

            IFeatureWorkspace featureWorkspace = workspace as IFeatureWorkspace;

            try
            {
                featureClass = featureWorkspace.CreateFeatureClass(featureClassNameAsText, fields, objectClassDescription.InstanceCLSID, objectClassDescription.ClassExtensionCLSID, featureType, featureClassDescription.ShapeFieldName, esConfigKeyword.Value);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Error, "Invalid Output Feature Class. Unable To Create Feature Class In Feature Workspace." + ObjectToString.GetNewline() + "featureWorkspace: " + ObjectToString.GetToString(featureWorkspace) + "featureClassNameAsText: " + ObjectToString.GetToString(featureClassNameAsText) + "fixedFields: " + ObjectToString.GetToString(fixedFields) + "objectClassDescription: " + ObjectToString.GetToString(objectClassDescription) + "featureType: " + ObjectToString.GetToString(featureType) + "featureClassDescription: " + ObjectToString.GetToString(featureClassDescription) + "esConfigKeyword: " + ObjectToString.GetToString(esConfigKeyword), ex);
            }

            return featureClass;
        }

        private IFields GetFixedFieldsFromFeatureWorkspace(IName name, IFields fields)
        {
            IFields fixedFields;

            object openedName = name.Open();

            IWorkspace workspace = openedName as IWorkspace;

            IFieldChecker fieldChecker = new FieldCheckerClass();

            fieldChecker.ValidateWorkspace = workspace;

            IEnumFieldError enumFieldError;

            fieldChecker.Validate(fields, out enumFieldError, out fixedFields);

            return fixedFields;
        }
    }
}