@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.
Files changed (62) hide show
  1. package/.vscode/onlymap.code-snippets +13 -0
  2. package/LICENSE.md +4 -0
  3. package/README.md +30 -7
  4. package/THIRD-PARTY-LICENSES.md +7874 -0
  5. package/dist/{Arrow.dom-7AXne1TU.js → Arrow.dom-BNjbQ9jb.js} +23 -23
  6. package/dist/LercDecode.es-CVmNpXBp.js +303 -0
  7. package/dist/assets/LercDecode.es-BJb2dZu5.js +305 -0
  8. package/dist/assets/__vite-browser-external-DDArsqdc.js +29 -0
  9. package/dist/assets/lerc-wasm.wasm +0 -0
  10. package/dist/assets/lzw-BY5a_5nb.js +83 -0
  11. package/dist/assets/worker-DtIrTJB7.js +305 -0
  12. package/dist/assets/zstd-QGAv3PMm.js +383 -0
  13. package/dist/{basemap-BdvB-SHy.js → basemap-BSb-g03Z.js} +5729 -5684
  14. package/dist/basemap-registry.d.ts +10 -0
  15. package/dist/basemap.d.ts +23 -0
  16. package/dist/color.d.ts +9 -0
  17. package/dist/{convert-arrow-schema-CvZ3cT5m.js → convert-arrow-schema-DrAihRf9.js} +31 -31
  18. package/dist/ctx.d.ts +3 -0
  19. package/dist/deck.d.ts +28 -0
  20. package/dist/deck.js +16 -0
  21. package/dist/elements/om-map.d.ts +29 -0
  22. package/dist/html-data.d.ts +2 -2
  23. package/dist/index-CIEkseeV.js +3108 -0
  24. package/dist/index-D67FynN5.js +594 -0
  25. package/dist/{index-CJOMgp8k.js → index-QSkZjzIL.js} +4 -4
  26. package/dist/index-WeMwLoaQ.js +1446 -0
  27. package/dist/{index-BxzDKZpu.js → index-lV5eG2Xt.js} +22439 -21347
  28. package/dist/index.d.ts +13 -0
  29. package/dist/ir-snapshot.d.ts +2 -0
  30. package/dist/ir.d.ts +10 -0
  31. package/dist/layer-registry.d.ts +15 -1
  32. package/dist/legend-spec.d.ts +66 -0
  33. package/dist/lerc-DJzRIT6z.js +30 -0
  34. package/dist/lerc-wasm.wasm +0 -0
  35. package/dist/lzw-BY5a_5nb.js +83 -0
  36. package/dist/mgrs-BY9bIvp4.js +799 -0
  37. package/dist/onlymapjs.css +1 -1
  38. package/dist/onlymapjs.js +55 -35
  39. package/dist/programmatic.d.ts +35 -0
  40. package/dist/raster-BKf1dx54.js +8843 -0
  41. package/dist/raster.d.ts +61 -0
  42. package/dist/react/om-layer.d.ts +2 -0
  43. package/dist/react.js +18 -16
  44. package/dist/{recordbatch-HRu0SMKp.js → recordbatch-Bpc0uxFn.js} +200 -200
  45. package/dist/runtime-core.d.ts +73 -1
  46. package/dist/scene-lighting.d.ts +83 -0
  47. package/dist/selection.d.ts +1 -1
  48. package/dist/snapshot.d.ts +14 -0
  49. package/dist/{table-accessors-DBjWgN0C.js → table-accessors-CYWTzpQI.js} +71 -76
  50. package/dist/terrain.d.ts +113 -0
  51. package/dist/version.d.ts +1 -1
  52. package/dist/zstd-jXobGRcq.js +14 -0
  53. package/docs/custom-layers.md +99 -0
  54. package/llms.txt +5 -5
  55. package/onlymapjs.html-data.json +163 -29
  56. package/package.json +35 -15
  57. package/skills/onlymapjs/SKILL.md +3 -1
  58. package/skills/onlymapjs/references/syntax.md +53 -3
  59. package/dist/index-BVQ0s1Zh.js +0 -1457
  60. package/dist/index-CvBHiOGD.js +0 -605
  61. package/dist/index-Vc8QfNEf.js +0 -3907
  62. package/dist/onlymapjs.umd.cjs +0 -8634
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Scene lighting (spec: "Scene Lighting") — the manifest surface over
3
+ * deck.gl's LightingEffect, resolving the backlog TODO ("Lighting/effects
4
+ * manifest surface"). Attributes on <om-map>, the basemap= precedent:
5
+ * singleton scene state as attributes gets undo/redo for free (attribute
6
+ * history) and live switching through the same MutationObserver path.
7
+ *
8
+ * <om-map lighting="daylight">
9
+ * <om-map lighting="custom" lighting-ambient="0.6" lighting-sun="1.5"
10
+ * lighting-sun-azimuth="135" lighting-sun-elevation="45">
11
+ * <om-map lighting="daylight" lighting-sun-date="2026-07-15T14:00:00Z">
12
+ *
13
+ * The sun is ALWAYS a DirectionalLight driven by azimuth/elevation;
14
+ * `lighting-sun-date` is sugar that computes az/el from solar position at
15
+ * the map center when lighting is applied (deliberately NOT deck's
16
+ * experimental `_SunLight`, which recomputes per frame from the viewport —
17
+ * an attribute-declared scene should be deterministic, and the underscore
18
+ * API is upgrade churn). Absent `lighting` = deck's default lights —
19
+ * today's behavior, so the whole surface is additive.
20
+ */
21
+ import { AmbientLight, _CameraLight as CameraLight, DirectionalLight, LightingEffect } from "@deck.gl/core";
22
+ export interface LightingIR {
23
+ /** Preset the values were seeded from (validation/UI echo; "custom" for a fully hand-tuned scene). */
24
+ preset: LightingPresetName;
25
+ /** Ambient intensity — always-on fill. */
26
+ ambient: number;
27
+ /** Sun (directional) intensity; 0 omits the sun light entirely. */
28
+ sun: number;
29
+ /** Sun compass bearing, ° clockwise from north. */
30
+ sunAzimuth: number;
31
+ /** Sun height above the horizon, ° (0 = horizon, 90 = zenith). */
32
+ sunElevation: number;
33
+ /** Camera-following fill intensity (model inspection); 0 omits it. */
34
+ camera: number;
35
+ /** Epoch ms — when set, az/el are recomputed from solar position at apply time. */
36
+ sunDate?: number;
37
+ }
38
+ export type LightingPresetName = "daylight" | "studio" | "flat" | "custom";
39
+ export declare const LIGHTING_PRESET_NAMES: readonly ["daylight", "studio", "flat", "custom"];
40
+ export declare function isLightingPreset(name: string): name is LightingPresetName;
41
+ /** Programmatic twin of the lighting-* attributes (MapControllerOptions.lighting / setLighting). */
42
+ export interface LightingOptions {
43
+ preset?: LightingPresetName;
44
+ ambient?: number;
45
+ sun?: number;
46
+ sunAzimuth?: number;
47
+ sunElevation?: number;
48
+ camera?: number;
49
+ /** Epoch ms or ISO 8601 — the lighting-sun-date twin. */
50
+ sunDate?: number | string;
51
+ }
52
+ /** Preset seeds values; explicit fields override individual seeds; sunDate (resolved at apply time) wins over az/el. */
53
+ export declare function resolveLighting(options: LightingOptions): LightingIR;
54
+ /**
55
+ * `lighting*` attributes → IR. Returns null when `lighting` itself is
56
+ * absent (deck default lights; stray lighting-* overrides without it are
57
+ * inert — validation warns). Malformed numbers fall back to the preset seed
58
+ * (validation flags them; the live path must still render something).
59
+ */
60
+ export declare function parseLightingAttrs(getAttr: (name: string) => string | null): LightingIR | null;
61
+ /**
62
+ * Solar azimuth/elevation at a timestamp + location, in the manual-sun
63
+ * convention (azimuth ° CW from north, elevation ° above horizon). The
64
+ * standard suncalc derivation (the same math deck's `_SunLight` uses via
65
+ * @math.gl/sun): solar azimuth measured south→west, so matching the
66
+ * DirectionalLight convention is `azimuth = solar + 180°`. Below-horizon
67
+ * sun clamps to the horizon — a flat scene can't render a sun beneath it.
68
+ */
69
+ export declare function solarAzElDegrees(timestamp: number, latitude: number, longitude: number): {
70
+ azimuth: number;
71
+ elevation: number;
72
+ };
73
+ /**
74
+ * IR → the named lights map a LightingEffect is constructed from —
75
+ * separated so tests can assert composition without reaching into
76
+ * LightingEffect's private fields. The ambient light is always present;
77
+ * sun and camera lights only at intensity > 0 (deck renders a 0-intensity
78
+ * light as subtle noise, not nothing). `center` (current map center) feeds
79
+ * the sunDate → az/el resolution.
80
+ */
81
+ export declare function buildLights(ir: LightingIR, center?: [longitude: number, latitude: number]): Record<string, AmbientLight | DirectionalLight | CameraLight>;
82
+ /** IR → deck.gl LightingEffect (see buildLights for the composition rules). */
83
+ export declare function buildLightingEffect(ir: LightingIR, center?: [longitude: number, latitude: number]): LightingEffect;
@@ -27,5 +27,5 @@ interface PickingInfoLike {
27
27
  x: number;
28
28
  y: number;
29
29
  }
30
- export declare function toSelection(info: PickingInfoLike, type: "hover" | "click"): Selection | null;
30
+ export declare function toSelection(info: PickingInfoLike, type: "hover" | "click", resolveLayerId?: (renderedId: string) => string): Selection | null;
31
31
  export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Snapshot serialization (spec: "Snapshot API") — shared by OmMapElement
3
+ * and MapController: the composite canvas RuntimeCore.snapshot() produces,
4
+ * serialized to the consumer's preferred shape.
5
+ */
6
+ export interface SnapshotOptions {
7
+ /** Image MIME type — default "image/png". */
8
+ type?: "image/png" | "image/jpeg" | "image/webp";
9
+ /** Lossy-format quality 0–1 (jpeg/webp). */
10
+ quality?: number;
11
+ /** Output shape — default "dataURL" (what print/export pipelines embed directly); "blob" for uploads/files. */
12
+ as?: "dataURL" | "blob";
13
+ }
14
+ export declare function serializeSnapshot(canvas: HTMLCanvasElement, opts: SnapshotOptions): Promise<string | Blob>;
@@ -1,6 +1,5 @@
1
- function w(e) {
2
- var a;
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 ((a = e == null ? void 0 : e.data) == null ? void 0 : a.numRows) !== void 0;
11
+ return e?.data?.numRows !== void 0;
13
12
  default:
14
13
  return !1;
15
14
  }
16
15
  }
17
- function l(e) {
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 a of Object.values(e.data))
28
- return a.length || 0;
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 (l(e) === 0)
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, a) {
55
- var o;
53
+ function j(e, r, t) {
56
54
  switch (e.shape) {
57
55
  case "array-row-table":
58
- const n = m(e, a);
59
- return e.data[r][n];
56
+ const o = m(e, t);
57
+ return e.data[r][o];
60
58
  case "object-row-table":
61
- return e.data[r][a];
59
+ return e.data[r][t];
62
60
  case "geojson-table":
63
- return e.features[r][a];
61
+ return e.features[r][t];
64
62
  case "columnar-table":
65
- return e.data[a][r];
63
+ return e.data[t][r];
66
64
  case "arrow-table":
67
- const c = e.data, u = c.schema.fields.findIndex((s) => s.name === a);
68
- return (o = c.getChildAt(u)) == null ? void 0 : o.get(r);
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, a) {
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][a];
74
+ return e.data[r][t];
78
75
  case "object-row-table":
79
- const n = i(e, a);
80
- return e.data[r][n];
76
+ const o = l(e, t);
77
+ return e.data[r][o];
81
78
  case "geojson-table":
82
- const f = i(e, a);
83
- return e.features[r][f];
79
+ const u = l(e, t);
80
+ return e.features[r][u];
84
81
  case "columnar-table":
85
- const c = i(e, a);
86
- return e.data[c][r];
82
+ const n = l(e, t);
83
+ return e.data[n][r];
87
84
  case "arrow-table":
88
- return (o = e.data.getChildAt(a)) == null ? void 0 : o.get(r);
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
- var o;
95
- const a = (o = e.schema) == null ? void 0 : o.fields.findIndex((n) => n.name === r);
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 a;
94
+ return t;
99
95
  }
100
- function i(e, r) {
101
- var o, n;
102
- const a = (n = (o = e.schema) == null ? void 0 : o.fields[r]) == null ? void 0 : n.name;
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 a;
100
+ return t;
106
101
  }
107
- function T(e, r, a, o) {
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 = a || {};
114
- for (let t = 0; t < e.schema.fields.length; t++)
115
- s[e.schema.fields[t].name] = e.data[r][t];
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 = a || {};
122
- for (let t = 0; t < e.schema.fields.length; t++)
123
- s[e.schema.fields[t].name] = e.features[r][t];
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 = a || {};
130
- for (let t = 0; t < e.schema.fields.length; t++)
131
- s[e.schema.fields[t].name] = e.data[e.schema.fields[t].name][r];
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 = a || {};
135
- for (const [t, h] of Object.entries(e.data))
136
- s[t] = h[r];
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 n = e.data, f = a || {}, c = n.get(r), u = n.schema;
141
- for (let s = 0; s < u.fields.length; s++)
142
- f[u.fields[s].name] = c == null ? void 0 : c[u.fields[s].name];
143
- return f;
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 d(e, r, a, o) {
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 = a || [];
155
- for (let t = 0; t < e.schema.fields.length; t++)
156
- s[t] = e.data[r][e.schema.fields[t].name];
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 = a || [];
163
- for (let t = 0; t < e.schema.fields.length; t++)
164
- s[t] = e.features[r][e.schema.fields[t].name];
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 = a || [];
171
- for (let t = 0; t < e.schema.fields.length; t++)
172
- s[t] = e.data[e.schema.fields[t].name][r];
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 = a || [];
176
- let t = 0;
170
+ const s = t || [];
171
+ let a = 0;
177
172
  for (const h of Object.values(e.data))
178
- s[t] = h[r], t++;
173
+ s[a] = h[r], a++;
179
174
  return s;
180
175
  }
181
176
  case "arrow-table":
182
- const n = e.data, f = a || [], c = n.get(r), u = n.schema;
183
- for (let s = 0; s < u.fields.length; s++)
184
- f[s] = c == null ? void 0 : c[u.fields[s].name];
185
- return f;
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 a = l(e);
192
- for (let o = 0; o < a; o++)
193
- yield d(e, o, r);
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
- l as g,
202
- w as i,
196
+ i as g,
197
+ d as i,
203
198
  p as m
204
199
  };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Terrain (spec: "Terrain") — 3D elevation surface + per-layer draping,
3
+ * declared as `terrain*` attributes on <om-map> (the basemap=/lighting=
4
+ * scene-attribute precedent: free undo/redo, live MutationObserver
5
+ * switching).
6
+ *
7
+ * <om-map terrain="terrarium" terrain-exaggeration="1.5" pitch="55">
8
+ * <om-map terrain="https://tiles.example.com/dem/{z}/{x}/{y}.png"
9
+ * terrain-decoder="terrarium" terrain-max-zoom="14"
10
+ * terrain-texture="https://…/satellite/{z}/{x}/{y}.jpg">
11
+ *
12
+ * The seventh plugin registry (`OmMap.registerTerrain`) resolves preset
13
+ * names; a `{z}/{x}/{y}` URL is bring-your-own DEM (requires
14
+ * terrain-decoder). Geographic layers DRAPE onto the surface by default;
15
+ * a per-layer `terrain="drape|offset|off"` attribute overrides (3D-model
16
+ * layers default to `offset` — models sit ON terrain, not painted onto
17
+ * it).
18
+ *
19
+ * Terrain REPLACES an active basemap: a flat MapLibre canvas sits at sea
20
+ * level and visibly desyncs from a raised surface (true coexistence is
21
+ * the interleaved-compositing TODO). RuntimeCore suppresses the basemap
22
+ * while terrain is active and restores it when terrain turns off.
23
+ *
24
+ * `applyTerrain` is PURE (unit-testable without a GPU — headless never
25
+ * reaches the deck handoff): it computes each layer's terrain patches and
26
+ * the terrain-surface layer props; RuntimeCore feeds them to buildLayers.
27
+ */
28
+ import type { Layer } from "@deck.gl/core";
29
+ import type { LayerIR } from "./ir";
30
+ /** RGB DEM decoder: height = offset + R·rScaler + G·gScaler + B·bScaler. */
31
+ export interface ElevationDecoder {
32
+ rScaler: number;
33
+ gScaler: number;
34
+ bScaler: number;
35
+ offset: number;
36
+ }
37
+ export declare const DECODERS: Readonly<Record<string, ElevationDecoder>>;
38
+ export interface TerrainPreset {
39
+ /** `{z}/{x}/{y}` DEM tile URL template (may carry a `{key}` placeholder for keyed providers). */
40
+ elevationData: string;
41
+ /** Decoder name ("terrarium" / "mapbox-rgb") or explicit scalers. */
42
+ decoder: keyof typeof DECODERS | ElevationDecoder;
43
+ /**
44
+ * The provider's REAL tileset cap — deck over-samples past it for closer
45
+ * cameras; a too-high cap requests tiles that 404 and blanks the terrain.
46
+ */
47
+ maxZoom: number;
48
+ /** Optional draped imagery `{z}/{x}/{y}` URL template. */
49
+ texture?: string;
50
+ /** Display name (switcher UIs). */
51
+ label?: string;
52
+ /** Which key `{key}` substitutes — the basemap-registry convention. */
53
+ requiresKey?: "maptiler";
54
+ }
55
+ export declare function registerTerrain(name: string, preset: TerrainPreset): void;
56
+ export declare function getTerrain(name: string): TerrainPreset | undefined;
57
+ export declare function getAllTerrains(): ReadonlyMap<string, TerrainPreset>;
58
+ export interface TerrainIR {
59
+ elevationData: string;
60
+ elevationDecoder: ElevationDecoder;
61
+ maxZoom: number;
62
+ /** Multiplies decoded DEM heights (1 = true relief). */
63
+ exaggeration: number;
64
+ texture?: string;
65
+ }
66
+ export interface ResolvedTerrain {
67
+ ir: TerrainIR | null;
68
+ /** Resolution problem (unknown preset / missing key / missing decoder) — the caller decides how loudly to fail; `ir` is null when unresolvable. */
69
+ error?: string;
70
+ }
71
+ /** Is this `terrain` value a bring-your-own DEM URL rather than a preset name? (The basemap isStyleUrl convention.) */
72
+ export declare function isDemUrl(raw: string): boolean;
73
+ /**
74
+ * `terrain*` attributes → TerrainIR. Returns `{ir: null}` when `terrain`
75
+ * is absent/"off"; `{ir: null, error}` when set but unresolvable (unknown
76
+ * preset, keyless keyed preset, raw URL without a decoder) — terrain
77
+ * renders NOTHING in that case rather than a wrong surface, and validation
78
+ * carries the loud version of the same message.
79
+ */
80
+ export declare function parseTerrainAttrs(getAttr: (name: string) => string | null, maptilerKey?: string): ResolvedTerrain;
81
+ export declare const TERRAIN_LAYER_ID = "__onlymapjs-terrain";
82
+ export type LayerTerrainMode = "drape" | "offset" | "off";
83
+ export declare const LAYER_TERRAIN_MODES: readonly ["drape", "offset", "off"];
84
+ export declare function defaultTerrainMode(type: string): LayerTerrainMode;
85
+ export interface TerrainLayerPatch {
86
+ /**
87
+ * The deck-layer id to construct with. Suffixed `#t<generation>` while
88
+ * terrain is active: adding/removing an extension on a LIVE layer
89
+ * recompiles shaders and corrupts attribute state (the DataFilterExtension
90
+ * birth-mount rule), and a stale TerrainExtension vertical offset survives
91
+ * a terrain toggle on a stable id — a fresh id forces clean teardown +
92
+ * rebuild. RuntimeCore maps these generated ids back to authored ids for
93
+ * picks without guessing from the string shape.
94
+ */
95
+ deckId: string;
96
+ /** Extra constructor props: extensions (APPENDED to existing — DataFilterExtension stays mounted) + terrainDrawMode. Empty for mode "off". */
97
+ extraProps: Record<string, unknown>;
98
+ }
99
+ /**
100
+ * Per-layer terrain patches, PURE. `terrainActive` false → identity patches
101
+ * (stable ids, no props). Runtime-internal layers (trace temps, draw
102
+ * preview, tooltip plumbing) never drape — they're screen-space/transient.
103
+ */
104
+ export declare function applyTerrain(irs: readonly LayerIR[], terrainActive: boolean, generation: number): Map<string, TerrainLayerPatch>;
105
+ /**
106
+ * The terrain-surface layer itself. `operation: "terrain+draw"` makes it
107
+ * double as the elevation surface extension-carrying layers drape onto;
108
+ * `pickable: "3d"` runs the depth pick pass so click/hover coordinates
109
+ * unproject at the SURFACE's depth instead of z=0 sea level (without it,
110
+ * tilted-view picks/drawn vertices land behind the cursor). Exempt from
111
+ * license gates by the `__onlymapjs-` prefix.
112
+ */
113
+ export declare function buildTerrainLayer(ir: TerrainIR): Layer;
package/dist/version.d.ts CHANGED
@@ -5,4 +5,4 @@
5
5
  * the build rootDir, and a `define` would need repeating across vite/vitest/
6
6
  * vite-node configs.
7
7
  */
8
- export declare const LIBRARY_VERSION = "0.2.3";
8
+ export declare const LIBRARY_VERSION = "0.3.1";
@@ -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
+ };
@@ -0,0 +1,99 @@
1
+ # Registering an external layer class
2
+
3
+ `OmMap.registerLayer` makes any deck.gl layer class a first-class manifest
4
+ type: validation, IntelliSense generation, and attribute resolution all read
5
+ the same schema the built-ins use. This page is the recipe for bringing a
6
+ layer class the library doesn't bundle — a community layer, a company shim, a
7
+ composite that wraps a whole data pipeline.
8
+
9
+ ## Rule 1: build on `@nika-js/onlymap/deck`, never on your own deck.gl
10
+
11
+ OnlyMapJS bundles deck.gl (it has zero runtime dependencies). If your class
12
+ extends a `@deck.gl/*` copy you installed yourself, it belongs to a
13
+ **different class hierarchy** — two `Layer` base classes, two luma.gl
14
+ runtimes — and fails inside the bundled renderer. The `deck` subpath
15
+ re-exports the bundled classes, so your shim extends the exact objects the
16
+ core renders with:
17
+
18
+ ```js
19
+ import { CompositeLayer, TileLayer, BitmapLayer } from "@nika-js/onlymap/deck";
20
+ ```
21
+
22
+ Available: `Layer`, `CompositeLayer`, `LayerExtension`, `WebMercatorViewport`,
23
+ `COORDINATE_SYSTEM`, `GeoJsonLayer`, `ScatterplotLayer`, `IconLayer`,
24
+ `BitmapLayer`, `TileLayer`, `Tile3DLayer`, `SimpleMeshLayer`,
25
+ `ScenegraphLayer` — plus authoring types (`LayerProps`, `DefaultProps`,
26
+ `UpdateParameters`, `PickingInfo`, `LayersList`; typechecking against them
27
+ needs `@deck.gl/core` as a dev dependency).
28
+
29
+ ## Rule 2: functions ride `static defaultProps`, not attributes
30
+
31
+ HTML attributes are strings — function-valued props (`renderSubLayers`,
32
+ `getTileData`, load callbacks) are inexpressible by design. Put them on a
33
+ thin subclass; deck.gl's own defaultProps merge delivers them to every
34
+ instance:
35
+
36
+ ```js
37
+ class CogLayer extends CompositeLayer {
38
+ static layerName = "CogLayer";
39
+ static defaultProps = {
40
+ renderTile: { type: "function", value: renderTile },
41
+ onRasterLoad: { type: "function", value: () => {} },
42
+ };
43
+ renderLayers() {
44
+ /* compose TileLayer/BitmapLayer from the subpath here */
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## Rule 3: the schema wires attributes to deck props
50
+
51
+ ```js
52
+ import { OmMap } from "@nika-js/onlymap";
53
+
54
+ OmMap.registerLayer({
55
+ type: "CogLayer",
56
+ deckClass: CogLayer,
57
+ props: [
58
+ // deck's URL prop is `data`, but OnlyMapJS reserves the `data` attribute
59
+ // for its own loader — alias it, and the layer class fetches for itself
60
+ // (the built-in Tile3DLayer `tileset` attribute uses the same trick).
61
+ { attr: "src", kind: "scalar", deckProp: "data", type: "string", required: true },
62
+ // JSON attributes and dot-path descriptors compose: the JSON sets the
63
+ // object, later dot-paths merge into it.
64
+ { attr: "load-options", kind: "scalar", deckProp: "loadOptions", type: "json" },
65
+ { attr: "max-error", kind: "scalar", deckProp: "loadOptions.cog.maxError", type: "number" },
66
+ { attr: "opacity", kind: "scalar", deckProp: "opacity", type: "number", default: 1 },
67
+ { attr: "visible", kind: "scalar", deckProp: "visible", type: "boolean", default: true },
68
+ // accessor-kind props get the full expression language:
69
+ // { attr: "get-color", kind: "accessor", deckProp: "getColor" },
70
+ ],
71
+ });
72
+ ```
73
+
74
+ Then the manifest just works:
75
+
76
+ ```html
77
+ <om-layer id="rast" type="CogLayer" src="https://example.com/landcover.tif"
78
+ max-error="16" opacity="0.9"></om-layer>
79
+ ```
80
+
81
+ ## Rule 4: register before the manifest mounts
82
+
83
+ Registration after mount does not retrigger reconciles — an unknown-type
84
+ layer is warn-skipped until the next DOM mutation. Call `registerLayer` at
85
+ module top level (the `registerSource`/`registerFormat` convention), before
86
+ the `<om-map>` connects.
87
+
88
+ ## Verifying without a GPU
89
+
90
+ `OmMap.snapshotIR(html)` resolves your registered type through the same
91
+ pipeline the live reconciler runs — assert the aliased URL lands on
92
+ `props.data`, dot-paths nest, functions appear as `[function]` — in plain
93
+ jsdom/happy-dom, no WebGL.
94
+
95
+ ## The programmatic alternative
96
+
97
+ On the `MapController` front-end (and the React adapter), `props` passes
98
+ function values directly — no subclass needed. The subclass recipe exists so
99
+ the **manifest** front-end can express what attributes can't.