@nika-js/onlymap 0.1.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/.vscode/onlymap.code-snippets +173 -0
- package/LICENSE.md +133 -0
- package/README.md +188 -0
- package/bin/onlymapjs.mjs +182 -0
- package/dist/Arrow.dom-7AXne1TU.js +5233 -0
- package/dist/actions.d.ts +30 -0
- package/dist/attribute-resolution.d.ts +73 -0
- package/dist/basemap-D95W0IIA.js +25203 -0
- package/dist/basemap.d.ts +72 -0
- package/dist/convert-arrow-schema-CvZ3cT5m.js +94 -0
- package/dist/csp-sandbox.d.ts +20 -0
- package/dist/ctx.d.ts +49 -0
- package/dist/d3-bundle.d.ts +218 -0
- package/dist/data-emit.d.ts +10 -0
- package/dist/data-layer.d.ts +150 -0
- package/dist/declarative-filter.d.ts +12 -0
- package/dist/dev-error-panel.d.ts +15 -0
- package/dist/draw-controller.d.ts +51 -0
- package/dist/draw.d.ts +102 -0
- package/dist/effects.d.ts +39 -0
- package/dist/elements/om-behavior.d.ts +13 -0
- package/dist/elements/om-layer.d.ts +11 -0
- package/dist/elements/om-map.d.ts +168 -0
- package/dist/elements/om-overlay.d.ts +82 -0
- package/dist/elements/om-step.d.ts +12 -0
- package/dist/elements/om-story.d.ts +67 -0
- package/dist/elements/om-widget.d.ts +29 -0
- package/dist/env.d.ts +41 -0
- package/dist/expr/cache.d.ts +28 -0
- package/dist/expr/compile.d.ts +27 -0
- package/dist/expr/errors.d.ts +10 -0
- package/dist/expr/full-js-block.d.ts +17 -0
- package/dist/expr/index.d.ts +12 -0
- package/dist/expr/output-type.d.ts +12 -0
- package/dist/expr/scale.d.ts +13 -0
- package/dist/expr/script-block.d.ts +16 -0
- package/dist/expr/whitelist.d.ts +28 -0
- package/dist/field-access.d.ts +24 -0
- package/dist/html-data.d.ts +25 -0
- package/dist/index-2v2NXF_n.js +435 -0
- package/dist/index-BFjXVHly.js +605 -0
- package/dist/index-C9w78NS9.js +4883 -0
- package/dist/index-CU-iOTdr.js +75292 -0
- package/dist/index-D74olQ9w.js +3907 -0
- package/dist/index-Do7hmHwi.js +1457 -0
- package/dist/index.d.ts +76 -0
- package/dist/ir-snapshot.d.ts +54 -0
- package/dist/ir.d.ts +66 -0
- package/dist/layer-registry.d.ts +27 -0
- package/dist/layers/popup-layer.d.ts +65 -0
- package/dist/onlymapjs.css +1 -0
- package/dist/onlymapjs.js +30 -0
- package/dist/onlymapjs.umd.cjs +8618 -0
- package/dist/parse-manifest.d.ts +19 -0
- package/dist/recordbatch-HRu0SMKp.js +4742 -0
- package/dist/runtime-core.d.ts +170 -0
- package/dist/selection.d.ts +31 -0
- package/dist/stats.d.ts +29 -0
- package/dist/table-accessors-DBjWgN0C.js +204 -0
- package/dist/template-interpolation.d.ts +6 -0
- package/dist/testing.d.ts +90 -0
- package/dist/timeline.d.ts +37 -0
- package/dist/validation.d.ts +22 -0
- package/dist/vega-loader.d.ts +22 -0
- package/dist/viewport-filter.d.ts +25 -0
- package/dist/widget-registry.d.ts +23 -0
- package/dist/widget-script.d.ts +1 -0
- package/dist/widgets/built-in.d.ts +1 -0
- package/dist/widgets/dom-utils.d.ts +4 -0
- package/llms.txt +43 -0
- package/onlymapjs.html-data.json +1688 -0
- package/package.json +111 -0
- package/skills/onlymapjs/SKILL.md +61 -0
- package/skills/onlymapjs/agents/openai.yaml +4 -0
- package/skills/onlymapjs/references/patterns.md +213 -0
- package/skills/onlymapjs/references/syntax.md +342 -0
- package/skills/onlymapjs/references/testing.md +126 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# OnlyMapJS Syntax Reference
|
|
2
|
+
|
|
3
|
+
This reference is public-safe and self-contained. Use it when authoring or reviewing OnlyMapJS manifests.
|
|
4
|
+
|
|
5
|
+
## Import Patterns
|
|
6
|
+
|
|
7
|
+
Vite/npm project:
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<script type="module">
|
|
11
|
+
import "@nika-js/onlymap";
|
|
12
|
+
import "onlymapjs/onlymapjs.css";
|
|
13
|
+
</script>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Static CDN page:
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<script type="module" src="https://esm.sh/@nika-js/onlymap@0.1.0"></script>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If using MapLibre basemaps from a built package, include the CSS import or stylesheet. Standalone `basemap="none"` maps do not need the CSS.
|
|
23
|
+
|
|
24
|
+
## Elements
|
|
25
|
+
|
|
26
|
+
### `<om-map>`
|
|
27
|
+
|
|
28
|
+
Root element. Children are layers, widgets, overlays, behaviors, and stories.
|
|
29
|
+
|
|
30
|
+
Common attributes:
|
|
31
|
+
|
|
32
|
+
- `center="[lng, lat]"`
|
|
33
|
+
- `zoom="11"`
|
|
34
|
+
- `pitch="55"`
|
|
35
|
+
- `bearing="20"`
|
|
36
|
+
- `basemap="maplibre"` or `basemap="none"` or a MapLibre style URL
|
|
37
|
+
- `validate` to show live validation errors during authoring
|
|
38
|
+
- `headless width="800" height="600"` for test harness use
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<om-map center="[-122.42, 37.77]" zoom="11" basemap="maplibre" validate>
|
|
44
|
+
...
|
|
45
|
+
</om-map>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### `<om-layer>`
|
|
49
|
+
|
|
50
|
+
Declares a deck.gl layer. Required: `id`, `type`.
|
|
51
|
+
|
|
52
|
+
Core attributes:
|
|
53
|
+
|
|
54
|
+
- `id="quakes"` — stable identity for behaviors, widgets, tests, stories.
|
|
55
|
+
- `type="ScatterplotLayer"` — any bundled layer type.
|
|
56
|
+
- `data="./points.json"` — URL, stream, draw store, or omit for inline JSON.
|
|
57
|
+
- `label="Earthquakes"` and `color="#b30000"` — legend metadata.
|
|
58
|
+
- `pickable` — enable click/hover behaviors.
|
|
59
|
+
- `visible="false"` or `opacity="0"` — initial visibility/opacity.
|
|
60
|
+
|
|
61
|
+
Accessors:
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
get-position="[$lon, $lat]"
|
|
65
|
+
get-radius="$population * 0.001"
|
|
66
|
+
get-fill-color="$value > 100 ? [255,0,0] : [0,128,255]"
|
|
67
|
+
get-fill-color="scale($depth, sequential, ['#ffffcc','#800026'], domain=[0,700])"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`$field` works on flat rows, GeoJSON properties, columnar JSON, CSV/TSV columns, and Arrow point columns.
|
|
71
|
+
|
|
72
|
+
Smart shorthands:
|
|
73
|
+
|
|
74
|
+
- `color="#1f9e89"` sets a constant `getFillColor` when no explicit `get-fill-color` exists, and always feeds the legend swatch.
|
|
75
|
+
- `radius="6"` sets a constant `getRadius` when no explicit `get-radius` exists.
|
|
76
|
+
|
|
77
|
+
Transitions:
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
transition="get-fill-color 800ms, get-radius 400ms"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Filtering:
|
|
84
|
+
|
|
85
|
+
```html
|
|
86
|
+
filter-field="magnitude" filter-range="[4, 10]"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Built-In Layer Types
|
|
90
|
+
|
|
91
|
+
Use the `type` value exactly:
|
|
92
|
+
|
|
93
|
+
`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`.
|
|
94
|
+
|
|
95
|
+
Common choices:
|
|
96
|
+
|
|
97
|
+
- Points: `ScatterplotLayer`, `IconLayer`, `TextLayer`, `PopupLayer`.
|
|
98
|
+
- Lines/routes: `PathLayer`, `LineLayer`, `ArcLayer`, `TripsLayer`.
|
|
99
|
+
- Polygons/choropleths: `GeoJsonLayer`, `PolygonLayer`.
|
|
100
|
+
- Aggregation: `HeatmapLayer`, `HexagonLayer`, `GridLayer`, `ScreenGridLayer`.
|
|
101
|
+
- Tiles: `TileLayer`, `MVTLayer`, `Tile3DLayer`.
|
|
102
|
+
- 3D models: `ScenegraphLayer`, `SimpleMeshLayer`, `PointCloudLayer`, `Tile3DLayer`.
|
|
103
|
+
|
|
104
|
+
### Data Sources
|
|
105
|
+
|
|
106
|
+
| Source | Manifest | Notes |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| JSON / GeoJSON URL | `data="./points.json"` | Arrays or FeatureCollections. |
|
|
109
|
+
| Inline JSON | child `<script type="application/json">` | Good for tests/demos. |
|
|
110
|
+
| Columnar JSON | `{"columns": {"lon": [...], "lat": [...]}}` | Fast point path. |
|
|
111
|
+
| CSV / TSV | `data="./quakes.csv"` | Parsed to typed columns. |
|
|
112
|
+
| Arrow / GeoArrow IPC | `data="./big.arrow"` | Points stay columnar; lines/polygons become GeoJSON features. |
|
|
113
|
+
| Shapefile | `data="./countries.shp"` | Loads sidecars and joins `.dbf` attributes. |
|
|
114
|
+
| KML | `data="./tour.kml"` | Placemarks become GeoJSON features. |
|
|
115
|
+
| WebSocket | `data="wss://feed" key="id" flush="250ms" source="decoder"` | Upsert-by-key stream. |
|
|
116
|
+
| Polling | `data="/api/fleet.json" refresh="5s"` | Snapshot replace. |
|
|
117
|
+
| Draw store | `data="draw:sketch"` | Written by draw widget. |
|
|
118
|
+
|
|
119
|
+
Authenticated fetches:
|
|
120
|
+
|
|
121
|
+
```js
|
|
122
|
+
import { OmMap } from "@nika-js/onlymap";
|
|
123
|
+
OmMap.configureData({ headers: { Authorization: `Bearer ${token}` } });
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Custom format:
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
OmMap.registerFormat({
|
|
130
|
+
match: (url, contentType) => url.endsWith(".custom"),
|
|
131
|
+
parse: async (res, url) => /* return rows or columnar data */
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Custom stream decoder:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
OmMap.registerSource("fleet", {
|
|
139
|
+
onOpen: (send) => send(JSON.stringify({ subscribe: "vehicles" })),
|
|
140
|
+
decode: (msg) => msg.type === "position" ? { id: msg.id, lon: msg.lon, lat: msg.lat } : null
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Accessor Blocks
|
|
145
|
+
|
|
146
|
+
Restricted expression block:
|
|
147
|
+
|
|
148
|
+
```html
|
|
149
|
+
<script type="om/accessors">
|
|
150
|
+
export const getPosition = d => [$lon, $lat];
|
|
151
|
+
export const getRadius = d => Math.max(2, $magnitude * 3);
|
|
152
|
+
</script>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Full JavaScript block:
|
|
156
|
+
|
|
157
|
+
```html
|
|
158
|
+
<om-layer id="routes" type="PathLayer" js>
|
|
159
|
+
<script type="om/accessors">
|
|
160
|
+
const speeds = { slow: [80, 120, 255], fast: [255, 80, 80] };
|
|
161
|
+
export const getColor = d => speeds[d.speedClass] ?? [180, 180, 180];
|
|
162
|
+
</script>
|
|
163
|
+
</om-layer>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Do not use full-JS blocks on columnar/Arrow layers.
|
|
167
|
+
|
|
168
|
+
### `<om-widget>`
|
|
169
|
+
|
|
170
|
+
Built-ins:
|
|
171
|
+
|
|
172
|
+
- `legend`
|
|
173
|
+
- `layer-switcher`
|
|
174
|
+
- `zoom-controls`
|
|
175
|
+
- `scale-bar`
|
|
176
|
+
- `attribution`
|
|
177
|
+
- `filter`
|
|
178
|
+
- `draw`
|
|
179
|
+
- `vega-lite`
|
|
180
|
+
- `player`
|
|
181
|
+
|
|
182
|
+
Positions: `top-left`, `top-right`, `bottom-left`, `bottom-right`.
|
|
183
|
+
|
|
184
|
+
Examples:
|
|
185
|
+
|
|
186
|
+
```html
|
|
187
|
+
<om-widget type="legend" position="bottom-right" title="Layers" interactive></om-widget>
|
|
188
|
+
<om-widget type="filter" layer="quakes" field="magnitude" position="top-left"></om-widget>
|
|
189
|
+
<om-widget type="draw" target="sketch" modes="point line polygon" save="both"></om-widget>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Custom widget:
|
|
193
|
+
|
|
194
|
+
```html
|
|
195
|
+
<om-widget position="top-left">
|
|
196
|
+
<div id="out"></div>
|
|
197
|
+
<script type="om/widget">
|
|
198
|
+
this.watch = ["data:quakes", "viewport", "selection"];
|
|
199
|
+
this.render = (ctx) => {
|
|
200
|
+
const s = ctx.stats("quakes", "magnitude", { scope: "viewport" });
|
|
201
|
+
this.$("#out").textContent = `${s.count} quakes`;
|
|
202
|
+
};
|
|
203
|
+
</script>
|
|
204
|
+
</om-widget>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Widget context:
|
|
208
|
+
|
|
209
|
+
- `ctx.layers`
|
|
210
|
+
- `ctx.data(id)`
|
|
211
|
+
- `ctx.dataInViewport(id)`
|
|
212
|
+
- `ctx.stats(id, field, { scope: "viewport" })`
|
|
213
|
+
- `ctx.selection`
|
|
214
|
+
- `ctx.viewport`
|
|
215
|
+
- `ctx.emit(action, payload)`
|
|
216
|
+
|
|
217
|
+
Use `this.$()` and `this.root`; widgets render in shadow DOM.
|
|
218
|
+
|
|
219
|
+
### `<om-overlay>`
|
|
220
|
+
|
|
221
|
+
Rich sparse HTML anchored to the map.
|
|
222
|
+
|
|
223
|
+
Anchors:
|
|
224
|
+
|
|
225
|
+
- `anchor="[lng, lat]"`
|
|
226
|
+
- `anchor-from="selection"`
|
|
227
|
+
- `anchor-layer="regions" anchor-feature-id="mission"`
|
|
228
|
+
|
|
229
|
+
Templates:
|
|
230
|
+
|
|
231
|
+
- `{{field}}` HTML-escaped interpolation.
|
|
232
|
+
- `{{{field}}}` raw HTML; avoid unless trusted.
|
|
233
|
+
|
|
234
|
+
Example:
|
|
235
|
+
|
|
236
|
+
```html
|
|
237
|
+
<om-overlay id="detail" anchor-from="selection" visible="false">
|
|
238
|
+
<div><b>{{place}}</b> M {{magnitude}}</div>
|
|
239
|
+
</om-overlay>
|
|
240
|
+
<om-behavior on="click" layer="quakes" action="show-overlay" target="detail"></om-behavior>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### `<om-behavior>`
|
|
244
|
+
|
|
245
|
+
Declarative event to action binding.
|
|
246
|
+
|
|
247
|
+
Events: `click`, `hover`, `drag`, `load`, `data-loaded`.
|
|
248
|
+
|
|
249
|
+
Common built-in actions:
|
|
250
|
+
|
|
251
|
+
- `show-overlay`, `hide-overlay`
|
|
252
|
+
- `show-tooltip`, `hide-tooltip`
|
|
253
|
+
- `toggle-layer`
|
|
254
|
+
- `highlight-feature`
|
|
255
|
+
- `zoom-to-feature`
|
|
256
|
+
- `filter-layer`
|
|
257
|
+
- `zoom-in`, `zoom-out`
|
|
258
|
+
- `fly-to`
|
|
259
|
+
- story actions: `story-play`, `story-pause`, `story-seek`
|
|
260
|
+
- effect actions: `fade`, `pulse`, `trace`, `populate`
|
|
261
|
+
- draw actions: `draw-mode`, `draw-commit`, `draw-cancel`, `draw-delete`, `draw-clear`, `draw-config`, `draw-save`
|
|
262
|
+
|
|
263
|
+
Payload attributes are kebab-case and become camelCase payload keys.
|
|
264
|
+
|
|
265
|
+
Example:
|
|
266
|
+
|
|
267
|
+
```html
|
|
268
|
+
<om-behavior on="click" layer="regions" action="zoom-to-feature" duration="1200ms"></om-behavior>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Stories
|
|
272
|
+
|
|
273
|
+
Use `<om-story>` with `<om-step>` children. Stories are siblings of layers/overlays, not containers.
|
|
274
|
+
|
|
275
|
+
```html
|
|
276
|
+
<om-story id="tour" interrupt="pause">
|
|
277
|
+
<om-step duration="2s" action="fly-to" center="[-122.44, 37.78]" zoom="12" curve></om-step>
|
|
278
|
+
<om-step duration="1s" action="show-overlay" target="intro" parallel></om-step>
|
|
279
|
+
<om-step duration="1500ms" fade layer="regions"></om-step>
|
|
280
|
+
<om-step duration="2s" trace layer="regions" feature-id="mission"></om-step>
|
|
281
|
+
</om-story>
|
|
282
|
+
<om-widget type="player" story="tour" position="bottom-left"></om-widget>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Story timing attributes:
|
|
286
|
+
|
|
287
|
+
- `duration`
|
|
288
|
+
- `delay`
|
|
289
|
+
- `parallel`
|
|
290
|
+
|
|
291
|
+
Use declarative payloads for scrub-safe state: `visible="true"` when toggling, explicit `filter-range`, explicit camera target.
|
|
292
|
+
|
|
293
|
+
### Manual Drawing
|
|
294
|
+
|
|
295
|
+
Use a normal GeoJSON layer bound to a draw store plus a draw widget:
|
|
296
|
+
|
|
297
|
+
```html
|
|
298
|
+
<om-layer id="sketch" type="GeoJsonLayer" data="draw:sketch"
|
|
299
|
+
get-fill-color="[80, 140, 255, 90]"
|
|
300
|
+
get-line-color="[40, 90, 220]"
|
|
301
|
+
point-radius-min-pixels="6"
|
|
302
|
+
line-width-min-pixels="3"
|
|
303
|
+
stroked filled pickable></om-layer>
|
|
304
|
+
<om-widget type="draw" target="sketch" position="top-left"
|
|
305
|
+
modes="point line polygon" save="both"
|
|
306
|
+
autosave="my-sketch"></om-widget>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The draw widget supports points, lines, polygons, delete-last, clear, save, and autosave. Lines/polygons close with double-click or Enter; Escape cancels the in-progress shape.
|
|
310
|
+
|
|
311
|
+
### 3D
|
|
312
|
+
|
|
313
|
+
ScenegraphLayer places GLB/glTF models at coordinates:
|
|
314
|
+
|
|
315
|
+
```html
|
|
316
|
+
<om-layer id="vehicles" type="ScenegraphLayer"
|
|
317
|
+
scenegraph="./truck.glb"
|
|
318
|
+
data="./vehicles.json"
|
|
319
|
+
get-position="[$lon, $lat]"
|
|
320
|
+
get-orientation="[0, $heading, 90]"
|
|
321
|
+
get-scale="[1, 1, 1]"
|
|
322
|
+
lighting="pbr" pickable></om-layer>
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Roll `90` stands Y-up glTF assets upright. Use `Tile3DLayer` for large 3D Tiles datasets.
|
|
326
|
+
|
|
327
|
+
3D Tiles roots use `tileset`, not `data`, because OnlyMapJS reserves `data` for parsed datasets:
|
|
328
|
+
|
|
329
|
+
```html
|
|
330
|
+
<om-layer id="city" type="Tile3DLayer"
|
|
331
|
+
tileset="https://example.com/tileset.json"></om-layer>
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
For 3D Tiles LOD/refinement experiments, use:
|
|
335
|
+
|
|
336
|
+
```html
|
|
337
|
+
<om-layer id="city" type="Tile3DLayer"
|
|
338
|
+
tileset="https://example.com/tileset.json"
|
|
339
|
+
maximum-screen-space-error="2"
|
|
340
|
+
maximum-memory-usage="256"
|
|
341
|
+
view-distance-scale="0.85"></om-layer>
|
|
342
|
+
```
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# OnlyMapJS Validation And Testing
|
|
2
|
+
|
|
3
|
+
Use this reference when a user asks to debug, validate, review, or test an OnlyMapJS page.
|
|
4
|
+
|
|
5
|
+
## Static Validation
|
|
6
|
+
|
|
7
|
+
Use `OmMap.validate(htmlString)` before finalizing agent-written maps.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { OmMap } from "@nika-js/onlymap";
|
|
11
|
+
|
|
12
|
+
const result = OmMap.validate(html);
|
|
13
|
+
if (!result.valid) {
|
|
14
|
+
console.log(result.errors);
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Errors and warnings have:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
{
|
|
22
|
+
severity: "error" | "warning",
|
|
23
|
+
element: "om-layer#quakes",
|
|
24
|
+
attribute: "get-position",
|
|
25
|
+
message: "...",
|
|
26
|
+
fix: "..."
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Treat `fix` as the direct instruction to apply.
|
|
31
|
+
|
|
32
|
+
Common validation failures:
|
|
33
|
+
|
|
34
|
+
- Self-closing custom elements cause bad DOM nesting. Use explicit closing tags.
|
|
35
|
+
- Missing layer `id`.
|
|
36
|
+
- Unknown layer `type`.
|
|
37
|
+
- Missing required accessor such as `get-position`.
|
|
38
|
+
- `scale()` missing explicit `domain=`.
|
|
39
|
+
- Unknown attributes from camelCase or typos.
|
|
40
|
+
- `js` full-JS accessor block on columnar/Arrow data.
|
|
41
|
+
- `filter-field` without `filter-range`.
|
|
42
|
+
- Inline handlers in widget/overlay content.
|
|
43
|
+
- Story steps containing child elements.
|
|
44
|
+
|
|
45
|
+
## Snapshot IR
|
|
46
|
+
|
|
47
|
+
Use `OmMap.snapshotIR(html)` to inspect what a manifest means without WebGL or network.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
expect(OmMap.snapshotIR(html)).toMatchSnapshot();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The snapshot contains resolved layer descriptors. Accessors appear as behavioral fingerprints, so expression changes show up in diffs without serializing functions.
|
|
54
|
+
|
|
55
|
+
## Headless Behavior Harness
|
|
56
|
+
|
|
57
|
+
Use `mountForTest` for most interaction tests.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// @vitest-environment happy-dom
|
|
61
|
+
import "@nika-js/onlymap";
|
|
62
|
+
import { mountForTest } from "@nika-js/onlymap";
|
|
63
|
+
|
|
64
|
+
const h = await mountForTest(`
|
|
65
|
+
<om-map center="[-122.42, 37.77]" zoom="11">
|
|
66
|
+
<om-layer id="points" type="ScatterplotLayer" get-position="[$lon, $lat]" pickable>
|
|
67
|
+
<script type="application/json">
|
|
68
|
+
[{ "id": "p1", "name": "A", "lon": -122.42, "lat": 37.77 }]
|
|
69
|
+
</script>
|
|
70
|
+
</om-layer>
|
|
71
|
+
<om-overlay id="detail" anchor-from="selection" visible="false">
|
|
72
|
+
<div>{{name}}</div>
|
|
73
|
+
</om-overlay>
|
|
74
|
+
<om-behavior on="click" layer="points" action="show-overlay" target="detail"></om-behavior>
|
|
75
|
+
</om-map>
|
|
76
|
+
`);
|
|
77
|
+
|
|
78
|
+
await h.pick({ layer: "points", featureId: "p1" });
|
|
79
|
+
expect(h.map.querySelector("#detail").shadowRoot.textContent).toContain("A");
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Harness operations:
|
|
83
|
+
|
|
84
|
+
- `h.pick({ layer, featureId | index, type })`
|
|
85
|
+
- `h.clearSelection()`
|
|
86
|
+
- `h.emit(action, payload)`
|
|
87
|
+
- `h.setView({ center, zoom, pitch, bearing })`
|
|
88
|
+
- `h.layers()`
|
|
89
|
+
- `h.story(id).advance(ms)`
|
|
90
|
+
- `h.story(id).seek(ms)`
|
|
91
|
+
- `h.unmount()`
|
|
92
|
+
|
|
93
|
+
Use `vi.stubGlobal("fetch", ...)` for URL data and `vi.stubGlobal("WebSocket", ...)` for streams.
|
|
94
|
+
|
|
95
|
+
## Browser/E2E Testing
|
|
96
|
+
|
|
97
|
+
Use Playwright only for pixels, real GPU picking, basemap composition, or asset rendering.
|
|
98
|
+
|
|
99
|
+
Wait for readiness:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
await page.evaluate(() => document.querySelector("om-map").ready);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Project data coordinates to click pixels:
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const p = await page.evaluate(() => document.querySelector("om-map").projectInternal([-122.42, 37.77]));
|
|
109
|
+
const box = await page.locator("om-map").boundingBox();
|
|
110
|
+
await page.mouse.click(box.x + p[0], box.y + p[1]);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Do not use sleeps. Wait for `ready`, DOM state, screenshots, or `expect.poll`.
|
|
114
|
+
|
|
115
|
+
## Debugging Checklist
|
|
116
|
+
|
|
117
|
+
1. Is every OnlyMapJS custom element explicitly closed?
|
|
118
|
+
2. Does every layer have `id` and a valid `type`?
|
|
119
|
+
3. Are attributes kebab-case?
|
|
120
|
+
4. Is the data shape compatible with the accessor? Prefer `$field`.
|
|
121
|
+
5. Does every `scale()` include `domain=`?
|
|
122
|
+
6. Are behaviors pointing at existing `layer`/`target` ids?
|
|
123
|
+
7. Is `pickable` present where click/hover behaviors are expected?
|
|
124
|
+
8. Are credentials configured through `OmMap.configureData`, not markup?
|
|
125
|
+
9. Is the relevant CSS imported when using MapLibre basemaps?
|
|
126
|
+
10. Is a story step setting state rather than toggling ambiguous state?
|