Calculating a route with rectangular geographic restrictions

This use-case describes how to calculate a routeA route corresponds to a path of a vehicle through the underlying transport network. The main attributes of a route are the distance and the time that the vehicle travels along the path. with rectangular geographic restrictions.

Benefits

Prerequisites

Please ensure following prerequisites are fulfilled before you start with the use case:

Programming Guide

In this example we provide our waypointsA waypoint is a geographic location used to specify start, destination and possible stopovers for a route. A, B and C as x, y coordinates in the default EPSG:4326 format (see RequestBase.coordinateFormat).

At the very beginning a high-performance routing network with rectangular geographic restrictions is calculated.

A calculateRoute is then done with routing type HIGH_PERFORMANCE_ROUTING to use the high-performance routing network previously calculated.

Once xRoute has processed the request, the callback routingCompleted is invoked which gives us access to the result of the calculation in the form of a RouteResponse object which contains the response routing type.

After the route is calculated successfully, the high-performance routing network is deleted.

One of the downsides when using rectangular geographic restrictions is that we can get a wrong (or bad) route because of the limitation of search bounds. This problem is illustrated with the next two samples.

This first example below shows how to request a calculateRoute when the search space bounds have no effect on the route. The rectangle is big enough to contain the optimal route.

var options = { "geographicRestrictions": { "allowedCountries": [ "LU" ], "searchSpaceBounds": { "restrictionMode": "CUSTOM", "customBounds": { "minX": "6.10", "maxX": "6.35", "minY": "49.47", "maxY": "49.63" } } }, }; job = xdata.startCreateHighPerformanceRoutingNetwork({ "label": "Luxembourg with a high-performance routing network calculated with rectangular geographic restrictions.", "highPerformanceRoutingNetworkOptions": options }); var id = waitForJobToFinish(job); function waitForJobToFinish(job, exception) { while (job.status == "RUNNING" || job.status == "QUEUING") { job = xdata.watchJob({ "id": job.id }); } if (job.status == "SUCCEEDED") { return xdata.fetchHighPerformanceRoutingNetworkResponse(job.id).id; } else { return null; } }; var A = { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": {"x": 6.1256572, "y": 49.5983745 } } }; var B = { "$type": "OnRoadWaypoint", "location": { "coordinate": {"x": 6.1256572, "y": 49.4816576 } } }; var C = { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": {"x": 6.3256572, "y": 49.5516576 } } }; var map = new L.Map('map', { center: [49.61, 6.125], zoom: 13 }); // Add tile layer to map var tileUrl = xServerUrl + '/services/rest/XMap/tile/{z}/{x}/{y}'; var tileLayer = new L.TileLayer(tileUrl, { minZoom: 3, maxZoom: 18, noWrap: true }).addTo(map); xroute.calculateRoute({ "waypoints" : [A, C, B], "resultFields" : { "report" : "true", "polyline" : true }, "routeOptions" : { "routingType": "HIGH_PERFORMANCE_ROUTING", "geographicRestrictions": options.geographicRestrictions, "polylineOptions" : { "elevations" : true } } }, routingFinished); function convert2LatLong(x, y) { var ll = proj4('EPSG:4326' ,'EPSG:4326', {x: x, y: y}); return L.latLng([ll.y, ll.x]); }; function displayPolyline(poly) { var polyline = []; for (var i = 0; i < poly.length;++i) { polyline.push(L.latLng(poly[i].y, poly[i].x)); } polylineLayer = L.polyline(polyline, { color: '#2882C8', weight: 8 }); polylineLayer.addTo(map); map.fitBounds(polylineLayer.getBounds()); }; function routingFinished(route, exception) { var polyline = route.polyline.plain.polyline; displayPolyline(polyline); var southWest = convert2LatLong(options.geographicRestrictions.searchSpaceBounds.customBounds.minX, options.geographicRestrictions.searchSpaceBounds.customBounds.maxY); var northEast = convert2LatLong(options.geographicRestrictions.searchSpaceBounds.customBounds.maxX, options.geographicRestrictions.searchSpaceBounds.customBounds.minY); rectangle = L.rectangle(L.latLngBounds(southWest, northEast), {}); rectangle.addTo(map); print(route.distance + 'm in ' + route.travelTime + 's starting at ' + polyline[0].z + 'm elevation' + ' routing type ' + route.report.routingType ); xdata.deleteHighPerformanceRoutingNetwork({ "id": id }); }

This second example below shows how to request a calculateRoute when the search space bounds have an effect on the route. The route is modified so as it doesn't go beyond the rectangle. The results show that this is not the optimal route.

var options = { "geographicRestrictions": { "allowedCountries": [ "LU" ], "searchSpaceBounds": { "restrictionMode": "CUSTOM", "customBounds": { "minX": "6.10", "maxX": "6.35", "minY": "49.47", "maxY": "49.60" } } }, }; job = xdata.startCreateHighPerformanceRoutingNetwork({ "label": "Luxembourg with a high-performance routing network calculated with rectangular geographic restrictions.", "highPerformanceRoutingNetworkOptions": options }); var id = waitForJobToFinish(job); function waitForJobToFinish(job, exception) { while (job.status == "RUNNING" || job.status == "QUEUING") { job = xdata.watchJob({ "id": job.id }); } if (job.status == "SUCCEEDED") { return xdata.fetchHighPerformanceRoutingNetworkResponse(job.id).id; } else { return null; } }; var A = { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": {"x": 6.1256572, "y": 49.5983745 } } }; var B = { "$type": "OnRoadWaypoint", "location": { "coordinate": {"x": 6.1256572, "y": 49.4816576 } } }; var C = { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": {"x": 6.3256572, "y": 49.5516576 } } }; var map = new L.Map('map', { center: [49.61, 6.125], zoom: 13 }); // Add tile layer to map var tileUrl = xServerUrl + '/services/rest/XMap/tile/{z}/{x}/{y}'; var tileLayer = new L.TileLayer(tileUrl, { minZoom: 3, maxZoom: 18, noWrap: true }).addTo(map); xroute.calculateRoute({ "waypoints" : [A, C, B], "resultFields" : { "report" : "true", "polyline" : true }, "routeOptions" : { "routingType": "HIGH_PERFORMANCE_ROUTING", "geographicRestrictions": options.geographicRestrictions, "polylineOptions" : { "elevations" : true } } }, routingFinished); function convert2LatLong(x, y) { var ll = proj4('EPSG:4326' ,'EPSG:4326', {x: x, y: y}); return L.latLng([ll.y, ll.x]); }; function displayPolyline(poly) { var polyline = []; for (var i = 0; i < poly.length;++i) { polyline.push(L.latLng(poly[i].y, poly[i].x)); } polylineLayer = L.polyline(polyline, { color: '#2882C8', weight: 8 }); polylineLayer.addTo(map); map.fitBounds(polylineLayer.getBounds()); }; function routingFinished(route, exception) { var polyline = route.polyline.plain.polyline; displayPolyline(polyline); var southWest = convert2LatLong(options.geographicRestrictions.searchSpaceBounds.customBounds.minX, options.geographicRestrictions.searchSpaceBounds.customBounds.maxY); var northEast = convert2LatLong(options.geographicRestrictions.searchSpaceBounds.customBounds.maxX, options.geographicRestrictions.searchSpaceBounds.customBounds.minY); rectangle = L.rectangle(L.latLngBounds(southWest, northEast), {}); rectangle.addTo(map); print(route.distance + 'm in ' + route.travelTime + 's starting at ' + polyline[0].z + 'm elevation' + ' routing type ' + route.report.routingType ); xdata.deleteHighPerformanceRoutingNetwork({ "id": id }); }

Please refer to the documentation of the RouteRequest for more information on request parameterization.

The rectangular geographic restrictions with search space bounds can even be used in a calculateRoute request without high-performance routing networks.