Network Analyst routing
ArcGIS_Routing_CSharp\App_Code\NetworkAnalystUtility.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.
// 



using System;
using ESRI.ArcGIS.Server;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.NetworkAnalyst;
using System.Collections;
using System.Data;
using System.Collections.Specialized;
using ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer;
using ESRI.ArcGIS.ADF.Web.Geometry;

namespace ESRI.ArcGIS.Server.Web.NetworkAnalyst
{
    /// <summary>
    /// Utility class for working with Network analyst
    /// </summary>
    public class NetworkAnalystUtility
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public NetworkAnalystUtility()
        {
        }

        /// <summary>
        /// Add network location
        /// </summary>
        /// <param name="naContext">Layer to add to</param>
        /// <param name="locationClassName">Type of location to add</param>
        /// <param name="point">Geometry of location</param>
        /// <param name="locationName">Name of location</param>
        /// <param name="tolerance">Tolerance to use</param>
        /// <returns>Whether the location was added</returns>
        public static bool AddLocation(INAContext naContext, string locationClassName, IPoint point, string locationName, double tolerance)
        {
            // Get NAClass
            INAClass naClass = naContext.NAClasses.get_ItemByName(locationClassName) as INAClass;
            IFeatureClass featureClass = naClass as IFeatureClass;
            IFields fields = featureClass.Fields;

            #region Find the network location using the locator

            INALocator naLocator = naContext.Locator;
            naLocator.SnapTolerance = tolerance;
            INALocation naLocation = null;
            IPoint outPoint = null;
            double distanceFromPoint = 0;
            naLocator.QueryLocationByPoint(point, ref naLocation, ref outPoint, ref distanceFromPoint);
            #endregion

            // Get total count of features in class (needed for Sequence)
            int count = featureClass.FeatureCount(null);

            // Create feature and set shape
            IFeature feature = featureClass.CreateFeature();
            ((IRowSubtypes)feature).InitDefaultValues();
            feature.Shape = point;

            #region Set field values for NALocation, Name, Sequence, Status

            // Set NALocation
            ((INALocationObject)feature).NALocation = naLocation;

            // Set name field
            if (locationName.Trim().Length > 0)
                feature.set_Value(fields.FindField("Name"), locationName);

            // If Stops, set Sequence field (required to go from 1 to N)
            int sequenceFieldIndex = fields.FindField("Sequence");
            if (sequenceFieldIndex >= 0)
                feature.set_Value(sequenceFieldIndex, count + 1);

            // Set status if not located
            if (naLocation.IsLocated == false)
                feature.set_Value(fields.FindField("Status"), esriNAObjectStatus.esriNAObjectStatusNotLocated);
            #endregion

            // Make sure to store the feature
            feature.Store();

            return naLocation.IsLocated;
        }

        /// <summary>
        /// Get directions
        /// </summary>
        /// <param name="naContext">The network analyst layer</param>
        /// <param name="locationClassName">The output class name, e.g. Routes</param>
        /// <param name="serverContext">The server context</param>
        /// <returns>Route results</returns>
        public static NetworkAnalystRouteResult GetDirections(INAContext naContext, string locationClassName, IServerContext serverContext)
        {
            #region Check if we have results
            INATraversalResultQuery result = naContext.Result as INATraversalResultQuery;
            if (result == null) return null;
            int resultCount = result.get_FeatureClass(esriNetworkElementType.esriNETEdge).FeatureCount(null);
            if (resultCount == 0) return null;
            #endregion

            INAClass naClass = naContext.NAClasses.get_ItemByName(locationClassName) as INAClass;

            #region Get Result Features
            IFeatureClass fClass = naClass as IFeatureClass;
            ISet resultSet;
            IQueryFilter filter;
            if (serverContext != null)
            {
                resultSet = serverContext.CreateObject("esriSystem.Set") as ISet;
                filter = serverContext.CreateObject("esriGeodatabase.QueryFilter") as IQueryFilter;
            }
            else
            {
                resultSet = new SetClass() as ISet;
                filter = new QueryFilterClass() as IQueryFilter;
            }

            ICursor cursor = fClass.Search(null, false) as ICursor;
            IRow row = cursor.NextRow();
            while (row != null)
            {
                resultSet.Add(row);
                row = cursor.NextRow();
            }
            #endregion

            #region Prepare Directions
            INamedSet namedSet = naContext.Agents;
            string agentName = namedSet.get_Name(0);
            INAStreetDirectionsAgent agent = namedSet.get_ItemByName(agentName) as INAStreetDirectionsAgent;
            ITrackCancel track;
            if (serverContext != null)
                track = serverContext.CreateObject("esriDisplay.CancelTracker") as ITrackCancel;
            else
                track = new ESRI.ArcGIS.Display.CancelTrackerClass() as ITrackCancel;
            INAAgent naAgent = agent as INAAgent;
            naAgent.OnResultUpdated();
            agent.Execute(resultSet, track);
            #endregion

            #region Get Directions

            ArrayList nasResultItems = new ArrayList();
            INAStreetDirectionsContainer container = agent.DirectionsContainer;
            int count = 0;
            string columnName, columnValue;
            DataRow dataRow;
            IEnvelope envelope;

            for (int i = 0; i < container.DirectionsCount; i++)
            {
                NetworkAnalystRouteResult resultItem = new NetworkAnalystRouteResult();
                INAStreetDirections streetDirections = container.get_Directions(i);
                INAStreetDirection streetDirection = streetDirections.Summary;
                esriDirectionsStringType type = esriDirectionsStringType.esriDSTGeneral;
                #region Get Summary
                StringDictionary summary = new StringDictionary();
                summary["Route"] = streetDirections.RouteName;
                count = streetDirection.StringCount;
                for (int k = 0; k < count; k++)
                {
                    type = streetDirection.get_StringType(k);
                    columnName = GetDirectionsStringType(type);
                    columnValue = summary[columnName] as string;
                    if (columnValue != null && columnValue.Length > 0)
                        summary[columnName] = columnValue + "<br>" + streetDirection.get_String(k);
                    else
                        summary[columnName] = streetDirection.get_String(k);
                }
                envelope = streetDirection.Envelope;

                // Make sure we create a DefinitionSpatialReferenceInfo object in the case 
                // where the spatial reference has a factory code.  Use it for the envelope's
                // CoordinateSystem.  Otherwise, the envelope's CoordinateSystem will be an 
                // IDSpatialReferenceInfo object which may cause the map to not draw correctly.
                ISpatialReference spatialReference = envelope.SpatialReference;
                ESRI.ArcGIS.ADF.Web.SpatialReference.SpatialReferenceInfo spatialReferenceInfo = null;
                if (spatialReference != null)
                {
                    if (spatialReference.FactoryCode != 0)
                    {
                        IESRISpatialReferenceGEN esriSR = (IESRISpatialReferenceGEN)envelope.SpatialReference;
                        string definition;
                        int bWrote;
                        esriSR.ExportToESRISpatialReference(out definition, out bWrote);
                        spatialReferenceInfo = new ESRI.ArcGIS.ADF.Web.SpatialReference.DefinitionSpatialReferenceInfo(definition);
                    }
                }

                envelope.Expand(1.25, 1.25, true);
                ESRI.ArcGIS.ADF.Web.Geometry.Envelope adf_envelope = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Local.Converter.FromIEnvelope(envelope);
                if (spatialReferenceInfo != null)
                    adf_envelope.SpatialReference.CoordinateSystem = spatialReferenceInfo;
                resultItem.RouteExtent = adf_envelope;
                resultItem.RouteID = streetDirections.RouteID;
                #endregion

                #region Get Directions
                DataTable directions = new DataTable("Directions");
                directions.Columns.Add("Step", typeof(string));
                directions.Columns.Add("Directions", typeof(string));
                directions.Columns.Add("Length", typeof(string));
                directions.Columns.Add("Summary", typeof(string));
                directions.Columns.Add("Time", typeof(string));
                directions.Columns.Add("Type", typeof(string));
                directions.Columns.Add("Cumulative Length", typeof(string));
                directions.Columns.Add("Street Name", typeof(string));
                directions.Columns.Add("Cross Street", typeof(string));


                resultItem.StepExtents = new ESRI.ArcGIS.ADF.Web.Geometry.Envelope[streetDirections.DirectionCount];

                for (int j = 0; j < streetDirections.DirectionCount; j++)
                {
                    streetDirection = streetDirections.get_Direction(j);
                    count = streetDirection.StringCount;
                    dataRow = directions.NewRow();
                    directions.Rows.Add(dataRow);

                    for (int k = 0; k < count; k++)
                    {
                        #region Get type of direction; column name
                        type = streetDirection.get_StringType(k);
                        columnName = GetDirectionsStringType(type);

                        if (type == esriDirectionsStringType.esriDSTArrive ||
                            type == esriDirectionsStringType.esriDSTDepart)
                        {
                            directions.Rows[j]["Type"] = columnName;
                            columnName = "Directions";
                        }
                        else if (type == esriDirectionsStringType.esriDSTGeneral)
                        {
                            directions.Rows[j]["Type"] = "General";
                        }
                        #endregion

                        #region Populate column
                        directions.Rows[j]["Step"] = j + 1;
                        columnValue = directions.Rows[j][columnName] as string;
                        if (columnValue != null && columnValue.Length > 0)
                            directions.Rows[j][columnName] = columnValue + "<br>" + streetDirection.get_String(k);
                        else
                            directions.Rows[j][columnName] = streetDirection.get_String(k);
                        #endregion
                    }
                    envelope = streetDirection.Envelope;
                    envelope.Expand(1.25, 1.25, true);
                    adf_envelope = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Local.Converter.FromIEnvelope(envelope);
                    if (spatialReferenceInfo != null)
                        adf_envelope.SpatialReference.CoordinateSystem = spatialReferenceInfo;
                    resultItem.StepExtents[j] = adf_envelope;
                }
                #endregion

                resultItem.Directions = directions;
                resultItem.Summary = summary;
                nasResultItems.Add(resultItem);
            }
            #endregion

            // Assume only 1 route in the analysis problem so just return the directions for array[0]
            return nasResultItems[0] as NetworkAnalystRouteResult;
        }


        /// <summary>
        /// Gets the column name for directions string type
        /// </summary>
        /// <param name="type">The type to get string for.</param>
        /// <returns>The string for direction type.</returns>
        public static string GetDirectionsStringType(esriDirectionsStringType type)
        {
            switch (type)
            {
                case esriDirectionsStringType.esriDSTArrive:
                    return "Arrive";
                case esriDirectionsStringType.esriDSTDepart:
                    return "Depart";
                case esriDirectionsStringType.esriDSTGeneral:
                    return "Directions";
                case esriDirectionsStringType.esriDSTLength:
                    return "Length";
                case esriDirectionsStringType.esriDSTSummary:
                    return "Summary";
                case esriDirectionsStringType.esriDSTTime:
                    return "Time";
                case esriDirectionsStringType.esriDSTCumulativeLength:
                    return "Cumulative Length";
                case esriDirectionsStringType.esriDSTEstimatedArrivalTime:
                    return "ETA";
                case esriDirectionsStringType.esriDSTServiceTime:
                    return "Service Time";
                case esriDirectionsStringType.esriDSTTimeWindow:
                    return "Time Window";
                case esriDirectionsStringType.esriDSTViolationTime:
                    return "Violation Time";
                case esriDirectionsStringType.esriDSTWaitTime:
                    return "Wait Time";
                case esriDirectionsStringType.esriDSTStreetName:
                    return "Street Name";
                case esriDirectionsStringType.esriDSTCrossStreet:
                    return "Cross Street";
                default:
                    return "Default";
            }
        }

        /// <summary>
        /// Gets the layer using the layer id
        /// </summary>
        /// <param name="map">The map to which the layer belongs</param>
        /// <param name="layerID">The id of the layer</param>
        /// <returns>The layer at the id.</returns>
        public static ILayer LayerFromLayerID(MapResourceLocal gisresource, int layerID)
        {
            IServerContext mapContext = gisresource.ServerContextInfo.ServerContext;
            IMapServerObjects mso = (IMapServerObjects)gisresource.MapServer;
            IMap map = mso.get_Map(gisresource.DataFrame);

            UID uidINALayer = (UID)mapContext.CreateObject("esriSystem.UID");
            uidINALayer.Value = "{667B776B-5905-4450-9C94-18B214ECE8FB}";

            IEnumLayer elayers = map.get_Layers(uidINALayer, true);
            ILayer elayer = elayers.Next();
            int i = 0;
            while (elayer != null)
            {
                if (i == layerID)
                    return elayer;
                ++i;
                elayer = elayers.Next();
            }
            return null;
        }
    }
}