Guide

A hiking map in MapLibre GL JS

A map for hikers needs trails with their difficulty, huts, stops and water, and the terrain. With the outdoor style, or with the trails added to a style of your own, a popup for each trail and your route on top. Tested with MapLibre GL JS 6.13.0.

The outdoor style has trails by difficulty, contour lines and hillshading built in:
const map = new maplibregl.Map({
  container: "map",
  style: "https://api.overpass.ch/v1/YOUR_KEY/maps/styles/outdoor.json",
  center: [7.96, 46.6],
  zoom: 13
});
Add ?terrain=1 for 3D, and ?lang=de or another of 41 languages for the labels.
Go to:
Loading the map…
Trails: hiking (T1) mountain hiking (T2–T3) alpine (T4–T6) no grade

Trails by difficulty

The colours follow the SAC scale of the Swiss Alpine Club, which OpenStreetMap stores in the sac_scale tag, and the colours of Swiss signposts:

gradeSAC scalesac_scale in OpenStreetMapColour
1T1 hikinghikingyellow
2T2 mountain hikingmountain_hikingred
3T3 demanding mountain hikingdemanding_mountain_hikingred
4T4 alpine hikingalpine_hikingblue
5T5 demanding alpine hikingdemanding_alpine_hikingblue
6T6 difficult alpine hikingdifficult_alpine_hikingblue
0no grade in the datanonegrey, from zoom 12

The outdoor style also shows tracks, via ferratas, huts, campsites, viewpoints, stations and stops, drinking water, peaks with their height, rock, glaciers and cliffs. Graded trails show from zoom 11.

Trails on a style of your own

The trails come as vector tiles of their own, so you can draw them on any style. This adds them to the light style, with a popup for the trail under the mouse:

map.on("load", () => {
  map.addSource("trails", {
    type: "vector",
    url: "https://api.overpass.ch/v1/YOUR_KEY/maps/trails.json"
  });
  map.addLayer({
    id: "trails",
    type: "line",
    source: "trails",
    "source-layer": "trails",
    filter: [">", ["get", "grade"], 0],
    paint: {
      // T1 yellow, T2 and T3 red, T4 to T6 blue, as on Swiss signposts
      "line-color": ["match", ["get", "grade"], 1, "#d39b00", [2, 3], "#d0312d", "#1f5fbf"],
      "line-width": ["interpolate", ["linear"], ["zoom"], 11, 1, 16, 3],
      "line-dasharray": [3, 1.6]
    }
  });
  map.on("click", "trails", (e) => {
    const t = e.features[0].properties;
    new maplibregl.Popup()
      .setLngLat(e.lngLat)
      .setText(`${t.name || "Trail"} · T${t.grade}`)
      .addTo(map);
  });
  map.on("mouseenter", "trails", () => (map.getCanvas().style.cursor = "pointer"));
  map.on("mouseleave", "trails", () => (map.getCanvas().style.cursor = ""));
});
Loading the map…
The light style with hillshading and the code above. Click a trail.

The tiles have two layers, from zoom 11 to 13; closer in, MapLibre enlarges zoom 13:

  • trails: lines with grade (0 to 6, as in the table), kind, name, ref and visibility. Paths without a grade are in the tiles from zoom 12.
  • points, from zoom 13: bus and tram stops, stations, ferry terminals, cable car stations, drinking water and springs, with kind and name.

They come from the live OpenStreetMap data of our Overpass server, and a tile is at most 7 days old. A newly graded trail shows within a week.

Your route on top

A route you planned or recorded goes on top as GeoJSON. Convert GPX files to GeoJSON first, for example with the @tmcw/togeojson package.

map.addSource("route", { type: "geojson", data: "/route.geojson" });
map.addLayer({
  id: "route",
  type: "line",
  source: "route",
  layout: { "line-cap": "round", "line-join": "round" },
  paint: { "line-color": "#7b2cbf", "line-width": 4, "line-opacity": 0.8 }
});

Added last, the route lies above trails and labels. For the profile of the route, with ascent and descent, send it to the elevation API with the same key; the example elevation profile of a hiking route does this for a route from OpenStreetMap.

Limits

  • The grade is what mappers entered. Many paths have none and show grey; a grade can be old or wrong. Say so in your app, and do not use the map as the only source for a mountain tour.
  • The base map is updated every week, the trails and stops within 7 days.
  • Contour lines and terrain come from a grid with 90 m spacing: good for mountains, too coarse for small slopes.
  • Every trail, contour, terrain and vector tile counts as one map tile. The first view of the outdoor style at zoom 13.2, 1280 × 720 pixels, loaded 19 tiles. Every account includes 100'000 a month.