Routing
- 7 minutes to read
The Map control supports Microsoft’s Azure Route service. This service provides the most optimal route, either from major roads in four directions, or calculated between two or more locations on a map.
AzureRouteDataProvider provide the Map control’s routing functionality. The sections below explain how to use the AzureRouteDataProvider
in the map control.
Important
On May 21, 2024, Microsoft announced that Bing Maps for Enterprise and its API will be discontinued. Azure Maps will be a single unified enterprise mapping platform available from Microsoft.
To obtain and display map data from Azure Maps, we implemented the following providers:
- AzureMapDataProvider
- AzureSearchDataProvider
- AzureRouteDataProvider
- AzureGeocodeDataProvider
- AzureTrafficIncidentDataProvider
- AzureRouteIsochroneDataProvider
For information on how to migrate your app from Bing Maps to Azure Maps, see the following help topic: DevExpress Map Control for WPF: Migrate from Bing Maps to Azure Maps.
If you already have a Bing Maps for Enterprise license, you can keep using the current API. You must transition to the new API by June 30, 2025 (for free/basic licenses) or June 30, 2028 (for enterprise licenses). New licenses will no longer be available after June 30, 2025. Bing Maps will not work with our map controls without a license after that date.
Enable Routing
Do the following to enable routing in the Map control:
Create an information layer and add it to the map.
The information provides vector elements that represent GIS data obtained from the Search service in the Map control. See Layers for more information.
- Create a
AzureRouteDataProvider
instance and assign it to the InformationLayer.DataProvider property. - Specify the Azure key using the AzureKey property.
The code snippet below shows how to do this.
<dxm:InformationLayer EnableHighlighting="False"
>
<dxm:InformationLayer.DataProvider>
<dxm:AzureRouteDataProvider x:Name="routeProvider"
AzureKey="{Binding Source={StaticResource YourAzureKey}}"
LayerItemsGenerating="routeProvider_LayerItemsGenerating"/>
</dxm:InformationLayer.DataProvider>
</dxm:InformationLayer>
There are two ways to use the map routing feature in your application, depending on your task:
- Calculate a route between two or more locations on a map;
- Get a route from major roads to the specified destination.
The sections below describe each approach.
Calculate a Route between Locations
When the Map control is connected to the Azure Route service (see the section above for details), you can calculate a route between two or more locations on a map. To accomplish this, call the AzureRouteDataProvider.CalculateRoute method and pass the list of locations (waypoints) as its argument, as shown below.
public MainWindow() {
InitializeComponent();
// Create three waypoints and add them to a route waypoints list.
List<RouteWaypoint> waypoints = new List<RouteWaypoint>();
waypoints.Add(new RouteWaypoint("New York", new GeoPoint(41.145556, -73.995)));
waypoints.Add(new RouteWaypoint("Oklahoma", new GeoPoint(36.131389, -95.937222)));
waypoints.Add(new RouteWaypoint("Las Vegas", new GeoPoint(36.175, -115.136389)));
routeProvider.CalculateRoute(waypoints);
}
private void routeProvider_LayerItemsGenerating(object sender, LayerItemsGeneratingEventArgs args) {
char letter = 'A';
foreach (MapItem item in args.Items) {
MapPushpin pushpin = item as MapPushpin;
if (pushpin != null)
pushpin.Text = letter++.ToString();
}
}
The image below shows the calculated route between the specified locations on the map.
Routing Result
The RouteCalculationResult
object that is provided by the AzureRouteDataProvider.RouteCalculated event handler arguments’ AzureRouteCalculatedEventArgs.CalculationResult stores the route calculation results from the Azure Route service.
For instance, you can access a route path between locations calculated in the Calculate a Route between Locations section of this document.
To accomplish this, handle the AzureRouteDataProvider.RouteCalculated event.
private void routeDataProvider_RouteCalculated(object sender, BingRouteCalculatedEventArgs e) {
RouteCalculationResult result = e.CalculationResult;
StringBuilder resultList = new StringBuilder();
resultList.Append(String.Format("Status: {0}\n", result.ResultCode));
resultList.Append(String.Format("Fault reason: {0}\n", result.FaultReason));
resultList.Append(ProcessIntermediatePoints(result.IntermediatePoints));
resultList.Append(ProcessRouteResults(result.RouteResults));
tbResults.Text = resultList.ToString();
}
string ProcessIntermediatePoints(List<RouteWaypoint> points) {
if (points == null) return "";
StringBuilder sb = new StringBuilder("Intermediate Points:\n");
sb.Append(String.Format("_________________________\n"));
for (int i = 0; i < points.Count; ++i)
sb.Append(String.Format(
"Intermediate point {0}: {1} ({2})\n",
i+1,
points[i].Description,
points[i].Location
));
return sb.ToString();
}
string ProcessRouteResults(List<BingRouteResult> results) {
if (results == null) return "";
StringBuilder sb = new StringBuilder("RouteResults:\n");
for (int i = 0; i < results.Count; i++) {
sb.Append(String.Format("_________________________\n"));
sb.Append(String.Format("Path {0}:\n", i+1));
sb.Append(String.Format("Distance: {0}\n", results[i].Distance));
sb.Append(String.Format("Time: {0}\n", results[i].Time));
sb.Append(ProcessLegs(results[i].Legs));
}
return sb.ToString();
}
string ProcessLegs(List<BingRouteLeg> legs) {
if (legs == null) return "";
StringBuilder sb = new StringBuilder("Legs:\n");
for (int i = 0; i < legs.Count; i++) {
sb.Append(String.Format("\tLeg {0}:\n", i+1));
sb.Append(String.Format("\tStart: {0}\n", legs[i].StartPoint));
sb.Append(String.Format("\tEnd: {0}\n", legs[i].EndPoint));
sb.Append(String.Format("\tDistance: {0}\n", legs[i].Distance));
sb.Append(String.Format("\tTime: {0}\n", legs[i].Time));
sb.Append(ProcessItinerary(legs[i].Itinerary));
}
return sb.ToString();
}
string ProcessItinerary(List<BingItineraryItem> items) {
if (items == null) return "";
StringBuilder sb = new StringBuilder("\tInternary Items:\n");
for (int i = 0; i < items.Count; i++) {
sb.Append(String.Format("\t\tItinerary {0}:\n", i+1));
sb.Append(String.Format("\t\tManeuver: {0}\n", items[i].Maneuver));
sb.Append(String.Format("\t\tLocation: {0}\n", items[i].Location));
sb.Append(String.Format("\t\tInstructions: {0}\n", items[i].ManeuverInstruction));
sb.Append(ProcessWarnings(items[i].Warnings));
}
return sb.ToString();
}
string ProcessWarnings(List<BingItineraryItemWarning> warnings) {
if (warnings == null) return "";
StringBuilder sb = new StringBuilder("\t\tWarnings:\n");
for (int i = 0; i < warnings.Count; i++) {
sb.Append(String.Format("\t\t\tWarning {0}:\n", i + 1));
sb.Append(String.Format("\t\t\tType: {0}\n", warnings[i].Type));
sb.Append(String.Format("\t\t\tText: {0}\n", warnings[i].Text));
}
return sb.ToString();
}
The result is shown in the image below.