Skip to main content

Map Guide

The @wemap/map package is a thin, opinionated wrapper around maplibre-gl for Wemap livemaps. It reads your livemap configuration (style, bounds, zoom range, indoor settings, …) from @wemap/core and adds first-class helpers for camera control, event subscription, POI interaction, and multi-level / indoor display.

It is not a generic maplibre helper — always initialize @wemap/core before constructing a WemapMap. The raw maplibre instance stays available as map.maplibre for anything the wrapper doesn't cover.

Installation​

maplibre-gl is a peer dependency — install it alongside the SDK:

npm install @wemap/core @wemap/map maplibre-gl

Create a map​

The map renders into a container element you provide by id. Like maplibre, the container must have an explicit height — a bare <div> collapses to 0px and the map appears blank:

<div id="map"></div>

<style>
/* The container needs a height, or the map renders at 0px and stays blank. */
#map { position: absolute; inset: 0; }
/* or: #map { width: 100%; height: 100vh; } */
</style>
import { core } from '@wemap/core';
import { WemapMap } from '@wemap/map';
import 'maplibre-gl/dist/maplibre-gl.css';
import '@wemap/map/style.css'; // SDK marker / cluster / user-location styles

await core.init({ emmid: '31668', token: 'YOUR_TOKEN' });

const map = new WemapMap({ container: 'map' });
await map.whenReady(); // resolves once the style is ready — no race with on('load')

Snippet defaults (style, bounds, zoom range, pitch, bearing, indoor, …) apply automatically. Override any of them — plus all maplibre MapOptions — in the constructor:

const map = new WemapMap({ container: 'map', minZoom: 14, pitch: 0 });

To ignore the snippet's bounds or maxBounds without replacing them, pass null (no initial fit / an unconstrained camera); omitting them keeps the snippet's.

preloadPinpoints (default true) fetches the viewport's pinpoints on load, after each move and on level change, feeding getViewportPinpoints() and onViewportPinpointsChange. Set it to false when your app loads pinpoints itself.

autoResize (default true) attaches a ResizeObserver to the container and calls resize() when it changes size; detached on remove(). The default attribution credits Wemap, MapLibre and OpenStreetMap; pass attributionControl: false to hide it.

The interaction option opts into the automatic POI userflow (click-to-select, search highlight/filter) — see POIs & search.

Coordinate conventions​

Position inputs (setCenter, flyTo/easeTo centers, and marker / itinerary / user-location positions) accept any LatLngLike — a Coordinates from @wemap/geo, a { lat, lng, level? } / { latitude, longitude } object, or a [lat, lng, level?] tuple. All are latitude-first. Position outputs (getCenter(), the onPoiClick payload) return Coordinates, and bounds use BoundingBox. The raw map.maplibre instance uses maplibre's own [lng, lat] convention — if you work directly against it (including map.on('click') passthrough events), you own the conversion.

Control the camera​

import { Coordinates, BoundingBox } from '@wemap/geo';

map.setCenter(new Coordinates(48.8566, 2.3522));
map.setZoom(16.5);
map.flyTo({ center: new Coordinates(48.85, 2.35), zoom: 19, duration: 1200 });
map.fitBounds(
new BoundingBox(new Coordinates(48.8618, 2.361), new Coordinates(48.8528, 2.343))
);

jumpTo() moves the camera instantly. Every camera method (setCenter, setZoom, flyTo, easeTo, jumpTo, fitBounds) takes an optional last eventData argument, merged into the move* / zoom* events it fires — use it to tell your own camera moves from user gestures:

map.easeTo({ center: user, zoom: 18 }, { programmatic: true });
map.on('movestart', (e) => {
if (!('programmatic' in e)) stopFollowingUser();
});

getCenter(), getZoom(), and getBounds() read the current camera state.

Listen to events​

on / once / off are typed passthroughs to maplibre, chainable:

map.on('click', (e) => console.log(e.lngLat)); // map events
map.on('click', 'my-layer', (e) => console.log(e.features)); // layer-scoped

Clean up​

Optional layers (UserLocationLayer, DomMarkerLayer, ItineraryLayer, ShapeLayer) register with the map but are not destroyed by map.remove() by default — call each layer's destroy() first, or cascade with { layers: true }:

map.remove({ layers: true });

Feature guides​

  • Indoor & levels — multi-level display, automatic building selection, the LevelControl floor switcher, and level-aware custom layers.
  • POIs & content search — click, highlight, select and filter stylesheet pinpoints, viewport pinpoints, and content search.
  • Markers & clustering — DomMarkerLayer runtime pins and optional supercluster clustering.
  • Itineraries — draw multilevel routes with ItineraryLayer.
  • Shapes — draw GeoJSON lines and polygons with simplestyle-spec styling, hover and click.
  • User location — show the user from a positioning stream with UserLocationLayer.
  • Theming — brand the map with theme tokens and CSS variables.

For the full API, see the Map API reference.