Creates and mounts the map inside the given container. Must be called exactly once before any other method.
Map creation options.
Returns true after init() has completed successfully.
Methods that depend on a live map instance should guard on this flag.
Destroys the map instance and releases all associated resources.
After destroy(), no other methods may be called.
Sets the map center and zoom level without animation.
New geographic center.
New zoom level.
Returns the current geographic center of the map viewport.
Returns the current zoom level.
Sets the zoom level without changing the center.
New zoom level.
Pans the map to the given center without changing the zoom level.
MapLibre uses easeTo() without zoom change.
Target geographic center.
Smoothly animates the viewport to the target center and zoom level.
Target geographic center.
Optionalzoom: number
Target zoom level (optional — keeps current zoom when omitted).
Adjusts the viewport to fit the given bounding box.
Geographic area to fit.
Optionaloptions: { padding?: GeoLeafPoint; animate?: boolean; maxZoom?: number }
padding: room kept around the bounds, in screen pixels. animate:
false to jump. maxZoom: a ceiling on the zoom the framing may reach — a small
area is then shown from further away instead of filling the viewport.
Returns the current viewport as a geographic bounding box.
Subscribes to a map event.
The handler receives an engine-specific event object (unknown).
Adapters normalise geographic coordinates to GeoLeafLatLng within
the event object where applicable.
Event type token.
Callback invoked on each occurrence.
Unsubscribes a previously registered event handler. Silently does nothing if the handler was not registered.
Event type token.
The exact function reference passed to on().
Subscribes to a map event for a single occurrence, then auto-unsubscribes.
Event type token.
Callback invoked on the first occurrence.
Creates a GeoJSON layer from raw GeoJSON data and adds it to the map.
The adapter stores the layer internally keyed by id. Use removeLayer(),
showLayer() / hideLayer(), updateLayerData(), and setLayerStyle() to
manage it afterwards. The MapLibre adapter throws when id is already taken.
Unique layer identifier.
GeoJSON data (FeatureCollection, Feature, or Geometry). The adapter
does not validate it: it is handed to the engine's GeoJSON source as it is.
Optionaloptions: GeoLeafLayerOptions
Optional layer display options.
Removes the layer identified by id from the map and disposes it.
The layer identifier used in addGeoJSONLayer().
Returns true if a layer with the given id is currently managed by the adapter.
Layer identifier to check.
Shows a previously hidden layer.
Layer identifier.
Hides a layer without removing it from the adapter's internal registry.
Layer identifier.
Replaces the GeoJSON data of an existing layer.
Internally clears the current features and adds the new data. The layer keeps its id, options, and style.
Layer identifier.
New GeoJSON data (FeatureCollection, Feature, or Geometry).
OptionalapplyApplies a LayerDataDiff to an existing layer — what MOVED, instead of the whole collection — and reports whether it took it.
🛑 The return value is the whole point, and it is a BOOLEAN, not a promise.
The only question the caller has is "did you take it?", and that is decidable
synchronously: the source is present or it is not, it can identify its features
or it cannot. false means the caller must fall back to
IMapAdapter.updateLayerData with the full collection — which the caller
holds and the adapter deliberately does not. Keeping the fallback on the caller's
side is what makes it OBSERVABLE from a test with a fake adapter, and a fallback
nobody can see fire is a fallback that silently becomes the only path.
⚠️ Not a promise, even though the underlying write is asynchronous. None of
the public write methods promises completion today — setData is already
asynchronous underneath and its promise is already dropped — so returning one
here would widen a contract nobody asked to widen.
⚠️ Optional (?), like setFeatureState. One real adapter implements this
interface, but a number of narrower structural look-alikes stand in for it; a
required member would redden each of them and force an engine with no diff
primitive to fake one out of setData, which is exactly the silent
always-fallback this seam exists to prevent.
Layer identifier.
The change to apply.
true when the diff was applied; false when the caller must re-feed
the whole collection.
OptionalsetSets feature-state on a layer's source feature, targeted by its promoted
id — reactive paint (e.g. POI sync badge, hover/selection halo) without a
source data rebuild. Maps to map.setFeatureState(). The source must
declare promoteId for a string id to resolve.
Layer identifier (resolves to the backing source).
Promoted feature id to target.
Feature-state key/values to set.
Applies a style to an existing GeoJSON layer.
Layer identifier.
Vector fill and stroke style options.
Applies a filter expression to an existing layer.
Maps to map.setFilter() with a MapLibre expression
array (e.g. ["==", ["get", "type"], "park"]).
A layer holds one filter PER OWNER, and draws what passes all of them: a call writes and
clears its own owner's filter only. The default owner, "filter", is the panel's and the
visible subset's; GeoLeaf.Layers.hideFeatures writes "hidden". Until 3.12.0 a layer
held one filter, and hiding an edited feature replaced the panel's.
Layer identifier.
Engine-specific filter expression (unknown).
Pass null to clear this owner's filter.
Optionalowner: string
The filter slot written. Defaults to "filter".
Updates the zoom range of an existing layer, on every one of its sub-layers.
Maps to map.setLayerZoomRange(). Needed because a scale bound converts to a zoom
level THROUGH THE LATITUDE: the same 1:X sits at different zooms in Cayenne and in
Tromsø. A range posted at load time therefore drifts as the user pans north or
south, and has to be re-pushed — but never on zoom, which does not affect the
conversion.
Layer identifier.
Lowest zoom at which the layer renders, or null to clear.
Highest zoom at which the layer renders, or null to clear.
OptionalsetRe-ranks an existing GeoJSON layer in place, and reports whether it did.
The layer lands where an insertion at that rank would put it NOW: under the lowest layer of strictly higher rank — at the top of the ranked layers when there is none —, hence above the layers already holding that rank. Its sub-layers move together and keep their order: a fill stays under its outline.
Nothing is rebuilt. The source and its data, the filters, the zoom range, the visibility and the listeners are kept: a hidden layer is re-ranked and stays hidden. The order obtained crosses a basemap switch.
🛑 false means nothing moved, and the caller rebuilds — removeLayer, then
addGeoJSONLayer at the rank it wants. It is the answer when:
id names no layer of this adapter;zIndex is not a finite number;Asking for the rank the layer already holds answers true and moves nothing: the layer
is NOT lifted above the layers sharing that rank.
⚠️ What does not follow. The zIndex a layer was DECLARED with is not rewritten: the
layer manager still orders its rows by it, and the hover tooltip still arbitrates two
overlapping layers by it. A layer's labels do not move with it. No event is emitted.
Optional and boolean, for the two reasons IMapAdapter.applyDataDiff gives. Call it
as adapter.setLayerZIndex?.(id, zIndex) === true, so that an adapter without it — an
older core — reads as a refusal.
Layer identifier.
The rank to give the layer; higher is drawn on top. Need not be an integer.
true when the layer holds that rank on return; false when nothing moved and
the caller must rebuild.
Creates a marker at the given position and adds it to the map.
The adapter stores the marker internally keyed by id.
Unique marker identifier.
Geographic position of the marker anchor.
Optionaloptions: GeoLeafMarkerOptions
Marker display options.
Removes a marker from the map and releases its resources.
The marker identifier used in createMarker().
Updates the geographic position of an existing marker.
The marker identifier used in createMarker().
New geographic position.
OptionalsetRotates an existing marker's icon.
Pair it with rotationAlignment: "map" at creation: the engine then re-derives the
on-screen angle from the map bearing on every frame, so a caller updating this once per
position fix still gets a continuously correct icon during an animated camera move.
Optional: an adapter whose engine has no marker-rotation model may omit it, and callers
must handle undefined as they do for the other optional members.
The marker identifier used in createMarker().
Rotation clockwise, in degrees.
OptionalgetReturns a handle on an existing marker, or null when no marker carries that id.
The id-based surface above covers the common cases; this exists for the two things
it cannot express — reading a marker's position after the user dragged it, and
subscribing to its own events. Prefer updateMarkerPosition() and the map-level
on() whenever they suffice.
Optional: an adapter whose engine has no per-marker event model may omit it, and
callers must handle undefined as they do for the other optional members.
The marker identifier used in createMarker().
Creates a popup with the given content.
Returns an opaque handle (unknown) to pass to openPopup() /
closePopup(). The popup is not opened automatically — call openPopup()
after creation.
HTML string or DOM element to display inside the popup.
Optionaloptions: GeoLeafPopupOptionsWhen content is a string, it must be sanitised by the
caller before being passed here. The adapter does not sanitise content.
Use GeoLeaf.Security.sanitizeHTML() (allowlist) or stripHtml(), or pass an
already-built HTMLElement — the internal renderers use setDOMContent, never a
raw string.
Opens (displays) a popup on the map.
The opaque handle returned by createPopup().
Optionalposition: GeoLeafLatLng
Geographic position at which to anchor the popup. When omitted, the popup opens at its previously stored position.
Closes a popup without destroying it.
When popup is omitted, all open popups are closed.
Optionalpopup: unknown
The opaque handle returned by createPopup(). Optional.
Adds a native control object to the map at the specified corner.
Returns a GeoLeafControl handle that can be passed to removeControl().
Native control object (engine-specific, typed unknown).
Map corner where the control is rendered.
Removes a control from the map.
The GeoLeafControl handle returned by addControl().
Projects a geographic coordinate to a pixel point relative to the map container's top-left corner.
Geographic coordinate to project.
Unprojects a pixel point (relative to the map container) back to a geographic coordinate.
Pixel coordinate to unproject.
Returns the map's root DOM container element. Useful for positioning custom overlays or reading container dimensions.
OptionalgetReturns the engine's drawing canvas — the element that actually paints the pointer.
⚠️ Not interchangeable with IMapAdapter.getContainer. MapLibre styles the
cursor through CSS classes on .maplibregl-canvas-container, an element BETWEEN the
root container and the canvas (cursor: grab on .maplibregl-interactive). A rule
set on that descendant beats anything inherited from the root, so writing
getContainer().style.cursor is silently overridden and never shows. Any code
changing the map cursor must target the canvas.
Optional, like IMapAdapter.resize: test doubles implement the contract
without it, and making it required would turn every mock red for a capability none
of them exercise. Call it as adapter.getCanvas?.() ?? adapter.getContainer() — the
same fallback the editor plugin already uses.
OptionalresizeAsks the engine to re-read its container size and resize its drawing buffer.
Needed after the container is moved into a parent of different dimensions —
Core.reattach() calls it — because the WebGL canvas keeps the size it was
built with and would otherwise render at the old dimensions in the new slot.
Optional, like IMapAdapter.getNativeMap: test doubles implement the
contract without it, and making it required would turn every mock red for a
capability none of them exercise. ⚠️ Optional means a caller can invoke it on
undefined and never notice — call it as adapter.resize?.() and treat its
absence as "the engine does not need telling", never as success.
OptionalgetReturns the underlying native map instance (MapLibre GL Map object).
Intended for low-level integrations that cannot be expressed through the standard adapter interface. Use sparingly — direct access bypasses the adapter contract and may break on engine upgrades.
Returns unknown to avoid importing engine-specific types in this file.
OptionalbuildBuilds a transformStyle callback (setStyle option, since MapLibre v5) that
preserves the adapter-owned sources and layers across a basemap style swap — with
those declared to it through IMapAdapter.declareOwnedStyleIds —, so they
survive natively rather than being torn down and re-injected, and carries the
incoming basemap's declared credit onto its sources.
🛑 What the adapter owns must be read when the engine RUNS the callback, not when it is built: for a style given by URL, that is once the style has downloaded, and the layers created in between are the adapter's too. For the same reason an empty registry at call time is no ground for returning nothing.
Called by the basemap registry immediately before setStyle(). The return
type is intentionally opaque here to keep the contract engine-agnostic. A falsy
return makes the registry call a plain setStyle(), which carries nothing over.
Optionaloptions: { attribution?: string }
attribution: the incoming basemap's declared credit.
OptionaldeclareDeclares engine layers and sources placed on the map OUTSIDE this adapter — a plugin's own, drawn straight on the engine — so that a basemap switch replacing the style carries them into the incoming one, as it carries the adapter's own.
Undeclared, such a layer is erased by the switch: the transform claims what is DECLARED, never what an id's shape suggests.
Declarations are keyed by owner: a second call under the same key replaces the first,
and null withdraws it. They are read when the switch RUNS, so a declaration made while
a style downloads counts; an id no longer in the style by then is skipped, which makes a
declaration that outlives its layers harmless. They die with the map, on destroy().
⚠️ Only what does not depend on the basemap belongs here. A label layer resolves its font against the style's glyph server: carried as-is into another style, it would ask the new server for a font it may not serve. The labels capability rebuilds its layers on the new style instead.
Optional, like IMapAdapter.buildStyleChangeTransform: an adapter without it
carries nothing it did not create. Call it as adapter.declareOwnedStyleIds?.(…).
A stable key naming the declarer, e.g. "measure" or "cog:<layerId>".
The engine ids to carry, or null to withdraw the declaration.
OptionalenterEnters exclusive mode: a tool now owns the map's clicks and cursor. While it holds, the core opens no feature popup on click and its hover handlers leave the cursor alone.
With cursor, that CSS cursor is painted on the drawing canvas — the element that shows
the pointer (see IMapAdapter.getCanvas) — and the one shown before is kept, to
be restored by IMapAdapter.exitExclusiveMode. Without it, only the mode is
entered and the cursor is left as it is, for a tool that manages its own or shows none.
There is no ownership token: tools exclude each other. A tool exits the others before it enters; the last entry wins, and any exit releases the mode. Entering twice keeps the cursor shown before the first entry.
Optional, like IMapAdapter.declareOwnedStyleIds: an adapter without it offers no
exclusive mode, and nothing throws. Call it as adapter.enterExclusiveMode?.(…).
Optionalcursor: string
CSS cursor keyword, e.g. "crosshair"; omit it to leave the cursor alone.
OptionalexitExits exclusive mode: the core's click and hover handlers resume. When IMapAdapter.enterExclusiveMode painted a cursor, the one shown before it comes back; otherwise the cursor is left untouched.
Safe without a prior entry, and releases the mode whichever tool entered it — see
IMapAdapter.enterExclusiveMode for why there is no token. Optional: call it as
adapter.exitExclusiveMode?.().
OptionalisReports whether a tool currently holds exclusive mode — for a handler that must stand aside while another tool owns the map, e.g. one that writes the cursor on hover.
Optional: call it as adapter.isExclusiveMode?.() === true, which reads false on an
adapter that offers no exclusive mode.
true between an IMapAdapter.enterExclusiveMode and the next exit.
OptionalreregisterPuts back the runtime images (hatch patterns, POI sprite icons) a style change may have left behind. Sources and layers are carried by the style transform; images are not part of the style spec, and an engine that rebuilds the style instead of diffing it starts with none. Only what is missing is added.
Called by the basemap registry inside the post-swap style.load handler.
OptionaladdAdds a native vector-tile source and its styled sub-layers (fill / line /
circle / fill-extrusion / casing), registering them in the adapter's layer
registry. Returns the created sub-layer ids (for interaction binding and layer
bookkeeping). Engine-specific (vector tiles are a MapLibre-native concept) — a
non-MapLibre adapter may omit it. Socle B.1: the vector-tiles capability
resolves config/style and delegates all rendering here.
OptionalupdateUpdates the paint of an existing vector-tile layer's sub-layers from raw GeoLeaf style objects (normalised by the adapter). Companion to addVectorTileLayer.
Engine-agnostic map adapter interface.
MaplibreAdapterimplements this interface. All GeoLeaf modules interact with the map exclusively through this interface — nomaplibregl.*references outside of the adapter implementation.Id-based management: Layers, markers, and cluster groups are managed by string identifiers. The adapter stores native engine objects internally — callers never interact with MapLibre overlays directly.
Opaque handles:
createPopup()returnsunknownbecause the underlying type differs between engines. Callers must treat popup handles as opaque and pass them back toopenPopup()/closePopup().Example