@nika-js/onlymap 0.4.4 → 0.5.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.
Files changed (39) hide show
  1. package/README.md +10 -7
  2. package/THIRD-PARTY-LICENSES.md +2 -2
  3. package/bin/onlymapjs.mjs +14 -1
  4. package/dist/{LercDecode.es-CgN9Gb9e.js → LercDecode.es-B-OFS9hR.js} +1 -1
  5. package/dist/{basemap-Bfn5Z__c.js → basemap-CXVNH22A.js} +3 -3
  6. package/dist/cityjson-urQeujQv.js +407 -0
  7. package/dist/cityjson.d.ts +138 -0
  8. package/dist/data-layer.d.ts +10 -0
  9. package/dist/elements/om-map.d.ts +70 -0
  10. package/dist/{index-lXrP3rPo.js → index-BonJP_St.js} +1 -1
  11. package/dist/index-CW1n5LdO.js +4006 -0
  12. package/dist/{index-CnitG1VX.js → index-CjZMcGou.js} +2 -2
  13. package/dist/{index-M8KfTTol.js → index-DE7T4lfa.js} +1 -1
  14. package/dist/{index-KgO0MBqA.js → index-DiYFebYs.js} +11514 -11259
  15. package/dist/{index-C4cWRigY.js → index-DuY3Zn6r.js} +1 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/layer-registry.d.ts +22 -0
  18. package/dist/{lerc-l-QFh62d.js → lerc-BhtFlCQ3.js} +2 -2
  19. package/dist/license.d.ts +34 -8
  20. package/dist/onlymap.standalone.js +23608 -22941
  21. package/dist/onlymapjs.js +69 -65
  22. package/dist/programmatic.d.ts +4 -0
  23. package/dist/quota-notice.d.ts +43 -0
  24. package/dist/raster-BpVAEzbU.js +4842 -0
  25. package/dist/react.js +162 -161
  26. package/dist/runtime-core.d.ts +9 -0
  27. package/dist/testing.d.ts +6 -0
  28. package/dist/version.d.ts +1 -1
  29. package/dist/widget-layout.d.ts +20 -0
  30. package/docs/3d-assets.md +99 -1
  31. package/docs/testing.md +4 -2
  32. package/llms.txt +2 -2
  33. package/onlymapjs.html-data.json +12 -8
  34. package/package.json +3 -2
  35. package/skills/onlymapjs/SKILL.md +4 -2
  36. package/skills/onlymapjs/references/patterns.md +4 -0
  37. package/skills/onlymapjs/references/syntax.md +70 -17
  38. package/skills/onlymapjs/references/testing.md +2 -1
  39. package/dist/raster-BBA_oI-d.js +0 -8843
@@ -0,0 +1,138 @@
1
+ /**
2
+ * CityJSON / CityJSONSeq ingestion (spec: "CityJSON ingestion"; issue #25).
3
+ *
4
+ * A LAZY chunk — reached only through `CITYJSON_FORMAT`'s dynamic import in
5
+ * data-layer.ts, so neither this decoder nor proj4 weighs on the eager
6
+ * bundle (the src/raster.ts precedent). proj4 itself is imported lazily
7
+ * AGAIN inside `resolveCrs`, so a file already in lon/lat never pays for it.
8
+ *
9
+ * Two output shapes share one geometry computation (`computeCityObjectGeometry`):
10
+ *
11
+ * - DEFAULT — one GeoJSON `Feature` per CityObject: a 2D footprint polygon
12
+ * plus DERIVED height properties, rendered through GeoJsonLayer's
13
+ * `extruded` + `get-elevation`. Lit, pickable, GPU-filterable,
14
+ * terrain-aware — and, because extrusion is a flat-topped prism, it
15
+ * cannot draw the actual shape of a pitched/hipped LoD2.2 roof no matter
16
+ * how accurate the derived height is.
17
+ * - `?om-surfaces=1` — one flat row PER FACE (wall, roof, ground), each
18
+ * carrying its real 3D ring coordinates (real per-vertex height, not a
19
+ * uniform extrusion) for `SolidPolygonLayer` with `extruded="false"` and
20
+ * `full3d`. This draws the true reconstructed shape. The tradeoff: deck's
21
+ * solid-polygon vertex shader computes lighting only inside its
22
+ * `if (solidPolygon.extruded)` branch (verified against
23
+ * solid-polygon-layer-vertex-main.glsl.js), so this path is unavoidably
24
+ * unlit — flat per-face fill color, no shading from surface orientation.
25
+ * `_full3d` is what makes near-vertical wall polygons triangulate at all:
26
+ * without it earcut works in the flat xy plane, where a wall's projected
27
+ * area is ~zero and the face silently vanishes.
28
+ *
29
+ * Semantics are not discarded in either mode: they drive which vertices
30
+ * count as roof (so the derived heights are real roof heights, not
31
+ * bounding-box heights) and survive as a `surface_type` per-face property in
32
+ * surfaces mode.
33
+ */
34
+ /** [x, y, z] in the source CRS — decompressed, not yet reprojected. */
35
+ type Vec3 = [number, number, number];
36
+ /** A face is a list of rings; ring[0] is the outer boundary, the rest are holes. */
37
+ type Face = number[][];
38
+ interface CityJsonTransform {
39
+ scale?: number[];
40
+ translate?: number[];
41
+ }
42
+ interface CityJsonSemantics {
43
+ surfaces?: {
44
+ type?: string;
45
+ }[];
46
+ values?: unknown;
47
+ }
48
+ interface CityJsonGeometry {
49
+ type?: string;
50
+ lod?: string | number;
51
+ boundaries?: unknown;
52
+ semantics?: CityJsonSemantics;
53
+ }
54
+ interface CityObject {
55
+ type?: string;
56
+ attributes?: Record<string, unknown>;
57
+ geometry?: CityJsonGeometry[];
58
+ parents?: string[];
59
+ children?: string[];
60
+ }
61
+ export declare function decompressVertices(vertices: number[][] | undefined, transform: CityJsonTransform | undefined): Vec3[];
62
+ /**
63
+ * Geometry → flat face list. Surface-less geometry types (points, lines) and
64
+ * anything unrecognized yield no faces, so the CityObject is skipped rather
65
+ * than emitted with a broken footprint.
66
+ */
67
+ export declare function flattenToFaces(geometry: CityJsonGeometry): Face[];
68
+ /** One semantic surface type per face (`"RoofSurface"`, …), aligned by index. */
69
+ export declare function faceSemantics(geometry: CityJsonGeometry, faceCount: number): (string | undefined)[];
70
+ /**
71
+ * Highest-LoD geometry that actually has surfaces. Higher LoD wins even though
72
+ * the output is an extrusion: an LoD2 solid carries real roof vertices and
73
+ * semantics, so its derived heights beat LoD1's single stated height.
74
+ * `?om-lod=` pins a specific one (see `parseCityJsonDocument`).
75
+ */
76
+ export declare function chooseGeometry(geometries: CityJsonGeometry[] | undefined, lodPin?: number): CityJsonGeometry | undefined;
77
+ /** `"https://www.opengis.net/def/crs/EPSG/0/7415"` / `"urn:ogc:def:crs:EPSG::7415"` / `"EPSG:7415"` → 7415. */
78
+ export declare function parseEpsgCode(referenceSystem: string | undefined): number | undefined;
79
+ /** Source [x, y] → WGS84 [lon, lat]. */
80
+ export interface CrsAdapter {
81
+ toLngLat(x: number, y: number): [number, number];
82
+ /**
83
+ * True when source coordinates are already metres (a projected grid), so
84
+ * horizontal areas can be measured on them directly. Geographic sources are
85
+ * in degrees and need a local metric frame first — see `buildFeature`.
86
+ */
87
+ projected: boolean;
88
+ }
89
+ /**
90
+ * Build the reprojection adapter for a document. An unsupported EPSG code
91
+ * throws rather than silently misplacing a whole city — the failure surfaces
92
+ * through the Data Layer's own `Failed to fetch data="…"` path, the same as
93
+ * any other format error.
94
+ */
95
+ export declare function resolveCrs(referenceSystem: string | undefined, url: string): Promise<CrsAdapter>;
96
+ /** Derived property names. Documented as reserved — they win over same-named source attributes. */
97
+ export interface DerivedProperties {
98
+ cityobject_id: string;
99
+ cityobject_type: string;
100
+ parent_id?: string;
101
+ lod?: string;
102
+ ground_height: number;
103
+ roof_height: number;
104
+ eaves_height: number;
105
+ ridge_height: number;
106
+ roof_area: number;
107
+ surface_count: number;
108
+ }
109
+ export declare function defaultFillColor(surfaceType: string | undefined, cityObjectType: string | undefined): string;
110
+ /**
111
+ * CityObjects → features (default) or per-face surface rows (`mode:
112
+ * "surfaces"`, see `parseSurfacesFlag`). Geometry usually hangs off child
113
+ * objects (`BuildingPart`, `BridgePart`), so a parent's attributes are
114
+ * inherited by its children — the child's own values win, and `parent_id`
115
+ * keeps the hierarchy walkable for selection.
116
+ */
117
+ export declare function decodeCityObjects(cityObjects: Record<string, CityObject> | undefined, vertices: Vec3[], crs: CrsAdapter, lodPin: number | undefined, mode?: "footprint" | "surfaces"): unknown[];
118
+ /** `?om-lod=1.2` → 1.2. The DataFormat contract sees only the URL, so this is the option channel. */
119
+ export declare function parseLodPin(url: string): number | undefined;
120
+ /**
121
+ * `?om-surfaces=1` → per-face `SolidPolygonLayer` rows instead of the default
122
+ * footprint-extrusion `GeoJsonLayer` Features. Same option-channel idiom as
123
+ * `om-lod` — and the same URL-keyed cache benefit: a plain URL and its
124
+ * `?om-surfaces=1` counterpart parse and cache independently, so one manifest
125
+ * can point a `GeoJsonLayer` at one and a `SolidPolygonLayer` at the other.
126
+ */
127
+ export declare function parseSurfacesFlag(url: string): boolean;
128
+ /** A whole `.city.json` document. One JSON parse — a single JSON value has no incremental path. */
129
+ export declare function parseCityJsonDocument(res: Response, url: string): Promise<unknown[]>;
130
+ /**
131
+ * Streaming `.city.jsonl`. `push` hands the Data Layer a fresh array reference
132
+ * every batch so deck.gl's shallow `data` diff fires and the city fills in as
133
+ * it downloads; the returned array is the complete set.
134
+ */
135
+ export declare function parseCityJsonSeq(res: Response, url: string, push: (features: unknown[]) => void): Promise<unknown[]>;
136
+ /** Test hook — resets the once-per-page missing-CRS warning. */
137
+ export declare function resetCityJsonWarningsForTests(): void;
138
+ export {};
@@ -70,6 +70,16 @@ export interface DataFormat {
70
70
  match(url: string, contentType?: string): boolean;
71
71
  /** Parse the claimed response. The parser module should be lazy-imported here (HU1 — the apache-arrow precedent). `url` is the requested URL (res.url is empty on constructed Responses — test fetches, service workers). */
72
72
  parse(res: Response, url: string): Promise<LayerData>;
73
+ /**
74
+ * Optional progressive variant for record-per-line formats (CityJSONSeq):
75
+ * `push` publishes a partial snapshot mid-download, taking the same
76
+ * cache-swap + notify path a stream flush takes, so the map fills in as
77
+ * bytes arrive instead of staying empty until EOF. Pass a FRESH array
78
+ * reference each time — deck.gl diffs `data` by identity. When present this
79
+ * is used instead of `parse`; the returned value is the complete dataset.
80
+ * `push` is a no-op during a `refresh` re-fetch — see getData for why.
81
+ */
82
+ parseIncremental?(res: Response, url: string, push: (partial: LayerData) => void): Promise<LayerData>;
73
83
  }
74
84
  /**
75
85
  * Row objects → ColumnarData (the memory-efficient shape: one array per
@@ -55,6 +55,9 @@ export declare class OmMapElement extends HTMLElementBase {
55
55
  private errorPanel;
56
56
  private runtimeErrors;
57
57
  private readyFired;
58
+ private visibleSizeChecked;
59
+ /** Custom-state holder for the `om-collapsed` height-floor marker — see applyHeightFloor. */
60
+ private internals;
58
61
  private resolveReady;
59
62
  readonly ready: Promise<void>;
60
63
  private readonly viewSettle;
@@ -67,6 +70,8 @@ export declare class OmMapElement extends HTMLElementBase {
67
70
  private widgetLayer;
68
71
  private readonly slotContainers;
69
72
  private readonly mandatedChromeHosts;
73
+ /** Slots that host mandated chrome (badge/attribution) — set at host creation, so the collision-dim exemption is a slot-level flag, not a per-flush DOM scan. */
74
+ private readonly slotsWithMandatedChrome;
70
75
  private widgetResizeObserver;
71
76
  private widgetsFolded;
72
77
  private foldPassPending;
@@ -99,13 +104,22 @@ export declare class OmMapElement extends HTMLElementBase {
99
104
  * adapters mount/unmount the actual controls inside.
100
105
  */
101
106
  private ensureMandatedChromeHost;
107
+ /** True while an internal reparent (slotting/manual-flip) is moving a widget — its transient disconnect must NOT be read as an author removal. */
108
+ private inSlotReparent;
109
+ /** Wrap an internal reparent: suppress history AND flag it as a move so unregisterWidgetInternal keeps the fold/anchor bookkeeping. */
110
+ private reparent;
102
111
  slotWidgetInternal(el: HTMLElement, slot: WidgetSlot): void;
103
112
  /** Called by <om-widget> when its live `fold` attribute changes. */
104
113
  refreshWidgetFoldInternal(): void;
105
114
  private startWidgetFoldObserver;
115
+ private foldBreakpointRaw?;
116
+ private foldBreakpointCache;
117
+ /** Cached breakpoint resolution — re-probes only when the token string actually changes, not on every ResizeObserver tick. */
106
118
  private foldBreakpointPx;
107
119
  private measureWidgetFold;
108
120
  private updateWidgetFoldForWidth;
121
+ /** Lift the bottom-end row clear of the bottom drawer toggle while folded. Applied here AND in the fold pass so a lazily-created bottom-end container still gets it. */
122
+ private syncFoldBottomOffset;
109
123
  private scheduleFoldPass;
110
124
  private shouldFoldWidget;
111
125
  private ensureFoldDrawer;
@@ -172,6 +186,7 @@ export declare class OmMapElement extends HTMLElementBase {
172
186
  * modeled (headless has no rects; this no-ops there).
173
187
  */
174
188
  private dimSlotsAgainstOverlays;
189
+ /** widgets-toggle can sit in any author-chosen slot, so still scan for it (attribution/badge are covered by the slot flag). */
175
190
  private slotHasNeverHidesWidget;
176
191
  emit(event: string, payload?: Record<string, unknown>): void;
177
192
  /** Recenters (and optionally rezooms) the map — instant, not an animated fly. */
@@ -218,6 +233,20 @@ export declare class OmMapElement extends HTMLElementBase {
218
233
  */
219
234
  injectPickInternal(selection: Selection | null): void;
220
235
  injectDragPickInternal(selection: Selection): void;
236
+ /** Every click/hover map coordinate (spec: "Manual Drawing"): drives the
237
+ * internal draw controller AND fires the public `om-map-point` event, so
238
+ * consumer capture tools the built-in draw widget doesn't cover
239
+ * (rectangle/circle AOIs) can subscribe. The two consumers are
240
+ * DECOUPLED: the event dispatches first (dispatchEvent isolates a
241
+ * throwing listener), then the draw controller runs — so neither a
242
+ * throwing draw session nor a throwing listener can starve the other.
243
+ * Fires at pointer rate on hover (no debounce — vertex capture needs
244
+ * every point); heavy listeners should throttle their own work. */
245
+ private handleMapPoint;
246
+ /** Harness map-point injection (spec: "Consumer Testing Surface") — the
247
+ * same path a real deck click/hover coordinate takes, so the om-map-point
248
+ * event and custom capture tools are testable without a GPU. */
249
+ injectMapPointInternal(coordinate: [number, number] | null, kind?: "click" | "hover"): void;
221
250
  /** Harness setView (spec: "Consumer Testing Surface") — the one path that reaches pitch/bearing. */
222
251
  setViewInternal(partial: {
223
252
  longitude?: number;
@@ -258,6 +287,33 @@ export declare class OmMapElement extends HTMLElementBase {
258
287
  private dispatchRendererLoad;
259
288
  /** Fires `om-map-ready` / resolves `.ready` once the first reconcile ran, the renderer is up, and no declared data URL is still loading. */
260
289
  private checkReady;
290
+ /**
291
+ * Per-element half of the base-layout guardrail: mark the host `om-collapsed`
292
+ * so the layered `min-height` floor applies, but ONLY while it genuinely
293
+ * measures zero. Gating the floor this way is what lets an explicit author
294
+ * height win outright — a map with any height never carries the state, so the
295
+ * floor rule never matches it and a 300px map stays 300px.
296
+ *
297
+ * Clear-then-re-measure makes it idempotent and self-correcting: the connect-
298
+ * time call runs mid-parse, where a parent sized by later siblings still
299
+ * measures zero, and the post-ready call drops a floor that turned out to be
300
+ * unnecessary.
301
+ *
302
+ * A custom state rather than an attribute or inline style, deliberately: the
303
+ * host's own MutationObserver treats attribute writes as manifest edits (a
304
+ * reconcile), history.ts records them as undo steps, and either would
305
+ * serialize into a saved manifest. A custom state touches none of that.
306
+ */
307
+ private applyHeightFloor;
308
+ /**
309
+ * First-shot guardrail: warn (once) if the map has no visible size once it is
310
+ * ready. The base-layout default covers a bare `<om-map>`, but a constrained
311
+ * parent (a 0-height flex/grid cell, `height:100%` under an unsized ancestor)
312
+ * can still collapse it — a silent blank map otherwise.
313
+ * Skipped headless: jsdom/happy-dom report 0×0 by design, and `mountForTest`
314
+ * sets the `headless` attribute, so this never fires in the test harness.
315
+ */
316
+ private checkVisibleSize;
261
317
  private pickPayload;
262
318
  /**
263
319
  * Dispatches to every `<om-behavior on="eventName">` (live-queried, like
@@ -302,6 +358,20 @@ declare global {
302
358
  /** "user" if any change in the settled burst came from a canvas gesture; "programmatic" for pure API/action/story moves. */
303
359
  origin: "user" | "programmatic";
304
360
  }>;
361
+ /** Every click/hover's map coordinate (null when the pointer is off any
362
+ * geometry deck can unproject) — the consumer hook for custom capture
363
+ * tools beyond the built-in draw widget. */
364
+ "om-map-point": CustomEvent<{
365
+ coordinate: [number, number] | null;
366
+ kind: "click" | "hover";
367
+ }>;
368
+ /** A Tile3DLayer finished loading its root tileset — detail carries the
369
+ * authored layer id and the live deck `Tileset3D` for tools (e.g. region
370
+ * export) that need the real tileset, not the IR. */
371
+ "om-tileset-load": CustomEvent<{
372
+ layerId: string;
373
+ tileset: unknown;
374
+ }>;
305
375
  }
306
376
  }
307
377
  export {};
@@ -1,4 +1,4 @@
1
- import { ac as _t, ad as Ct } from "./index-KgO0MBqA.js";
1
+ import { ac as _t, ad as Ct } from "./index-DiYFebYs.js";
2
2
  function Yt(e, r, t = 2, i, o = "xy") {
3
3
  const s = r && r.length, l = s ? r[0] * t : e.length;
4
4
  let c = Ut(e, 0, l, t, !0, i && i[0], o);