Show an Elevation Profile in Maptoolkit Maps JS
This example renders a route as a GeoJSON line layer and adds an interactive elevation profile below the map. Hovering over the profile highlights the corresponding segment and shows a position marker. The MTK.ElevationProfile class is loaded from the Maptoolkit CDN and works alongside Maps JS since both use the same underlying GL renderer.
const API_KEY = 'YOUR_API_KEY';
MTK.apiKey = API_KEY;
const routeCoords = [
[11.458077, 47.259163], [11.458337, 47.258754], [11.457979, 47.258611],
[11.457556, 47.258567], [11.456872, 47.258058], [11.456416, 47.257937],
[11.456319, 47.257837], [11.456384, 47.257638], [11.455895, 47.257362],
[11.455407, 47.257307], [11.454625, 47.257318], [11.454235, 47.257196],
[11.454121, 47.256544], [11.454235, 47.255826], [11.454511, 47.255473],
[11.454544, 47.255285], [11.454544, 47.255152], [11.454886, 47.254953],
[11.455097, 47.254876], [11.455097, 47.254843], [11.454495, 47.254953],
[11.454153, 47.254169], [11.452851, 47.253240], [11.450978, 47.252898],
[11.450571, 47.252666], [11.450083, 47.252279], [11.449057, 47.252012],
[11.448096, 47.251890], [11.447429, 47.251503],
];
const map = new maptoolkit.Map({
container: 'map',
apiKey: API_KEY,
style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
center: [11.452883, 47.255333],
zoom: 14,
attributionControl: { compact: false }
});
map.once('load', () => {
map.addLayer({ id: 'route', type: 'line', source: { type: 'geojson', data: { type: 'Feature', geometry: { type: 'LineString', coordinates: routeCoords } } }, layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-color': '#2a3561', 'line-width': 5 } });
map.addLayer({ id: 'highlight', type: 'line', source: { type: 'geojson', data: { type: 'Feature', geometry: { type: 'LineString', coordinates: [] } } }, layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-color': '#fa3f38', 'line-width': 5 } });
const img = document.createElement('img');
img.src = 'https://static.maptoolkit.net/sprites/toursprung/marker.svg';
img.width = 29; img.height = 30;
const marker = new maptoolkit.Marker({ element: img, anchor: 'bottom' }).setLngLat([0, 0]);
const elevation = new MTK.ElevationProfile().addTo('profile').setLngLats(routeCoords);
elevation.on('mousemove', ev => { !marker._map && marker.addTo(map); marker.setLngLat(ev.lngLat); });
elevation.on('mouseout', () => { marker._map && marker.remove(); });
elevation.on('highlight', ev => { map.getSource('highlight').setData({ type: 'Feature', geometry: { type: 'LineString', coordinates: ev.lngLats } }); });
});<!DOCTYPE html>
<html lang="en">
<head>
<title>Elevation Profile - Maptoolkit Maps JS</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@maptoolkit/maps@11.0.0-beta.3/dist/maptoolkit.css" />
<link rel="stylesheet" href="https://static.maptoolkit.net/mtk/apis/v1.1/elevation.css" />
<style>
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
#map { width: 100%; height: 100%; }
#profile { position: absolute; bottom: 0; right: 0; width: 500px; margin: 0 10px 30px 0; z-index: 999; }
#profile .mtk-elevation-profile { background: #fff; box-shadow: 0 0 15px #68686880; border-radius: 6px; }
#profile .mtk-elevation-curve-stroke { stroke: #303f7e; stroke-width: 1rem; stroke-opacity: 1; }
#profile .mtk-elevation-curve-fill { fill: #303f7e; }
#profile .mtk-elevation-curve-bar { stroke: #fa3f38; }
#profile .mtk-elevation-section { fill: rgba(250, 63, 56, 0.3); }
</style>
</head>
<body>
<div id="map"></div>
<div id="profile"></div>
<script src="https://static.maptoolkit.net/mtk/apis/v1.1/elevation.js?api_key=YOUR_API_KEY"></script>
<script>
const API_KEY = 'YOUR_API_KEY';
MTK.apiKey = API_KEY;
const routeCoords = [
[11.458077, 47.259163], [11.458337, 47.258754], [11.457979, 47.258611],
[11.457556, 47.258567], [11.456872, 47.258058], [11.456416, 47.257937],
[11.456319, 47.257837], [11.456384, 47.257638], [11.455895, 47.257362],
[11.455407, 47.257307], [11.454625, 47.257318], [11.454235, 47.257196],
[11.454121, 47.256544], [11.454235, 47.255826], [11.454511, 47.255473],
[11.454544, 47.255285], [11.454544, 47.255152], [11.454886, 47.254953],
[11.455097, 47.254876], [11.455097, 47.254843], [11.454495, 47.254953],
[11.454153, 47.254169], [11.452851, 47.253240], [11.450978, 47.252898],
[11.450571, 47.252666], [11.450083, 47.252279], [11.449057, 47.252012],
[11.448096, 47.251890], [11.447429, 47.251503],
];
const map = new maptoolkit.Map({
container: 'map',
apiKey: API_KEY,
style: `https://styles.maptoolkit.net/maptoolkit/maptoolkit.summer.json?api_key=${API_KEY}`,
center: [11.452883, 47.255333],
zoom: 14,
attributionControl: { compact: false }
});
map.once('load', () => {
map.addLayer({ id: 'route', type: 'line', source: { type: 'geojson', data: { type: 'Feature', geometry: { type: 'LineString', coordinates: routeCoords } } }, layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-color': '#2a3561', 'line-width': 5 } });
map.addLayer({ id: 'highlight', type: 'line', source: { type: 'geojson', data: { type: 'Feature', geometry: { type: 'LineString', coordinates: [] } } }, layout: { 'line-join': 'round', 'line-cap': 'round' }, paint: { 'line-color': '#fa3f38', 'line-width': 5 } });
const img = document.createElement('img');
img.src = 'https://static.maptoolkit.net/sprites/toursprung/marker.svg';
img.width = 29; img.height = 30;
const marker = new maptoolkit.Marker({ element: img, anchor: 'bottom' }).setLngLat([0, 0]);
const elevation = new MTK.ElevationProfile().addTo('profile').setLngLats(routeCoords);
elevation.on('mousemove', ev => { !marker._map && marker.addTo(map); marker.setLngLat(ev.lngLat); });
elevation.on('mouseout', () => { marker._map && marker.remove(); });
elevation.on('highlight', ev => { map.getSource('highlight').setData({ type: 'Feature', geometry: { type: 'LineString', coordinates: ev.lngLats } }); });
});
</script>
</body>
</html>Use the prompt below with any LLM to get the same result. Make sure the Maptoolkit MCP server is connected first — check out AI Integration & MCP to get started.
How it works
This example loads two Maptoolkit libraries, and they authorize separately. The map
takes its key as the apiKey constructor option, while elevation.js reads the global
MTK.apiKey. Set one and not the other and either the map or the profile comes back empty.
MTK.ElevationProfile fetches elevations for the coordinates you give it and draws the
chart. It is an event emitter rather than a widget you poll:
mousemovereports the position under the cursor, which moves a marker along the route.highlightreports a coordinate range, which is fed into a second line layer so the matching stretch of route lights up.mouseoutremoves the marker again.
That second highlight layer starts with empty coordinates. It exists from the beginning so
the event handler can call setData on it rather than adding and removing a layer on every
mouse move.
The route here is a fixed coordinate array. In a real application it would come from the Routing API, whose encoded polyline you decode first.
Next steps
Feed it a real route first. The Routing API returns the geometry, and route plus profile is the standard outdoor pairing.
After that, the interesting work is in the numbers rather than the chart: total ascent and descent, the steepest section, metres gained per kilometre. All of it comes from the elevation array without another request. The Elevation API reference covers the single-point and batch request shapes for the cases a profile does not fit, and Terrain Tiles put the relief under the route so the profile matches what the map shows.