ArcGIS_Routing_CSharp\App_Code\NetworkAnalystUtility.cs
// Copyright 2011 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; } } }