Getting started with MapLibre
Installation#
Not yet published on npm. The command below is what installation will look like once the packages are released. Until then, work inside this repository (a pnpm workspace) or link the packages from it. Their entry points are TypeScript sources, so your bundler must compile TypeScript (Vite does).
npm install @vectorstream/core @vectorstream/editor @vectorstream/maplibre maplibre-gl
maplibre-gl is a peer dependency (^6.11.0); @vectorstream/adapter comes along with
@vectorstream/maplibre.
The complete example#
A real file of this repository (docs/handbuch/beispiele/maplibre-quickstart.ts); a test in a real
browser draws a triangle through it, exports, undoes and switches tools by key. The test passes an
empty style instead of osmStyle so it fetches no tiles.
import * as maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
import { exportGeoJSON, importGeoJSON, type FeatureCollection } from '@vectorstream/core';
import { Editor, Toolbar, defaultToolbarEntries } from '@vectorstream/editor';
import { MapLibreAdapter } from '@vectorstream/maplibre';
/** A raster style with OpenStreetMap tiles. Any MapLibre style works. */
export const osmStyle: maplibregl.StyleSpecification = {
version: 8,
sources: {
osm: {
type: 'raster',
tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
attribution: '© OpenStreetMap contributors',
},
},
layers: [{ id: 'osm', type: 'raster', source: 'osm' }],
};
export function startEditor(container: HTMLElement, data: FeatureCollection, style = osmStyle) {
// 1. The map. MapLibre counts zoom in 512-pixel tiles: 16 here shows what 17 shows in Leaflet.
const map = new maplibregl.Map({ container, style, center: [7.4555, 51.5112], zoom: 16 });
// 2. The adapter may be created right away; it adds its sources and layers once the style loads.
const adapter = new MapLibreAdapter(map);
// 3. The editor works on a partition — here one built from GeoJSON.
const editor = new Editor(importGeoJSON(data), adapter);
// 4. The toolbar is state, not UI: it knows the tools and their shortcut keys.
const toolbar = new Toolbar(editor, defaultToolbarEntries());
toolbar.select('draw');
// 5. Every transaction ends in exactly one of three events.
editor.on('change', ({ label }) => console.log('changed:', label));
editor.on('unchanged', ({ label }) => console.log('nothing to do:', label));
editor.on('rejected', ({ label, violations }) => {
console.warn('rejected:', label, violations.map((v) => v.detail));
});
return {
map,
editor,
toolbar,
// 6. Export the active layer as GeoJSON.
save: (): FeatureCollection => exportGeoJSON(editor.partition),
// 7. Undo and redo; `false` means there was nothing to do, or the step was rejected.
undo: (): boolean => editor.undo(),
redo: (): boolean => editor.redo(),
destroy(): void {
toolbar.destroy();
editor.destroy();
adapter.destroy();
map.remove();
},
};
}
The steps are the same as on Getting started with Leaflet: map, adapter, editor, toolbar, events, export, undo. Only the map and the adapter differ.
MapLibre specifics#
- Create the adapter right away. You do not have to wait for the map's
loadevent: the adapter adds its sources and layers as soon as the style has loaded, and adds them again after every later style change (map.setStyle(…)). - Zoom levels. MapLibre counts zoom in 512-pixel tiles. Zoom 16 in MapLibre shows the same scale as zoom 17 in Leaflet or OpenLayers.
- No rotation, no pitch. While the adapter exists, it disables drag-rotate, touch pitch,
two-finger rotation and keyboard rotation; the editor assumes north is up.
adapter.destroy()restores what was enabled before. - Pointer events are taken from the map container. A press only counts on the map canvas
(not on a zoom button); a drag that started on the map keeps going over the controls. Keys come
from
keydownon the document, except while typing in an input field, text area, select box orcontenteditableelement. - Stacking. The adapter sets
isolation: isolateon the canvas container, so its markers and labels stay below MapLibre's controls and popups;destroy()restores the previous value. - The worker in a Vite build. MapLibre loads a web worker from a URL next to its own file. The
application in this repository (
app/) tells MapLibre where that file is, before the first map is created:maplibregl.setWorkerUrl(url), withurlimported asmaplibre-gl/dist/maplibre-gl-worker.mjs?worker&url(a Vite import suffix). Without it, Vite cannot see the worker file, and it is missing from the built bundle. - Tested version. The adapter is tested with MapLibre GL JS 6.11.2. It reads one internal field of MapLibre's style to know whether the style has loaded; a test fails if a MapLibre version removes it.
- Options.
new MapLibreAdapter(map, { ebenenFarbe })— the color of a layer, as with Leaflet.
Next: Faces and edges, Modes, API reference: MapLibreAdapter.