@nika-js/onlymap 0.3.0 → 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 +4 -3
- 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-DrK6zcu8.js → basemap-BSb-g03Z.js} +6228 -6247
- package/dist/{convert-arrow-schema-CvZ3cT5m.js → convert-arrow-schema-DrAihRf9.js} +31 -31
- package/dist/ctx.d.ts +3 -0
- package/dist/index-CIEkseeV.js +3108 -0
- package/dist/index-D67FynN5.js +594 -0
- package/dist/{index-C9tgnPNw.js → index-QSkZjzIL.js} +4 -4
- package/dist/index-WeMwLoaQ.js +1446 -0
- package/dist/{index-CsicbycJ.js → index-lV5eG2Xt.js} +21442 -21860
- package/dist/index.d.ts +2 -0
- package/dist/ir.d.ts +3 -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 +24 -23
- package/dist/raster-BKf1dx54.js +8843 -0
- package/dist/raster.d.ts +61 -0
- package/dist/{recordbatch-HRu0SMKp.js → recordbatch-Bpc0uxFn.js} +200 -200
- package/dist/{table-accessors-DBjWgN0C.js → table-accessors-CYWTzpQI.js} +71 -76
- package/dist/version.d.ts +1 -1
- package/dist/zstd-jXobGRcq.js +14 -0
- package/llms.txt +3 -3
- package/onlymapjs.html-data.json +23 -0
- package/package.json +18 -15
- package/skills/onlymapjs/SKILL.md +3 -1
- package/skills/onlymapjs/references/syntax.md +29 -2
- package/dist/index-CiuGqS0i.js +0 -605
- package/dist/index-DXoRERAy.js +0 -1457
- package/dist/index-Ztkd30f8.js +0 -3907
- package/dist/onlymapjs.umd.cjs +0 -8708
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
switch (typeof e == "object" && (e == null ? void 0 : e.shape)) {
|
|
1
|
+
function d(e) {
|
|
2
|
+
switch (typeof e == "object" && e?.shape) {
|
|
4
3
|
case "array-row-table":
|
|
5
4
|
case "object-row-table":
|
|
6
5
|
return Array.isArray(e.data);
|
|
@@ -9,12 +8,12 @@ function w(e) {
|
|
|
9
8
|
case "columnar-table":
|
|
10
9
|
return e.data && typeof e.data == "object";
|
|
11
10
|
case "arrow-table":
|
|
12
|
-
return
|
|
11
|
+
return e?.data?.numRows !== void 0;
|
|
13
12
|
default:
|
|
14
13
|
return !1;
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
|
-
function
|
|
16
|
+
function i(e) {
|
|
18
17
|
switch (e.shape) {
|
|
19
18
|
case "array-row-table":
|
|
20
19
|
case "object-row-table":
|
|
@@ -24,8 +23,8 @@ function l(e) {
|
|
|
24
23
|
case "arrow-table":
|
|
25
24
|
return e.data.numRows;
|
|
26
25
|
case "columnar-table":
|
|
27
|
-
for (const
|
|
28
|
-
return
|
|
26
|
+
for (const t of Object.values(e.data))
|
|
27
|
+
return t.length || 0;
|
|
29
28
|
return 0;
|
|
30
29
|
default:
|
|
31
30
|
throw new Error("table");
|
|
@@ -34,7 +33,7 @@ function l(e) {
|
|
|
34
33
|
function g(e) {
|
|
35
34
|
if (e.schema)
|
|
36
35
|
return e.schema.fields.length;
|
|
37
|
-
if (
|
|
36
|
+
if (i(e) === 0)
|
|
38
37
|
throw new Error("empty table");
|
|
39
38
|
switch (e.shape) {
|
|
40
39
|
case "array-row-table":
|
|
@@ -51,154 +50,150 @@ function g(e) {
|
|
|
51
50
|
throw new Error("table");
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
|
-
function j(e, r,
|
|
55
|
-
var o;
|
|
53
|
+
function j(e, r, t) {
|
|
56
54
|
switch (e.shape) {
|
|
57
55
|
case "array-row-table":
|
|
58
|
-
const
|
|
59
|
-
return e.data[r][
|
|
56
|
+
const o = m(e, t);
|
|
57
|
+
return e.data[r][o];
|
|
60
58
|
case "object-row-table":
|
|
61
|
-
return e.data[r][
|
|
59
|
+
return e.data[r][t];
|
|
62
60
|
case "geojson-table":
|
|
63
|
-
return e.features[r][
|
|
61
|
+
return e.features[r][t];
|
|
64
62
|
case "columnar-table":
|
|
65
|
-
return e.data[
|
|
63
|
+
return e.data[t][r];
|
|
66
64
|
case "arrow-table":
|
|
67
|
-
const
|
|
68
|
-
return
|
|
65
|
+
const n = e.data, f = n.schema.fields.findIndex((c) => c.name === t);
|
|
66
|
+
return n.getChildAt(f)?.get(r);
|
|
69
67
|
default:
|
|
70
68
|
throw new Error("todo");
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
|
-
function y(e, r,
|
|
74
|
-
var o;
|
|
71
|
+
function y(e, r, t) {
|
|
75
72
|
switch (e.shape) {
|
|
76
73
|
case "array-row-table":
|
|
77
|
-
return e.data[r][
|
|
74
|
+
return e.data[r][t];
|
|
78
75
|
case "object-row-table":
|
|
79
|
-
const
|
|
80
|
-
return e.data[r][
|
|
76
|
+
const o = l(e, t);
|
|
77
|
+
return e.data[r][o];
|
|
81
78
|
case "geojson-table":
|
|
82
|
-
const
|
|
83
|
-
return e.features[r][
|
|
79
|
+
const u = l(e, t);
|
|
80
|
+
return e.features[r][u];
|
|
84
81
|
case "columnar-table":
|
|
85
|
-
const
|
|
86
|
-
return e.data[
|
|
82
|
+
const n = l(e, t);
|
|
83
|
+
return e.data[n][r];
|
|
87
84
|
case "arrow-table":
|
|
88
|
-
return
|
|
85
|
+
return e.data.getChildAt(t)?.get(r);
|
|
89
86
|
default:
|
|
90
87
|
throw new Error("todo");
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
90
|
function m(e, r) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (a === void 0)
|
|
91
|
+
const t = e.schema?.fields.findIndex((o) => o.name === r);
|
|
92
|
+
if (t === void 0)
|
|
97
93
|
throw new Error(r);
|
|
98
|
-
return
|
|
94
|
+
return t;
|
|
99
95
|
}
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (!a)
|
|
96
|
+
function l(e, r) {
|
|
97
|
+
const t = e.schema?.fields[r]?.name;
|
|
98
|
+
if (!t)
|
|
104
99
|
throw new Error(`${r}`);
|
|
105
|
-
return
|
|
100
|
+
return t;
|
|
106
101
|
}
|
|
107
|
-
function T(e, r,
|
|
102
|
+
function T(e, r, t, o) {
|
|
108
103
|
switch (e.shape) {
|
|
109
104
|
case "object-row-table":
|
|
110
105
|
return o ? Object.fromEntries(Object.entries(e.data[r])) : e.data[r];
|
|
111
106
|
case "array-row-table":
|
|
112
107
|
if (e.schema) {
|
|
113
|
-
const s =
|
|
114
|
-
for (let
|
|
115
|
-
s[e.schema.fields[
|
|
108
|
+
const s = t || {};
|
|
109
|
+
for (let a = 0; a < e.schema.fields.length; a++)
|
|
110
|
+
s[e.schema.fields[a].name] = e.data[r][a];
|
|
116
111
|
return s;
|
|
117
112
|
}
|
|
118
113
|
throw new Error("no schema");
|
|
119
114
|
case "geojson-table":
|
|
120
115
|
if (e.schema) {
|
|
121
|
-
const s =
|
|
122
|
-
for (let
|
|
123
|
-
s[e.schema.fields[
|
|
116
|
+
const s = t || {};
|
|
117
|
+
for (let a = 0; a < e.schema.fields.length; a++)
|
|
118
|
+
s[e.schema.fields[a].name] = e.features[r][a];
|
|
124
119
|
return s;
|
|
125
120
|
}
|
|
126
121
|
throw new Error("no schema");
|
|
127
122
|
case "columnar-table":
|
|
128
123
|
if (e.schema) {
|
|
129
|
-
const s =
|
|
130
|
-
for (let
|
|
131
|
-
s[e.schema.fields[
|
|
124
|
+
const s = t || {};
|
|
125
|
+
for (let a = 0; a < e.schema.fields.length; a++)
|
|
126
|
+
s[e.schema.fields[a].name] = e.data[e.schema.fields[a].name][r];
|
|
132
127
|
return s;
|
|
133
128
|
} else {
|
|
134
|
-
const s =
|
|
135
|
-
for (const [
|
|
136
|
-
s[
|
|
129
|
+
const s = t || {};
|
|
130
|
+
for (const [a, h] of Object.entries(e.data))
|
|
131
|
+
s[a] = h[r];
|
|
137
132
|
return s;
|
|
138
133
|
}
|
|
139
134
|
case "arrow-table":
|
|
140
|
-
const
|
|
141
|
-
for (let s = 0; s <
|
|
142
|
-
|
|
143
|
-
return
|
|
135
|
+
const u = e.data, n = t || {}, f = u.get(r), c = u.schema;
|
|
136
|
+
for (let s = 0; s < c.fields.length; s++)
|
|
137
|
+
n[c.fields[s].name] = f?.[c.fields[s].name];
|
|
138
|
+
return n;
|
|
144
139
|
default:
|
|
145
140
|
throw new Error("shape");
|
|
146
141
|
}
|
|
147
142
|
}
|
|
148
|
-
function
|
|
143
|
+
function w(e, r, t, o) {
|
|
149
144
|
switch (e.shape) {
|
|
150
145
|
case "array-row-table":
|
|
151
146
|
return o ? Array.from(e.data[r]) : e.data[r];
|
|
152
147
|
case "object-row-table":
|
|
153
148
|
if (e.schema) {
|
|
154
|
-
const s =
|
|
155
|
-
for (let
|
|
156
|
-
s[
|
|
149
|
+
const s = t || [];
|
|
150
|
+
for (let a = 0; a < e.schema.fields.length; a++)
|
|
151
|
+
s[a] = e.data[r][e.schema.fields[a].name];
|
|
157
152
|
return s;
|
|
158
153
|
}
|
|
159
154
|
return Object.values(e.data[r]);
|
|
160
155
|
case "geojson-table":
|
|
161
156
|
if (e.schema) {
|
|
162
|
-
const s =
|
|
163
|
-
for (let
|
|
164
|
-
s[
|
|
157
|
+
const s = t || [];
|
|
158
|
+
for (let a = 0; a < e.schema.fields.length; a++)
|
|
159
|
+
s[a] = e.features[r][e.schema.fields[a].name];
|
|
165
160
|
return s;
|
|
166
161
|
}
|
|
167
162
|
return Object.values(e.features[r]);
|
|
168
163
|
case "columnar-table":
|
|
169
164
|
if (e.schema) {
|
|
170
|
-
const s =
|
|
171
|
-
for (let
|
|
172
|
-
s[
|
|
165
|
+
const s = t || [];
|
|
166
|
+
for (let a = 0; a < e.schema.fields.length; a++)
|
|
167
|
+
s[a] = e.data[e.schema.fields[a].name][r];
|
|
173
168
|
return s;
|
|
174
169
|
} else {
|
|
175
|
-
const s =
|
|
176
|
-
let
|
|
170
|
+
const s = t || [];
|
|
171
|
+
let a = 0;
|
|
177
172
|
for (const h of Object.values(e.data))
|
|
178
|
-
s[
|
|
173
|
+
s[a] = h[r], a++;
|
|
179
174
|
return s;
|
|
180
175
|
}
|
|
181
176
|
case "arrow-table":
|
|
182
|
-
const
|
|
183
|
-
for (let s = 0; s <
|
|
184
|
-
|
|
185
|
-
return
|
|
177
|
+
const u = e.data, n = t || [], f = u.get(r), c = u.schema;
|
|
178
|
+
for (let s = 0; s < c.fields.length; s++)
|
|
179
|
+
n[s] = f?.[c.fields[s].name];
|
|
180
|
+
return n;
|
|
186
181
|
default:
|
|
187
182
|
throw new Error("shape");
|
|
188
183
|
}
|
|
189
184
|
}
|
|
190
185
|
function* p(e, r = []) {
|
|
191
|
-
const
|
|
192
|
-
for (let o = 0; o <
|
|
193
|
-
yield
|
|
186
|
+
const t = i(e);
|
|
187
|
+
for (let o = 0; o < t; o++)
|
|
188
|
+
yield w(e, o, r);
|
|
194
189
|
}
|
|
195
190
|
export {
|
|
196
191
|
g as a,
|
|
197
192
|
y as b,
|
|
198
193
|
j as c,
|
|
199
|
-
d,
|
|
194
|
+
w as d,
|
|
200
195
|
T as e,
|
|
201
|
-
|
|
202
|
-
|
|
196
|
+
i as g,
|
|
197
|
+
d as i,
|
|
203
198
|
p as m
|
|
204
199
|
};
|
package/dist/version.d.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { decompress as r } from "./index-2v2NXF_n.js";
|
|
2
|
+
async function o(t) {
|
|
3
|
+
const e = r(new Uint8Array(t));
|
|
4
|
+
return n(e);
|
|
5
|
+
}
|
|
6
|
+
function n(t) {
|
|
7
|
+
if (t.byteOffset === 0 && t.byteLength === t.buffer.byteLength)
|
|
8
|
+
return t.buffer;
|
|
9
|
+
const e = new Uint8Array(t.byteLength);
|
|
10
|
+
return e.set(t), e.buffer;
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
o as decode
|
|
14
|
+
};
|
package/llms.txt
CHANGED
|
@@ -11,14 +11,14 @@ 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
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.
|
|
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")`.
|
package/onlymapjs.html-data.json
CHANGED
|
@@ -216,6 +216,9 @@
|
|
|
216
216
|
{
|
|
217
217
|
"name": "BitmapLayer"
|
|
218
218
|
},
|
|
219
|
+
{
|
|
220
|
+
"name": "COGLayer"
|
|
221
|
+
},
|
|
219
222
|
{
|
|
220
223
|
"name": "ColumnLayer"
|
|
221
224
|
},
|
|
@@ -559,6 +562,26 @@
|
|
|
559
562
|
"name": "point-size",
|
|
560
563
|
"description": "deck.gl pointSize."
|
|
561
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
|
+
},
|
|
562
585
|
{
|
|
563
586
|
"name": "operation",
|
|
564
587
|
"description": "deck.gl operation."
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nika-js/onlymap",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Declarative deck.gl maps for HTML and React
|
|
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"
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
".": {
|
|
55
55
|
"types": "./dist/index.d.ts",
|
|
56
56
|
"import": "./dist/onlymapjs.js",
|
|
57
|
-
"require": "./dist/onlymapjs.umd.cjs",
|
|
58
57
|
"default": "./dist/onlymapjs.js"
|
|
59
58
|
},
|
|
60
59
|
"./react": {
|
|
@@ -90,6 +89,7 @@
|
|
|
90
89
|
"dist",
|
|
91
90
|
"README.md",
|
|
92
91
|
"LICENSE.md",
|
|
92
|
+
"THIRD-PARTY-LICENSES.md",
|
|
93
93
|
"docs",
|
|
94
94
|
"llms.txt",
|
|
95
95
|
"onlymapjs.html-data.json",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
],
|
|
98
98
|
"scripts": {
|
|
99
99
|
"dev": "vite",
|
|
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",
|
|
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",
|
|
101
101
|
"build:types": "tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
102
102
|
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.e2e.json --noEmit && tsc -p cloud/workers/telemetry/tsconfig.json",
|
|
103
103
|
"test": "vitest run",
|
|
@@ -108,17 +108,19 @@
|
|
|
108
108
|
"gen:public": "npm run gen:html-data && vite-node dev/build-public.ts",
|
|
109
109
|
"test:public": "npm run gen:public -- --dry-run --strict",
|
|
110
110
|
"deploy:telemetry": "wrangler deploy --config cloud/workers/telemetry/wrangler.toml",
|
|
111
|
-
"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"
|
|
112
113
|
},
|
|
113
|
-
"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.",
|
|
114
115
|
"devDependencies": {
|
|
115
|
-
"@deck.gl/aggregation-layers": "
|
|
116
|
-
"@deck.gl/core": "
|
|
117
|
-
"@deck.gl/extensions": "
|
|
118
|
-
"@deck.gl/geo-layers": "
|
|
119
|
-
"@deck.gl/layers": "
|
|
120
|
-
"@deck.gl/mapbox": "
|
|
121
|
-
"@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",
|
|
122
124
|
"@loaders.gl/csv": "^4.4.3",
|
|
123
125
|
"@loaders.gl/kml": "^4.4.3",
|
|
124
126
|
"@loaders.gl/shapefile": "^4.4.3",
|
|
@@ -149,5 +151,6 @@
|
|
|
149
151
|
"vitest": "^4.1.9",
|
|
150
152
|
"wrangler": "^4.110.0",
|
|
151
153
|
"ws": "^8.21.0"
|
|
152
|
-
}
|
|
153
|
-
|
|
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.3.
|
|
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">`.
|
|
@@ -100,7 +100,7 @@ filter-field="magnitude" filter-range="[4, 10]"
|
|
|
100
100
|
|
|
101
101
|
Use the `type` value exactly:
|
|
102
102
|
|
|
103
|
-
`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`.
|
|
104
104
|
|
|
105
105
|
Common choices:
|
|
106
106
|
|
|
@@ -110,6 +110,22 @@ Common choices:
|
|
|
110
110
|
- Aggregation: `HeatmapLayer`, `HexagonLayer`, `GridLayer`, `ScreenGridLayer`.
|
|
111
111
|
- Tiles: `TileLayer`, `MVTLayer`, `Tile3DLayer`.
|
|
112
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.
|
|
113
129
|
|
|
114
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.
|
|
115
131
|
|
|
@@ -183,7 +199,7 @@ Do not use full-JS blocks on columnar/Arrow layers.
|
|
|
183
199
|
|
|
184
200
|
Built-ins:
|
|
185
201
|
|
|
186
|
-
- `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.
|
|
187
203
|
- `layer-switcher`
|
|
188
204
|
- `zoom-controls`
|
|
189
205
|
- `scale-bar`
|
|
@@ -198,6 +214,17 @@ Built-ins:
|
|
|
198
214
|
|
|
199
215
|
Positions: `top-left`, `top-right`, `bottom-left`, `bottom-right`.
|
|
200
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
|
+
|
|
201
228
|
Examples:
|
|
202
229
|
|
|
203
230
|
```html
|