How to create a Route task

Initializing a Route task

To initialize a Route task, declare a RouteTask object, instantiate it with the new keyword, and pass the URL of a network analysis service REST endpoint to the constructor. To find the URL, you can use the ArcGIS Services Directory. See the Discovering services topic for more information. This example uses the Route layer of the ESRI_Route_NA service. The runtime local server does not support local network analysis services and therefore the RouteTask class can only be used with online services. To perform routing operations against local data for example to support offline or disconnected usage, you can use local geoprocessing services based on geoprocessing packages which contain Network Analyst tools.

RouteTask routeTask = new RouteTask("http://tasks.arcgisonline.com/ArcGIS/rest/services/" + 
           "NetworkAnalysis/ESRI_Route_NA/NAServer/Route");

Specifying a Route task's input parameters

The Route task's execution method, SolveAsync, takes a RouteParameters object as input. At a minimum, you will need to specify the Stops parameter, as this determines the locations between which a route will be calculated. Stops may be defined as a FeatureSet, GraphicsLayer, or any other type that implements IEnumerable<Graphic>. Often, you may also want to define the Barriers parameter, which defines locations that must be routed around. This parameter is of the same type as Stops. Other commonly used boolean parameters include ReturnRoutes, which specifies whether route geometry is returned, ReturnDirections, which specifies whether directions are returned, and FindBestSequence, which determines whether to visit stops in the order specified (false) or that optimizes the route (true). When specifying optmized route calculation (FindBestSequence = true), you can exclude the first and last stops from being re-ordered by setting the PreserveFirstStop and PreserveLastStop properties to true.

Below is an example of initializing a RouteParameters object with stops and barriers defined by GraphicsLayers. Properties are specified such that the route will be optimized, the first and last stops will be preserved, and the SolveAsync operation will return both geometry and directions for the calculated route. Note that the ReturnRoute property is not explicitly specified because it has a default value of true. The snippet assumes that there is a Map control in the current scope named MyMap that contains GraphicsLayers with IDs of "MyStopsGraphicsLayer" and "MyBarriersGraphicsLayer."

GraphicsLayer stopsGraphicsLayer = MyMap.Layers["MyStopsGraphicsLayer"] as GraphicsLayer; 
GraphicsLayer barriersGraphicsLayer = MyMap.Layers["MyBarriersGraphicsLayer"] as GraphicsLayer; 
RouteParameters routeParameters = new RouteParameters() 
{ 
	Stops = stopsGraphicsLayer, 
	Barriers = barriersGraphicsLayer, 
	ReturnDirections = true, 
	FindBestSequence = true, 
	PreserveFirstStop = true, 
	PreserveLastStop = true 
};

Executing a routing operation and handling results

Once you have initialized a RouteParameters object with the desired input, calculating the route simply requires a call to the SolveAsync method, as shown below:

routeTask.SolveAsync(routeParameters);

Of course, executing a routing operation alone is not very useful; the whole point of the operation is to get its results. The Route task passes a route operation's results to the SolveCompleted event, which fires whenever a route operation is finished. To get those results, you need to implement a handler for this event. The code below demonstrates declaring such a handler using a lambda expression. Note that we attach the handler before initiating the route operation. This is to ensure that the handler is attached before the operation completes.

routeTask.SolveCompleted += (source, args) =>{};
routeTask.SolveAsync(routeParameters);

On completion of a route operation, the Route task passes a RouteEventArgs object to SolveCompleted. This object contains the operation's results in the RouteResults property. The route's geometry is returned as a Graphic in the Route property of RouteResults. The code below builds on the SolveCompleted handler declared above to retrieve the route, apply a symbol to it, and add it to a graphics layer. The code assumes that a LineSymbol named RouteSymbol and a Map control named MyMap are available in the current scope.

routeTask.SolveCompleted += (source, args) =>
{
    // Get the route and apply a symbol to it
    RouteResult routeResult = args.RouteResults[0];
    routeResult.Route.Symbol = RouteSymbol;

    // Add the route to a graphics layer
    GraphicsLayer graphicsLayer = MyMap.Layers["MyRouteGraphicsLayer"] as GraphicsLayer;
    graphicsLayer.Graphics.Add(routeResult.Route);
};
routeTask.SolveAsync(routeParameters);

Directions are returned in the Directions property as a DirectionsFeatureSet. Each graphic contained within this feature set represents one step in the directions. The graphic's geometry is the segment of the route covered by the step, while the graphic's "text," "length," and "time" attributes store the step's description, distance, and estimated travel time. The code below steps through the directions, retrieving and formatting the description, distance, and travel time of each. Note that, to keep the example simple, the formatting used is very basic and null checks are omitted. Refer to the Driving Directions Online sample in the sample application for a more robust implementation.

routeTask.SolveCompleted += (source, args) =>
{
    // Get the route and apply a symbol to it
    RouteResult routeResult = args.RouteResults[0];
    routeResult.Route.Symbol = RouteSymbol;

    // Add the route to a graphics layer
    GraphicsLayer graphicsLayer = MyMap.Layers["MyRouteGraphicsLayer"] as GraphicsLayer;
    graphicsLayer.Graphics.Add(routeResult.Route);

    int i = 1;
    // Loop through each step of the directions
    foreach (Graphic graphic in routeResult.Directions)
    {
        // Get the current step's description and format as <number>. <description>
        // (e.g. "1. Turn right at...")
        System.Text.StringBuilder text = new System.Text.StringBuilder();
        text.AppendFormat("{0}. {1}", i, graphic.Attributes["text"]);
        if (i > 1 && i < routeResult.Directions.Features.Count)
        {
            // Append distance and duration
            decimal distance = (decimal)graphic.Attributes["length"];
            text.AppendFormat("   {0} miles", distance.ToString("#0.00"));
            decimal time = (decimal)graphic.Attributes["time"];
            text.AppendFormat(", {0} minutes", time.ToString("#0"));
        }
        i++;
    }
};
routeTask.SolveAsync(routeParameters);

Once you have a nicely formatted string for each step of directions, all that's left is to display this somewhere in your application. WPF provides many ways of doing this. One simple approach, shown below, is to create a TextBlock containing the text and add it to a StackPanel. Note the code assumes that a StackPanel named DirectionsStackPanel is available in the current scope.

routeTask.SolveCompleted += (source, args) =>
{
    // Get the route and apply a symbol to it
    RouteResult routeResult = args.RouteResults[0];
    routeResult.Route.Symbol = RouteSymbol;

    // Add the route to a graphics layer
    GraphicsLayer graphicsLayer = MyMap.Layers["MyRouteGraphicsLayer"] as GraphicsLayer;
    graphicsLayer.Graphics.Add(routeResult.Route);

    int i = 1;
    // Loop through each step of the directions
    foreach (Graphic graphic in routeResult.Directions)
    {
        // Get the current step's description and format as <number>. <description>
        // (e.g. "1. Turn right at...")
        System.Text.StringBuilder text = new System.Text.StringBuilder();
        text.AppendFormat("{0}. {1}", i, graphic.Attributes["text"]);
        if (i > 1 && i < routeResult.Directions.Features.Count)
        {
            // Append distance and duration
            decimal distance = (decimal)graphic.Attributes["length"];
            text.AppendFormat("   {0} miles", distance.ToString("#0.00"));
            decimal time = (decimal)graphic.Attributes["time"];
            text.AppendFormat(", {0} minutes", time.ToString("#0"));
        }

        // Add the current step to the directions stack panel
        DirectionsStackPanel.Children.Add(new TextBlock() { 
            Text = text.ToString(), 
            Margin = new Thickness(4) 
        });
        i++;
    }
};
routeTask.SolveAsync(routeParameters);

1/27/2015