MapLibre GL JS

MapLibre GL JS renders the full vector outdoor style with hardware-accelerated WebGL — interactive, smooth, and supports 3D terrain.

Full working example: github.com/mapriot/map-examples/MaplibreGLJS — clone and run with npm install && npm run dev.


Quick start (CDN)

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>MapRiot — MapLibre GL JS</title>
  <link href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css" rel="stylesheet" />
  <script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
  <style>
    body { margin: 0; }
    #map { width: 100%; height: 100dvh; }
  </style>
</head>
<body>
  <div id="map"></div>
  <script>
    const MAPRIOT_APIKEY = 'YOUR_API_KEY';

    const map = new maplibregl.Map({
      container: 'map',
      style: 'https://api.mapriot.com/styles/outdoor.json',
      center: [14.42, 50.08],
      zoom: 13,
      attributionControl: {
        customAttribution:
          '<a href="https://mapriot.com/copyright" target="_blank">&copy; MapRiot.com</a> ' +
          '<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>'
      },
      transformRequest: (url) => {
        if (url.startsWith('https://api.mapriot.com') && !url.includes('apiKey')) {
          return {
            url: url + (url.includes('?') ? '&' : '?') + 'apiKey=' + MAPRIOT_APIKEY,
          };
        }
        return { url };
      },
    });

    map.addControl(new maplibregl.NavigationControl());
    map.addControl(new maplibregl.ScaleControl());
  </script>
</body>
</html>

Authentication with transformRequest

The style JSON references tile, font, and sprite URLs on api.mapriot.com. Each of these subrequests also requires your API key. The transformRequest callback appends it automatically to every request:

transformRequest: (url) => {
  if (url.startsWith('https://api.mapriot.com') && !url.includes('apiKey')) {
    return {
      url: url + (url.includes('?') ? '&' : '?') + 'apiKey=' + MAPRIOT_APIKEY,
    };
  }
  return { url };
},

Without transformRequest, only the initial style JSON loads — tile and font requests will fail.


With 3D terrain

map.on('load', () => {
  map.addSource('terrain', {
    type: 'raster-dem',
    url: 'https://api.mapriot.com/hillshading',
  });

  map.setTerrain({ source: 'terrain', exaggeration: 1.5 });
  map.addControl(new maplibregl.TerrainControl({
    source: 'terrain',
    exaggeration: 1.5,
  }));

  map.setSky({
    'sky-color': '#93bcdb',
    'sky-horizon-blend': 0.5,
    'horizon-color': '#d8dde1',
    'horizon-fog-blend': 0.5,
    'fog-color': '#b8bcb7',
    'fog-ground-blend': 0.5,
  });
});

The hillshading source is already defined inside the outdoor style for 2D hillshading. The terrain source above is an additional reference to the same DEM data, used specifically for 3D terrain extrusion.


npm / bundler

npm install maplibre-gl
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';

const MAPRIOT_APIKEY = 'YOUR_API_KEY';

const map = new maplibregl.Map({
  container: 'map',
  style: 'https://api.mapriot.com/styles/outdoor.json',
  center: [14.42, 50.08],
  zoom: 13,
  transformRequest: (url) => {
    if (url.startsWith('https://api.mapriot.com') && !url.includes('apiKey')) {
      return {
        url: url + (url.includes('?') ? '&' : '?') + 'apiKey=' + MAPRIOT_APIKEY,
      };
    }
    return { url };
  },
});

Attribution

All maps must display MapRiot and OpenStreetMap attribution. The attributionControl option handles this:

attributionControl: {
  customAttribution:
    '<a href="https://mapriot.com/copyright">&copy; MapRiot.com</a> ' +
    '<a href="https://www.openstreetmap.org/copyright">&copy; OpenStreetMap contributors</a>'
},

On mobile, MapLibre automatically collapses the attribution behind a button.