Calculating reachable locations and areas
This article discusses the use case of calculating reachable locations. Reachable locations are usefull for use cases like dealer search (detect position of dealers in a given radius) and corridor search (find points of interest around a given route A 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.).
Benefits
Thanks to this feature, the user can plan which locations are reachable from a position or a route. This can be used, for example, to check if point of interests such as petrol stations or hotels along an itinerary are reachable within a certain distance or period.
Prerequisites
Check if the following prerequisites are fulfilled before you start with the use case.
- Installed and licensed PTV xRoute and xMap services.
Programming Guide
The user needs to fill a ReachableLocationsRequest:
- a waypoint A waypoint is a geographic location used to specify start, destination and possible stopovers for a route. : The waypoint defines the place where the engine should start the dealer search. In case of a PathWaypoint, it indicates where the engine should start the corridor search.
- an array of routeLocations to define all the points to be reached.
- ReachableLocationsOptions with at least a defined Horizon , to define the distance or time threshold to determine wether locations are reached or not. The other parameters are optionnal, but the user can also provides :
- a ReachableLocationsSearchType.
- a TimeConsideration.
- a contentSnapshotId.
- GeographicRestrictions.
How to check which stores are within range of a truck's position.
let map = new L.Map('map', { center: [49.612013, 6.129699], zoom: 16 }); // Add tile layer to map let tileUrl = xServerUrl + '/services/rest/XMap/tile/{z}/{x}/{y}'; let tileLayer = new L.TileLayer(tileUrl, { minZoom: 3, maxZoom: 18, noWrap: true }).addTo(map); const waypoint = { "x": 6.129699, "y": 49.612013 }; const locations = [ { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.129941940307618, "y": 49.610800314572835 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.127849817276002, "y": 49.61114791492889 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.127946376800538, "y": 49.61196824194125 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.129019260406495, "y": 49.61280245864238 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.1313796043396, "y": 49.61267037528272 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.1315083503723145, "y": 49.611829204436255 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.131122112274171, "y": 49.61139818565037 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.131712198257447, "y": 49.610543088713484 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.127452850341797, "y": 49.610438807573004 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.1273992061615, "y": 49.61283026561984 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.129051446914674, "y": 49.612406207489606 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.1287617683410645, "y": 49.611210482729724 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.130360364913941, "y": 49.61342116014081 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.1303818225860605, "y": 49.61258695402905 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.132270097732544, "y": 49.612357544845224 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.130220890045166, "y": 49.61153027245755 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.12864375114441, "y": 49.610793362540434 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.127249002456666, "y": 49.612468773675275 } } ]; function calculateReachableLocations(){ xroute.calculateReachableLocations({ 'locations':locations, 'waypoint':{ "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": waypoint, } }, "reachableLocationsOptions": { "horizon": { "$type": "TravelTimeBasedHorizon", "travelTime": "160" }, "searchType": "LOCATION_REACHABLE_FROM_WAYPOINT" } },function(response, exception){ print(`${response.reachableLocations.length} locations reached,${response.unreachableLocations.length} not reached.`) response.reachableLocations.forEach(location =>{ let index = location.inputLocationIndex; let circle = L.circle([locations[index].offRoadCoordinate.y,locations[index].offRoadCoordinate.x], { color: 'green', fillColor: '#00ff00', fillOpacity: 0.5, radius: 10 }).addTo(map); }); response.unreachableLocations.forEach(index =>{ let circle = L.circle([locations[index].offRoadCoordinate.y,locations[index].offRoadCoordinate.x], { color: 'red', fillColor: '#ff0000', fillOpacity: 0.5, radius: 10 }).addTo(map); }); }) } new L.Marker([waypoint.y, waypoint.x]).addTo(map); calculateReachableLocations();How to check which petrol stations are reachable from a planned truck route.
let map = new L.Map('map', { center: [49.612013, 6.129699], zoom: 12 }); // Add tile layer to map let tileUrl = xServerUrl + '/services/rest/XMap/tile/{z}/{x}/{y}'; let tileLayer = new L.TileLayer(tileUrl, { minZoom: 3, maxZoom: 18, noWrap: true }).addTo(map); let encodedPath = ''; const start = { "x": 6.069654, "y": 49.629370 }; const destination = { "x": 6.215317, "y": 49.642660 }; const locations = [ { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.084365844726563, "y": 49.60214426638993 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.125564575195313, "y": 49.577996548628214 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.151657104492188, "y": 49.5797774337284 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.174659729003907, "y": 49.61471415139774 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.087799072265625, "y": 49.58723418419467 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.0859107971191415, "y": 49.61860682119642 } }, { "$type": "OffRoadRouteLocation", "offRoadCoordinate": { "x": 6.189422607421876, "y": 49.642067472009096 } } ]; function calculateReachableLocations(){ xroute.calculateReachableLocations({ 'locations':locations, 'waypoint':{ "$type": "PathWaypoint", "encodedPath": encodedPath }, "reachableLocationsOptions": { "horizon": { "$type": "TravelTimeBasedHorizon", "travelTime": "160" }, "searchType": "LOCATION_REACHABLE_FROM_WAYPOINT" } },function(response, exception){ print(`${response.reachableLocations.length} locations reached,${response.unreachableLocations.length} unreached.`) response.reachableLocations.forEach(location =>{ let index = location.inputLocationIndex; let circle = L.circle([locations[index].offRoadCoordinate.y,locations[index].offRoadCoordinate.x], { color: 'green', fillColor: '#00ff00', fillOpacity: 0.5, radius: 200 }).addTo(map); }); response.unreachableLocations.forEach(index =>{ let circle = L.circle([locations[index].offRoadCoordinate.y,locations[index].offRoadCoordinate.x], { color: 'red', fillColor: '#ff0000', fillOpacity: 0.5, radius: 200 }).addTo(map); }); }) } function calculateRoute() { xroute.calculateRoute({ "waypoints": [{ "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": start, } }, { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": destination, } }], "resultFields": { "alternativeRoutes": true, "polyline": true, "encodedPath": true }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(route, exception) { encodedPath = route.encodedPath; displayGeoJson(route.polyline.geoJSON); calculateReachableLocations(); }); }; function displayGeoJson(geoJson) { var jsonObject = JSON.parse(geoJson); var geoJsonLayer = new L.GeoJSON(jsonObject, { style: { color: "#2882C8", weight: 5 } }).addTo(map); }; new L.Marker([start.y, start.x]).addTo(map); new L.Marker([destination.y, destination.x]).addTo(map); calculateRoute();How to calculate different reachable areas all starting at the same place.
let map = new L.Map('map', { center: [49.612013, 6.129699], zoom: 13 }); // Add tile layer to map let tileUrl = xServerUrl + '/services/rest/XMap/tile/{z}/{x}/{y}'; let tileLayer = new L.TileLayer(tileUrl, { minZoom: 3, maxZoom: 18, noWrap: true }).addTo(map); const waypoint = { "x": 6.129699, "y": 49.612013 }; function calculateReachableAreas() { xroute.calculateReachableAreas({ 'waypoint': { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": waypoint, } }, "reachableAreasOptions": { "horizons":[ { "$type": "TravelTimeBasedHorizon", "travelTime": "100" }, { "$type": "TravelTimeBasedHorizon", "travelTime": "500" }] }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(response, exception) { response.polygons.forEach(polygon => { displayGeoJson(polygon.geoJSON); }) }) } function displayGeoJson(geoJson) { var jsonObject = JSON.parse(geoJson); var geoJsonLayer = new L.GeoJSON(jsonObject, { style: { color: "#2882C8", weight: 5 } }).addTo(map); }; new L.Marker([waypoint.y, waypoint.x]).addTo(map); calculateReachableAreas();How to calculate a corridor around a planned truck route.
let map = new L.Map('map', { center: [49.605, 6.15], zoom: 12 }); // Add tile layer to map let tileUrl = xServerUrl + '/services/rest/XMap/tile/{z}/{x}/{y}'; let tileLayer = new L.TileLayer(tileUrl, { minZoom: 3, maxZoom: 18, noWrap: true }).addTo(map); let encodedPath = ''; const start = { "x": 6.069654, "y": 49.629370 }; const destination = { "x": 6.215317, "y": 49.642660 }; function calculateReachableAreas() { xroute.calculateReachableAreas({ 'waypoint': { "$type": "PathWaypoint", "encodedPath": encodedPath }, "reachableAreasOptions": { "horizons": [{ "$type": "TravelTimeBasedHorizon", "travelTime": "160" }], "drivingDirection": "OUTBOUND" }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(response, exception) { response.polygons.forEach(polygon => { displayGeoJson(polygon.geoJSON); }) }) } function calculateRoute() { xroute.calculateRoute({ "waypoints": [{ "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": start, } }, { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": destination, } }], "resultFields": { "alternativeRoutes": true, "polyline": true, "encodedPath": true }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(route, exception) { encodedPath = route.encodedPath; displayGeoJson(route.polyline.geoJSON); calculateReachableAreas(); }); }; function displayGeoJson(geoJson) { var jsonObject = JSON.parse(geoJson); var geoJsonLayer = new L.GeoJSON(jsonObject, { style: { color: "#2882C8", weight: 5 } }).addTo(map); }; new L.Marker([start.y, start.x]).addTo(map); new L.Marker([destination.y, destination.x]).addTo(map); calculateRoute();How to retrieve segments reached during corridor search around a planned truck route.
let map = new L.Map('map', { center: [49.605, 6.15], zoom: 12 }); // Add tile layer to map let tileUrl = xServerUrl + '/services/rest/XMap/tile/{z}/{x}/{y}'; let tileLayer = new L.TileLayer(tileUrl, { minZoom: 3, maxZoom: 18, noWrap: true }).addTo(map); let encodedPath = ''; const start = { "x": 6.069654, "y": 49.629370 }; const destination = { "x": 6.215317, "y": 49.642660 }; function calculateReachableAreas() { xroute.calculateReachableAreas({ 'waypoint': { "$type": "PathWaypoint", "encodedPath": encodedPath }, "reachableAreasOptions": { "horizons": [{ "$type": "TravelTimeBasedHorizon", "travelTime": "160" }] }, "reachableAreasResultFields":{ "segments":{ "enabled":true, "polyline":true }, "polygons": false }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(response, exception) { response.segments.forEach(segment => { displayGeoJson(segment.polyline.geoJSON); }) }) } function calculateRoute() { xroute.calculateRoute({ "waypoints": [{ "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": start, } }, { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": destination, } }], "resultFields": { "polyline": true, "encodedPath": true }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(route, exception) { encodedPath = route.encodedPath; displayGeoJson(route.polyline.geoJSON); calculateReachableAreas(); }); }; function displayGeoJson(geoJson) { var jsonObject = JSON.parse(geoJson); var geoJsonLayer = new L.GeoJSON(jsonObject, { style: { color: "#2882C8", weight: 5 } }).addTo(map); }; new L.Marker([start.y, start.x]).addTo(map); new L.Marker([destination.y, destination.x]).addTo(map); calculateRoute();How to find and display the string formed by the predecessors index. It allows the user to know where to leave the road and which way to go to reach the position (outbound case).
let map = new L.Map('map', { center: [49.605, 6.15], zoom: 12 }); // Add tile layer to map let tileUrl = xServerUrl + '/services/rest/XMap/tile/{z}/{x}/{y}'; let tileLayer = new L.TileLayer(tileUrl, { minZoom: 3, maxZoom: 18, noWrap: true }).addTo(map); let encodedPath = ''; const start = { "x": 6.069654, "y": 49.629370 }; const destination = { "x": 6.215317, "y": 49.642660 }; function calculateReachableAreas() { xroute.calculateReachableAreas({ 'waypoint': { "$type": "PathWaypoint", "encodedPath": encodedPath }, "reachableAreasOptions": { "horizons": [{ "$type": "TravelTimeBasedHorizon", "travelTime": "160" }] }, "reachableAreasResultFields": { "segments": { "enabled": true, "polyline": true, "predecessorIndex": true }, "polygons": false }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(response, exception) { displayPredecessors(response.segments, 0); displayPredecessors(response.segments, 1); displayPredecessors(response.segments, 2); displayPredecessors(response.segments, 3); }) } function calculateRoute() { xroute.calculateRoute({ "waypoints": [{ "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": start, } }, { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": destination, } }], "resultFields": { "polyline": true, "encodedPath": true }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(route, exception) { encodedPath = route.encodedPath; displayGeoJson(route.polyline.geoJSON, "#2882C8"); calculateReachableAreas(); }); }; function displayPredecessors(segments, predecessorIndex){ let index = predecessorIndex; var jsonObject = JSON.parse(segments[index].polyline.geoJSON); new L.Marker([jsonObject.coordinates[0][1],jsonObject.coordinates[0][0]]).addTo(map); while(index != -1){ displayGeoJson(segments[index].polyline.geoJSON, "#ff0000"); index = segments[index].predecessorIndex; } } function displayGeoJson(geoJson, color) { console.log(geoJson); var jsonObject = JSON.parse(geoJson); var geoJsonLayer = new L.GeoJSON(jsonObject, { style: { color: color, weight: 5 } }).addTo(map); }; new L.Marker([start.y, start.x]).addTo(map); new L.Marker([destination.y, destination.x]).addTo(map); calculateRoute();How to retrieve segments reached during corridor search around a planned truck route, and create a custom feature layer with those segments. In this example, we try to block the segments reached during the calculateReachableAreas calculation.
let encodedPath = ''; var themeId = "PTV_RoadAttributes"; var scenarioId = "IntegrationSample_ReachableAreas"; const start = { "x": 6.069654, "y": 49.629370 }; const destination = { "x": 6.215317, "y": 49.642660 }; function calculateReachableAreas() { xroute.calculateReachableAreas({ 'waypoint': { "$type": "PathWaypoint", "encodedPath": encodedPath }, "reachableAreasOptions": { "horizons": [{ "$type": "TravelTimeBasedHorizon", "travelTime": "160" }] }, "reachableAreasResultFields": { "segments": { "enabled": true, "polyline": true, "id": true }, "polygons": false }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(response, exception) { createPersistentFeatureLayerWithBlockedSegment(response.segments); // Add tile layer to map new L.tileLayer.xserver(xServerUrl + '/services/rest/XMap/experimental/tile/{z}/{x}/{y}' + '?layers=background,transport,labels' + ',' + themeId + '.' + scenarioId + '&contentType=JSON', { pane: "overlayPane", maxZoom: 20, }).addTo(map); }) } function calculateRoute() { xroute.calculateRoute({ "waypoints": [{ "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": start, } }, { "$type": "OffRoadWaypoint", "location": { "offRoadCoordinate": destination, } }], "resultFields": { "polyline": true, "encodedPath": true }, "geometryOptions": { "responseGeometryTypes": ["GEOJSON"] } }, function(route, exception) { encodedPath = route.encodedPath; displayGeoJson(route.polyline.geoJSON); calculateReachableAreas(); }); }; function displayGeoJson(geoJson) { var jsonObject = JSON.parse(geoJson); var geoJsonLayer = new L.GeoJSON(jsonObject, { style: { color: "#2882C8", weight: 5 } }).addTo(map); }; //----- Create FeatureLayer ----- function createPersistentFeatureLayerWithBlockedSegment(segments) { let segmentIds = []; segments.forEach(segment=>{ segmentIds.push(segment.id); }); xdata.createFeatureLayer({ "themeId": themeId, "featureScenario": scenarioId, "features": [{ "segmentIds": segmentIds, "descriptions": [{ "attributes": [{ "key": "opening", "value": 0 }] }] }], "resultFields": { "binaryFeatureLayer": false } }); }; var map = new L.Map('map', { center: [start.y, start.x], zoom: 17 }); new L.Marker([start.y, start.x]).addTo(map); new L.Marker([destination.y, destination.x]).addTo(map); calculateRoute();Related Topics
Showcase | Calculate Reachable Locations |
Showcase | Calculate Reachable Areas |
Showcase | Consider Reachable Area Inner Segments |
Technical concept | Reachable Locations and Areas |