Skip to content
Elevation profile

Elevation profile

The vertices go to /v1/elevation/profile as coordinates; it returns samples evenly spaced points plus ascent and descent. A canvas draws the profile.

Click the map to extend the line.

Code

// Elevation profile: click a line on the map, /v1/elevation/profile returns the heights, a canvas draws the profile.
const TOKEN = 'YOUR_TOKEN'; // your public token (mx_pk_…) from https://account.mapx.at
// Adds the token to every request MapLibre makes to mapx.at (style, tiles, fonts, sprites), never to other hosts.
const transformRequest = (url) => ({ url: /^https:\/\/([\w-]+\.)?mapx\.at\//.test(url) ? url + (url.includes('?') ? '&' : '?') + 'access_token=' + TOKEN : url });
const AUTH = { headers: { Authorization: 'Bearer ' + TOKEN } };

maplibregl.addProtocol('pmtiles', new pmtiles.Protocol().tile);

const map = new maplibregl.Map({ transformRequest, container: 'map', style: 'https://api.mapx.at/v1/styles/outdoors', center: [12.70, 47.08], zoom: 12 });
map.addControl(new maplibregl.NavigationControl());
let punkte = [[12.67, 47.065], [12.71, 47.09]];

map.on('load', () => {
  map.addSource('linie', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
  map.addLayer({ id: 'linie', type: 'line', source: 'linie', paint: { 'line-color': '#2346d8', 'line-width': 4 } });
  zeichne();
});
map.on('click', (e) => { punkte.push([e.lngLat.lng, e.lngLat.lat]); zeichne(); });
document.getElementById('neu').addEventListener('click', () => { punkte = []; zeichne(); });

async function zeichne() {
  map.getSource('linie').setData({ type: 'Feature', geometry: { type: 'LineString', coordinates: punkte } });
  const canvas = document.getElementById('profil'), g = canvas.getContext('2d');
  g.clearRect(0, 0, canvas.width, canvas.height);
  if (punkte.length < 2) { document.getElementById('info').textContent = 'Mindestens zwei Punkte setzen.'; return; }
  const pfad = punkte.map((p) => p.map((v) => v.toFixed(5)).join(',')).join(';');
  const antwort = await fetch(`https://api.mapx.at/v1/elevation/profile?coordinates=${pfad}&samples=120`, AUTH);
  const { distances, elevations } = (await antwort.json()).properties;
  const pts = distances.map((d, i) => ({ d, h: elevations[i] })).filter((p) => p.h != null);
  const dMax = pts.at(-1).d, hMin = Math.min(...pts.map((p) => p.h)), hMax = Math.max(...pts.map((p) => p.h));
  const W = canvas.width, H = canvas.height, x = (d) => 50 + (d / dMax) * (W - 60), y = (h) => H - 24 - ((h - hMin) / (hMax - hMin || 1)) * (H - 40);
  g.beginPath(); pts.forEach((p, i) => (i ? g.lineTo(x(p.d), y(p.h)) : g.moveTo(x(p.d), y(p.h))));
  g.strokeStyle = '#2346d8'; g.lineWidth = 2; g.stroke();
  g.fillStyle = '#6b7079'; g.font = '12px sans-serif';
  g.fillText(`${Math.round(hMax)} m`, 4, 16); g.fillText(`${Math.round(hMin)} m`, 4, H - 28); g.fillText(`${(dMax / 1000).toFixed(2)} km`, W - 60, H - 6);
  let auf = 0, ab = 0; pts.forEach((p, i) => { if (i) { const dh = p.h - pts[i - 1].h; dh > 0 ? (auf += dh) : (ab -= dh); } });
  document.getElementById('info').textContent = `${(dMax / 1000).toFixed(2)} km, Aufstieg ${Math.round(auf)} m, Abstieg ${Math.round(ab)} m`;
}

Complete HTML file

Save and open in a browser. All it needs is MapLibre GL JS 4.7.1 and pmtiles 3.2.1 from a CDN, plus your public token (replace YOUR_TOKEN), no build.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>mapx Beispiel</title>
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css">
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/pmtiles@3.2.1/dist/pmtiles.js"></script>
<style>html,body{margin:0;font:14px system-ui,sans-serif}#map{height:70vh}</style>
</head>
<body>
<button type="button" id="neu">Clear line</button><span id="info">Click the map to extend the line.</span>
<div id="map"></div>
<canvas id="profil" width="720" height="200" aria-label="Elevation profile"></canvas>
<script>
// Elevation profile: click a line on the map, /v1/elevation/profile returns the heights, a canvas draws the profile.
const TOKEN = 'YOUR_TOKEN'; // your public token (mx_pk_…) from https://account.mapx.at
// Adds the token to every request MapLibre makes to mapx.at (style, tiles, fonts, sprites), never to other hosts.
const transformRequest = (url) => ({ url: /^https:\/\/([\w-]+\.)?mapx\.at\//.test(url) ? url + (url.includes('?') ? '&' : '?') + 'access_token=' + TOKEN : url });
const AUTH = { headers: { Authorization: 'Bearer ' + TOKEN } };

maplibregl.addProtocol('pmtiles', new pmtiles.Protocol().tile);

const map = new maplibregl.Map({ transformRequest, container: 'map', style: 'https://api.mapx.at/v1/styles/outdoors', center: [12.70, 47.08], zoom: 12 });
map.addControl(new maplibregl.NavigationControl());
let punkte = [[12.67, 47.065], [12.71, 47.09]];

map.on('load', () => {
  map.addSource('linie', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
  map.addLayer({ id: 'linie', type: 'line', source: 'linie', paint: { 'line-color': '#2346d8', 'line-width': 4 } });
  zeichne();
});
map.on('click', (e) => { punkte.push([e.lngLat.lng, e.lngLat.lat]); zeichne(); });
document.getElementById('neu').addEventListener('click', () => { punkte = []; zeichne(); });

async function zeichne() {
  map.getSource('linie').setData({ type: 'Feature', geometry: { type: 'LineString', coordinates: punkte } });
  const canvas = document.getElementById('profil'), g = canvas.getContext('2d');
  g.clearRect(0, 0, canvas.width, canvas.height);
  if (punkte.length < 2) { document.getElementById('info').textContent = 'Mindestens zwei Punkte setzen.'; return; }
  const pfad = punkte.map((p) => p.map((v) => v.toFixed(5)).join(',')).join(';');
  const antwort = await fetch(`https://api.mapx.at/v1/elevation/profile?coordinates=${pfad}&samples=120`, AUTH);
  const { distances, elevations } = (await antwort.json()).properties;
  const pts = distances.map((d, i) => ({ d, h: elevations[i] })).filter((p) => p.h != null);
  const dMax = pts.at(-1).d, hMin = Math.min(...pts.map((p) => p.h)), hMax = Math.max(...pts.map((p) => p.h));
  const W = canvas.width, H = canvas.height, x = (d) => 50 + (d / dMax) * (W - 60), y = (h) => H - 24 - ((h - hMin) / (hMax - hMin || 1)) * (H - 40);
  g.beginPath(); pts.forEach((p, i) => (i ? g.lineTo(x(p.d), y(p.h)) : g.moveTo(x(p.d), y(p.h))));
  g.strokeStyle = '#2346d8'; g.lineWidth = 2; g.stroke();
  g.fillStyle = '#6b7079'; g.font = '12px sans-serif';
  g.fillText(`${Math.round(hMax)} m`, 4, 16); g.fillText(`${Math.round(hMin)} m`, 4, H - 28); g.fillText(`${(dMax / 1000).toFixed(2)} km`, W - 60, H - 6);
  let auf = 0, ab = 0; pts.forEach((p, i) => { if (i) { const dh = p.h - pts[i - 1].h; dh > 0 ? (auf += dh) : (ab -= dh); } });
  document.getElementById('info').textContent = `${(dMax / 1000).toFixed(2)} km, Aufstieg ${Math.round(auf)} m, Abstieg ${Math.round(ab)} m`;
}
</script>
</body>
</html>

APIs used