OptionalcreatePanel?: (root: Element) => anyper-instance Layer Panel factory
OptionalgetMap?: () => anyfallback map accessor for a runtime that doesn't report its map
Optionalinjected?: booleandid FimViz inject widget markup into root
Optionalroot?: Elementthe mount container (query scope)
Optionalteardown?: () => voidruntime-supplied teardown, run by destroy()
All action names registered on THIS instance (excludes the window fallback).
The owning FimViz (default or private) — the up-chain reference.
Is a modal interaction currently capturing events?
This instance's runtime config — always the app that owns THIS map, so two isolated apps on one page never read each other's settings. This is THE way the engine reads config: there is no ambient config pointer (the only module-level runtime value is gdalPath, which is process-global because GDAL is a per-page singleton). See package/config.js.
Datasets parsed on this app — SHARED with every other map on it, exactly like storage.
A Dataset is a free value with no back-pointer to a map, so a file parsed here (by addDataset,
or implicitly by addLayer(rawFile)) is directly usable as a source for a Layer on another
map, of another provider, with no re-parse and no second decode. Rendering state — layers —
stays per-map.
The Layer currently holding the exclusive display slot, or null.
This instance's Layer Panel — the per-instance UI object, lazily built against #root via the
factory mount.js injects (createLayerPanel). The model never imports ui/; the composition root
wires it in, same rule as getMap/teardown. Null when no factory was injected (headless boot).
Replaces the module-level window.layerPanel singleton, so two maps drive their own panels.
the provider's map object — a google.maps.Map, an L.Map, or another provider's type
All currently-registered named user-file Layers.
The mount container element (query scope).
Dispatch mode. false (default) = precedence: top hit layer first, absorption stops propagation. true = simultaneous: every hit-tested layer receives the event (no veto). PACKAGE_ROADMAP §1.
Shared client-side storage (owned by the app).
Scoped query helpers — resolve against this instance's root, not the whole document.
The linchpin for multi-instance: duplicate IDs across instances stop being ambiguous
Prefer these over document.getElementById so two widgets on one page cannot collide.
A bare #id is rewritten to the equivalent [id="..."]. Per spec these are identical, but
some engines optimize #id by routing through document.getElementById — which returns only
the FIRST match in the document and then filters to the subtree, so a scoped lookup finds
NOTHING when ids repeat across instances. That is precisely our case: two widgets both carry
#layer-panel. jsdom/nwsapi does exactly this (browsers do not), so without the rewrite the
property cannot even be tested. The attribute form is engine-independent.
Only an exact #identifier is rewritten — that is what the ~604 getElementById call sites
migrate to. Compound selectors are passed through untouched (rewriting inside them risks
mangling quoted attribute values), so they remain subject to the engine quirk above.
Parse a source (File | Blob | ArrayBuffer | URL) into a Dataset and register it on this instance. Returns the Dataset (not yet rendered — addLayer draws it).
Format comes from the extension: geotiff, geojson, kml, kmz, shp, csv, xyz, and the
multi-dimensional scientific formats (.nc/.nc4/.cdf, .grib/.grib2/.grb2, .zarr), which return a
Dataset carrying a time axis to select() and reduce() over. options is passed through
to the parser — see parseSource for the per-format keys.
Optionaloptions: any = {}
see io/parse.js's parseSource
Create a rendered Layer of type. Dispatches to the subsystem factory registered via
registerLayerType. type can be a bare Dataset instead of a registry string — fim.addLayer(ds)
— or omitted, in either case inferred from the (single) source's Dataset.kind (see
createLayer). A raw File/Blob/ArrayBuffer/URL works too, in the type slot or in
opts.source/opts.sources — it is parsed into a Dataset via addDataset() first (and lands in
this.datasets, same as calling addDataset() yourself), so fim.addLayer(file) needs no separate
addDataset() step. Throws for types with no registered factory. Some factories (e.g. 'raster', whose
RasterLayer.render() is the async base implementation) return a Promise rather than a Layer, so
this must await before pushing — pushing an un-awaited Promise onto this.layers would silently
corrupt the registry for every consumer that iterates it.
Optionaltype: any
a registry name, a bare source to infer from, or a raw file/URL to parse first
Optionalopts: any = {}Draw a GeoJSON overlay that is NOT a Layer — a tool's in-progress shape, a rubber band, a
highlight. It never enters fim.layers, so it is not hit-tested, not reordered, not listed in the
layer panel and not saved: it is scaffolding the user is looking at, not data they loaded.
This exists because the selection tools are headless by design — they produce {lat,lng} and name
no map SDK — which left "show me what I am drawing" with nowhere to live. Putting it here rather
than in fimviz/ui keeps the provider registry (and Leaflet) out of the dist/ui.js bundle.
a Feature or FeatureCollection
Optionalopts: { style?: any } = {}
the neutral style vocabulary VectorLayer uses
an opaque handle to pass to removeScratchVector, or null if there is no map
Register a Dataset parsed elsewhere on this map's app.
Push this instance's layer order down to the map, so what is DRAWN on top matches what
layers says is on top.
Worth being explicit about why this is needed at all: dispatchMapEventToLayers already walks
layers top-down and treats the last entry as the topmost for hit-testing, but visual stacking
has only ever been whatever order the provider happened to insert overlays in. The two could
therefore disagree — the layer that received a click was not necessarily the one drawn on top.
This makes the array authoritative for both.
Attach the delegated listener to this instance's root. Idempotent.
Register a MODAL interaction (e.g. a region-draw tool) that takes ALL map events until released.
While captured, the normal layer dispatch + map:${type} mirror are suppressed — every event
goes only to handler({ type, lat, lng, originalEvent }). Returns a release function; only one
capture at a time (a new one replaces the prior). PACKAGE_ROADMAP §1.
Best-effort teardown for SPA unmount. Detaches the map, removes injected markup, and releases this map from its app (which frees the ambient default's shared services when the last map goes). Module-level singletons in host subsystems are NOT reset.
Stop routing provider map events.
Optionalpayload: anyStart routing provider map events (default: click + hover) to layers via hitTest + z-order
dispatch, and mirror each as map:${type} on the bus. Idempotent; needs a mounted map and a
provider that implements onMapEvent.
Optionaltypes: string[] = ...Optionalopts: { simultaneous?: boolean } = {}
simultaneous:true → every hit layer gets the event
The handler for name — this instance's registry first, then the transitional window bridge.
Register a handler for data-action="name".
Register many at once: registerActions({ foo, bar }).
Optionalmap: {} = {}Register a Layer under a filename key — the user-file registry that floodExtent's
toggle_uploaded_file drives (one displayed user file → one Layer). Wires the up-chain _map
ref so layer.remove() can unregister itself, evicts any prior layer under the same name, and
adds the layer to this.layers.
Release any modal interaction, restoring normal layer dispatch.
Forget a Dataset — drops it from the app registry and releases its decode. Throws while any
layer on this app still renders it (pass { force: true } to override).
Optionalopts: { force?: boolean }Remove a Layer by id or instance — tears down its render and unregisters it.
Turn pan-by-drag on or off.
Exists for drag-based selection: a freehand or brush stroke is the SAME gesture as a map pan, so
one of the two has to give. The tool suppresses dragging for the length of the stroke and
restores it on finish/cancel — which is why restoring is in a finally, not on the happy path.
A no-op when the provider declares no setDraggable, so a caller never has to feature-detect.
Ground metres per screen pixel, plus the map's pixel size — what lets a tool be sized in SCREEN units (a brush that stays the same width as you zoom) without touching a map SDK.
null when unavailable
the owning FimViz