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
LevelControlfloor switcher, and level-aware custom layers. - POIs & content search — click, highlight, select and filter stylesheet pinpoints, viewport pinpoints, and content search.
- Markers & clustering —
DomMarkerLayerruntime 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.