Route task

The Route task allows you to easily implement routing in your ArcGIS API for WPF applications. With the Route task, you can retrieve routes and directions between a set of input features.

Since the Route task is built on the advanced capabilities of ArcGIS Server's network analysis services, it offers many options and can take many parameters into account when solving a route. For instance, routes can be solved such that the stops are visited in the optimal order (as opposed to the order defined); barriers can be defined that must be routed around; and impedance attributes (for example, speed limit, traffic, pipe diameter, and so on) can be taken into account.

To enable routing, use the RouteTask class in the ArcGIS API for WPF. This class requires that its Url property reference the REST endpoint of a network analysis service. To create such a service, you need to create a network dataset using Network Analyst, then publish that dataset to ArcGIS Server. Alternatively, to calculate driving routes in the U.S., Canada, or Europe, you can use ArcGIS Online's routing services. These services can be used without charge up to 5,000 times per year for non-commercial purposes, or by subscription for commercial purposes or for more than 5,000 annual uses.

As with all the tasks, initialize the Route task either in a XAML resource dictionary or .NET code (that is, code-behind), then execute and process the task's results in code. The Route task does not contain visual components, so it cannot be interacted with via XAML.

This remainder of this topic focuses on manipulating the task in code. The code in this topic is written in C#.

TipTip:

You'll typically define an interface for the task's input and output primarily in XAML. For instance, you may use a Map and a Button that initializes a Draw object to accept input points, and use a GraphicsLayer and StackPanel to display the output route and directions. For examples of implementing a Route task's input and output interfaces, see the routing samples in the Interactive SDK. For step-by-step walkthroughs of implementing similar functionality, see Query task, Find task, and Identify 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 routing layer's REST endpoint to the constructor. To find the URL, you can use the ArcGIS Services Directory. See Discovering Services for more information. This example uses the Route layer of the ESRI_Route_NA service.

NoteNote:

The code sections in this topic require a reference to the ESRI.ArcGIS.Client assembly, and a using statement for the ESRI.ArcGIS.Client and ESRI.ArcGIS.Client.Tasks namespaces.

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 need to specify the Stops parameter, as this determines the locations between which a route is calculated. Stops can be defined as a FeatureSet, GraphicsLayer, or any other type that implements IEnumerable<Graphic>.

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 the following:

The following code example shows the initialization of a RouteParameters object with stops and barriers defined by GraphicsLayers. Properties are specified so 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. The ReturnRoute property is not explicitly specified because it has a default value of true.

This 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've initialized a RouteParameters object with the desired input, calculating the route simply requires a call to the SolveAsync method, as shown in the following code:

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 following code demonstrates declaring such a handler using a lambda expression.

TipTip:

Attach the handler before initiating the route operation to ensure that it's 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 following code builds on the SolveCompleted handler previously declared to retrieve the route, apply a symbol to it, and add it to a graphics layer. This 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 in 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 following code steps through the directions, retrieving and formatting the description, distance, and travel time of each. To keep the example simple, the formatting used is very basic and null checks are omitted. See the Driving Directions sample in the Interactive SDK 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 formatted string for each step of the directions, you need to display this information in your application. WPF provides many ways of doing this. The approach shown in the following code is to create a TextBlock containing the text and add it to a StackPanel. This 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/23/2012