// The API Key provided is restricted to JSFiddle website // Get your own API Key on https://myprojects.geoapify.com const myAPIKey = "6dc7fb95a3b246cfa0f3bcef5ce9ed9a"; const map = new maplibregl.Map({ container: 'my-map', style: `https://maps.geoapify.com/v1/styles/klokantech-basic/style.json?apiKey=${myAPIKey}`, center: [-72.79419772520356, 44.53361448499783], zoom: 14 }); map.addControl(new maplibregl.NavigationControl()); const popup = new maplibregl.Popup(); // calculate and display routing: // from 38.937165,-77.045590 (1208 Hourglass Drive, Stowe, VT 05672, United States of America) const fromWaypoint = [-72.78056761690857, 44.53000255267429]; // longitude, latutude const fromWaypointMarker = new maplibregl.Marker().setLngLat(fromWaypoint) .setPopup(new maplibregl.Popup().setText( '1208 Hourglass Drive, Stowe, VT 05672, United States of America' )).addTo(map); // to 38.881152,-76.990693 (Switchback, Stowe, VT 05672-5111, United States of America) const toWaypoint = [-72.80797096598127, 44.536552001130076]; // longitude, latutude const toWaypointMarker = new maplibregl.Marker().setLngLat(toWaypoint) .setPopup(new maplibregl.Popup().setText( 'Switchback, Stowe, VT 05672-5111, United States of America' )).addTo(map); let routeData; let routeStepsData; let instructionsData; let stepPointsData; fetch(`https://api.geoapify.com/v1/routing?waypoints=lonlat:${fromWaypoint.join(",")}|lonlat:${toWaypoint.join(",")}&mode=hike&details=route_details,elevation&apiKey=${myAPIKey}`).then(res => res.json()).then(routeResult => { routeData = routeResult; const steps = []; const instructions = []; const stepPoints = []; routeData.features[0].properties.legs.forEach((leg, legIndex) => { const legGeometry = routeData.features[0].geometry.coordinates[legIndex]; leg.steps.forEach((step, index) => { if (step.instruction) { instructions.push({ "type": "Feature", "geometry": { "type": "Point", "coordinates": legGeometry[step.from_index] }, properties: { text: step.instruction.text } }); } if (index !== 0) { stepPoints.push({ "type": "Feature", "geometry": { "type": "Point", "coordinates": legGeometry[step.from_index] }, properties: step }) } if (step.from_index === step.to_index) { // destination point return; } const stepGeometry = legGeometry.slice(step.from_index, step.to_index + 1); steps.push({ "type": "Feature", "geometry": { "type": "LineString", "coordinates": stepGeometry }, properties: step }); }); }); routeStepsData = { type: "FeatureCollection", features: steps } instructionsData = { type: "FeatureCollection", features: instructions } stepPointsData = { type: "FeatureCollection", features: stepPoints } map.addSource('route', { type: 'geojson', data: routeData }); map.addSource('points', { type: 'geojson', data: instructionsData }); addLayerEvents(); drawRoute(); }, err => console.log(err)); function drawRoute() { if (!routeData) { return; } if (map.getLayer('route-layer')) { map.removeLayer('route-layer') } if (map.getLayer('points-layer')) { map.removeLayer('points-layer') } if (document.getElementById("showDetails").checked) { map.getSource('route').setData(routeStepsData); map.addLayer({ 'id': 'route-layer', 'type': 'line', 'source': 'route', 'layout': { 'line-join': "round", 'line-cap': "round" }, 'paint': { 'line-color': [ 'match', ['get', 'road_class'], 'motorway', '#009933', 'trunk', '#00cc99', 'primary', '#009999', 'secondary', '#00ccff', 'tertiary', '#9999ff', 'residential', '#9933ff', 'service_other', '#ffcc66', 'unclassified', '#666699', /* other */ '#666699' ], 'line-width': 8 } }); map.getSource('points').setData(stepPointsData); map.addLayer({ 'id': 'points-layer', 'type': 'circle', 'source': 'points', 'paint': { 'circle-radius': 4, 'circle-color': "#ddd", 'circle-stroke-color': "#aaa", 'circle-stroke-width': 1, } }); } else { map.getSource('route').setData(routeData); map.addLayer({ 'id': 'route-layer', 'type': 'line', 'source': 'route', 'layout': { 'line-cap': "round", 'line-join': "round" }, 'paint': { 'line-color': "#6084eb", 'line-width': 8 }, 'filter': ['==', '$type', 'LineString'] }); map.getSource('points').setData(instructionsData); map.addLayer({ 'id': 'points-layer', 'type': 'circle', 'source': 'points', 'paint': { 'circle-radius': 4, 'circle-color': "#fff", 'circle-stroke-color': "#aaa", 'circle-stroke-width': 1, } }); } } function addLayerEvents() { map.on('mouseenter', 'route-layer', () => { map.getCanvas().style.cursor = 'pointer'; }); map.on('mouseleave', 'route-layer', () => { map.getCanvas().style.cursor = ''; }); map.on('mouseenter', 'points-layer', () => { map.getCanvas().style.cursor = 'pointer'; }); map.on('mouseleave', 'points-layer', () => { map.getCanvas().style.cursor = ''; }); map.on('click', 'route-layer', (e) => { if (document.getElementById("showDetails").checked) { const stepData = e.features[0].properties; const propertiesToShow = ["surface", "elevation", "elevation_gain"]; const dataToShow = {}; propertiesToShow.forEach(property => { if (stepData[property] || stepData[property] === 0) { dataToShow[property] = stepData[property]; } }); showPopup(dataToShow, e.lngLat); } else { showPopup({ distance: `${e.features[0].properties.distance} m`, time: `${e.features[0].properties.time} s` }, e.lngLat); } e.preventDefault(); }) map.on('click', 'points-layer', (e) => { const properties = e.features[0].properties; const point = e.features[0].geometry.coordinates; if (properties.text) { popup.setText(properties.text); popup.setLngLat(point); popup.addTo(map); e.preventDefault(); } }); } function showPopup(data, lngLat) { let popupHtml = Object.keys(data).map(key => { return `<div class="popup-property-container"> <span class="popup-property-label">${key}: </span> <span class="popup-property-value">${data[key]}</span> </div>` }).join(''); popup.setLngLat(lngLat).setHTML(popupHtml).addTo(map); }
<div class="route-settings"> <label class="switch"> <input id="showDetails" type="checkbox" onclick="drawRoute()"> <span class="slider round"></span> </label> Show route details </div> <div id="my-map"></div>
html, body { margin: 0; padding: 0; height: 100%; display: flex; flex-direction: column; } .route-settings { padding: 5px } #my-map { flex: 1; } /* The switch - the box around the slider */ .switch { position: relative; display: inline-block; width: 30px; height: 17px; } /* Hide default HTML checkbox */ .switch input { opacity: 0; width: 0; height: 0; } /* The slider */ .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; -webkit-transition: .4s; transition: .4s; } .slider:before { position: absolute; content: ""; height: 13px; width: 13px; left: 2px; bottom: 2px; background-color: white; -webkit-transition: .4s; transition: .4s; } input:checked+.slider { background-color: #2196F3; } input:focus+.slider { box-shadow: 0 0 1px #2196F3; } input:checked+.slider:before { -webkit-transform: translateX(13px); -ms-transform: translateX(13px); transform: translateX(13px); } /* Rounded sliders */ .slider.round { border-radius: 17px; } .slider.round:before { border-radius: 50%; }