ArcGIS Routing with NAServer
ArcGIS_NAServer_Routing_CSharp\Default.aspx.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 System.Data;
using System.Configuration;
using System.Collections;
using System.Collections.Specialized;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using ESRI.ArcGIS.ADF.ArcGISServer;
using ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer;

public partial class _Default : System.Web.UI.Page
{
  // Member Variables
  private const string SERVER_NAME = "localhost";
  private const string ROUTE_SERVICE_NAME = "SanFrancisco";
  private const string LOCATOR_SERVICE_NAME = "SanFranciscoLocator";

  protected void Page_Load(object sender, EventArgs e)
  {
    Page.Title = "Simple Routing Application";
  }
  protected void btnGetDirections_Click(object sender, EventArgs e)
  {
    Solve();
  }

  /// <summary>
  /// This function
  ///     - sets the server and solver parameters
  ///     - populates the stops NALocations
  ///     - gets and displays the server results (map, directions)
  /// </summary>
  private void Solve()
  {

    try
    {
      // Get the NAServer
      using (NAServerProxy naServer = NAServerProxy.Create(SERVER_NAME, ROUTE_SERVICE_NAME, null))
      {

        if (naServer == null)
          throw (new System.Exception("Could not find the web service."));

        // Get the NAServerSolverParams
        string[] naLayers = naServer.GetNALayerNames(esriNAServerLayerType.esriNAServerRouteLayer);

        NAServerSolverParams solverParams = naServer.GetSolverParameters(naLayers[0]) as NAServerSolverParams;

        // Set the NAServerRouteParams
        NAServerRouteParams routeParams = solverParams as NAServerRouteParams;
        DateTime time;
        routeParams.UseStartTime = DateTime.TryParse(txtStartTime.Value, out time);
        if (routeParams.UseStartTime)
          routeParams.StartTime = time;

        routeParams.ReturnMap = false;
        routeParams.ReturnRouteGeometries = true;
        routeParams.ReturnStops = true;
        routeParams.ReturnDirections = chkShowDirections.Checked;
        routeParams.DirectionsTimeAttributeName = "Minutes";
        routeParams.ImpedanceAttributeName = "Minutes";

        // Set Output Spatial Reference
        MapFunctionality mapFunctionality = (MapFunctionality)Map1.GetFunctionality(0);
        routeParams.OutputSpatialReference = mapFunctionality.MapDescription.SpatialReference;


        // Geocode two addresses and create the stop network locations
        LoadLocations(solverParams);

        // Solve the Route
        NAServerSolverResults solverResults;
        solverResults = naServer.Solve(solverParams);

        // Display results
        OutputResults(solverResults);
      }

    }
    catch (Exception exception)
    {
      pnlDirectionSummary.Visible = false;
      lblDirections.Visible = false;
      lblTotalDistance.Visible = false;
      dataGridDirections.Visible = false;
      lblError.Text = "An error has occurred Mesage = " + exception.Message;
    }
  }

  /// <summary>
  /// This function shows how to populate stop locations using an array of PropertySets
  /// </summary>
  private void LoadLocations(NAServerSolverParams solverParams)
  {
    // Geocode Addresses
    PropertySet[] propSets = new PropertySet[2];
    propSets[0] = GeocodeAddress(txtFromStreet.Value, txtFromCity.Value, txtFromState.Value, txtFromZip.Value);
    propSets[1] = GeocodeAddress(txtToStreet.Value, txtToCity.Value, txtToState.Value, txtToZip.Value);

    NAServerPropertySets StopsPropSets = new NAServerPropertySets();
    StopsPropSets.PropertySets = propSets;

    NAServerRouteParams routeParams = solverParams as NAServerRouteParams;
    routeParams.Stops = StopsPropSets;
  }

  /// <summary>
  /// Geocode an address based on the street name, city, state, and zip code
  /// Throws and exception and returns null if the address was unmatched.
  /// </summary> 
  private PropertySet GeocodeAddress(string streetAddress, string city, string state, string zipCode)
  {
    PropertySet propSet = null;

    try
    {
      using (GeocodeServerProxy gc = GeocodeServerProxy.Create(SERVER_NAME, LOCATOR_SERVICE_NAME, null))
      {
        PropertySet addressProperties = new PropertySet();

        Fields addressFields;
        Field field;

        PropertySetProperty[] propSetProperty = new PropertySetProperty[4];
        addressFields = gc.GetAddressFields();
        for (int i = 0; i < addressFields.FieldArray.GetLength(0); i++)
        {
          field = addressFields.FieldArray[i];

          if (field.Name.Equals("STREET", StringComparison.OrdinalIgnoreCase))
            propSetProperty[0] = CreatePropertySetProperty(field.AliasName, streetAddress) as PropertySetProperty;

          if (field.Name.Equals("CITY", StringComparison.OrdinalIgnoreCase))
            propSetProperty[1] = CreatePropertySetProperty(field.AliasName, city) as PropertySetProperty;

          if (field.Name.Equals("STATE", StringComparison.OrdinalIgnoreCase))
            propSetProperty[2] = CreatePropertySetProperty(field.AliasName, state) as PropertySetProperty;

          if (field.Name.Equals("ZIP", StringComparison.OrdinalIgnoreCase) || field.Name.Equals("ZONE", StringComparison.OrdinalIgnoreCase))
            propSetProperty[3] = CreatePropertySetProperty(field.AliasName, zipCode) as PropertySetProperty;

        }

        addressProperties.PropertyArray = propSetProperty;

        // find the matching address  
        propSet = gc.GeocodeAddress(addressProperties, null);
      }
    }
    catch (Exception exception)
    {
      lblError.Text = "An error has occurred Mesage = " + exception.Message;
    }

    // Throw and error if the geocoded address is "Unmatched"
    if ((propSet != null) && (propSet.PropertyArray[1].Value.ToString() == "U"))
      throw (new System.Exception("Could not geocode [" + streetAddress + "]"));

    // Overwrite the "matched" property with the "Name" of the street
    propSet.PropertyArray[1].Key = "Name";
    propSet.PropertyArray[1].Value = streetAddress;

    return propSet;
  }

  /// <summary>
  /// CreatePropertySetProperty
  /// </summary> 
  private PropertySetProperty CreatePropertySetProperty(string key, object value)
  {
    PropertySetProperty propSetProperty = new PropertySetProperty();
    propSetProperty.Key = key;
    propSetProperty.Value = value;
    return propSetProperty;
  }

  /// <summary>
  /// Output Results map, Directions
  /// </summary>
  private void OutputResults(NAServerSolverResults solverResults)
  {
    string messagesSolverResults = "";

    // Output Solve messages
    GPMessages gpMessages = solverResults.SolveMessages;
    GPMessage[] arrGPMessage = gpMessages.GPMessages1;
    if (arrGPMessage != null)
    {
      for (int i = 0; i < arrGPMessage.GetLength(0); i++)
      {
        GPMessage gpMessage = arrGPMessage[i];
        messagesSolverResults += "\n" + gpMessage.MessageDesc;
      }
    }
    lblError.Text = messagesSolverResults;

    NAServerRouteResults RouteSolverResults = solverResults as NAServerRouteResults;
    // Display turn-by-turn directions
    if (chkShowDirections.Checked == true)
      OutputDirections(RouteSolverResults.Directions);
    else
    {
      // Or simply display the total impedance for the route
      lblTotalTime.Text = "Total Time: " + RouteSolverResults.TotalImpedances[0].ToString("F") + " mn";
      lblTotalDistance.Visible = false;
      pnlDirectionSummary.Visible = true;
      lblDirections.Visible = false;
      dataGridDirections.Visible = false;
    }

    // Add graphics from route and stops
    GraphicElement[] graphicElements = new GraphicElement[RouteSolverResults.RouteGeometries.Length + RouteSolverResults.Stops.Records.Length];
    AddRoutesToGraphicElements(RouteSolverResults.RouteGeometries, graphicElements);
    AddStopsToGraphicElements(RouteSolverResults.Stops, graphicElements);
    MapFunctionality mapFunctionality = (MapFunctionality)Map1.GetFunctionality(0);
    mapFunctionality.MapDescription.CustomGraphics = graphicElements;

    // Zoom to extent
    PolylineN polylineN = RouteSolverResults.RouteGeometries[0] as PolylineN;
    EnvelopeN envelopeN = polylineN.Extent as EnvelopeN;
    double width = envelopeN.XMax - envelopeN.XMin;
    double height = envelopeN.YMax - envelopeN.YMin;
    double fivePercent;
    if (width > height)
      fivePercent = width * .05;
    else
      fivePercent = height * .05;

    envelopeN.XMin = envelopeN.XMin - fivePercent;
    envelopeN.YMin = envelopeN.YMin - fivePercent;
    envelopeN.XMax = envelopeN.XMax + fivePercent;
    envelopeN.YMax = envelopeN.YMax + fivePercent;

    Map1.Extent = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.ToAdfEnvelope(envelopeN);

    Map1.Refresh();
  }

  // Add routes as blue lines
  public void AddRoutesToGraphicElements(Polyline[] polylines, GraphicElement[] graphicElements)
  {
    RgbColor rgb = new RgbColor();
    rgb.Red = 0;
    rgb.Green = 0;
    rgb.Blue = 255;
    rgb.AlphaValue = 32;

    SimpleLineSymbol sls = new SimpleLineSymbol();
    sls.Color = rgb;
    sls.Style = esriSimpleLineStyle.esriSLSSolid;
    sls.Width = 6;

    for (int i = 0; i < polylines.Length; i++)
    {
      LineElement le = new LineElement();
      le.Line = polylines[i];
      le.Symbol = sls;

      graphicElements[i] = le;
    }
  }

  // Add all stops as black circles
  public void AddStopsToGraphicElements(RecordSet stops, GraphicElement[] graphicElements)
  {
    Record[] stopRecords = stops.Records;
    int stopCount = stopRecords.Length;

    RgbColor rgb = new RgbColor();
    rgb.Red = 0;
    rgb.Green = 0;
    rgb.Blue = 0;
    rgb.AlphaValue = 255;

    SimpleMarkerSymbol sms = new SimpleMarkerSymbol();
    sms.Color = rgb;
    sms.Style = esriSimpleMarkerStyle.esriSMSCircle;
    sms.Size = 16;

    int iGraphicElement = graphicElements.Length - stopCount;
    for (int iStop = 0; iStop < stopCount; iGraphicElement++, iStop++)
    {
      MarkerElement me = new MarkerElement();
      me.Point = stopRecords[iStop].Values[1] as PointN;
      me.Symbol = sms;

      graphicElements[iGraphicElement] = me;
    }
  }

  /// <summary>
  /// Output Directions if a dataGrid control
  /// </summary> 
  private void OutputDirections(NAStreetDirections[] serverDirections)
  {
    if (serverDirections == null)
      return;

    // get Directions from the ith route
    NAStreetDirections directions;
    directions = serverDirections[0];

    // get Summary (Total Distance and Time)
    NAStreetDirection direction = directions.Summary;
    string totallength = null, totaltime = null;
    string[] SummaryStrings = direction.Strings;
    for (int k = SummaryStrings.GetLowerBound(0); k < SummaryStrings.GetUpperBound(0); k++)
    {
      if (direction.StringTypes[k] == esriDirectionsStringType.esriDSTLength)
        totallength = SummaryStrings[k];
      if (direction.StringTypes[k] == esriDirectionsStringType.esriDSTTime)
        totaltime = SummaryStrings[k];
    }

    // Display the direction in a DataGrid
    DataSet dataSet = new DataSet("dataSet");
    DataTable dataTable = new DataTable("Results");
    dataSet.Tables.Add(dataTable);

    DataColumn dataColumn = null;
    dataColumn = new DataColumn("Step ");
    dataTable.Columns.Add(dataColumn);
    dataColumn = new DataColumn("Directions ");
    dataTable.Columns.Add(dataColumn);
    dataColumn = new DataColumn("Estimated Arrival Time ");
    dataTable.Columns.Add(dataColumn);

    // Then add a node for each step-by-step directions
    DataRow newDataRow;
    NAStreetDirection[] StreetDirections = directions.Directions;
    for (int directionIndex = StreetDirections.GetLowerBound(0); directionIndex <= StreetDirections.GetUpperBound(0); directionIndex++)
    {
      NAStreetDirection streetDirection = StreetDirections[directionIndex];
      string[] StringStreetDirection = streetDirection.Strings;
      newDataRow = dataTable.NewRow();
      newDataRow[0] = System.Convert.ToString(directionIndex + 1);

      for (int stringIndex = StringStreetDirection.GetLowerBound(0); stringIndex <= StringStreetDirection.GetUpperBound(0); stringIndex++)
      {
        if (streetDirection.StringTypes[stringIndex] == esriDirectionsStringType.esriDSTGeneral ||
          streetDirection.StringTypes[stringIndex] == esriDirectionsStringType.esriDSTDepart ||
          streetDirection.StringTypes[stringIndex] == esriDirectionsStringType.esriDSTArrive)
          newDataRow[1] = StringStreetDirection[stringIndex];

        if (streetDirection.StringTypes[stringIndex] == esriDirectionsStringType.esriDSTEstimatedArrivalTime)
          newDataRow[2] = StringStreetDirection[stringIndex];
      }

      dataTable.Rows.Add(newDataRow);
    }

    dataGridDirections.DataSource = dataSet;
    dataGridDirections.DataBind();
    dataGridDirections.Visible = true;

    // Display Total Distance and Total Time
    lblTotalDistance.Text = "Total Distance: " + totallength;
    lblTotalTime.Text = "Total Time: " + totaltime;
    pnlDirectionSummary.Visible = true;
    lblDirections.Visible = true;
    lblTotalDistance.Visible = true;
  }
}