// The API Key provided is restricted to JSFiddle website
// Get your own API Key on https://myprojects.geoapify.com
const myAPIKey = "6dc7fb95a3b246cfa0f3bcef5ce9ed9a";
// create a map object
const map = new maplibregl.Map({
container: 'my-map',
center: [5.59132, 45.36671],
zoom: 14,
style: `https://maps.geoapify.com/v1/styles/klokantech-basic/style.json?apiKey=${myAPIKey}`,
});
map.addControl(new maplibregl.NavigationControl());
// this object describes the task
const serviceJobsOptimizationInput = {
"mode": "drive",
"agents": [{
"start_location": [
5.586790932250917,
45.36538915
],
"end_location": [
5.586790932250917,
45.36538915
],
"time_windows": [
[
3600,
10800
]
],
"capabilities": [
"Cooling system tester",
"Motor locking tool"
]
},
{
"start_location": [
5.589290483511979,
45.36931755
],
"end_location": [
5.589290483511979,
45.36931755
],
"time_windows": [
[
3600,
10800
]
],
"capabilities": [
"Welding machine",
"Motor locking tool"
]
},
{
"start_location": [
5.590490376922535,
45.36242105
],
"end_location": [
5.590490376922535,
45.36242105
],
"time_windows": [
[
0,
7200
]
],
"capabilities": [
"Cooling system tester",
"Motor locking tool"
]
},
{
"start_location": [
5.590442806010234,
45.36164335
],
"end_location": [
5.590442806010234,
45.36164335
],
"time_windows": [
[
0,
7200
]
],
"capabilities": [
"Welding machine",
"Motor locking tool"
]
},
{
"start_location": [
5.586790932250917,
45.36538915
],
"end_location": [
5.586790932250917,
45.36538915
],
"time_windows": [
[
3600,
10800
]
],
"capabilities": [
"Electrician"
]
}
],
"jobs": [{
"location": [
5.587157987701584,
45.3692519
],
"duration": 1800,
"requirements": [
"Electrician"
]
},
{
"location": [
5.588772879745656,
45.367290249999996
],
"duration": 1800,
"requirements": [
"Cooling system tester",
"Motor locking tool"
]
},
{
"location": [
5.590147649963082,
45.367139800000004
],
"duration": 3600
},
{
"location": [
5.590781375689465,
45.3654216
],
"duration": 1800
},
{
"location": [
5.593280954470921,
45.36225855
],
"duration": 3600
},
{
"location": [
5.590501000823879,
45.36292535
],
"duration": 3600,
"requirements": [
"Welding machine"
]
},
{
"location": [
5.587599902200691,
45.369926648797545
],
"duration": 1800,
"requirements": [
"Electrician"
]
},
{
"location": [
5.5877760746307095,
45.36990488807438
],
"duration": 3600,
"requirements": [
"Electrician"
]
},
{
"location": [
5.5892522,
45.3699106
],
"duration": 1800
},
{
"location": [
5.590835590428573,
45.36363853866901
],
"duration": 1800
},
{
"location": [
5.5930482,
45.3618131
],
"duration": 1800,
"requirements": [
"Cooling system tester"
]
},
{
"location": [
5.594323121173246,
45.365694649999995
],
"duration": 3600,
"requirements": [
"Electrician"
]
},
{
"location": [
5.594073970423049,
45.3654464
],
"duration": 1800,
"requirements": [
"Cooling system tester",
"Electrician"
]
},
{
"location": [
5.594512636574548,
45.36535895
],
"duration": 3600
},
{
"location": [
5.594121624053946,
45.36565495
],
"duration": 3600
},
{
"location": [
5.594095027750809,
45.36527355
],
"duration": 1800
},
{
"location": [
5.594121624053946,
45.36565495
],
"duration": 3600
},
{
"location": [
5.5945166,
45.3655609
],
"duration": 1800,
"requirements": [
"Electrician"
]
},
{
"location": [
5.594279024725736,
45.3654164
],
"duration": 3600
},
{
"location": [
5.593703701230378,
45.365869450000005
],
"duration": 3600
}
]
};
// colors
const colors = ["#ff4d4d", "#1a8cff", "#00cc66", "#b300b3", "#e6b800", "#ff3385",
"#0039e6", "#408000", "#ffa31a", "#990073", "#cccc00", "#cc5200", "#6666ff", "#009999"
];
map.on('load', () => {
visualizeLocations(serviceJobsOptimizationInput, map);
// solve route optimization task
fetch(`https://api.geoapify.com/v1/routeplanner?apiKey=${myAPIKey}`, {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(serviceJobsOptimizationInput)
}).then(res => res.json())
.then(res => {
notifyAboutIssues(res);
res.features.forEach((feature, index) => visualizeAgentWaypoints(feature, colors[index]));
res.features.forEach((feature, index) => visualizeAgentRoute(feature, colors[index], index));
});
});
function visualizeLocations(routeOptimizationTask, map) {
// collect unique locations
const locationMap = {};
routeOptimizationTask.jobs.forEach(job => {
const locationStr = `${job.location[1]} ${job.location[0]}`;
locationMap[locationStr] = job.location;
});
// visualize lication as a layer
const geoJSONObj = {
"type": "FeatureCollection",
"features": Object.keys(locationMap).map(locationKey => {
return {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": locationMap[locationKey]
}
}
})
};
map.addSource('locations', {
type: 'geojson',
data: geoJSONObj
});
map.addLayer({
'id': 'locations',
'type': 'circle',
'source': 'locations',
'paint': {
'circle-radius': 5,
'circle-color': "#ff9933",
'circle-stroke-width': 1,
'circle-stroke-color': '#994d00',
}
});
}
function notifyAboutIssues(result) {
if (result.properties.issues) {
alert(`The solution has issues: ${Object.keys(result.properties.issues).join(', ')}`);
}
}
function visualizeAgentWaypoints(feature, color) {
const waypoints = feature.properties.waypoints
.map((waypoint, index) => {
return {
"type": "Feature",
"properties": {
index: index + 1
},
"geometry": {
"type": "Point",
"coordinates": waypoint.location
}
}
});
// create points source + layer
map.addSource(`agent-${feature.properties.agent_index}-waypoints`, {
type: 'geojson',
data: {
"type": "FeatureCollection",
"features": waypoints
}
});
map.addLayer({
'id': `agent-${feature.properties.agent_index}-waypoints-circle`,
'type': 'circle',
'source': `agent-${feature.properties.agent_index}-waypoints`,
'paint': {
'circle-radius': 10,
'circle-color': color,
'circle-stroke-width': 1,
'circle-stroke-color': "rgba(0,0,0,0.2)"
}
});
map.addLayer({
'id': `agent-${feature.properties.agent_index}-waypoints-text`,
'type': 'symbol',
'source': `agent-${feature.properties.agent_index}-waypoints`,
'layout': {
"text-field": '{index}',
'text-allow-overlap': false,
"text-font": [
"Roboto", "Helvetica Neue", "sans-serif"
],
"text-size": 12
},
'paint': {
"text-color": "rgba(255, 255, 255, 1)"
}
});
}
function visualizeAgentRoute(feature, color, index) {
const lineWidth = 7 - index;
const shift = -2 + index * 2;
// generate a route and visualite it
const waypoints = feature.properties.waypoints.map(waypoint => waypoint.location[1] + ',' + waypoint.location[0]).join('|');
fetch(`https://api.geoapify.com/v1/routing?waypoints=${waypoints}&mode=drive&apiKey=${myAPIKey}`)
.then(res => res.json())
.then(res => {
map.addSource(`agent-${feature.properties.agent_index}-route`, {
type: 'geojson',
data: res
});
map.addLayer({
'id': `agent-${feature.properties.agent_index}-route`,
'type': 'line',
'source': `agent-${feature.properties.agent_index}-route`,
'layout': {
'line-cap': "round",
'line-join': "round"
},
'paint': {
'line-color': color,
'line-width': lineWidth,
'line-translate': [shift, shift]
}
});
map.moveLayer(`agent-${feature.properties.agent_index}-waypoints-circle`);
map.moveLayer(`agent-${feature.properties.agent_index}-waypoints-text`);
});
}