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 ).
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:
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