@nika-js/onlymap 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/onlymap.code-snippets +13 -0
- package/LICENSE.md +4 -0
- package/README.md +30 -7
- package/THIRD-PARTY-LICENSES.md +7874 -0
- package/dist/{Arrow.dom-7AXne1TU.js → Arrow.dom-BNjbQ9jb.js} +23 -23
- package/dist/LercDecode.es-CVmNpXBp.js +303 -0
- package/dist/assets/LercDecode.es-BJb2dZu5.js +305 -0
- package/dist/assets/__vite-browser-external-DDArsqdc.js +29 -0
- package/dist/assets/lerc-wasm.wasm +0 -0
- package/dist/assets/lzw-BY5a_5nb.js +83 -0
- package/dist/assets/worker-DtIrTJB7.js +305 -0
- package/dist/assets/zstd-QGAv3PMm.js +383 -0
- package/dist/{basemap-BdvB-SHy.js → basemap-BSb-g03Z.js} +5729 -5684
- package/dist/basemap-registry.d.ts +10 -0
- package/dist/basemap.d.ts +23 -0
- package/dist/color.d.ts +9 -0
- package/dist/{convert-arrow-schema-CvZ3cT5m.js → convert-arrow-schema-DrAihRf9.js} +31 -31
- package/dist/ctx.d.ts +3 -0
- package/dist/deck.d.ts +28 -0
- package/dist/deck.js +16 -0
- package/dist/elements/om-map.d.ts +29 -0
- package/dist/html-data.d.ts +2 -2
- package/dist/index-CIEkseeV.js +3108 -0
- package/dist/index-D67FynN5.js +594 -0
- package/dist/{index-CJOMgp8k.js → index-QSkZjzIL.js} +4 -4
- package/dist/index-WeMwLoaQ.js +1446 -0
- package/dist/{index-BxzDKZpu.js → index-lV5eG2Xt.js} +22439 -21347
- package/dist/index.d.ts +13 -0
- package/dist/ir-snapshot.d.ts +2 -0
- package/dist/ir.d.ts +10 -0
- package/dist/layer-registry.d.ts +15 -1
- package/dist/legend-spec.d.ts +66 -0
- package/dist/lerc-DJzRIT6z.js +30 -0
- package/dist/lerc-wasm.wasm +0 -0
- package/dist/lzw-BY5a_5nb.js +83 -0
- package/dist/mgrs-BY9bIvp4.js +799 -0
- package/dist/onlymapjs.css +1 -1
- package/dist/onlymapjs.js +55 -35
- package/dist/programmatic.d.ts +35 -0
- package/dist/raster-BKf1dx54.js +8843 -0
- package/dist/raster.d.ts +61 -0
- package/dist/react/om-layer.d.ts +2 -0
- package/dist/react.js +18 -16
- package/dist/{recordbatch-HRu0SMKp.js → recordbatch-Bpc0uxFn.js} +200 -200
- package/dist/runtime-core.d.ts +73 -1
- package/dist/scene-lighting.d.ts +83 -0
- package/dist/selection.d.ts +1 -1
- package/dist/snapshot.d.ts +14 -0
- package/dist/{table-accessors-DBjWgN0C.js → table-accessors-CYWTzpQI.js} +71 -76
- package/dist/terrain.d.ts +113 -0
- package/dist/version.d.ts +1 -1
- package/dist/zstd-jXobGRcq.js +14 -0
- package/docs/custom-layers.md +99 -0
- package/llms.txt +5 -5
- package/onlymapjs.html-data.json +163 -29
- package/package.json +35 -15
- package/skills/onlymapjs/SKILL.md +3 -1
- package/skills/onlymapjs/references/syntax.md +53 -3
- package/dist/index-BVQ0s1Zh.js +0 -1457
- package/dist/index-CvBHiOGD.js +0 -605
- package/dist/index-Vc8QfNEf.js +0 -3907
- package/dist/onlymapjs.umd.cjs +0 -8634
package/llms.txt
CHANGED
|
@@ -11,20 +11,20 @@ OnlyMapJS is NOT raw deck.gl and NOT generic HTML/JSX. The rules below are the d
|
|
|
11
11
|
- Accessors are expressions in attributes, not JS functions: `get-position="[$lon, $lat]"`, `get-radius="$population * 0.001"`. `$field` reads a datum field; the library resolves flat vs. GeoJSON shape for you — never write `d.properties.x`.
|
|
12
12
|
- Inline event handlers (`onclick="..."`) are rejected. Use `data-emit` attributes (`<span data-emit="hide-overlay" data-target="popup1">`) or `addEventListener` inside a `<script type="om/widget">` block where `ctx` is in scope.
|
|
13
13
|
- `scale()` requires an explicit `domain=`: `get-fill-color="scale($depth, sequential, ['#ffffcc','#800026'], domain=[0,700])"`. A missing domain is a validation error.
|
|
14
|
-
- `id` is required on every `<om-layer>`. `label` and `color` feed the legend; an explicit `get-fill-color` overrides `color` for rendering (both together is valid and common).
|
|
14
|
+
- `id` is required on every `<om-layer>`. `label` and `color` feed the legend; an explicit `get-fill-color` overrides `color` for rendering (both together is valid and common). The legend widget also reads `get-fill-color` itself: a `sequential`/`diverging` scale renders as a gradient ramp with domain labels, a `threshold` scale as discrete class ranges, and an equality ternary chain (`$f == 'a' ? '#c1' : '#c2'`) as a category palette — write those canonical shapes and the legend describes the symbology automatically.
|
|
15
15
|
- Full JavaScript in accessor blocks needs the `js` attribute on the layer (`<om-layer js>` + `<script type="om/accessors">`). Without it, blocks are restricted to `export const name = d => <expression>` — no statements, no loops, no nested functions.
|
|
16
16
|
|
|
17
17
|
## Element vocabulary
|
|
18
18
|
|
|
19
|
-
- `<om-map center="[lng, lat]" zoom="11" pitch="55" bearing="20" basemap="positron">` — the root. `basemap` accepts a free preset (`liberty`, `bright`, `positron`, `dark-matter`, `voyager`, `osm` — no keys; or `maptiler-streets`/`maptiler-dataviz`/`maptiler-satellite` with `basemap-key="…"` or `OmMap.configureBasemap({ maptilerKey })`), `maplibre` (bare demo style), a style URL (e.g. a MapTiler-customized style), or `none` (standalone canvas). The attribute is LIVE — writing it switches the basemap in place (camera + layers survive); the `set-basemap {basemap}` action and `<om-widget type="basemap-switcher" options="positron dark-matter osm">` do the same. Register more via `OmMap.registerBasemap(name, { style })`. Required provider attribution renders automatically (`attribution="false"` opts out). `pitch`/`bearing` tilt the initial camera (use for 3D content). `validate` attribute enables live validation + on-page error panel. Include a `map-id="<random UUID>"` on complete pages (identifies the map artifact for usage telemetry — not a visitor id; the page author deletes it to opt out); `telemetry="off"` disables usage telemetry + library-error reporting for the map (global: `OmMap.configureTelemetry({ disabled: true })`; schema: docs/telemetry.md). Free plan limits: 5 layers, 25k rows per layer — layers past a limit don't render and validation says why; a `license-key="om_live_…"` attribute (publishable, origin-restricted) or `OmMap.configureLicense(key)` lifts limits and removes the corner badge. When a user's map exceeds free limits, say so and point at the validation message rather than silently restructuring their data.
|
|
20
|
-
- `<om-layer id="..." type="ScatterplotLayer" data="./points.json">` — any deck.gl layer class by `type` (all 33 bundled), plus `PopupLayer` (WebGL badges/labels at scale: `layout="badge|pin-label|card"`, `min-zoom`/`max-zoom`). Data: `data` URL (JSON, GeoJSON, CSV/TSV `.csv` — parsed to typed columns — Shapefile `.shp` (+`.dbf` attributes) and KML `.kml` as GeoJSON features, or Arrow IPC `.arrow`/`.feather` — large point datasets stay columnar, GeoArrow line/polygon geometry becomes GeoJSON features, zstd-compressed IPC is handled; other formats plug in via `OmMap.registerFormat({match, parse})
|
|
21
|
-
- `<om-widget type="legend|layer-switcher|basemap-switcher|zoom-controls|undo-redo|scale-bar|attribution|filter|vega-lite" position="bottom-right">` — static UI panels. No `type` + HTML + `<script type="om/widget">` = custom widget with `ctx` (`ctx.layers`, `ctx.data(id)`, `ctx.dataInViewport(id)`, `ctx.stats(id, field)`, `ctx.viewport`, `ctx.selection`, `ctx.emit(action, payload)`), `this.watch = ['data:<layerId>', 'viewport', 'selection', 'layers', 'history', 'basemap']` (`layers` also fires on visibility/filter changes; `basemap` on basemap switches; `history` on undo/redo availability), `this.$(sel)`, `vegaEmbed`/`d3` as globals.
|
|
19
|
+
- `<om-map center="[lng, lat]" zoom="11" pitch="55" bearing="20" basemap="positron">` — the root. `basemap` accepts a free preset (`liberty`, `bright`, `positron`, `dark-matter`, `voyager`, `osm` — no keys; or `maptiler-streets`/`maptiler-dataviz`/`maptiler-satellite` with `basemap-key="…"` or `OmMap.configureBasemap({ maptilerKey })`), `maplibre` (bare demo style), a style URL (e.g. a MapTiler-customized style; any scheme fetch supports, including desktop asset protocols like Tauri's, query strings fine), or `none` (standalone canvas). The attribute is LIVE — writing it switches the basemap in place (camera + layers survive); the `set-basemap {basemap}` action and `<om-widget type="basemap-switcher" options="positron dark-matter osm">` do the same. Register more via `OmMap.registerBasemap(name, { style })`. Required provider attribution renders automatically (`attribution="false"` opts out). `pitch`/`bearing` tilt the initial camera (use for 3D content). Terrain: `terrain="terrarium"` (keyless AWS DEM; also `maptiler-terrain` with a key, a raw `{z}/{x}/{y}` DEM URL + `terrain-decoder="terrarium|mapbox-rgb|<JSON>"`, or `off`) raises a 3D surface — geographic layers DRAPE onto it automatically (per-layer `terrain="drape|offset|off"` overrides; 3D-model layers sit ON it), `terrain-exaggeration` scales relief, `terrain-texture` drapes imagery; terrain REPLACES an active basemap while on (restored when off); `set-terrain` action, `terrain` watch token, `OmMap.registerTerrain(name, {...})` for more sources. Scene lighting for 3D content: `lighting="daylight|studio|flat|custom"` (absent = deck defaults) with `lighting-ambient`/`lighting-sun`/`lighting-sun-azimuth`/`lighting-sun-elevation`/`lighting-camera` overrides and `lighting-sun-date` (ISO/epoch — solar-position sun computed at the map center, wins over azimuth/elevation); attribute-backed (undoable, live-editable), story-steppable via the `set-lighting {lighting, sunAzimuth, ...}` action (`lighting="default"` removes the attribute set; a bare preset is a clean reset — stale overrides clear), native UI via `<om-widget type="lighting">` (preset radios + tuning sliders), widget watch token `lighting`. GeoJsonLayer extrudes polygons declaratively: `extruded get-elevation="$height"` (+ `wireframe`). `validate` attribute enables live validation + on-page error panel. Include a `map-id="<random UUID>"` on complete pages (identifies the map artifact for usage telemetry — not a visitor id; the page author deletes it to opt out); `telemetry="off"` disables usage telemetry + library-error reporting for the map (global: `OmMap.configureTelemetry({ disabled: true })`; schema: docs/telemetry.md). Free plan limits: 5 layers, 25k rows per layer — layers past a limit don't render and validation says why; a `license-key="om_live_…"` attribute (publishable, origin-restricted) or `OmMap.configureLicense(key)` lifts limits and removes the corner badge. When a user's map exceeds free limits, say so and point at the validation message rather than silently restructuring their data. Events on the element: `om-map-ready` (boot complete; `await mapEl.ready` is the promise twin), `om-validation-error`, `om-view-changed` (camera settled after a move, debounced; `detail = {longitude, latitude, zoom, pitch, bearing}` — the camera-persistence hook). `await mapEl.snapshot()` returns a canvas-only PNG dataURL of the scene (basemap + layers at device pixels; `{as:"blob"}` for files) — DOM widgets/overlays/attribution are NOT captured, so exports must render provider credits themselves.
|
|
20
|
+
- `<om-layer id="..." type="ScatterplotLayer" data="./points.json">` — any deck.gl layer class by `type` (all 33 bundled), plus `PopupLayer` (WebGL badges/labels at scale: `layout="badge|pin-label|card"`, `min-zoom`/`max-zoom`). External layer classes register via `OmMap.registerLayer({type, deckClass, props})` — build them on `@nika-js/onlymap/deck` (the bundled `CompositeLayer`/`TileLayer`/… re-exports), never a separately-installed deck.gl (different class hierarchy, breaks in the renderer); function-valued props ride the subclass's `static defaultProps`; register at module top level BEFORE the manifest mounts (see docs/custom-layers.md). Data: `data` URL (JSON, GeoJSON, CSV/TSV `.csv` — parsed to typed columns — Shapefile `.shp` (+`.dbf` attributes) and KML `.kml` as GeoJSON features, or Arrow IPC `.arrow`/`.feather` — large point datasets stay columnar, GeoArrow line/polygon geometry becomes GeoJSON features, zstd-compressed IPC is handled; other formats plug in via `OmMap.registerFormat({match, parse})`; data URLs accept any scheme the runtime's fetch supports — desktop webviews (Tauri, Electron) pass asset-protocol URLs straight in), inline `<script type="application/json">` (row arrays or column-oriented `{"columns": {"lon": [...], "lat": [...]}}`), or `wss://` streaming (`key="mmsi"` upserts entities in place, `flush="250ms"` coalesces bursts, `source="name"` selects a `OmMap.registerSource` decoder plugin), or a polled REST snapshot (`refresh="5s"` re-fetches and replaces — for live endpoints that return the full current state). Authenticated endpoints: call `OmMap.configureData({ headers: {...} })` in a script — never put tokens in attributes. `$field` accessors work identically on all of them — never write column-index code yourself. One columnar restriction: the `js` full-JS opt-in is not allowed on Arrow/columnar layers (validation will tell you; use `$field` accessors instead). For 3D models use `type="ScenegraphLayer"` with `scenegraph="./model.glb"` (required) and `get-orientation="[0, $heading, 90]"` — the roll of 90 stands Y-up glTF models upright; see docs/3d-assets.md. GeoTIFF/COG rasters use `type="COGLayer"` with `src="./dem.tif"` (NOT `data` — rasters stream tiles by Range request, they are not parsed rows): `min`/`max` set the rescale window (default 0–255; ALWAYS set them for float/16-bit data like DEMs), `colormap` picks a bundled ramp for single-band sources (gray, viridis, plasma, inferno, magma, cividis, rdylgn, rdbu, spectral, terrain, jet, turbo), `nodata` overrides the source sentinel (renders transparent); plain 8-bit RGB COGs need no styling attributes; restretch/recolor are GPU uniforms (no refetch) and the legend ramp derives from colormap+min/max automatically. Sources must be Cloud-Optimized (`gdal_translate -of COG` otherwise).
|
|
21
|
+
- `<om-widget type="legend|layer-switcher|basemap-switcher|lighting|zoom-controls|undo-redo|scale-bar|attribution|filter|vega-lite" position="bottom-right">` — static UI panels. Built-ins are themeable from page CSS via custom properties (they inherit through the shadow root): `om-map { --om-widget-bg: #111827; --om-widget-fg: #f9fafb; }` — full set: `--om-widget-bg/-fg/-muted/-border/-hover-bg/-accent`; scope to a single widget with an `om-widget[type=legend]` selector instead. No `type` + HTML + `<script type="om/widget">` = custom widget with `ctx` (`ctx.layers`, `ctx.data(id)`, `ctx.dataInViewport(id)`, `ctx.stats(id, field)`, `ctx.viewport`, `ctx.selection`, `ctx.emit(action, payload)`), `this.watch = ['data:<layerId>', 'viewport', 'selection', 'layers', 'history', 'basemap']` (`layers` also fires on visibility/filter changes; `basemap` on basemap switches; `history` on undo/redo availability), `this.$(sel)`, `vegaEmbed`/`d3` as globals.
|
|
22
22
|
- `<om-overlay id="..." anchor-from="selection">` — rich geo-anchored HTML (≤ ~20 per map). Anchors: `anchor="[lng, lat]"` (static), `anchor-from="selection"` (follows picks), or `anchor-layer="regions" anchor-feature-id="mission"` (anchored to a feature's own geometry — bbox center — no coordinates in markup; `{{field}}` interpolates that feature's attributes). `{{field}}` interpolates the picked feature HTML-escaped; `{{{field}}}` is raw (avoid). For labels on many features use `PopupLayer`, not overlays.
|
|
23
23
|
- `<om-behavior on="click|hover|drag|load|data-loaded" layer="..." action="...">` — declarative interaction. Built-in actions: `show-overlay`, `hide-overlay`, `show-tooltip`, `hide-tooltip`, `toggle-layer`, `filter-layer`, `highlight-feature`, `zoom-to-feature`, `set-basemap`, `undo`, `redo`. One payload contract everywhere: `{ layer, target, feature, featureId, coordinate }`.
|
|
24
24
|
- Undo/redo is built in: user-facing manifest changes (layer toggles, filter changes, basemap switches, element add/remove, drawn sketches) are recorded automatically — the manifest is the state. `<om-widget type="undo-redo">` renders the buttons; Cmd/Ctrl-Z, Shift-Cmd/Ctrl-Z, and Ctrl-Y work on any map (text inputs keep their native undo). Camera moves, hover effects, and story playback are deliberately NOT undo steps. Widget scripts: `ctx.history.canUndo/canRedo` with watch token `history`; `ctx.emit("undo")`/`ctx.emit("redo")`.
|
|
25
25
|
- `<om-fallback>` — static no-JS fallback, direct child of `<om-map>` (one per map, no attributes, plain HTML content — links allowed). Shown ONLY where scripts never run (chat-app/email file previews — iOS QuickLook renders HTML attachments with JS off — file managers, sandboxed webviews); hidden automatically once the map boots. GOOD PRACTICE: include one on every complete page, especially pages that may be shared as a file ("This interactive map requires JavaScript — open this file in a web browser", plus a hosted-version link when one exists). Without one, the stylesheet shows a generic text-only banner. The gate is pure CSS (`om-map:not(:defined)` in onlymapjs.css), so the CSS must load without JS — a real `<link rel="stylesheet">` or inlined `<style>` on no-build pages; a bundler-emitted stylesheet is fine in npm projects.
|
|
26
26
|
- Animation: `transition="get-fill-color 800ms, get-radius 400ms"` on a layer GPU-animates prop changes (also smooths streaming updates via `get-position`). Camera: the `fly-to` action takes `center`/`zoom`/`pitch`/`bearing`/`duration` (e.g. `duration="2s"`) — use it in behaviors or `data-emit` buttons; `zoom-to-feature` also accepts `duration`.
|
|
27
|
-
- `<om-story id="tour" autoplay loop interrupt="pause|ignore">` — a storyboard of `<om-step>` children. Each step: `action="..."` + payload attributes (same kebab-case rule as behaviors) + `duration`/`delay`/`parallel` timing. Steps REFERENCE layers/overlays by id (`layer=`/`target=`) — a step must NEVER contain elements (validation error). Control: `<om-widget type="player" story="tour">`, the story-play/story-pause/story-seek actions, or `storyEl.play()/pause()/seek(ms)`. Seeking restores initial state then applies steps before T; use declarative payloads (e.g. `action="toggle-layer" visible="true"`, not bare toggles) so scrubbing is deterministic. Effect verbs as bare step attributes: `<om-step fade layer="regions" duration="1s">` (opacity reveal — start the layer at `opacity="0"`), `pulse` (attention flash), `trace` (progressive draw — whole-layer needs a TripsLayer; add `feature-id="..."` to make ONE polygon/line draw itself on inside any layer, or use it from a click behavior for click-to-trace), `populate` (rows drop in one by one — ordered by the authored filter-field, a payload `field`, or data order).
|
|
27
|
+
- `<om-story id="tour" autoplay loop interrupt="pause|ignore">` — a storyboard of `<om-step>` children. Each step: `action="..."` + payload attributes (same kebab-case rule as behaviors) + `duration`/`delay`/`parallel` timing. Steps REFERENCE layers/overlays by id (`layer=`/`target=`) — a step must NEVER contain elements (validation error). Control: `<om-widget type="player" story="tour">`, the story-play/story-pause/story-seek actions, or `storyEl.play()/pause()/seek(ms)`. Seeking restores initial state then applies steps before T; use declarative payloads (e.g. `action="toggle-layer" visible="true"`, not bare toggles) so scrubbing is deterministic. Scene actions are story-steppable AND scrub-capturable: `set-basemap`, `set-lighting` (a sunset story: steps walking sun-elevation down; a bare preset step is a clean reset), and `set-terrain` all rewind on seek — the story captures the map's scene attributes before first play. Effect verbs as bare step attributes: `<om-step fade layer="regions" duration="1s">` (opacity reveal — start the layer at `opacity="0"`), `pulse` (attention flash), `trace` (progressive draw — whole-layer needs a TripsLayer; add `feature-id="..."` to make ONE polygon/line draw itself on inside any layer, or use it from a click behavior for click-to-trace), `populate` (rows drop in one by one — ordered by the authored filter-field, a payload `field`, or data order).
|
|
28
28
|
- Filtering: `filter-field="magnitude" filter-range="[4, 10]"` on a layer (GPU-side, live-updatable via the `filter-layer` action); pair with `<om-widget type="filter" layer="..." field="...">`.
|
|
29
29
|
|
|
30
30
|
## Decision rule for annotations
|
package/onlymapjs.html-data.json
CHANGED
|
@@ -73,6 +73,87 @@
|
|
|
73
73
|
}
|
|
74
74
|
]
|
|
75
75
|
},
|
|
76
|
+
{
|
|
77
|
+
"name": "lighting",
|
|
78
|
+
"description": "Scene lighting preset for 3D content — seeds ambient/sun/camera values that lighting-* attributes override. Absent = deck.gl default lights.",
|
|
79
|
+
"values": [
|
|
80
|
+
{
|
|
81
|
+
"name": "daylight"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "studio"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "flat"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "custom"
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"name": "lighting-ambient",
|
|
96
|
+
"description": "Ambient light intensity (always-on fill), e.g. 0.9."
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "lighting-sun",
|
|
100
|
+
"description": "Sun (directional light) intensity; 0 removes the sun."
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "lighting-sun-azimuth",
|
|
104
|
+
"description": "Sun compass bearing, degrees clockwise from north [0, 360)."
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"name": "lighting-sun-elevation",
|
|
108
|
+
"description": "Sun height above the horizon, degrees [0, 90]."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "lighting-sun-date",
|
|
112
|
+
"description": "ISO 8601 or epoch ms — computes the sun's azimuth/elevation from solar position at the map center (shadow studies). Overrides azimuth/elevation."
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "lighting-camera",
|
|
116
|
+
"description": "Camera-following fill light intensity (model inspection); 0 removes it."
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"name": "terrain",
|
|
120
|
+
"description": "3D elevation surface: a registered terrain preset, a {z}/{x}/{y} DEM URL (needs terrain-decoder), or \"off\". Replaces an active basemap while on. Geographic layers drape by default (per-layer terrain attribute overrides).",
|
|
121
|
+
"values": [
|
|
122
|
+
{
|
|
123
|
+
"name": "off"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"name": "terrarium"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "maptiler-terrain"
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"name": "terrain-decoder",
|
|
135
|
+
"description": "DEM RGB decoder: \"terrarium\", \"mapbox-rgb\", or {rScaler,gScaler,bScaler,offset} JSON. Required for raw DEM URLs; presets carry their own.",
|
|
136
|
+
"values": [
|
|
137
|
+
{
|
|
138
|
+
"name": "terrarium"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "mapbox-rgb"
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "terrain-exaggeration",
|
|
147
|
+
"description": "Vertical relief multiplier, applied in the DEM decoder; 1 = true relief."
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"name": "terrain-max-zoom",
|
|
151
|
+
"description": "DEM tileset zoom cap — the provider's REAL limit (a too-high cap requests 404 tiles and blanks the terrain)."
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"name": "terrain-texture",
|
|
155
|
+
"description": "Optional {z}/{x}/{y} imagery template draped over the surface (satellite, etc.)."
|
|
156
|
+
},
|
|
76
157
|
{
|
|
77
158
|
"name": "validate",
|
|
78
159
|
"description": "Run manifest validation and show the on-page error panel."
|
|
@@ -135,6 +216,9 @@
|
|
|
135
216
|
{
|
|
136
217
|
"name": "BitmapLayer"
|
|
137
218
|
},
|
|
219
|
+
{
|
|
220
|
+
"name": "COGLayer"
|
|
221
|
+
},
|
|
138
222
|
{
|
|
139
223
|
"name": "ColumnLayer"
|
|
140
224
|
},
|
|
@@ -287,6 +371,21 @@
|
|
|
287
371
|
"name": "highlighted-id",
|
|
288
372
|
"description": "Feature id to highlight (set by the highlight-feature action)."
|
|
289
373
|
},
|
|
374
|
+
{
|
|
375
|
+
"name": "terrain",
|
|
376
|
+
"description": "Behavior under an active map terrain: drape onto the surface, offset (sit on it — the 3D-model default), or off. Absent = the type default.",
|
|
377
|
+
"values": [
|
|
378
|
+
{
|
|
379
|
+
"name": "drape"
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"name": "offset"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
"name": "off"
|
|
386
|
+
}
|
|
387
|
+
]
|
|
388
|
+
},
|
|
290
389
|
{
|
|
291
390
|
"name": "key",
|
|
292
391
|
"description": "Stream entity identity field — messages upsert by this key (wss data)."
|
|
@@ -347,6 +446,34 @@
|
|
|
347
446
|
"name": "get-line-width",
|
|
348
447
|
"description": "Accessor for deck.gl getLineWidth — expression language: $field, scale(), arithmetic."
|
|
349
448
|
},
|
|
449
|
+
{
|
|
450
|
+
"name": "get-point-radius",
|
|
451
|
+
"description": "Accessor for deck.gl getPointRadius — expression language: $field, scale(), arithmetic."
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
"name": "point-radius-units",
|
|
455
|
+
"description": "deck.gl pointRadiusUnits."
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"name": "point-radius-min-pixels",
|
|
459
|
+
"description": "deck.gl pointRadiusMinPixels."
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
"name": "line-width-units",
|
|
463
|
+
"description": "deck.gl lineWidthUnits."
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
"name": "get-elevation",
|
|
467
|
+
"description": "Accessor for deck.gl getElevation — expression language: $field, scale(), arithmetic."
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
"name": "extruded",
|
|
471
|
+
"description": "deck.gl extruded."
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"name": "wireframe",
|
|
475
|
+
"description": "deck.gl wireframe."
|
|
476
|
+
},
|
|
350
477
|
{
|
|
351
478
|
"name": "get-text",
|
|
352
479
|
"description": "Accessor for deck.gl getText — expression language: $field, scale(), arithmetic."
|
|
@@ -435,6 +562,26 @@
|
|
|
435
562
|
"name": "point-size",
|
|
436
563
|
"description": "deck.gl pointSize."
|
|
437
564
|
},
|
|
565
|
+
{
|
|
566
|
+
"name": "src",
|
|
567
|
+
"description": "deck.gl geotiff."
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
"name": "min",
|
|
571
|
+
"description": "deck.gl rescaleMin."
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
"name": "max",
|
|
575
|
+
"description": "deck.gl rescaleMax."
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
"name": "colormap",
|
|
579
|
+
"description": "deck.gl colormap."
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
"name": "nodata",
|
|
583
|
+
"description": "deck.gl nodataOverride."
|
|
584
|
+
},
|
|
438
585
|
{
|
|
439
586
|
"name": "operation",
|
|
440
587
|
"description": "deck.gl operation."
|
|
@@ -595,10 +742,6 @@
|
|
|
595
742
|
"name": "elevation-scale",
|
|
596
743
|
"description": "deck.gl elevationScale."
|
|
597
744
|
},
|
|
598
|
-
{
|
|
599
|
-
"name": "line-width-units",
|
|
600
|
-
"description": "deck.gl lineWidthUnits."
|
|
601
|
-
},
|
|
602
745
|
{
|
|
603
746
|
"name": "line-width-scale",
|
|
604
747
|
"description": "deck.gl lineWidthScale."
|
|
@@ -607,22 +750,10 @@
|
|
|
607
750
|
"name": "line-width-max-pixels",
|
|
608
751
|
"description": "deck.gl lineWidthMaxPixels."
|
|
609
752
|
},
|
|
610
|
-
{
|
|
611
|
-
"name": "extruded",
|
|
612
|
-
"description": "deck.gl extruded."
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
"name": "wireframe",
|
|
616
|
-
"description": "deck.gl wireframe."
|
|
617
|
-
},
|
|
618
753
|
{
|
|
619
754
|
"name": "flat-shading",
|
|
620
755
|
"description": "deck.gl flatShading."
|
|
621
756
|
},
|
|
622
|
-
{
|
|
623
|
-
"name": "get-elevation",
|
|
624
|
-
"description": "Accessor for deck.gl getElevation — expression language: $field, scale(), arithmetic."
|
|
625
|
-
},
|
|
626
757
|
{
|
|
627
758
|
"name": "material",
|
|
628
759
|
"description": "deck.gl material."
|
|
@@ -867,18 +998,10 @@
|
|
|
867
998
|
"name": "point-radius-max-pixels",
|
|
868
999
|
"description": "deck.gl pointRadiusMaxPixels."
|
|
869
1000
|
},
|
|
870
|
-
{
|
|
871
|
-
"name": "point-radius-min-pixels",
|
|
872
|
-
"description": "deck.gl pointRadiusMinPixels."
|
|
873
|
-
},
|
|
874
1001
|
{
|
|
875
1002
|
"name": "point-radius-scale",
|
|
876
1003
|
"description": "deck.gl pointRadiusScale."
|
|
877
1004
|
},
|
|
878
|
-
{
|
|
879
|
-
"name": "point-radius-units",
|
|
880
|
-
"description": "deck.gl pointRadiusUnits."
|
|
881
|
-
},
|
|
882
1005
|
{
|
|
883
1006
|
"name": "point-antialiasing",
|
|
884
1007
|
"description": "deck.gl pointAntialiasing."
|
|
@@ -887,10 +1010,6 @@
|
|
|
887
1010
|
"name": "point-billboard",
|
|
888
1011
|
"description": "deck.gl pointBillboard."
|
|
889
1012
|
},
|
|
890
|
-
{
|
|
891
|
-
"name": "get-point-radius",
|
|
892
|
-
"description": "Accessor for deck.gl getPointRadius — expression language: $field, scale(), arithmetic."
|
|
893
|
-
},
|
|
894
1013
|
{
|
|
895
1014
|
"name": "icon-size-max-pixels",
|
|
896
1015
|
"description": "deck.gl iconSizeMaxPixels."
|
|
@@ -1433,6 +1552,12 @@
|
|
|
1433
1552
|
{
|
|
1434
1553
|
"name": "set-basemap"
|
|
1435
1554
|
},
|
|
1555
|
+
{
|
|
1556
|
+
"name": "set-lighting"
|
|
1557
|
+
},
|
|
1558
|
+
{
|
|
1559
|
+
"name": "set-terrain"
|
|
1560
|
+
},
|
|
1436
1561
|
{
|
|
1437
1562
|
"name": "undo"
|
|
1438
1563
|
},
|
|
@@ -1489,6 +1614,9 @@
|
|
|
1489
1614
|
{
|
|
1490
1615
|
"name": "basemap-switcher"
|
|
1491
1616
|
},
|
|
1617
|
+
{
|
|
1618
|
+
"name": "lighting"
|
|
1619
|
+
},
|
|
1492
1620
|
{
|
|
1493
1621
|
"name": "undo-redo"
|
|
1494
1622
|
}
|
|
@@ -1514,7 +1642,7 @@
|
|
|
1514
1642
|
},
|
|
1515
1643
|
{
|
|
1516
1644
|
"name": "watch",
|
|
1517
|
-
"description": "Space-separated watch tokens: viewport, selection, layers, data:<layerId>."
|
|
1645
|
+
"description": "Space-separated watch tokens: viewport, selection, layers, history, basemap, lighting, terrain, data:<layerId>."
|
|
1518
1646
|
},
|
|
1519
1647
|
{
|
|
1520
1648
|
"name": "layer",
|
|
@@ -1707,6 +1835,12 @@
|
|
|
1707
1835
|
{
|
|
1708
1836
|
"name": "set-basemap"
|
|
1709
1837
|
},
|
|
1838
|
+
{
|
|
1839
|
+
"name": "set-lighting"
|
|
1840
|
+
},
|
|
1841
|
+
{
|
|
1842
|
+
"name": "set-terrain"
|
|
1843
|
+
},
|
|
1710
1844
|
{
|
|
1711
1845
|
"name": "undo"
|
|
1712
1846
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nika-js/onlymap",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Declarative deck.gl maps for HTML and React \u2014 interactive WebGL mapping with GeoJSON/CSV/Arrow data, MapLibre basemaps, widgets, popups, and live streams from a custom-element manifest or typed React components. TypeScript, no build step.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -17,16 +17,28 @@
|
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"deck.gl",
|
|
20
|
+
"deckgl",
|
|
21
|
+
"map",
|
|
20
22
|
"maps",
|
|
23
|
+
"mapping",
|
|
21
24
|
"webgl",
|
|
22
25
|
"geospatial",
|
|
23
26
|
"gis",
|
|
27
|
+
"geojson",
|
|
28
|
+
"react",
|
|
29
|
+
"typescript",
|
|
24
30
|
"declarative",
|
|
25
31
|
"custom-elements",
|
|
26
32
|
"web-components",
|
|
27
33
|
"maplibre",
|
|
34
|
+
"cartography",
|
|
28
35
|
"dataviz",
|
|
36
|
+
"data-visualization",
|
|
29
37
|
"visualization",
|
|
38
|
+
"visualisation",
|
|
39
|
+
"heatmap",
|
|
40
|
+
"layers",
|
|
41
|
+
"geoarrow",
|
|
30
42
|
"llm",
|
|
31
43
|
"agent"
|
|
32
44
|
],
|
|
@@ -42,7 +54,6 @@
|
|
|
42
54
|
".": {
|
|
43
55
|
"types": "./dist/index.d.ts",
|
|
44
56
|
"import": "./dist/onlymapjs.js",
|
|
45
|
-
"require": "./dist/onlymapjs.umd.cjs",
|
|
46
57
|
"default": "./dist/onlymapjs.js"
|
|
47
58
|
},
|
|
48
59
|
"./react": {
|
|
@@ -50,6 +61,11 @@
|
|
|
50
61
|
"import": "./dist/react.js",
|
|
51
62
|
"default": "./dist/react.js"
|
|
52
63
|
},
|
|
64
|
+
"./deck": {
|
|
65
|
+
"types": "./dist/deck.d.ts",
|
|
66
|
+
"import": "./dist/deck.js",
|
|
67
|
+
"default": "./dist/deck.js"
|
|
68
|
+
},
|
|
53
69
|
"./onlymapjs.css": "./dist/onlymapjs.css",
|
|
54
70
|
"./onlymapjs.html-data.json": "./onlymapjs.html-data.json",
|
|
55
71
|
"./onlymap.code-snippets": "./.vscode/onlymap.code-snippets",
|
|
@@ -73,6 +89,7 @@
|
|
|
73
89
|
"dist",
|
|
74
90
|
"README.md",
|
|
75
91
|
"LICENSE.md",
|
|
92
|
+
"THIRD-PARTY-LICENSES.md",
|
|
76
93
|
"docs",
|
|
77
94
|
"llms.txt",
|
|
78
95
|
"onlymapjs.html-data.json",
|
|
@@ -80,7 +97,7 @@
|
|
|
80
97
|
],
|
|
81
98
|
"scripts": {
|
|
82
99
|
"dev": "vite",
|
|
83
|
-
"build": "npm run typecheck && vite build && vite build --config vite.react.config.ts && npm run build:types",
|
|
100
|
+
"build": "npm run typecheck && npm run gen:licenses && vite build && vite build --config vite.react.config.ts && vite build --config vite.deck.config.ts && npm run build:types",
|
|
84
101
|
"build:types": "tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
85
102
|
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.e2e.json --noEmit && tsc -p cloud/workers/telemetry/tsconfig.json",
|
|
86
103
|
"test": "vitest run",
|
|
@@ -91,17 +108,19 @@
|
|
|
91
108
|
"gen:public": "npm run gen:html-data && vite-node dev/build-public.ts",
|
|
92
109
|
"test:public": "npm run gen:public -- --dry-run --strict",
|
|
93
110
|
"deploy:telemetry": "wrangler deploy --config cloud/workers/telemetry/wrangler.toml",
|
|
94
|
-
"dev:telemetry": "wrangler dev --config cloud/workers/telemetry/wrangler.toml"
|
|
111
|
+
"dev:telemetry": "wrangler dev --config cloud/workers/telemetry/wrangler.toml",
|
|
112
|
+
"gen:licenses": "node dev/gen-third-party-licenses.mjs"
|
|
95
113
|
},
|
|
96
|
-
"comment:deps": "This library ships a fully self-contained bundle in dist/ (verified: zero external bare imports), so it has NO runtime dependencies
|
|
114
|
+
"comment:deps": "This library ships a fully self-contained bundle in dist/ (verified: zero external bare imports), so it has NO runtime dependencies \u2014 deck.gl, loaders.gl, MapLibre, Arrow, d3, acorn, etc. are build-time-only and get bundled by Vite. Keeping them here (not in dependencies) is what stops every consumer install from pulling ~250 MB it never uses.",
|
|
97
115
|
"devDependencies": {
|
|
98
|
-
"@deck.gl/aggregation-layers": "
|
|
99
|
-
"@deck.gl/core": "
|
|
100
|
-
"@deck.gl/extensions": "
|
|
101
|
-
"@deck.gl/geo-layers": "
|
|
102
|
-
"@deck.gl/layers": "
|
|
103
|
-
"@deck.gl/mapbox": "
|
|
104
|
-
"@deck.gl/mesh-layers": "
|
|
116
|
+
"@deck.gl/aggregation-layers": "9.3.5",
|
|
117
|
+
"@deck.gl/core": "9.3.5",
|
|
118
|
+
"@deck.gl/extensions": "9.3.5",
|
|
119
|
+
"@deck.gl/geo-layers": "9.3.5",
|
|
120
|
+
"@deck.gl/layers": "9.3.5",
|
|
121
|
+
"@deck.gl/mapbox": "9.3.5",
|
|
122
|
+
"@deck.gl/mesh-layers": "9.3.5",
|
|
123
|
+
"@developmentseed/deck.gl-geotiff": "^0.7.0",
|
|
105
124
|
"@loaders.gl/csv": "^4.4.3",
|
|
106
125
|
"@loaders.gl/kml": "^4.4.3",
|
|
107
126
|
"@loaders.gl/shapefile": "^4.4.3",
|
|
@@ -132,5 +151,6 @@
|
|
|
132
151
|
"vitest": "^4.1.9",
|
|
133
152
|
"wrangler": "^4.110.0",
|
|
134
153
|
"ws": "^8.21.0"
|
|
135
|
-
}
|
|
136
|
-
|
|
154
|
+
},
|
|
155
|
+
"comment:deck-pin": "deck.gl/luma.gl devDeps are EXACT pins: they get BUNDLED into dist, and both libraries hard-throw on duplicate-version detection \u2014 consumers that also ship their own deck.gl (nika-agent) must match these versions exactly, so bumps are breaking-coordination events, never side effects of a reinstall."
|
|
156
|
+
}
|
|
@@ -23,7 +23,7 @@ Use OnlyMapJS as a declarative HTML map library. Write custom elements such as `
|
|
|
23
23
|
</script>
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
For no-build CDN examples, use a published module URL such as `https://esm.sh/@nika-js/onlymap@0.
|
|
26
|
+
For no-build CDN examples, use a published module URL such as `https://esm.sh/@nika-js/onlymap@0.3.1`.
|
|
27
27
|
|
|
28
28
|
## React Projects
|
|
29
29
|
|
|
@@ -51,6 +51,7 @@ Load the smallest reference needed for the task:
|
|
|
51
51
|
- Attribute names are kebab-case: `get-fill-color`, `radius-units`, `line-width-min-pixels`.
|
|
52
52
|
- Accessor values are expressions: `get-position="[$lon, $lat]"`.
|
|
53
53
|
- `scale()` always needs an explicit `domain=`.
|
|
54
|
+
- Prefer canonical color expressions — a `sequential`/`diverging`/`threshold` `scale()` or an equality ternary chain — over hand-rolled arithmetic: the legend widget parses these shapes and renders a matching gradient ramp / class ranges / category palette automatically.
|
|
54
55
|
- Inline handlers such as `onclick` are wrong. Use `data-emit`, `<om-behavior>`, or widget scripts.
|
|
55
56
|
- Full JavaScript accessor blocks require the `js` attribute on `<om-layer>`.
|
|
56
57
|
- Do not put secrets in markup. Use `OmMap.configureData({ headers, credentials, fetch })`.
|
|
@@ -63,6 +64,7 @@ Load the smallest reference needed for the task:
|
|
|
63
64
|
- Guided tour or narrative sequence -> `<om-story>` with `<om-step>` siblings that reference existing layers/overlays by id.
|
|
64
65
|
- Basemap choice or user-switchable basemaps -> `basemap` presets (`positron`, `liberty`, `dark-matter`, `osm`, ...) + `<om-widget type="basemap-switcher">`; MapTiler custom styles via a style URL or `basemap-key`.
|
|
65
66
|
- Undoable UI (step back after layer toggles, filter changes, basemap switches, sketch edits) -> `<om-widget type="undo-redo">`; Cmd/Ctrl-Z works even without the widget. Camera moves and story playback are not undo steps.
|
|
67
|
+
- GeoTIFF/COG raster (DEM, satellite imagery, NDVI) -> `<om-layer type="COGLayer" src="…tif">` with `min`/`max`/`colormap` for single-band data (see syntax.md — `src`, not `data`).
|
|
66
68
|
- Live entity updates -> `wss://` stream with `key` and optional `source` decoder.
|
|
67
69
|
- REST snapshot that changes over time -> `refresh="5s"`.
|
|
68
70
|
- User sketching -> `data="draw:sketch"` layer plus `<om-widget type="draw" target="sketch">`.
|
|
@@ -33,14 +33,20 @@ Common attributes:
|
|
|
33
33
|
- `zoom="11"`
|
|
34
34
|
- `pitch="55"`
|
|
35
35
|
- `bearing="20"`
|
|
36
|
-
- `basemap` — a free preset (`liberty`, `bright`, `positron`, `dark-matter`, `voyager`, `osm`; keyed `maptiler-streets|dataviz|satellite` with `basemap-key="…"` or `OmMap.configureBasemap({ maptilerKey })`), `maplibre` (bare demo style), a MapLibre style URL, or `none` (standalone canvas). The attribute is live: writing it switches the basemap in place (camera and layers survive). Register more with `OmMap.registerBasemap(name, { style })`.
|
|
36
|
+
- `basemap` — a free preset (`liberty`, `bright`, `positron`, `dark-matter`, `voyager`, `osm`; keyed `maptiler-streets|dataviz|satellite` with `basemap-key="…"` or `OmMap.configureBasemap({ maptilerKey })`), `maplibre` (bare demo style), a MapLibre style URL (any scheme fetch supports, including desktop asset protocols; query strings fine), or `none` (standalone canvas). The attribute is live: writing it switches the basemap in place (camera and layers survive). Register more with `OmMap.registerBasemap(name, { style })`.
|
|
37
37
|
- `attribution="false"` to opt out of the automatic provider-attribution control (only if you render equivalent credits yourself)
|
|
38
38
|
- `validate` to show live validation errors during authoring
|
|
39
39
|
- `map-id="<random UUID>"` — identifies the map artifact for usage telemetry (not a visitor id). Include one on new complete pages, freshly generated — never copied from an example; deleting it is the opt-out.
|
|
40
40
|
- `telemetry="off"` disables usage telemetry for this map (global: `OmMap.configureTelemetry({ disabled: true })`)
|
|
41
41
|
- `license-key="om_live_…"` — lifts the free-plan limits (5 layers, 25k rows per layer) and removes the corner badge; publishable origin-restricted token, safe in page source (or `OmMap.configureLicense(key)` once). Free-plan violations don't break the map: the offending layer isn't rendered and validation names the limit.
|
|
42
|
+
- `terrain="terrarium|<preset>|<{z}/{x}/{y} DEM URL>|off"` — 3D elevation surface. `terrarium` is keyless (AWS); `maptiler-terrain` needs `basemap-key`/`configureBasemap`; raw DEM URLs need `terrain-decoder` (`terrarium`, `mapbox-rgb`, or `{rScaler,gScaler,bScaler,offset}` JSON). `terrain-exaggeration` scales relief (1 = true); `terrain-max-zoom` = the provider's REAL tileset cap; `terrain-texture` drapes a `{z}/{x}/{y}` imagery template. Geographic layers drape automatically; per-layer `terrain="drape|offset|off"` overrides (3D-model layers default to `offset`). Terrain REPLACES an active basemap while on (restored when off) — validation warns. Register presets with `OmMap.registerTerrain(name, {...})`; `set-terrain` action + `terrain` watch token; attribute-backed (undoable).
|
|
43
|
+
- `lighting="daylight|studio|flat|custom"` — scene lighting for 3D content (extruded polygons, models); absent = deck defaults. Preset seeds values; `lighting-ambient`, `lighting-sun` (intensity; 0 removes the sun), `lighting-sun-azimuth` (° CW from north), `lighting-sun-elevation` (° above horizon), `lighting-camera` (model-inspection fill) override individual fields; `lighting-sun-date` (ISO 8601 or epoch ms) computes the sun from solar position at the map center and wins over azimuth/elevation. Attribute-backed: changes are undoable, and the `set-lighting {lighting, sunAzimuth, …}` action makes lighting story-steppable (`lighting="default"` removes the whole attribute set; a bare preset is a clean reset). `<om-widget type="lighting">` is the native UI. Widget scripts can `watch = ["lighting"]`.
|
|
42
44
|
- `headless width="800" height="600"` for test harness use
|
|
43
45
|
|
|
46
|
+
Events: `om-map-ready` (boot complete; `await mapEl.ready` is the promise twin), `om-validation-error`, and `om-view-changed` — fires once the camera settles after a move (debounced; `detail = {longitude, latitude, zoom, pitch, bearing}`), the hook for persisting the camera. `MapController` takes an `onViewChange` option for the same signal.
|
|
47
|
+
|
|
48
|
+
`await mapEl.snapshot()` (also on `MapController`) returns a canvas-only PNG dataURL of the scene — basemap + layers composited at device pixels (`{as: "blob"}` for files; `type`/`quality` for jpeg/webp). DOM widgets, overlays, and the provider attribution are NOT in the pixels: exports must render credits themselves. Await `ready` first; headless maps reject.
|
|
49
|
+
|
|
44
50
|
Example:
|
|
45
51
|
|
|
46
52
|
```html
|
|
@@ -94,7 +100,7 @@ filter-field="magnitude" filter-range="[4, 10]"
|
|
|
94
100
|
|
|
95
101
|
Use the `type` value exactly:
|
|
96
102
|
|
|
97
|
-
`A5Layer`, `ArcLayer`, `BitmapLayer`, `ColumnLayer`, `ContourLayer`, `GeoJsonLayer`, `GeohashLayer`, `GreatCircleLayer`, `GridCellLayer`, `GridLayer`, `H3ClusterLayer`, `H3HexagonLayer`, `HeatmapLayer`, `HexagonLayer`, `IconLayer`, `LineLayer`, `MVTLayer`, `PathLayer`, `PointCloudLayer`, `PolygonLayer`, `PopupLayer`, `QuadkeyLayer`, `S2Layer`, `ScatterplotLayer`, `ScenegraphLayer`, `ScreenGridLayer`, `SimpleMeshLayer`, `SolidPolygonLayer`, `TerrainLayer`, `TextLayer`, `Tile3DLayer`, `TileLayer`, `TripsLayer`.
|
|
103
|
+
`A5Layer`, `ArcLayer`, `BitmapLayer`, `COGLayer`, `ColumnLayer`, `ContourLayer`, `GeoJsonLayer`, `GeohashLayer`, `GreatCircleLayer`, `GridCellLayer`, `GridLayer`, `H3ClusterLayer`, `H3HexagonLayer`, `HeatmapLayer`, `HexagonLayer`, `IconLayer`, `LineLayer`, `MVTLayer`, `PathLayer`, `PointCloudLayer`, `PolygonLayer`, `PopupLayer`, `QuadkeyLayer`, `S2Layer`, `ScatterplotLayer`, `ScenegraphLayer`, `ScreenGridLayer`, `SimpleMeshLayer`, `SolidPolygonLayer`, `TerrainLayer`, `TextLayer`, `Tile3DLayer`, `TileLayer`, `TripsLayer`.
|
|
98
104
|
|
|
99
105
|
Common choices:
|
|
100
106
|
|
|
@@ -104,6 +110,24 @@ Common choices:
|
|
|
104
110
|
- Aggregation: `HeatmapLayer`, `HexagonLayer`, `GridLayer`, `ScreenGridLayer`.
|
|
105
111
|
- Tiles: `TileLayer`, `MVTLayer`, `Tile3DLayer`.
|
|
106
112
|
- 3D models: `ScenegraphLayer`, `SimpleMeshLayer`, `PointCloudLayer`, `Tile3DLayer`.
|
|
113
|
+
- GeoTIFF/COG rasters: `COGLayer`.
|
|
114
|
+
|
|
115
|
+
### COGLayer (GeoTIFF / COG rasters)
|
|
116
|
+
|
|
117
|
+
```html
|
|
118
|
+
<om-layer id="dem" type="COGLayer" label="Elevation"
|
|
119
|
+
src="./elevation.tif" min="0" max="1900" colormap="viridis" nodata="-9999"></om-layer>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- `src` (required) — the GeoTIFF URL. NOT `data`: rasters stream tiles by HTTP Range request through the layer's own reader; they are never parsed rows (`$field`, `ctx.data()`, `ctx.stats()`, filters do not apply).
|
|
123
|
+
- Sources must be Cloud-Optimized GeoTIFFs (`gdal_translate -of COG` otherwise).
|
|
124
|
+
- `min`/`max` — the rescale window mapped onto the colormap. Defaults to 0–255, so ALWAYS set them for float or 16-bit data (DEMs, NDVI, temperature).
|
|
125
|
+
- `colormap` — single-band ramps from the bundled sprite: `gray` (default), `viridis`, `plasma`, `inferno`, `magma`, `cividis`, `rdylgn`, `rdbu`, `spectral`, `terrain`, `jet`, `turbo`. Sources with 3+ bands composite as RGB and ignore it.
|
|
126
|
+
- `nodata` — overrides the source's nodata sentinel; nodata pixels render transparent.
|
|
127
|
+
- Plain 8-bit RGB COGs (satellite truecolor) need no styling attributes at all.
|
|
128
|
+
- Restretch/recolor (min/max/colormap edits) are GPU uniform updates — tiles are not refetched. The legend widget renders the colormap ramp automatically when `colormap` + `min`/`max` are authored.
|
|
129
|
+
|
|
130
|
+
External layer classes become manifest types via `OmMap.registerLayer({type, deckClass, props})`. Build them on `@nika-js/onlymap/deck` (the bundled `CompositeLayer`/`TileLayer`/… re-exports — a separately-installed deck.gl is a different class hierarchy and breaks in the renderer); function-valued props ride the subclass's `static defaultProps`; register at module top level before the manifest mounts. Full recipe: docs/custom-layers.md.
|
|
107
131
|
|
|
108
132
|
### Data Sources
|
|
109
133
|
|
|
@@ -120,6 +144,8 @@ Common choices:
|
|
|
120
144
|
| Polling | `data="/api/fleet.json" refresh="5s"` | Snapshot replace. |
|
|
121
145
|
| Draw store | `data="draw:sketch"` | Written by draw widget. |
|
|
122
146
|
|
|
147
|
+
Data URLs accept any scheme the runtime's `fetch` supports — desktop webviews (Tauri, Electron) pass asset-protocol URLs (`asset://localhost/…`, custom schemes) straight in; format detection reads the path extension either way.
|
|
148
|
+
|
|
123
149
|
Authenticated fetches:
|
|
124
150
|
|
|
125
151
|
```js
|
|
@@ -173,7 +199,7 @@ Do not use full-JS blocks on columnar/Arrow layers.
|
|
|
173
199
|
|
|
174
200
|
Built-ins:
|
|
175
201
|
|
|
176
|
-
- `legend`
|
|
202
|
+
- `legend` — symbology-aware by default: it parses each layer's `get-fill-color`. A `sequential`/`diverging` `scale()` renders as a gradient ramp with the domain ends labeled; a `threshold` scale as discrete class ranges (`< b1`, `b1 – b2`, `≥ bN`); an equality ternary chain (`$f == 'a' ? '#c1' : $f == 'b' ? '#c2' : '#fallback'`) as a category palette with an "other" row. Any other expression falls back to the single `color` swatch — so writing the canonical shapes buys a self-describing legend for free.
|
|
177
203
|
- `layer-switcher`
|
|
178
204
|
- `zoom-controls`
|
|
179
205
|
- `scale-bar`
|
|
@@ -183,10 +209,22 @@ Built-ins:
|
|
|
183
209
|
- `vega-lite`
|
|
184
210
|
- `player`
|
|
185
211
|
- `basemap-switcher` — radio list of presets; `options="positron dark-matter osm"` (default: every keyless registered preset)
|
|
212
|
+
- `lighting` — scene-lighting controller: preset radios (Off/daylight/studio/flat/custom) + ambient/sun/azimuth/elevation/camera sliders, all over the lighting* attributes via `set-lighting` (undoable; re-syncs when anything else writes them). A bare preset click is a clean RESET (stale lighting-* overrides removed); a slider edit flips to `custom` and sets only the touched key.
|
|
186
213
|
- `undo-redo` — undo/redo buttons over the manifest history (layer toggles, filters, basemap switches, element edits, drawn sketches). Keyboard works without the widget: Cmd/Ctrl-Z, Shift-Cmd/Ctrl-Z, Ctrl-Y. Camera moves, hover effects, and story playback are not undo steps.
|
|
187
214
|
|
|
188
215
|
Positions: `top-left`, `top-right`, `bottom-left`, `bottom-right`.
|
|
189
216
|
|
|
217
|
+
Theming: built-in widgets read `--om-widget-*` CSS custom properties, which inherit through their shadow roots — so plain page CSS themes them, no JS:
|
|
218
|
+
|
|
219
|
+
```css
|
|
220
|
+
/* map-wide (every widget) */
|
|
221
|
+
om-map { --om-widget-bg: #111827; --om-widget-fg: #f9fafb; }
|
|
222
|
+
/* or one widget */
|
|
223
|
+
om-widget[type="legend"] { --om-widget-bg: #111827; --om-widget-fg: #f9fafb; }
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The full set: `--om-widget-bg` (panel/button background), `--om-widget-fg` (text), `--om-widget-muted` (secondary text: legend field/domain labels, disabled buttons, clocks), `--om-widget-border` (separators, button outlines), `--om-widget-hover-bg` (button hover), `--om-widget-accent` (player transport buttons). Unset properties fall back to the stock light palette.
|
|
227
|
+
|
|
190
228
|
Examples:
|
|
191
229
|
|
|
192
230
|
```html
|
|
@@ -322,6 +360,18 @@ Story timing attributes:
|
|
|
322
360
|
|
|
323
361
|
Use declarative payloads for scrub-safe state: `visible="true"` when toggling, explicit `filter-range`, explicit camera target.
|
|
324
362
|
|
|
363
|
+
Scene actions (`set-basemap`, `set-lighting`, `set-terrain`) are story-steppable AND scrub-capturable — the story snapshots the map's scene attributes before first play, so seeking rewinds basemap/lighting/terrain exactly like layer attributes. A sunset storyboard is just steps:
|
|
364
|
+
|
|
365
|
+
```html
|
|
366
|
+
<om-story id="sunset">
|
|
367
|
+
<om-step duration="2s" action="set-lighting" lighting="custom" sun-elevation="35" sun-azimuth="245"></om-step>
|
|
368
|
+
<om-step duration="2s" action="set-lighting" lighting="custom" sun-elevation="8" sun-azimuth="270" ambient="0.5"></om-step>
|
|
369
|
+
<om-step duration="1s" action="set-lighting" lighting="flat"></om-step> <!-- bare preset = clean reset -->
|
|
370
|
+
</om-story>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Lighting swaps are stepwise (the LightingEffect changes at each step's start — no tweening between steps); more steps = smoother sunsets.
|
|
374
|
+
|
|
325
375
|
### Manual Drawing
|
|
326
376
|
|
|
327
377
|
Use a normal GeoJSON layer bound to a draw store plus a draw widget:
|