@nika-js/onlymap 0.2.3 → 0.3.0
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/README.md +29 -7
- package/dist/{basemap-BdvB-SHy.js → basemap-DrK6zcu8.js} +2337 -2273
- package/dist/basemap-registry.d.ts +10 -0
- package/dist/basemap.d.ts +23 -0
- package/dist/color.d.ts +9 -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-CJOMgp8k.js → index-C9tgnPNw.js} +1 -1
- package/dist/{index-CvBHiOGD.js → index-CiuGqS0i.js} +2 -2
- package/dist/{index-BxzDKZpu.js → index-CsicbycJ.js} +16599 -15089
- package/dist/{index-BVQ0s1Zh.js → index-DXoRERAy.js} +1 -1
- package/dist/{index-Vc8QfNEf.js → index-Ztkd30f8.js} +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/ir-snapshot.d.ts +2 -0
- package/dist/ir.d.ts +7 -0
- package/dist/onlymapjs.js +54 -35
- package/dist/onlymapjs.umd.cjs +423 -349
- package/dist/programmatic.d.ts +35 -0
- package/dist/react/om-layer.d.ts +2 -0
- package/dist/react.js +18 -16
- 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/terrain.d.ts +113 -0
- package/dist/version.d.ts +1 -1
- package/docs/custom-layers.md +99 -0
- package/llms.txt +4 -4
- package/onlymapjs.html-data.json +140 -29
- package/package.json +20 -3
- package/skills/onlymapjs/SKILL.md +1 -1
- package/skills/onlymapjs/references/syntax.md +24 -1
package/llms.txt
CHANGED
|
@@ -16,15 +16,15 @@ OnlyMapJS is NOT raw deck.gl and NOT generic HTML/JSX. The rules below are the d
|
|
|
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.
|
|
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. 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."
|
|
@@ -287,6 +368,21 @@
|
|
|
287
368
|
"name": "highlighted-id",
|
|
288
369
|
"description": "Feature id to highlight (set by the highlight-feature action)."
|
|
289
370
|
},
|
|
371
|
+
{
|
|
372
|
+
"name": "terrain",
|
|
373
|
+
"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.",
|
|
374
|
+
"values": [
|
|
375
|
+
{
|
|
376
|
+
"name": "drape"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"name": "offset"
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"name": "off"
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
},
|
|
290
386
|
{
|
|
291
387
|
"name": "key",
|
|
292
388
|
"description": "Stream entity identity field — messages upsert by this key (wss data)."
|
|
@@ -347,6 +443,34 @@
|
|
|
347
443
|
"name": "get-line-width",
|
|
348
444
|
"description": "Accessor for deck.gl getLineWidth — expression language: $field, scale(), arithmetic."
|
|
349
445
|
},
|
|
446
|
+
{
|
|
447
|
+
"name": "get-point-radius",
|
|
448
|
+
"description": "Accessor for deck.gl getPointRadius — expression language: $field, scale(), arithmetic."
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"name": "point-radius-units",
|
|
452
|
+
"description": "deck.gl pointRadiusUnits."
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"name": "point-radius-min-pixels",
|
|
456
|
+
"description": "deck.gl pointRadiusMinPixels."
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"name": "line-width-units",
|
|
460
|
+
"description": "deck.gl lineWidthUnits."
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
"name": "get-elevation",
|
|
464
|
+
"description": "Accessor for deck.gl getElevation — expression language: $field, scale(), arithmetic."
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"name": "extruded",
|
|
468
|
+
"description": "deck.gl extruded."
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
"name": "wireframe",
|
|
472
|
+
"description": "deck.gl wireframe."
|
|
473
|
+
},
|
|
350
474
|
{
|
|
351
475
|
"name": "get-text",
|
|
352
476
|
"description": "Accessor for deck.gl getText — expression language: $field, scale(), arithmetic."
|
|
@@ -595,10 +719,6 @@
|
|
|
595
719
|
"name": "elevation-scale",
|
|
596
720
|
"description": "deck.gl elevationScale."
|
|
597
721
|
},
|
|
598
|
-
{
|
|
599
|
-
"name": "line-width-units",
|
|
600
|
-
"description": "deck.gl lineWidthUnits."
|
|
601
|
-
},
|
|
602
722
|
{
|
|
603
723
|
"name": "line-width-scale",
|
|
604
724
|
"description": "deck.gl lineWidthScale."
|
|
@@ -607,22 +727,10 @@
|
|
|
607
727
|
"name": "line-width-max-pixels",
|
|
608
728
|
"description": "deck.gl lineWidthMaxPixels."
|
|
609
729
|
},
|
|
610
|
-
{
|
|
611
|
-
"name": "extruded",
|
|
612
|
-
"description": "deck.gl extruded."
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
"name": "wireframe",
|
|
616
|
-
"description": "deck.gl wireframe."
|
|
617
|
-
},
|
|
618
730
|
{
|
|
619
731
|
"name": "flat-shading",
|
|
620
732
|
"description": "deck.gl flatShading."
|
|
621
733
|
},
|
|
622
|
-
{
|
|
623
|
-
"name": "get-elevation",
|
|
624
|
-
"description": "Accessor for deck.gl getElevation — expression language: $field, scale(), arithmetic."
|
|
625
|
-
},
|
|
626
734
|
{
|
|
627
735
|
"name": "material",
|
|
628
736
|
"description": "deck.gl material."
|
|
@@ -867,18 +975,10 @@
|
|
|
867
975
|
"name": "point-radius-max-pixels",
|
|
868
976
|
"description": "deck.gl pointRadiusMaxPixels."
|
|
869
977
|
},
|
|
870
|
-
{
|
|
871
|
-
"name": "point-radius-min-pixels",
|
|
872
|
-
"description": "deck.gl pointRadiusMinPixels."
|
|
873
|
-
},
|
|
874
978
|
{
|
|
875
979
|
"name": "point-radius-scale",
|
|
876
980
|
"description": "deck.gl pointRadiusScale."
|
|
877
981
|
},
|
|
878
|
-
{
|
|
879
|
-
"name": "point-radius-units",
|
|
880
|
-
"description": "deck.gl pointRadiusUnits."
|
|
881
|
-
},
|
|
882
982
|
{
|
|
883
983
|
"name": "point-antialiasing",
|
|
884
984
|
"description": "deck.gl pointAntialiasing."
|
|
@@ -887,10 +987,6 @@
|
|
|
887
987
|
"name": "point-billboard",
|
|
888
988
|
"description": "deck.gl pointBillboard."
|
|
889
989
|
},
|
|
890
|
-
{
|
|
891
|
-
"name": "get-point-radius",
|
|
892
|
-
"description": "Accessor for deck.gl getPointRadius — expression language: $field, scale(), arithmetic."
|
|
893
|
-
},
|
|
894
990
|
{
|
|
895
991
|
"name": "icon-size-max-pixels",
|
|
896
992
|
"description": "deck.gl iconSizeMaxPixels."
|
|
@@ -1433,6 +1529,12 @@
|
|
|
1433
1529
|
{
|
|
1434
1530
|
"name": "set-basemap"
|
|
1435
1531
|
},
|
|
1532
|
+
{
|
|
1533
|
+
"name": "set-lighting"
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
"name": "set-terrain"
|
|
1537
|
+
},
|
|
1436
1538
|
{
|
|
1437
1539
|
"name": "undo"
|
|
1438
1540
|
},
|
|
@@ -1489,6 +1591,9 @@
|
|
|
1489
1591
|
{
|
|
1490
1592
|
"name": "basemap-switcher"
|
|
1491
1593
|
},
|
|
1594
|
+
{
|
|
1595
|
+
"name": "lighting"
|
|
1596
|
+
},
|
|
1492
1597
|
{
|
|
1493
1598
|
"name": "undo-redo"
|
|
1494
1599
|
}
|
|
@@ -1514,7 +1619,7 @@
|
|
|
1514
1619
|
},
|
|
1515
1620
|
{
|
|
1516
1621
|
"name": "watch",
|
|
1517
|
-
"description": "Space-separated watch tokens: viewport, selection, layers, data:<layerId>."
|
|
1622
|
+
"description": "Space-separated watch tokens: viewport, selection, layers, history, basemap, lighting, terrain, data:<layerId>."
|
|
1518
1623
|
},
|
|
1519
1624
|
{
|
|
1520
1625
|
"name": "layer",
|
|
@@ -1707,6 +1812,12 @@
|
|
|
1707
1812
|
{
|
|
1708
1813
|
"name": "set-basemap"
|
|
1709
1814
|
},
|
|
1815
|
+
{
|
|
1816
|
+
"name": "set-lighting"
|
|
1817
|
+
},
|
|
1818
|
+
{
|
|
1819
|
+
"name": "set-terrain"
|
|
1820
|
+
},
|
|
1710
1821
|
{
|
|
1711
1822
|
"name": "undo"
|
|
1712
1823
|
},
|
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.0",
|
|
4
|
+
"description": "Declarative deck.gl maps for HTML and React — 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
|
],
|
|
@@ -50,6 +62,11 @@
|
|
|
50
62
|
"import": "./dist/react.js",
|
|
51
63
|
"default": "./dist/react.js"
|
|
52
64
|
},
|
|
65
|
+
"./deck": {
|
|
66
|
+
"types": "./dist/deck.d.ts",
|
|
67
|
+
"import": "./dist/deck.js",
|
|
68
|
+
"default": "./dist/deck.js"
|
|
69
|
+
},
|
|
53
70
|
"./onlymapjs.css": "./dist/onlymapjs.css",
|
|
54
71
|
"./onlymapjs.html-data.json": "./onlymapjs.html-data.json",
|
|
55
72
|
"./onlymap.code-snippets": "./.vscode/onlymap.code-snippets",
|
|
@@ -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 && 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",
|
|
@@ -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.0`.
|
|
27
27
|
|
|
28
28
|
## React Projects
|
|
29
29
|
|
|
@@ -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
|
|
@@ -105,6 +111,8 @@ Common choices:
|
|
|
105
111
|
- Tiles: `TileLayer`, `MVTLayer`, `Tile3DLayer`.
|
|
106
112
|
- 3D models: `ScenegraphLayer`, `SimpleMeshLayer`, `PointCloudLayer`, `Tile3DLayer`.
|
|
107
113
|
|
|
114
|
+
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.
|
|
115
|
+
|
|
108
116
|
### Data Sources
|
|
109
117
|
|
|
110
118
|
| Source | Manifest | Notes |
|
|
@@ -120,6 +128,8 @@ Common choices:
|
|
|
120
128
|
| Polling | `data="/api/fleet.json" refresh="5s"` | Snapshot replace. |
|
|
121
129
|
| Draw store | `data="draw:sketch"` | Written by draw widget. |
|
|
122
130
|
|
|
131
|
+
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.
|
|
132
|
+
|
|
123
133
|
Authenticated fetches:
|
|
124
134
|
|
|
125
135
|
```js
|
|
@@ -183,6 +193,7 @@ Built-ins:
|
|
|
183
193
|
- `vega-lite`
|
|
184
194
|
- `player`
|
|
185
195
|
- `basemap-switcher` — radio list of presets; `options="positron dark-matter osm"` (default: every keyless registered preset)
|
|
196
|
+
- `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
197
|
- `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
198
|
|
|
188
199
|
Positions: `top-left`, `top-right`, `bottom-left`, `bottom-right`.
|
|
@@ -322,6 +333,18 @@ Story timing attributes:
|
|
|
322
333
|
|
|
323
334
|
Use declarative payloads for scrub-safe state: `visible="true"` when toggling, explicit `filter-range`, explicit camera target.
|
|
324
335
|
|
|
336
|
+
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:
|
|
337
|
+
|
|
338
|
+
```html
|
|
339
|
+
<om-story id="sunset">
|
|
340
|
+
<om-step duration="2s" action="set-lighting" lighting="custom" sun-elevation="35" sun-azimuth="245"></om-step>
|
|
341
|
+
<om-step duration="2s" action="set-lighting" lighting="custom" sun-elevation="8" sun-azimuth="270" ambient="0.5"></om-step>
|
|
342
|
+
<om-step duration="1s" action="set-lighting" lighting="flat"></om-step> <!-- bare preset = clean reset -->
|
|
343
|
+
</om-story>
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Lighting swaps are stepwise (the LightingEffect changes at each step's start — no tweening between steps); more steps = smoother sunsets.
|
|
347
|
+
|
|
325
348
|
### Manual Drawing
|
|
326
349
|
|
|
327
350
|
Use a normal GeoJSON layer bound to a draw store plus a draw widget:
|