GPPointFileInfo\OutputFeatureBuffer.cs
Creating a non-trival custom GP tool - inputs, outputs, and responding to environment settings
GPPointFileInfo\OutputFeatureBuffer.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.Runtime.InteropServices;
using ESRI.ArcGIS.Geoprocessing;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.GeoDatabaseExtensions;

namespace GPPointFileInfo
{
    public class OutputFeatureBuffer : GPPointFileInfo.IOutputFeatureBuffer
    {
        private IFeatureBuffer _featureBuffer;

        public OutputFeatureBuffer(IInputFile inputFile, IGPInputFileFormat gpInputFileFormat, ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference, IOutputHasZ outputHasZ, IGPOutputFeatureClass gpOutputFeatureClass, IESExtent esExtent)
        {
            ITerrainDataImporter terrainDataImporter = GetTerrainDataImporter(gpInputFileFormat, inputFile);

            double pointCount = GetPointCount(terrainDataImporter);

            IEnvelope envelope = GetEnvelope(terrainDataImporter, inputSpatialReference, outputSpatialReference);

            ValidateEnvelope(envelope, esExtent);

            double xMin = GetXMin(envelope);
            double xMax = GetXMax(envelope);
            double yMin = GetYMin(envelope);
            double yMax = GetYMax(envelope);
            double zMin = GetZMin(envelope);
            double zMax = GetZMax(envelope);

            IGeometry geometry = GetGeometry(envelope, outputHasZ, inputSpatialReference, outputSpatialReference);

            ValidateGeometry(geometry);

            double averagePointSpacing = GetAveragePointSpacing(envelope, pointCount);

            ValidateAveragePointSpacing(averagePointSpacing);

            _featureBuffer = GetFeatureBuffer(gpOutputFeatureClass, geometry, inputFile, pointCount, averagePointSpacing, xMin, xMax, yMin, yMax, zMin, zMax);
        }

        public IFeatureBuffer FeatureBuffer
        {
            get
            {
                return _featureBuffer;
            }
        }

        private ITerrainDataImporter GetTerrainDataImporter(IGPInputFileFormat gpInputFileFormat, IInputFile inputFile)
        {
            ITerrainDataImporter terrainDataImporter;

            switch (gpInputFileFormat.FileFormat)
            {
                case "XYZ":
                    terrainDataImporter = GetTerrainXYZDataImporter(inputFile);
                    break;
                case "GENERATE":
                    terrainDataImporter = GetTerrainGENERATEDataImporter(inputFile);
                    break;
                case "LAS":
                    terrainDataImporter = GetTerrainLASDataImporter(inputFile);
                    break;
                default:
                    throw new GPException(GPExceptionSeverity.Error, "Invalid Output Feature Buffer. Unable To Get Terrain Data Importer. Unhandled Case." + ObjectToString.GetNewline() + "gpInputFileFormat: " + ObjectToString.GetToString(gpInputFileFormat));
            }

            return terrainDataImporter;
        }

        private ITerrainDataImporter GetTerrainXYZDataImporter(IInputFile inputFile)
        {
            ITerrainDataImporter terrainXYZDataImporter = new TerrainAsciiDataImporterClass();

            ITerrainAsciiDataImporter terrainAsciiDataImporter = terrainXYZDataImporter as ITerrainAsciiDataImporter;

            terrainAsciiDataImporter.FileFormat = esriTerrainAsciiDataFormatType.esriTerrainAsciiDataFormatXYZ;

            try
            {
                terrainXYZDataImporter.AddFile(inputFile.Path);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Add File To Terrain XYZ Data Importer." + ObjectToString.GetNewline() + "inputFile: " + ObjectToString.GetToString(inputFile), ex);
            }

            return terrainXYZDataImporter;
        }

        private ITerrainDataImporter GetTerrainGENERATEDataImporter(IInputFile inputFile)
        {
            ITerrainDataImporter terrainGENERATEDataImporter = new TerrainAsciiDataImporterClass();

            ITerrainAsciiDataImporter terrainAsciiDataImporter = terrainGENERATEDataImporter as ITerrainAsciiDataImporter;

            terrainAsciiDataImporter.FileFormat = esriTerrainAsciiDataFormatType.esriTerrainAsciiDataFormatGenerate;

            try
            {
                terrainGENERATEDataImporter.AddFile(inputFile.Path);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Add File To Terrain GENERATE Data Importer." + ObjectToString.GetNewline() + "inputFile: " + ObjectToString.GetToString(inputFile), ex);
            }

            return terrainGENERATEDataImporter;
        }

        private ITerrainDataImporter GetTerrainLASDataImporter(IInputFile inputFile)
        {
            const esriTerrainLasReturnType ReturnNumber = esriTerrainLasReturnType.esriTerrainLasReturnAll;

            ITerrainDataImporter terrainLASDataImporter = new TerrainLasDataImporterClass();

            ITerrainLasDataImporter terrainLasDataImporter = terrainLASDataImporter as ITerrainLasDataImporter;

            terrainLasDataImporter.AddReturnNumber(ReturnNumber);

            try
            {
                terrainLASDataImporter.AddFile(inputFile.Path);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Add File To Terrain LAS Data Importer." + ObjectToString.GetNewline() + "inputFile: " + ObjectToString.GetToString(inputFile), ex);
            }

            return terrainLASDataImporter;
        }

        private double GetPointCount(ITerrainDataImporter terrainDataImporter)
        {
            double pointCount;

            try
            {
                pointCount = terrainDataImporter.GetPointCount();
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Get Point Count From Terrain Data Importer." + ObjectToString.GetNewline() + "terrainDataImporter: " + ObjectToString.GetToString(terrainDataImporter), ex);
            }

            return pointCount;
        }

        private IEnvelope GetEnvelope(ITerrainDataImporter terrainDataImporter, ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference)
        {
            IEnvelope envelope;

            ESRI.ArcGIS.Geometry.ISpatialReference spatialReference = inputSpatialReference.IsUnknown ? outputSpatialReference.SpatialReference : inputSpatialReference.SpatialReference;

            try
            {
                envelope = terrainDataImporter.GetDataExtent(spatialReference);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Get Data Extent From Terrain Data Importer." + ObjectToString.GetNewline() + "terrainDataImporter: " + ObjectToString.GetToString(terrainDataImporter) + "spatialReference: " + ObjectToString.GetToString(spatialReference), ex);
            }

            return envelope;
        }

        private void ValidateEnvelope(IEnvelope envelope, IESExtent esExtent)
        {
            if (envelope == null)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Invalid Envelope. Envelope Is Null.");
            }
            else if (envelope.IsEmpty)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Invalid Envelope. Envelope Is Empty.");
            }
            else if (envelope.SpatialReference == null)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Invalid Envelope. Envelope Spatial Reference Is Null.");
            }
            else
            {
                if ((esExtent.Envelope != null) && (!Intersect(esExtent.Envelope, envelope)))
                {
                    throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Invalid Envelope. Envelope Does Not Intersect Extent." + ObjectToString.GetNewline() + "envelope: " + ObjectToString.GetToString(envelope) + "esExtent: " + ObjectToString.GetToString(esExtent));
                }
            }
        }

        private bool Intersect(IEnvelope envelopeA, IEnvelope envelopeB)
        {
            bool intersect;

            IRelationalOperator relationalOperator = envelopeA as IRelationalOperator;

            IGeometry geometry = envelopeB as IGeometry;

            try
            {
                intersect = !relationalOperator.Disjoint(geometry);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Determine If Envelopes Intersect." + ObjectToString.GetNewline() + "envelopeA: " + ObjectToString.GetToString(envelopeA) + "envelopeB: " + ObjectToString.GetToString(envelopeB), ex);
            }

            return intersect;
        }

        private double GetXMin(IEnvelope envelope)
        {
            return envelope.XMin;
        }

        private double GetXMax(IEnvelope envelope)
        {
            return envelope.XMax;
        }

        private double GetYMin(IEnvelope envelope)
        {
            return envelope.YMin;
        }

        private double GetYMax(IEnvelope envelope)
        {
            return envelope.YMax;
        }

        private double GetZMin(IEnvelope envelope)
        {
            return envelope.ZMin;
        }

        private double GetZMax(IEnvelope envelope)
        {
            return envelope.ZMax;
        }

        private IGeometry GetGeometry(IEnvelope envelope, IOutputHasZ outputHasZ, ISpatialReference inputSpatialReference, ISpatialReference outputSpatialReference)
        {
            IGeometry geometry;

            geometry = outputHasZ.HasZ ? Get3DGeometry(envelope) : Get2DGeometry(envelope);

            if ((!outputSpatialReference.IsUnknown) && (!inputSpatialReference.IsUnknown))
            {
                try
                {
                    geometry.Project(outputSpatialReference.SpatialReference);
                }
                catch (COMException ex)
                {
                    throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Project Geometry From Input Spatial Reference To Output Spatial Reference." + ObjectToString.GetNewline() + "geometry: " + ObjectToString.GetToString(geometry) + "inputSpatialReference: " + ObjectToString.GetToString(inputSpatialReference) + "outputSpatialReference: " + ObjectToString.GetToString(outputSpatialReference), ex);
                }
            }

            return geometry;
        }

        private IGeometry Get2DGeometry(IEnvelope envelope)
        {
            IGeometry geometry = new PolygonClass();

            IPointCollection pointCollection = geometry as IPointCollection;

            object missing = Type.Missing;

            pointCollection.AddPoint(envelope.UpperRight, ref missing, ref missing);
            pointCollection.AddPoint(envelope.LowerRight, ref missing, ref missing);
            pointCollection.AddPoint(envelope.LowerLeft, ref missing, ref missing);
            pointCollection.AddPoint(envelope.UpperLeft, ref missing, ref missing);

            IPolygon polygon = pointCollection as IPolygon;

            polygon.Close();

            geometry.SpatialReference = envelope.SpatialReference;

            return geometry;
        }

        private IGeometry Get3DGeometry(IEnvelope envelope)
        {
            IGeometry geometry;

            IGeometry geometry2D = Get2DGeometry(envelope);

            IExtrude extrude = new GeometryEnvironmentClass();

            try
            {
                geometry = extrude.ExtrudeFromTo(envelope.ZMin, envelope.ZMax, geometry2D);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Extrude From Envelope Z Min To Envelope Z Max Using Base 2D Geometry." + ObjectToString.GetNewline() + "extrude: " + ObjectToString.GetToString(extrude) + "envelope.ZMin: " + ObjectToString.GetToString(envelope.ZMin) + "envelope.ZMax: " + ObjectToString.GetToString(envelope.ZMax) + "geometry2D: " + ObjectToString.GetToString(geometry2D), ex);
            }

            return geometry;            
        }

        private void ValidateGeometry(IGeometry geometry)
        {
            if (geometry == null)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Invalid Geometry. Geometry Is Null.");
            }
            else if (geometry.IsEmpty)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Invalid Geometry. Geometry Is Empty.");
            }
        }

        private double GetAveragePointSpacing(IEnvelope envelope, double pointCount)
        {
            double averagePointSpacing;

            IArea area = envelope as IArea;

            if (area.Area <= 0)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Get Average Point Spacing. area.Area <= 0." + ObjectToString.GetNewline() + "area.Area: " + ObjectToString.GetToString(area.Area));
            }

            double averageAreaPerPoint = GetAverageAreaPerPoint(area, pointCount);

            if (averageAreaPerPoint <= 0)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable to Get Average Point Spacing. averageAreaPerPoint <= 0." + ObjectToString.GetNewline() + "averageAreaPerPoint: " + ObjectToString.GetToString(averageAreaPerPoint));
            }

            averagePointSpacing = Math.Sqrt(averageAreaPerPoint);

            return averagePointSpacing;
        }

        private double GetAverageAreaPerPoint(IArea area, double pointCount)
        {
            return area.Area / pointCount;
        }

        private void ValidateAveragePointSpacing(double averagePointSpacing)
        {
            if (averagePointSpacing <= 0)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Invalid Average Point Spacing. averagePointSpacing <= 0." + ObjectToString.GetNewline() + "averagePointSpacing: " + ObjectToString.GetToString(averagePointSpacing));
            }
        }

        private IFeatureBuffer GetFeatureBuffer(IGPOutputFeatureClass gpOutputFeatureClass, IGeometry geometry, IInputFile inputFile, double pointCount, double averagePointSpacing, double xMin, double xMax, double yMin, double yMax, double zMin, double zMax)
        {
            IFeatureBuffer featureBuffer;

            try
            {
                featureBuffer = gpOutputFeatureClass.FeatureClass.CreateFeatureBuffer();
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Create Feature Buffer." + ObjectToString.GetNewline() + "gpOutputFeatureClass: " + ObjectToString.GetToString(gpOutputFeatureClass), ex);
            }

            IFeature feature = featureBuffer as IFeature;

            SetShape(feature, geometry);

            SetField(feature, "FileName", inputFile.Name);
            SetField(feature, "PointCount", pointCount);
            SetField(feature, "AvgPtSpc", averagePointSpacing);
            SetField(feature, "XMin", xMin);
            SetField(feature, "XMax", xMax);
            SetField(feature, "YMin", yMin);
            SetField(feature, "YMax", yMax);
            SetField(feature, "ZMin", zMin);
            SetField(feature, "ZMax", zMax);

            return featureBuffer;
        }

        private void SetShape(IFeature feature, IGeometry geometry)
        {
            try
            {
                feature.Shape = geometry;
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Set Shape." + ObjectToString.GetNewline() + "feature: " + ObjectToString.GetToString(feature) + "geometry: " + ObjectToString.GetToString(geometry), ex);
            }
        }

        private void SetField(IFeature feature, string name, object value)
        {
            IFields fields = feature.Fields;

            int index = fields.FindField(name);

            try
            {
                feature.set_Value(index, value);
            }
            catch (COMException ex)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Unable To Set Field." + ObjectToString.GetNewline() + "feature: " + ObjectToString.GetToString(feature) + "name: " + ObjectToString.GetToString(name) + "value: " + ObjectToString.GetToString(value), ex);
            }
        }

        private void ValidateFeatureBuffer(IFeatureBuffer featureBuffer)
        {
            if (featureBuffer == null)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Feature Buffer Is Null.");
            }
            else if (featureBuffer.Shape == null)
            {
                throw new GPException(GPExceptionSeverity.Warn, "Invalid Output Feature Buffer. Feature Buffer Shape Is Null." + ObjectToString.GetNewline() + "featureBuffer: " + ObjectToString.GetToString(featureBuffer));
            }
        }
    }
}