ArcGIS Select Buffer Tool
ArcGIS_SelectBufferTool_CSharp\App_Code\Utility.cs
// 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.
// 

public class Utility
{
    /// <summary>
    /// Retrieves the well-known ID of a unit based on its name.  These names are 
    /// taken from the ESRI.ArcGIS.ADF.Web.DataSources.Units enumeration.
    /// </summary>
    /// <param name="unitName">Name of the unit to retrieve the WKID for</param>
    public static int GetWkidByUnitName(string unitName)
    {
        int wkid = -1;
        switch (unitName)
        {
            case "Inches" :
                wkid = 109009;
                break;
            case "Feet" :
                wkid = 9003;
                break;
            case "Yards" :
                wkid = 109002;
                break;
            case "Miles" :
                wkid = 9035;
                break;
            case "NauticalMiles" :
                wkid = 9030;
                break;
            case "Millimeters" :
                wkid = 109007;
                break;
            case "Centimeters" :
                wkid = 109006;
                break;
            case "Meters" :
                wkid = 9001;
                break;
            case "Kilometers" :
                wkid = 9036;
                break;
            case "Decimeters" :
                wkid = 109005;
                break;
        }

        return wkid;
    }

    /// <summary>
    /// Gets the minimum enclosing rectangle (i.e. bounding box) of the passed-in polygon
    /// </summary>
    /// <param name="agsSoapPolygon">Polygon to retrieve the MER for</param>
    public static ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN GetBoundingExtent(
        ESRI.ArcGIS.ADF.ArcGISServer.PolygonN agsSoapPolygon)
    {
        // Instantiate an envelope with max values minimized and min values maximized
        ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN agsSoapBoundingBox =
            new ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN();
        agsSoapBoundingBox.XMin = double.MaxValue;
        agsSoapBoundingBox.XMax = double.MinValue;
        agsSoapBoundingBox.YMin = double.MaxValue;
        agsSoapBoundingBox.YMax = double.MinValue;

        // Iterate through all the polygon's vertices
        for (int i = 0; i < agsSoapPolygon.RingArray.Length; i++)
        {
            ESRI.ArcGIS.ADF.ArcGISServer.Ring agsSoapRing = agsSoapPolygon.RingArray[i];
            for (int j = 0; j < agsSoapRing.PointArray.Length; j++)
            {
                // For each vertex, expand the bounds of the minimum enclosing rectangle with the 
                // vertex's coordinates if they fall outside the rectangle's current bounds
                ESRI.ArcGIS.ADF.ArcGISServer.PointN agsSoapCurrentPoint = agsSoapRing.PointArray[j] 
                    as ESRI.ArcGIS.ADF.ArcGISServer.PointN;

                if (agsSoapCurrentPoint.X < agsSoapBoundingBox.XMin)
                    agsSoapBoundingBox.XMin = agsSoapCurrentPoint.X;
                else if (agsSoapCurrentPoint.X > agsSoapBoundingBox.XMax)
                    agsSoapBoundingBox.XMax = agsSoapCurrentPoint.X;

                if (agsSoapCurrentPoint.Y < agsSoapBoundingBox.YMin)
                    agsSoapBoundingBox.YMin = agsSoapCurrentPoint.Y;
                else if (agsSoapCurrentPoint.Y > agsSoapBoundingBox.YMax)
                    agsSoapBoundingBox.YMax = agsSoapCurrentPoint.Y;
            }
        }

        return agsSoapBoundingBox;
    }

    /// <summary>
    /// Creates a spatial reference that will minimize distortion near the input polygon.  Ideal for operations 
    /// that will derive geometry based on that polygon (i.e. buffer).
    /// </summary>
    /// <param name="agsSoapPolygon">Polygon to use as the center of the spatial reference's datum</param>
    /// <param name="geometryServerProxy">Geometry service to use in creating the spatial reference</param>
    public static ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference CreateOperationSpatialReference(
        ESRI.ArcGIS.ADF.ArcGISServer.PolygonN agsSoapPolygon, 
        ESRI.ArcGIS.ADF.ArcGISServer.GeometryServerProxy geometryServerProxy)
    {
        // Get the polygon's minimum enclosing rectangle (MER)
        ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN agsSoapBoundingEnvelope = GetBoundingExtent(agsSoapPolygon);

        // If the input polygon's spatial reference uses a projected coordinate system, project the MER to a 
        // geographic coordinate system (WGS 1984 in this case).  We do this because the MER's coordinates 
        // will be used to initialize the datum of the operation spatial reference
        if (agsSoapPolygon.SpatialReference is ESRI.ArcGIS.ADF.ArcGISServer.ProjectedCoordinateSystem)
        {
            // Create an ArcGIS Server spatial reference initalized to use the WGS 1984 coordinate system
            ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsSoapGeographicSpatialReference =
                new ESRI.ArcGIS.ADF.ArcGISServer.GeographicCoordinateSystem();
            agsSoapGeographicSpatialReference.WKID = 4326;
            agsSoapGeographicSpatialReference.WKIDSpecified = true;
            
            // Place the input MER in an array for the project operation
            ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsSoapInputGeometryArray =
                new ESRI.ArcGIS.ADF.ArcGISServer.Geometry[1];
            agsSoapInputGeometryArray[0] = agsSoapBoundingEnvelope;
            
            // Execute the projection
            ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsSoapOutputGeometryArray =
                geometryServerProxy.Project(agsSoapPolygon.SpatialReference, agsSoapGeographicSpatialReference,
                true, null, null, agsSoapInputGeometryArray);

            // Retrieve the projected MER from the results array
            agsSoapBoundingEnvelope = agsSoapOutputGeometryArray[0] as ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN;
        }

        // Get the latitude (Y coordinate) at the center of the MER
        double centerLatitude = agsSoapBoundingEnvelope.YMax -
            (agsSoapBoundingEnvelope.YMax - agsSoapBoundingEnvelope.YMin) / 2;

        // Create the definition string for the operation spatial reference's coordinate system.  We will use 
        // the Hotine Oblique Mercator coordinate system because it lends itself well to minimizing operational
        // distortion anywhere on the earth
        string hotineObliqueMercatorDefinition = @"
            PROJCS[""World_Hotine"",
            GEOGCS[""GCS_WGS_1984"",
            DATUM[""D_WGS_1984"",
            SPHEROID[""WGS_1984"",6378137.0,298.257223563]],
            PRIMEM[""Greenwich"",0.0],
            UNIT[""Degree"",0.0174532925199433]],
            PROJECTION[""Hotine_Oblique_Mercator_Two_Point_Natural_Origin""],
            PARAMETER[""False_Easting"",0.0],
            PARAMETER[""False_Northing"",0.0],
            PARAMETER[""Latitude_Of_1st_Point"",{0}],   
            PARAMETER[""Latitude_Of_2nd_Point"",{1}],
            PARAMETER[""Scale_Factor"",1.0],
            PARAMETER[""Longitude_Of_1st_Point"",{2}],
            PARAMETER[""Longitude_Of_2nd_Point"",{3}],
            PARAMETER[""Latitude_Of_Center"",{4}],
            UNIT[""Meter"",1.0]]";

        // Specify the relevant coordinates of the MER for the coordinate system's datum parameters
        string customHotineObliqueCylindricalMercator = string.Format(
            hotineObliqueMercatorDefinition, agsSoapBoundingEnvelope.YMin, 
            agsSoapBoundingEnvelope.YMax, agsSoapBoundingEnvelope.XMin, 
            agsSoapBoundingEnvelope.XMax, centerLatitude);

        // Create the spatial reference
        ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsSoapBufferSpatialReference =
            geometryServerProxy.FindSRByWKT(customHotineObliqueCylindricalMercator, null, true, true);

        return agsSoapBufferSpatialReference;
    }
   
    /// <summary>
    /// Creates an ArcGIS Server simple fill symbol with a solid outline and other parameters as
    /// specified
    /// </summary>
    /// <param name="fillColor">Fill color of the symbol</param>
    /// <param name="fillStyle">Fill style of the symbol</param>
    /// <param name="outlineColor">Outline color of the symbol</param>
    /// <param name="outlineWidth">Outline width of the symbol</param>
    public static ESRI.ArcGIS.ADF.ArcGISServer.SimpleFillSymbol CreateSimpleFillSymbol(
        System.Drawing.Color fillColor, ESRI.ArcGIS.ADF.ArcGISServer.esriSimpleFillStyle fillStyle,
        System.Drawing.Color outlineColor, int outlineWidth)
    {
        // Create an ArcGIS Server color object for the fill symbol based on the passed-in color
        ESRI.ArcGIS.ADF.ArcGISServer.RgbColor agsSoapFillColor =
            new ESRI.ArcGIS.ADF.ArcGISServer.RgbColor();
        agsSoapFillColor.Red = fillColor.R;
        agsSoapFillColor.Green = fillColor.G;
        agsSoapFillColor.Blue = fillColor.B;

        // Create a simple fill symbol with the color and passed-in fill style
        ESRI.ArcGIS.ADF.ArcGISServer.SimpleFillSymbol agsSoapSimpleFillSymbol =
            new ESRI.ArcGIS.ADF.ArcGISServer.SimpleFillSymbol();
        agsSoapSimpleFillSymbol.Style = fillStyle;
        agsSoapSimpleFillSymbol.Color = agsSoapFillColor;

        // Create an ArcGIS Server color object for the outline
        ESRI.ArcGIS.ADF.ArcGISServer.RgbColor agsSoapOutlineColor =
            new ESRI.ArcGIS.ADF.ArcGISServer.RgbColor();
        agsSoapOutlineColor.Red = outlineColor.R;
        agsSoapOutlineColor.Green = outlineColor.G;
        agsSoapOutlineColor.Blue = outlineColor.B;

        // Create a simple line symbol with the color and passed-in width
        ESRI.ArcGIS.ADF.ArcGISServer.SimpleLineSymbol agsSoapBufferSimpleLineSymbol =
            new ESRI.ArcGIS.ADF.ArcGISServer.SimpleLineSymbol();
        agsSoapBufferSimpleLineSymbol.Color = agsSoapOutlineColor;
        agsSoapBufferSimpleLineSymbol.Style = ESRI.ArcGIS.ADF.ArcGISServer.esriSimpleLineStyle.esriSLSSolid;
        agsSoapBufferSimpleLineSymbol.Width = outlineWidth;

        // Apply the outline to the fill symbol
        agsSoapSimpleFillSymbol.Outline = agsSoapBufferSimpleLineSymbol;

        return agsSoapSimpleFillSymbol;
    }

    /// <summary>
    /// Selects features that intersect the passed-in geometry.
    /// </summary>
    /// <param name="agsMapFunctionality">The map functionality of the resource containing the layer to 
    /// select features from</param>
    /// <param name="agsSoapIntersectGeometry">The geometry used for selecting features</param>
    /// <param name="targetLayerName">The name of the layer to select features from</param>
    /// <param name="selectionColor">The color to use in displaying the selected features</param>
    public static void SelectIntersectingFeatures(
         ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapFunctionality agsMapFunctionality,
        ESRI.ArcGIS.ADF.ArcGISServer.Geometry agsSoapIntersectGeometry, string targetLayerName,
        System.Drawing.Color selectionColor)
    {
        // Retrieve the index of the target layer
        string[] layerIDs = null;
        string[] layerNames = null;
        agsMapFunctionality.GetLayers(out layerIDs, out layerNames);

        int targetLayerIndex = 0;
        for (int i = 0; i < layerNames.Length; i++)
        {
            if (layerNames[i] == targetLayerName)
            {
                targetLayerIndex = i;
                break;
            }
        }

        // Get a reference to the ArcGIS Server resource underlying the map functionality
        ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.MapResourceBase mapResourceBase =
            agsMapFunctionality.MapResource;

        // Get MapLayerInfo object for the selection layer and use it to determine the name of the layer's
        // geometry field.
        ESRI.ArcGIS.ADF.ArcGISServer.MapLayerInfo[] agsSoapMapLayerInfoArray =
            mapResourceBase.MapServerInfo.MapLayerInfos;
        ESRI.ArcGIS.ADF.ArcGISServer.MapLayerInfo agsSoapActiveMapLayerInfo =
            agsSoapMapLayerInfoArray[targetLayerIndex];

        string geometryFieldName = null;
        foreach (ESRI.ArcGIS.ADF.ArcGISServer.Field agsSoapField in agsSoapActiveMapLayerInfo.Fields.FieldArray)
        {
            if (agsSoapField.Type == ESRI.ArcGIS.ADF.ArcGISServer.esriFieldType.esriFieldTypeGeometry)
            {
                geometryFieldName = agsSoapField.Name;
                break;
            }
        }

        // Initialize a spatial filter to use in selecting features that intersect the buffer
        ESRI.ArcGIS.ADF.ArcGISServer.SpatialFilter agsSoapSpatialFilter =
            new ESRI.ArcGIS.ADF.ArcGISServer.SpatialFilter();
        agsSoapSpatialFilter.FilterGeometry = agsSoapIntersectGeometry;

        agsSoapSpatialFilter.GeometryFieldName = geometryFieldName;
        agsSoapSpatialFilter.SpatialRel =
            ESRI.ArcGIS.ADF.ArcGISServer.esriSpatialRelEnum.esriSpatialRelIntersects;

        // Retrieve the layer's LayerDescription to use in specifying the layer ID for the query operation
        ESRI.ArcGIS.ADF.ArcGISServer.MapDescription agsSoapMapDescription = agsMapFunctionality.MapDescription;
        ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription[] agsSoapLayerDescriptionArray =
            agsSoapMapDescription.LayerDescriptions;
        ESRI.ArcGIS.ADF.ArcGISServer.LayerDescription agsSoapActiveLayerDescription =
            agsSoapLayerDescriptionArray[targetLayerIndex];

        // Create an ArcGIS Server color object from the passed-in color
        ESRI.ArcGIS.ADF.ArcGISServer.RgbColor selectionAgsSoapRgbColor =
            new ESRI.ArcGIS.ADF.ArcGISServer.RgbColor();
        selectionAgsSoapRgbColor.Red = selectionColor.R;
        selectionAgsSoapRgbColor.Green = selectionColor.G;
        selectionAgsSoapRgbColor.Blue = selectionColor.B;

        // Set the layer's selection color to the passed-in color
        agsSoapActiveLayerDescription.SelectionColor = selectionAgsSoapRgbColor;

        // Execute a query to retrieve the IDs of features that intersect the buffer
        ESRI.ArcGIS.ADF.ArcGISServer.MapServerProxy agsSoapMapServerProxy =
            mapResourceBase.MapServerProxy;
        ESRI.ArcGIS.ADF.ArcGISServer.FIDSet selectionAgsSoapFIDSet =
            agsSoapMapServerProxy.QueryFeatureIDs(agsSoapMapDescription.Name,
            agsSoapActiveLayerDescription.LayerID, agsSoapSpatialFilter);

        // Set the selection layer's selected features to those intersecting the buffer
        agsSoapActiveLayerDescription.SelectionFeatures = selectionAgsSoapFIDSet.FIDArray;
    }

    /// <summary>
    /// Constructs a callback result that will display a javascript alert with an error message 
    /// based on the passed-in exception
    /// </summary>
    /// <param name="exception">The exception from which the error message will be derived</param>
    /// <returns></returns>
    public static ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult CreateErrorCallbackResult(
        System.Exception exception)
    {
        // Create a callback result to display an error message
        string jsAlertErrorMessage = GetJavaScriptErrorString(exception);
        ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult alertCallbackResult =
            ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsAlertErrorMessage);
        return alertCallbackResult;
    }

    /// <summary>
    /// Constructs the syntax to display a javascript alert with an error message based on the 
    /// passed-in exception
    /// </summary>
    /// <param name="exception">The exception from which the error message will be derived</param>
    /// <returns></returns>
    public static string GetJavaScriptErrorString(System.Exception exception)
    {
        // Get the website's configuration file
        System.Configuration.Configuration webConfig =
        System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(
            System.Web.HttpContext.Current.Request.ApplicationPath);

        // Get the "compilation" section of the config file
        System.Web.Configuration.CompilationSection compilationSection =
            webConfig.GetSection("system.web/compilation") as
            System.Web.Configuration.CompilationSection;

        // If the config file's compilation section specifies debug mode, include 
        // stack trace information in the error message.  Otherwise, just return 
        // the exception message.
        string errorMessage = null;
        if ((compilationSection != null) && (compilationSection.Debug))
        {
            string stackTrace = exception.StackTrace.Replace("\\", "\\\\");
            stackTrace = stackTrace.Replace("\n", "\\n");
            stackTrace = stackTrace.Replace("\r", "\\r");
            stackTrace = stackTrace.Replace("'", "\\'");
            errorMessage = exception.Message.Replace("\\", "\\\\");
            errorMessage = errorMessage.Replace("\n", "\\n");
            errorMessage = errorMessage.Replace("\r", "\\r");
            errorMessage = errorMessage.Replace("'", "\\'");

            errorMessage = errorMessage + "\\n\\n" + stackTrace.Trim();
        }
        else
            errorMessage = exception.Message;

        // Create a callback result to display an error message
        string jsAlertException = "alert('" + errorMessage + "')";
        return jsAlertException;
    }
}