@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.
- package/README.md +10 -7
- package/THIRD-PARTY-LICENSES.md +2 -2
- package/bin/onlymapjs.mjs +14 -1
- package/dist/{LercDecode.es-CgN9Gb9e.js → LercDecode.es-B-OFS9hR.js} +1 -1
- package/dist/{basemap-Bfn5Z__c.js → basemap-CXVNH22A.js} +3 -3
- package/dist/cityjson-urQeujQv.js +407 -0
- package/dist/cityjson.d.ts +138 -0
- package/dist/data-layer.d.ts +10 -0
- package/dist/elements/om-map.d.ts +70 -0
- package/dist/{index-lXrP3rPo.js → index-BonJP_St.js} +1 -1
- package/dist/index-CW1n5LdO.js +4006 -0
- package/dist/{index-CnitG1VX.js → index-CjZMcGou.js} +2 -2
- package/dist/{index-M8KfTTol.js → index-DE7T4lfa.js} +1 -1
- package/dist/{index-KgO0MBqA.js → index-DiYFebYs.js} +11514 -11259
- package/dist/{index-C4cWRigY.js → index-DuY3Zn6r.js} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/layer-registry.d.ts +22 -0
- package/dist/{lerc-l-QFh62d.js → lerc-BhtFlCQ3.js} +2 -2
- package/dist/license.d.ts +34 -8
- package/dist/onlymap.standalone.js +23608 -22941
- package/dist/onlymapjs.js +69 -65
- package/dist/programmatic.d.ts +4 -0
- package/dist/quota-notice.d.ts +43 -0
- package/dist/raster-BpVAEzbU.js +4842 -0
- package/dist/react.js +162 -161
- package/dist/runtime-core.d.ts +9 -0
- package/dist/testing.d.ts +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/widget-layout.d.ts +20 -0
- package/docs/3d-assets.md +99 -1
- package/docs/testing.md +4 -2
- package/llms.txt +2 -2
- package/onlymapjs.html-data.json +12 -8
- package/package.json +3 -2
- package/skills/onlymapjs/SKILL.md +4 -2
- package/skills/onlymapjs/references/patterns.md +4 -0
- package/skills/onlymapjs/references/syntax.md +70 -17
- package/skills/onlymapjs/references/testing.md +2 -1
- package/dist/raster-BBA_oI-d.js +0 -8843
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ deck.gl is the best WebGL data-visualization engine there is — and OnlyMapJS i
|
|
|
46
46
|
|---|---|---|
|
|
47
47
|
| Setup | `new Deck({...})`, canvas + basemap sync wiring | one `<om-map>` element (or `<OmMap>` in React) |
|
|
48
48
|
| State | your reducers/stores drive `setProps` | the manifest **is** the state — edit an attribute, the map reconciles; undo/redo built in |
|
|
49
|
-
| Data loading | fetch + parse + reload yourself | `data="…"` — GeoJSON, CSV, Arrow/GeoArrow, Shapefile, KML, WebSocket streams, polled REST; GeoTIFF/COG rasters via `type="COGLayer"` |
|
|
49
|
+
| Data loading | fetch + parse + reload yourself | `data="…"` — GeoJSON, CSV, Arrow/GeoArrow, Shapefile, KML, CityJSON, WebSocket streams, polled REST; GeoTIFF/COG rasters via `type="COGLayer"` |
|
|
50
50
|
| Accessors | JS functions + `updateTriggers` bookkeeping | `get-*` expressions; update triggers derived automatically |
|
|
51
51
|
| UI | build legends/popups/filters from scratch | built-in widgets, overlays, behaviors — declarative |
|
|
52
52
|
| Testing | mock WebGL or ship untested | `OmMap.validate`, IR snapshots, headless behavioral harness |
|
|
@@ -69,7 +69,7 @@ Or with no build step at all, straight from a CDN:
|
|
|
69
69
|
|
|
70
70
|
The bare package URL serves `dist/onlymap.standalone.js`, a single-file bundle built for exactly this (jsDelivr too). Use a CDN that serves the package's raw files — **not** a rebundling CDN like esm.sh, which re-splits the bundle into duplicate copies of the deck.gl/luma.gl runtime and breaks every layer's shader compilation.
|
|
71
71
|
|
|
72
|
-
Then `npx @nika-js/onlymap init` wires up VS Code IntelliSense and `!`-prefixed manifest snippets for your project. The library ships with
|
|
72
|
+
Then `npx @nika-js/onlymap init` wires up VS Code IntelliSense and `!`-prefixed manifest snippets for your project. The library ships with 590 unit/behavioral tests and 58 Playwright GPU tests.
|
|
73
73
|
|
|
74
74
|
The [examples](https://github.com/NikaGeospatial/onlymapjs/tree/main/examples) are the best tour: widgets, behaviors & overlays, basemaps, columnar/Arrow data, manual drawing, 3D models, scene lighting (with the native lighting widget), DEM terrain, a live WebSocket ship feed, and a polled driver fleet.
|
|
75
75
|
|
|
@@ -79,7 +79,7 @@ A handful of elements, one rule: **attributes are kebab-case versions of deck.gl
|
|
|
79
79
|
|
|
80
80
|
| Element | Role |
|
|
81
81
|
|---|---|
|
|
82
|
-
| `<om-map>` | The map. `center`, `zoom`, `pitch`, `bearing`; `basemap` takes a free preset (`positron`, `liberty`, `dark-matter`, `osm`, …), a style URL, or `"none"` (standalone canvas) — and switches **live**; `validate` for a live on-page error panel. |
|
|
82
|
+
| `<om-map>` | The map. `center`, `zoom`, `pitch`, `bearing`; `basemap` takes a free preset (`positron`, `liberty`, `dark-matter`, `osm`, …), a style URL, or `"none"` (standalone canvas) — and switches **live**; `validate` for a live on-page error panel. Give it a height: a custom element is `display:inline` by default, so the library injects a `display:block` default (fills a sized parent, else a 400px floor) to keep a bare map visible, but set an explicit height (`om-map { height: 100vh }`) for real layout — any height you set wins outright, including one below the floor. A map that still collapses warns in the console; `hidden` and `display:none` maps stay hidden and stay quiet. |
|
|
83
83
|
| `<om-layer>` | Any of **34 layer types** by name — all of deck.gl's core, geo, aggregation, and mesh layers (Scatterplot, GeoJson, Arc, Path, Heatmap, Hexagon, Trips, Tile, Tile3D, Scenegraph, …) plus the built-in `PopupLayer` for WebGL badges/labels at scale and the native `COGLayer` for GeoTIFF rasters. `id` required; `label`/`color` feed the legend. |
|
|
84
84
|
| `<om-widget>` | UI panels. Built-ins: `legend` (symbology-aware: color scales render as gradient ramps or class ranges, categorical ternaries as discrete palettes), `layer-switcher`, `basemap-switcher`, `lighting`, `zoom-controls`, `undo-redo`, `scale-bar`, `attribution`, `filter`, `draw`, `vega-lite` (live charts). Or write your own inline with HTML + a `<script type="om/widget">`. Adjacent compact button widgets (`zoom-controls`, `undo-redo`, `widgets-toggle`) **auto-cluster** into one control group (opt out per widget with `cluster="false"`), and `<om-map widgets-hidden>` / the `set-widgets-visible` action / `<om-widget type="widgets-toggle">` hide all authored chrome without destroying it — provider attribution and the license badge never hide. **Placement is managed**: `position` takes one of 8 logical, RTL-aware slots (`top-start`, `top-center`, `top-end`, `center-start`, `center-end`, `bottom-start`, `bottom-center`, `bottom-end`; legacy corner names alias) — same-slot widgets stack with flush edges and a shared gap, `order` sets in-slot ordering, and `position="manual"` opts out entirely (a plain block you style yourself, even outside the map). At map widths ≤640px, managed widgets automatically move into accessible top/end/bottom/start drawers; `fold="never"` keeps an essential control out, `widgets-fold="off"` disables folding, and `--om-widget-fold-breakpoint` changes the map-width threshold. Provider attribution is an in-flow member of `bottom-end` and the license badge of `bottom-start`, so neither covers a widget. A slot dims automatically while an open popup covers it (`widgets-dim="off"` to disable), except slots containing required chrome. Themeable from plain page CSS via custom properties: `om-map { --om-widget-bg: #111827; --om-widget-fg: #f9fafb; }` (also `-muted`, `-border`, `-hover-bg`, `-accent`), plus layout tokens (`--om-widget-inset-x/-y`, `--om-widget-gap-x/-y`, `--om-widget-opacity`, `--om-widget-radius`) or the no-CSS sugar `<om-map widget-style="gap:10 opacity:0.9">`. |
|
|
85
85
|
| `<om-overlay>` | Rich HTML anchored to a map location — a static `anchor="[lng, lat]"`, the current selection, or a feature's own geometry via `anchor-layer`/`anchor-feature-id`. `{{field}}` interpolates the picked feature, HTML-escaped by default. |
|
|
@@ -111,6 +111,7 @@ Built-ins: `scale()` (types: `sequential`, `diverging`, `threshold`, `sqrt`, `lo
|
|
|
111
111
|
| **CSV / TSV** | `data="./quakes.csv"` | parsed to typed columns (lazy ~48 KB chunk); numbers auto-typed, quoted fields intact |
|
|
112
112
|
| **Shapefile** | `data="./countries.shp"` | geometry + `.dbf` attributes joined into GeoJSON features (sidecars fetched with your auth config) |
|
|
113
113
|
| **KML** | `data="./tour.kml"` | placemarks → GeoJSON features; other formats plug in via `OmMap.registerFormat` |
|
|
114
|
+
| **CityJSON / CityJSONSeq** | `data="./tile.city.json"` + `extruded get-elevation="$roof_height"` | semantic 3D city models (3DBAG, PLATEAU) → extruded footprints with derived `roof_height`/`eaves_height`/`ridge_height`/`roof_area`; national grids reprojected automatically (NL/CH/DE/JP/AT/SG); `.city.jsonl` streams in as it downloads; lazy chunk — see [docs/3d-assets.md](docs/3d-assets.md#semantic-city-models-cityjson) |
|
|
114
115
|
| **GeoTIFF / COG raster** | `<om-layer type="COGLayer" src="./dem.tif" min="0" max="1900" colormap="viridis">` | Cloud-Optimized GeoTIFFs stream tiles by Range request (lazy chunk); min/max restretch + colormap swaps are GPU uniforms, nodata → transparent, legend ramp derives automatically; plain 8-bit RGB COGs need no attributes at all |
|
|
115
116
|
| **WebSocket stream** | `data="wss://feed" key="id" flush="250ms" source="myFormat"` | upsert-by-key, burst coalescing, auto-reconnect; decode any format via `OmMap.registerSource` |
|
|
116
117
|
| **Polled REST snapshot** | `data="/api/fleet.json" refresh="5s"` | full-snapshot replace per poll; outages keep the last good data |
|
|
@@ -244,7 +245,9 @@ await h.pick({ layer: "quakes", featureId: "q1" }); // same code path as a real
|
|
|
244
245
|
expect(overlay.shadowRoot.textContent).toContain("M 6.5");
|
|
245
246
|
```
|
|
246
247
|
|
|
247
|
-
Plus `OmMap.snapshotIR(html)` to lock down what a manifest *means* in a snapshot test, and `await mapEl.ready` / the `om-map-ready` event to de-flake browser e2e tests.
|
|
248
|
+
Plus `OmMap.snapshotIR(html)` to lock down what a manifest *means* in a snapshot test, and `await mapEl.ready` / the `om-map-ready` event to de-flake browser e2e tests.
|
|
249
|
+
|
|
250
|
+
**Layout, checked like types.** `npx @nika-js/onlymap check-layout <manifest.html>` audits a page's real rendered widget layout in headless Chromium — no screenshots, no eyeballing: it asserts no widgets overlap, each is hit-testable (not painted under the canvas), each sits in its slot, and same-slot edges are flush, across a viewport-width sweep, emitting structured `{severity, element, message, fix}` diagnostics and exiting 0/1. The same audit is importable as `auditLayout(page)` for your own Playwright suite. Full guide: [docs/testing.md](docs/testing.md).
|
|
248
251
|
|
|
249
252
|
## 3D
|
|
250
253
|
|
|
@@ -254,14 +257,14 @@ Plus `OmMap.snapshotIR(html)` to lock down what a manifest *means* in a snapshot
|
|
|
254
257
|
|
|
255
258
|
- **`OmMap.*`** — `validate`, `snapshotIR`, `registerLayer`, `registerWidget`, `registerAction`, `registerSource`, `registerFormat`, `registerBasemap`, `configureBasemap`, `configureData`, `configureTelemetry`, `configureLicense`, `getLayerSchema`
|
|
256
259
|
- **`@nika-js/onlymap/deck`** — the bundled deck.gl classes (`CompositeLayer`, `TileLayer`, …) for building custom layer types: shims must extend the same class hierarchy the core renders with, not a second installed deck.gl copy. Recipe: [docs/custom-layers.md](docs/custom-layers.md)
|
|
257
|
-
- **On a `<om-map>` element** — `ready` (promise), `flyTo(coords, zoom?)`, `setLayerVisible(id, bool)`, `getLayers()`, `emit(action, payload)`, `snapshot(opts?)` (canvas-only PNG of basemap + layers at device pixels — DOM widgets/overlays and provider attribution are NOT captured, so exports must render credits themselves; `{as: "blob"}` for files, default dataURL); the `om-view-changed` event fires once the camera settles (debounced; `detail` = `{longitude, latitude, zoom, pitch, bearing, origin}`, where `origin` is `"user"` for gesture-driven bursts vs `"programmatic"` for API/story moves — the echo-suppression signal for state sync) — the camera-persistence hook; `document.querySelector("om-map")` is fully typed
|
|
258
|
-
- **`MapController`** — the framework-grade programmatic front-end (typed `LayerDescriptor`s → the same reconcile core, no DOM manifest): `setLayers`, `watch`, `emit`, camera methods, `injectPick`, `ready`, `snapshot`, an `onViewChange(view, origin)` option (the `om-view-changed` twin). The React adapter rides it; usable directly from vanilla TS or other frameworks
|
|
260
|
+
- **On a `<om-map>` element** — `ready` (promise), `flyTo(coords, zoom?)`, `setLayerVisible(id, bool)`, `getLayers()`, `emit(action, payload)`, `snapshot(opts?)` (canvas-only PNG of basemap + layers at device pixels — DOM widgets/overlays and provider attribution are NOT captured, so exports must render credits themselves; `{as: "blob"}` for files, default dataURL); the `om-view-changed` event fires once the camera settles (debounced; `detail` = `{longitude, latitude, zoom, pitch, bearing, origin}`, where `origin` is `"user"` for gesture-driven bursts vs `"programmatic"` for API/story moves — the echo-suppression signal for state sync) — the camera-persistence hook; the `om-map-point` event (`detail = {coordinate: [lng,lat]|null, kind: "click"|"hover"}`) fires on every click/hover with the map coordinate, including empty-map clicks picks discard — the hook for custom capture tools the built-in draw widget doesn't cover; the `om-tileset-load` event (`detail = {layerId, tileset}`) surfaces a `Tile3DLayer`'s live deck `Tileset3D` for tools that need the real tileset (e.g. region export), not the IR; `document.querySelector("om-map")` is fully typed
|
|
261
|
+
- **`MapController`** — the framework-grade programmatic front-end (typed `LayerDescriptor`s → the same reconcile core, no DOM manifest): `setLayers`, `watch`, `emit`, camera methods, `injectPick`, `ready`, `snapshot`, an `onViewChange(view, origin)` option (the `om-view-changed` twin), plus `onMapPoint` / `onTilesetLoad` options (the `om-map-point` / `om-tileset-load` twins). The React adapter rides it; usable directly from vanilla TS or other frameworks
|
|
259
262
|
- **`getStore(token)`** — the external-store contract: per-token `{subscribe, getSnapshot}` stores (`viewport`/`selection`/`layers`/`data:<id>`) with cached immutable plain-data snapshots and `origin` tagging — directly consumable by `useSyncExternalStore` (the React adapter's own hooks ride it), MobX autoruns, Redux listeners, Zustand mirrors. ~20-line integration-tested recipes for Redux Toolkit, MobX/mobx-keystone, Zustand, and Jotai: [docs/external-stores.md](docs/external-stores.md)
|
|
260
263
|
- **Testing** — `mountForTest`, and imports are SSR-safe (importing in Node/jsdom never touches browser globals)
|
|
261
264
|
|
|
262
265
|
## Free tier & licensing
|
|
263
266
|
|
|
264
|
-
**Non-commercial use is free with attribution** (LICENSE.md §3); commercial use requires a license. Without a license key, maps run on the **free plan**: up to **5 layers** and **25,000 rows per layer** (20 MB per data fetch), with a small "OnlyMap by NIKA. Free for non-commercial use." badge in the corner. Exceeding a limit never breaks the map
|
|
267
|
+
**Non-commercial use is free with attribution** (LICENSE.md §3); commercial use requires a license. Without a license key, maps run on the **free plan**: up to **5 layers** and **25,000 rows per layer** (20 MB per data fetch), with a small "OnlyMap by NIKA. Free for non-commercial use." badge in the corner. Exceeding a limit never breaks the map. Past the **layer** cap the extra layer simply doesn't render; past the **row** cap the layer renders its first 25,000 rows (an arbitrary subset, in source order) rather than nothing, with a dismissible on-map notice saying the data is partial — so a capped map is still useful to look at without ever pretending it's complete. Either way the validation stream/error panel tells you exactly which limit, why, and how to lift it. Limits apply identically everywhere (including localhost — no dev/prod surprises).
|
|
265
268
|
|
|
266
269
|
A license key lifts all limits and removes the badge:
|
|
267
270
|
|
package/THIRD-PARTY-LICENSES.md
CHANGED
|
@@ -168,7 +168,7 @@ hand.
|
|
|
168
168
|
| pbf | 3.3.0 | BSD-3-Clause |
|
|
169
169
|
| potpack | 2.1.0 | ISC |
|
|
170
170
|
| process-nextick-args | 2.0.1 | MIT |
|
|
171
|
-
| proj4 | 2.
|
|
171
|
+
| proj4 | 2.20.9 | MIT |
|
|
172
172
|
| protocol-buffers-schema | 3.6.1 | MIT |
|
|
173
173
|
| quickselect | 3.0.0 | ISC |
|
|
174
174
|
| readable-stream | 2.3.8 | MIT |
|
|
@@ -7210,7 +7210,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
7210
7210
|
SOFTWARE.**
|
|
7211
7211
|
```
|
|
7212
7212
|
|
|
7213
|
-
## proj4@2.
|
|
7213
|
+
## proj4@2.20.9 (MIT)
|
|
7214
7214
|
|
|
7215
7215
|
```
|
|
7216
7216
|
## Proj4js -- Javascript reprojection library.
|
package/bin/onlymapjs.mjs
CHANGED
|
@@ -137,8 +137,21 @@ async function checkLayout(manifestArg) {
|
|
|
137
137
|
}
|
|
138
138
|
const context = await browser.newContext({ serviceWorkers: "block" });
|
|
139
139
|
const page = await context.newPage();
|
|
140
|
+
const pageErrors = [];
|
|
141
|
+
page.on("pageerror", (error) => pageErrors.push(String(error).split("\n")[0]));
|
|
142
|
+
page.on("console", (msg) => {
|
|
143
|
+
if (msg.type() === "error") pageErrors.push(msg.text().split("\n")[0]);
|
|
144
|
+
});
|
|
140
145
|
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
141
|
-
|
|
146
|
+
let diagnostics;
|
|
147
|
+
try {
|
|
148
|
+
diagnostics = await auditLayout(page);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
// The page never produced a map. Its own console/pageerror output is the
|
|
151
|
+
// real cause — surface it so the failure is self-explaining.
|
|
152
|
+
const detail = pageErrors.length ? `\nPage errors:\n ${pageErrors.slice(0, 3).join("\n ")}` : "";
|
|
153
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)}${detail}`);
|
|
154
|
+
}
|
|
142
155
|
if (diagnostics.length === 0) {
|
|
143
156
|
console.log("CLEAN: no widget layout problems at 360, 640, 768, or 1024px.");
|
|
144
157
|
return 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ny, L as sy, M as oy, m as ay, c as ka, z as oc, a as Sd, G as sm, V as ly, W as cy, b as uy, g as hy, d as Vu, f as Ed, e as dy, l as py, u as fy, D as Qf, h as my } from "./index-
|
|
1
|
+
import { C as ny, L as sy, M as oy, m as ay, c as ka, z as oc, a as Sd, G as sm, V as ly, W as cy, b as uy, g as hy, d as Vu, f as Ed, e as dy, l as py, u as fy, D as Qf, h as my } from "./index-DiYFebYs.js";
|
|
2
2
|
const Cd = Math.PI / 180, gy = 180 / Math.PI;
|
|
3
3
|
function em(Pe, Q = 0) {
|
|
4
4
|
const me = Math.min(180, Pe) * Cd;
|
|
@@ -25217,7 +25217,7 @@ class Ry {
|
|
|
25217
25217
|
* remount, since `customAttribution` is fixed at construction.
|
|
25218
25218
|
*/
|
|
25219
25219
|
mountAttribution(Q) {
|
|
25220
|
-
if (this.showAttribution && (this.attributionText = Q, this.attribution && (this.map.removeControl(this.attribution), this.attributionElement = void 0), this.attribution = new rm.AttributionControl({ compact: !0, ...Q ? { customAttribution: Q } : {} }), this.map.addControl(this.attribution), this.attributionHost)) {
|
|
25220
|
+
if (this.showAttribution && (this.attributionText = Q, this.attribution && (this.map.removeControl(this.attribution), this.attributionElement?.remove(), this.attributionElement = void 0), this.attribution = new rm.AttributionControl({ compact: !0, ...Q ? { customAttribution: Q } : {} }), this.map.addControl(this.attribution), this.attributionHost)) {
|
|
25221
25221
|
const me = this.map.getContainer().querySelector(".maplibregl-ctrl-attrib");
|
|
25222
25222
|
me && (me.setAttribute("data-om-mandated-chrome-item", "attribution"), me.style.margin = "0", this.attributionHost.appendChild(me), this.attributionElement = me);
|
|
25223
25223
|
}
|
|
@@ -25272,7 +25272,7 @@ class Ry {
|
|
|
25272
25272
|
for (const me of this.pendingCaptureRejects) me(Q);
|
|
25273
25273
|
this.pendingCaptureRejects.clear();
|
|
25274
25274
|
}
|
|
25275
|
-
this.deckCaptures = [], this.attributionElement = void 0, this.overlay.finalize(), this.map.remove();
|
|
25275
|
+
this.deckCaptures = [], this.attributionElement?.remove(), this.attributionElement = void 0, this.overlay.finalize(), this.map.remove();
|
|
25276
25276
|
}
|
|
25277
25277
|
}
|
|
25278
25278
|
export {
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
function P(t, e) {
|
|
2
|
+
if (!Array.isArray(t)) return [];
|
|
3
|
+
const n = e?.scale?.[0] ?? 1, o = e?.scale?.[1] ?? 1, s = e?.scale?.[2] ?? 1, a = e?.translate?.[0] ?? 0, i = e?.translate?.[1] ?? 0, c = e?.translate?.[2] ?? 0, r = new Array(t.length);
|
|
4
|
+
for (let f = 0; f < t.length; f++) {
|
|
5
|
+
const l = t[f];
|
|
6
|
+
r[f] = [l[0] * n + a, l[1] * o + i, (l[2] ?? 0) * s + c];
|
|
7
|
+
}
|
|
8
|
+
return r;
|
|
9
|
+
}
|
|
10
|
+
const T = {
|
|
11
|
+
MultiPoint: 1,
|
|
12
|
+
MultiLineString: 2,
|
|
13
|
+
MultiSurface: 3,
|
|
14
|
+
CompositeSurface: 3,
|
|
15
|
+
Solid: 4,
|
|
16
|
+
MultiSolid: 5,
|
|
17
|
+
CompositeSolid: 5
|
|
18
|
+
};
|
|
19
|
+
function J(t, e) {
|
|
20
|
+
let n = t;
|
|
21
|
+
for (let o = 0; o < e; o++) n = n.flat();
|
|
22
|
+
return n;
|
|
23
|
+
}
|
|
24
|
+
function A(t) {
|
|
25
|
+
const e = T[t.type ?? ""];
|
|
26
|
+
return !e || e < 3 || !Array.isArray(t.boundaries) ? [] : J(t.boundaries, e - 3);
|
|
27
|
+
}
|
|
28
|
+
function X(t, e) {
|
|
29
|
+
const n = new Array(e).fill(void 0), o = T[t.type ?? ""], s = t.semantics?.surfaces, a = t.semantics?.values;
|
|
30
|
+
if (!o || !Array.isArray(s) || !Array.isArray(a)) return n;
|
|
31
|
+
const i = J(a, o - 3);
|
|
32
|
+
for (let c = 0; c < e; c++) {
|
|
33
|
+
const r = i[c];
|
|
34
|
+
typeof r == "number" && (n[c] = s[r]?.type);
|
|
35
|
+
}
|
|
36
|
+
return n;
|
|
37
|
+
}
|
|
38
|
+
function L(t) {
|
|
39
|
+
const e = typeof t == "number" ? t : Number.parseFloat(String(t ?? ""));
|
|
40
|
+
return Number.isFinite(e) ? e : -1;
|
|
41
|
+
}
|
|
42
|
+
function q(t, e) {
|
|
43
|
+
if (!Array.isArray(t)) return;
|
|
44
|
+
const n = t.filter((o) => A(o).length > 0);
|
|
45
|
+
if (n.length !== 0) {
|
|
46
|
+
if (e !== void 0) {
|
|
47
|
+
const o = n.find((s) => L(s.lod) === e);
|
|
48
|
+
if (o) return o;
|
|
49
|
+
}
|
|
50
|
+
return n.reduce((o, s) => L(s.lod) > L(o.lod) ? s : o);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const K = {
|
|
54
|
+
7415: 28992,
|
|
55
|
+
// Amersfoort / RD New + NAP height — the 3DBAG default
|
|
56
|
+
6697: 6668,
|
|
57
|
+
// JGD2011 geographic 3D + height — the PLATEAU default
|
|
58
|
+
5555: 25832,
|
|
59
|
+
// ETRS89 / UTM 32N + DHHN92 height
|
|
60
|
+
5556: 25833
|
|
61
|
+
// ETRS89 / UTM 33N + DHHN92 height
|
|
62
|
+
}, G = [
|
|
63
|
+
[33, 129.5],
|
|
64
|
+
[33, 131],
|
|
65
|
+
[36, 132.166666666667],
|
|
66
|
+
[33, 133.5],
|
|
67
|
+
[36, 134.333333333333],
|
|
68
|
+
[36, 136],
|
|
69
|
+
[36, 137.166666666667],
|
|
70
|
+
[36, 138.5],
|
|
71
|
+
[36, 139.833333333333],
|
|
72
|
+
[40, 140.833333333333],
|
|
73
|
+
[44, 140.25],
|
|
74
|
+
[44, 142.25],
|
|
75
|
+
[44, 144.25],
|
|
76
|
+
[26, 142],
|
|
77
|
+
[26, 127.5],
|
|
78
|
+
[26, 124],
|
|
79
|
+
[26, 131],
|
|
80
|
+
[20, 136],
|
|
81
|
+
[26, 154]
|
|
82
|
+
], j = {
|
|
83
|
+
// Netherlands — Amersfoort / RD New (3DBAG).
|
|
84
|
+
28992: {
|
|
85
|
+
def: "+proj=sterea +lat_0=52.1561605555556 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.4171,50.3319,465.5524,-0.398957,0.343988,-1.8774,4.0725 +units=m +no_defs"
|
|
86
|
+
},
|
|
87
|
+
// Switzerland — CH1903+ / LV95.
|
|
88
|
+
2056: {
|
|
89
|
+
def: "+proj=somerc +lat_0=46.9524055555556 +lon_0=7.43958333333333 +k_0=1 +x_0=2600000 +y_0=1200000 +ellps=bessel +towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs"
|
|
90
|
+
},
|
|
91
|
+
// Germany — ETRS89 / UTM zones 32N and 33N.
|
|
92
|
+
25832: { def: "+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs" },
|
|
93
|
+
25833: { def: "+proj=utm +zone=33 +ellps=GRS80 +units=m +no_defs" },
|
|
94
|
+
// Plain WGS84 UTM zones 32N/33N (EPSG:326xx) — the generic, non-European-specific
|
|
95
|
+
// code many CityJSON files declare instead of the ETRS89 pair above (e.g.
|
|
96
|
+
// Ingolstadt's own open-data LoD3 export). Numerically near-identical to
|
|
97
|
+
// 25832/25833 at map-rendering precision (WGS84 vs. ETRS89 differ by ~cm in
|
|
98
|
+
// Europe), so no +towgs84 shift is needed — same bare projection string.
|
|
99
|
+
32632: { def: "+proj=utm +zone=32 +datum=WGS84 +units=m +no_defs" },
|
|
100
|
+
32633: { def: "+proj=utm +zone=33 +datum=WGS84 +units=m +no_defs" },
|
|
101
|
+
// Austria — MGI / Austria GK West/Central/East (the national cadastral
|
|
102
|
+
// system's three meridian-strip zones; Vienna sits in GK East). Same
|
|
103
|
+
// Bessel ellipsoid + towgs84 shift and y_0=-5,000,000 false-northing
|
|
104
|
+
// convention across all three — proj4 strings verified against epsg.io,
|
|
105
|
+
// not hand-derived, and cross-checked by reprojecting a real Vienna
|
|
106
|
+
// open-data file's own extent and confirming it lands in Vienna.
|
|
107
|
+
31254: { def: "+proj=tmerc +lat_0=0 +lon_0=10.3333333333333 +k=1 +x_0=0 +y_0=-5000000 +ellps=bessel +towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232 +units=m +no_defs" },
|
|
108
|
+
31255: { def: "+proj=tmerc +lat_0=0 +lon_0=13.3333333333333 +k=1 +x_0=0 +y_0=-5000000 +ellps=bessel +towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232 +units=m +no_defs" },
|
|
109
|
+
31256: { def: "+proj=tmerc +lat_0=0 +lon_0=16.3333333333333 +k=1 +x_0=0 +y_0=-5000000 +ellps=bessel +towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232 +units=m +no_defs" },
|
|
110
|
+
// Singapore — SVY21 / Singapore TM (the national grid; NUS's own Singapore
|
|
111
|
+
// HDB buildings dataset declares this via metadata.referenceSystem
|
|
112
|
+
// directly, unlike the Austria/3DBAG files, which had none). proj4 string
|
|
113
|
+
// verified against epsg.io, cross-checked by reprojecting a real dataset's
|
|
114
|
+
// own extent and confirming it lands in Singapore (103.68-103.99°E,
|
|
115
|
+
// 1.27-1.46°N — matches).
|
|
116
|
+
3414: { def: "+proj=tmerc +lat_0=1.36666666666667 +lon_0=103.833333333333 +k=1 +x_0=28001.642 +y_0=38744.572 +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs" },
|
|
117
|
+
// Japan — JGD2011 geographic 2D. EPSG axis order is (lat, lon).
|
|
118
|
+
6668: { def: "+proj=longlat +ellps=GRS80 +no_defs", geographic: !0, swapXY: !0 },
|
|
119
|
+
// WGS 84. Files in the wild store lon/lat despite EPSG's formal (lat, lon).
|
|
120
|
+
4326: { def: "+proj=longlat +datum=WGS84 +no_defs", geographic: !0 },
|
|
121
|
+
3857: { def: "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +no_defs" }
|
|
122
|
+
};
|
|
123
|
+
for (let t = 0; t < G.length; t++) {
|
|
124
|
+
const [e, n] = G[t];
|
|
125
|
+
j[6669 + t] = {
|
|
126
|
+
def: `+proj=tmerc +lat_0=${e} +lon_0=${n} +k=0.9999 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs`,
|
|
127
|
+
swapXY: !0
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const Q = /EPSG.*?(\d{4,6})\D*$/i;
|
|
131
|
+
function ee(t) {
|
|
132
|
+
if (!t) return;
|
|
133
|
+
const e = Number(Q.exec(t)?.[1]);
|
|
134
|
+
return Number.isFinite(e) ? e : void 0;
|
|
135
|
+
}
|
|
136
|
+
let x = !1;
|
|
137
|
+
async function k(t, e) {
|
|
138
|
+
const n = { toLngLat: (r, f) => [r, f], projected: !1 }, o = ee(t);
|
|
139
|
+
if (o === void 0)
|
|
140
|
+
return x || (x = !0, console.warn(
|
|
141
|
+
`[onlymapjs] data="${e}": CityJSON has no metadata.referenceSystem — assuming coordinates are already WGS84 lon/lat. Add a referenceSystem to the file if they are not.`
|
|
142
|
+
)), n;
|
|
143
|
+
const s = K[o] ?? o, a = j[s];
|
|
144
|
+
if (!a)
|
|
145
|
+
throw new Error(
|
|
146
|
+
`CityJSON declares EPSG:${o}${s === o ? "" : ` (horizontal component EPSG:${s})`}, which is not one of the bundled coordinate systems (${Object.keys(j).join(", ")}). Reproject the file to EPSG:4326 server-side (cjio --reproject 4326), or claim the URL with OmMap.registerFormat() and supply your own projection.`
|
|
147
|
+
);
|
|
148
|
+
if (a.geographic)
|
|
149
|
+
return a.swapXY ? { toLngLat: (r, f) => [f, r], projected: !1 } : n;
|
|
150
|
+
const i = (await import("./index-CW1n5LdO.js")).default, c = i(a.def, "+proj=longlat +datum=WGS84 +no_defs");
|
|
151
|
+
return {
|
|
152
|
+
toLngLat: (r, f) => {
|
|
153
|
+
const [l, p] = c.forward(a.swapXY ? [f, r] : [r, f]);
|
|
154
|
+
return [l, p];
|
|
155
|
+
},
|
|
156
|
+
projected: !0
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function F(t) {
|
|
160
|
+
let e = 0, n = 0, o = 0;
|
|
161
|
+
for (let s = 0; s < t.length; s++) {
|
|
162
|
+
const a = t[s], i = t[(s + 1) % t.length];
|
|
163
|
+
e += (a[1] - i[1]) * (a[2] + i[2]), n += (a[2] - i[2]) * (a[0] + i[0]), o += (a[0] - i[0]) * (a[1] + i[1]);
|
|
164
|
+
}
|
|
165
|
+
return Math.hypot(e, n, o) / 2;
|
|
166
|
+
}
|
|
167
|
+
function te(t) {
|
|
168
|
+
const e = t.slice().sort((o, s) => o - s), n = e.length >> 1;
|
|
169
|
+
return e.length % 2 ? e[n] : (e[n - 1] + e[n]) / 2;
|
|
170
|
+
}
|
|
171
|
+
function U(t, e, n, o, s) {
|
|
172
|
+
const a = q(e.geometry, s);
|
|
173
|
+
if (!a) return;
|
|
174
|
+
const i = A(a);
|
|
175
|
+
if (i.length === 0) return;
|
|
176
|
+
const c = X(a, i.length), r = i.map(
|
|
177
|
+
(d) => d.map((m) => m.map((u) => n[u]).filter((u) => u !== void 0))
|
|
178
|
+
);
|
|
179
|
+
let f = 1 / 0, l = -1 / 0;
|
|
180
|
+
for (const d of r)
|
|
181
|
+
for (const m of d)
|
|
182
|
+
for (const [, , u] of m)
|
|
183
|
+
u < f && (f = u), u > l && (l = u);
|
|
184
|
+
if (!Number.isFinite(f)) return;
|
|
185
|
+
const p = f, g = c.some((d) => d === "RoofSurface"), y = [];
|
|
186
|
+
let _ = 1 / 0, h = -1 / 0;
|
|
187
|
+
for (let d = 0; d < r.length; d++)
|
|
188
|
+
if (!(g && c[d] !== "RoofSurface"))
|
|
189
|
+
for (const m of r[d])
|
|
190
|
+
for (const [, , u] of m)
|
|
191
|
+
(g || u > p + 0.5) && (y.push(u), u < _ && (_ = u), u > h && (h = u));
|
|
192
|
+
y.length === 0 && (y.push(l), _ = l, h = l);
|
|
193
|
+
let S = c.indexOf("GroundSurface");
|
|
194
|
+
if (S < 0) {
|
|
195
|
+
let d = -1;
|
|
196
|
+
for (let m = 0; m < r.length; m++) {
|
|
197
|
+
const u = r[m][0];
|
|
198
|
+
if (!u || u.length < 3) continue;
|
|
199
|
+
const b = u.every(([, , v]) => v <= p + 0.5), w = F(u) * (b ? 1e3 : 1);
|
|
200
|
+
w > d && (d = w, S = m);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (S < 0) return;
|
|
204
|
+
const O = r[S];
|
|
205
|
+
if (!O[0] || O[0].length < 3) return;
|
|
206
|
+
const [, B] = o.toLngLat(O[0][0][0], O[0][0][1]), Z = 111320 * Math.cos(B * Math.PI / 180), W = 110540, Y = (d) => o.projected ? d : d.map(([m, u, b]) => {
|
|
207
|
+
const [w, v] = o.toLngLat(m, u);
|
|
208
|
+
return [w * Z, v * W, b];
|
|
209
|
+
});
|
|
210
|
+
let R = 0, E = 0;
|
|
211
|
+
for (let d = 0; d < r.length; d++) {
|
|
212
|
+
if (g && c[d] !== "RoofSurface") continue;
|
|
213
|
+
const m = r[d][0];
|
|
214
|
+
if (!m || m.length < 3) continue;
|
|
215
|
+
const u = F(Y(m));
|
|
216
|
+
R += u, E += u * (m.reduce((b, [, , w]) => b + w, 0) / m.length);
|
|
217
|
+
}
|
|
218
|
+
const V = R > 0 ? E / R : te(y), H = {
|
|
219
|
+
cityobject_id: t,
|
|
220
|
+
cityobject_type: e.type ?? "Unknown",
|
|
221
|
+
...e.parents?.[0] !== void 0 ? { parent_id: e.parents[0] } : {},
|
|
222
|
+
...a.lod !== void 0 ? { lod: String(a.lod) } : {},
|
|
223
|
+
ground_height: C(p),
|
|
224
|
+
roof_height: C(V - p),
|
|
225
|
+
eaves_height: C(_ - p),
|
|
226
|
+
ridge_height: C(h - p),
|
|
227
|
+
roof_area: C(R),
|
|
228
|
+
surface_count: i.length
|
|
229
|
+
};
|
|
230
|
+
return { faceRings: r, semantics: c, footprintIndex: S, derived: H };
|
|
231
|
+
}
|
|
232
|
+
function ne(t, e, n, o, s, a) {
|
|
233
|
+
const i = U(t, e, n, o, a);
|
|
234
|
+
if (!i) return;
|
|
235
|
+
const r = i.faceRings[i.footprintIndex].filter((f) => f.length >= 3).map((f) => {
|
|
236
|
+
const l = f.map(([p, g]) => o.toLngLat(p, g));
|
|
237
|
+
return l.push(l[0]), l;
|
|
238
|
+
});
|
|
239
|
+
return {
|
|
240
|
+
type: "Feature",
|
|
241
|
+
id: t,
|
|
242
|
+
geometry: { type: "Polygon", coordinates: r },
|
|
243
|
+
// Source attributes first: the derived names are the ones manifests bind
|
|
244
|
+
// to, so they must be deterministic even against a same-named source field.
|
|
245
|
+
properties: { ...s, ...e.attributes, ...i.derived }
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const I = {
|
|
249
|
+
GroundSurface: "#999999",
|
|
250
|
+
WallSurface: "#ffffff",
|
|
251
|
+
RoofSurface: "#ff0000",
|
|
252
|
+
TrafficArea: "#6e6e6e",
|
|
253
|
+
AuxiliaryTrafficArea: "#2c8200",
|
|
254
|
+
Window: "#0059ff",
|
|
255
|
+
Door: "#640000"
|
|
256
|
+
}, M = {
|
|
257
|
+
Building: "#7497df",
|
|
258
|
+
BuildingPart: "#7497df",
|
|
259
|
+
BuildingInstallation: "#7497df",
|
|
260
|
+
Bridge: "#999999",
|
|
261
|
+
BridgePart: "#999999",
|
|
262
|
+
BridgeInstallation: "#999999",
|
|
263
|
+
BridgeConstructiveElement: "#999999",
|
|
264
|
+
CityObjectGroup: "#ffffb3",
|
|
265
|
+
LandUse: "#ffffb3",
|
|
266
|
+
CityFurniture: "#cc0000",
|
|
267
|
+
GenericCityObject: "#cc0000",
|
|
268
|
+
PlantCover: "#39ac39",
|
|
269
|
+
SolitaryVegetationObject: "#39ac39",
|
|
270
|
+
Railway: "#000000",
|
|
271
|
+
Road: "#999999",
|
|
272
|
+
TransportSquare: "#999999",
|
|
273
|
+
TINRelief: "#ffdb99",
|
|
274
|
+
Tunnel: "#999999",
|
|
275
|
+
TunnelPart: "#999999",
|
|
276
|
+
TunnelInstallation: "#999999",
|
|
277
|
+
WaterBody: "#4da6ff"
|
|
278
|
+
}, oe = "#b0b0b0";
|
|
279
|
+
function re(t, e) {
|
|
280
|
+
return t && I[t] ? I[t] : e && M[e] ? M[e] : oe;
|
|
281
|
+
}
|
|
282
|
+
function ie(t, e, n, o, s, a) {
|
|
283
|
+
const i = U(t, e, n, o, a);
|
|
284
|
+
if (!i) return [];
|
|
285
|
+
const c = [];
|
|
286
|
+
for (let r = 0; r < i.faceRings.length; r++) {
|
|
287
|
+
const f = i.faceRings[r], l = f[0];
|
|
288
|
+
if (!l || l.length < 3) continue;
|
|
289
|
+
const p = f.filter((y) => y.length >= 3).map((y) => y.map(([_, h, S]) => [...o.toLngLat(_, h), S])), g = p[0];
|
|
290
|
+
c.push({
|
|
291
|
+
...s,
|
|
292
|
+
...e.attributes,
|
|
293
|
+
...i.derived,
|
|
294
|
+
surface_type: i.semantics[r],
|
|
295
|
+
fill_color: re(i.semantics[r], e.type),
|
|
296
|
+
polygon: p,
|
|
297
|
+
outline: [...g, g[0]]
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return c;
|
|
301
|
+
}
|
|
302
|
+
function C(t) {
|
|
303
|
+
return Number.isFinite(t) ? Math.round(t * 1e3) / 1e3 : 0;
|
|
304
|
+
}
|
|
305
|
+
function D(t, e, n, o, s = "footprint") {
|
|
306
|
+
if (!t) return [];
|
|
307
|
+
const a = (c) => Array.isArray(c.children) && c.children.some((r) => {
|
|
308
|
+
const f = t[r];
|
|
309
|
+
return f?.type === `${c.type}Part` && (f.geometry?.some((l) => A(l).length > 0) ?? !1);
|
|
310
|
+
}), i = [];
|
|
311
|
+
for (const [c, r] of Object.entries(t)) {
|
|
312
|
+
if (!r || typeof r != "object" || a(r)) continue;
|
|
313
|
+
const l = (r.parents?.[0] ? t[r.parents[0]] : void 0)?.attributes ?? {};
|
|
314
|
+
if (s === "surfaces")
|
|
315
|
+
i.push(...ie(c, r, e, n, l, o));
|
|
316
|
+
else {
|
|
317
|
+
const p = ne(c, r, e, n, l, o);
|
|
318
|
+
p && i.push(p);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return i;
|
|
322
|
+
}
|
|
323
|
+
function N(t, e, n) {
|
|
324
|
+
if (t?.type !== n)
|
|
325
|
+
throw new Error(
|
|
326
|
+
`expected a ${n} object but found type="${t?.type ?? "undefined"}" — this does not look like CityJSON. If it is a different newline-delimited format, claim the URL with OmMap.registerFormat().`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
function $(t) {
|
|
330
|
+
const e = /[?&]om-lod=([^&#]+)/.exec(t)?.[1];
|
|
331
|
+
if (e === void 0) return;
|
|
332
|
+
const n = Number.parseFloat(decodeURIComponent(e));
|
|
333
|
+
if (!Number.isFinite(n)) {
|
|
334
|
+
console.warn(`[onlymapjs] om-lod="${e}" is not a number — ignoring and using the highest available LoD.`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
return n;
|
|
338
|
+
}
|
|
339
|
+
function z(t) {
|
|
340
|
+
const e = /[?&]om-surfaces=([^&#]+)/.exec(t)?.[1];
|
|
341
|
+
if (e === void 0) return !1;
|
|
342
|
+
const n = decodeURIComponent(e).toLowerCase();
|
|
343
|
+
return n === "1" || n === "true";
|
|
344
|
+
}
|
|
345
|
+
async function ce(t, e) {
|
|
346
|
+
const n = await t.json();
|
|
347
|
+
N(n, e, "CityJSON");
|
|
348
|
+
const o = await k(n.metadata?.referenceSystem, e), s = P(n.vertices, n.transform), a = z(e) ? "surfaces" : "footprint";
|
|
349
|
+
return D(n.CityObjects, s, o, $(e), a);
|
|
350
|
+
}
|
|
351
|
+
const se = 250, ae = 512;
|
|
352
|
+
async function* fe(t) {
|
|
353
|
+
if (!t.body) {
|
|
354
|
+
for (const s of (await t.text()).split(`
|
|
355
|
+
`)) yield s;
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const e = t.body.getReader(), n = new TextDecoder();
|
|
359
|
+
let o = "";
|
|
360
|
+
for (; ; ) {
|
|
361
|
+
const { done: s, value: a } = await e.read();
|
|
362
|
+
if (s) break;
|
|
363
|
+
o += n.decode(a, { stream: !0 });
|
|
364
|
+
let i;
|
|
365
|
+
for (; (i = o.indexOf(`
|
|
366
|
+
`)) >= 0; )
|
|
367
|
+
yield o.slice(0, i), o = o.slice(i + 1);
|
|
368
|
+
}
|
|
369
|
+
o += n.decode(), o.length > 0 && (yield o);
|
|
370
|
+
}
|
|
371
|
+
async function le(t, e, n) {
|
|
372
|
+
const o = $(e), s = z(e) ? "surfaces" : "footprint";
|
|
373
|
+
let a, i, c = !1;
|
|
374
|
+
const r = [];
|
|
375
|
+
let f = 0, l = 0;
|
|
376
|
+
for await (const p of fe(t)) {
|
|
377
|
+
const g = p.trim();
|
|
378
|
+
if (g.length === 0) continue;
|
|
379
|
+
const y = JSON.parse(g);
|
|
380
|
+
if (c)
|
|
381
|
+
N(y, e, "CityJSONFeature");
|
|
382
|
+
else if (N(y, e, "CityJSON"), i = y.transform, a = await k(y.metadata?.referenceSystem, e), c = !0, y.CityObjects && Object.keys(y.CityObjects).length === 0) continue;
|
|
383
|
+
const _ = P(y.vertices, i);
|
|
384
|
+
r.push(...D(y.CityObjects, _, a, o, s)), f++;
|
|
385
|
+
const h = performance.now();
|
|
386
|
+
(f >= ae || h - l >= se) && (f = 0, l = h, n(r.slice()));
|
|
387
|
+
}
|
|
388
|
+
if (!c)
|
|
389
|
+
throw new Error(
|
|
390
|
+
'CityJSONSeq file is empty or has no header line — the first line must be a CityJSON object carrying "transform" and "metadata", followed by one CityJSONFeature per line.'
|
|
391
|
+
);
|
|
392
|
+
return r;
|
|
393
|
+
}
|
|
394
|
+
export {
|
|
395
|
+
q as chooseGeometry,
|
|
396
|
+
D as decodeCityObjects,
|
|
397
|
+
P as decompressVertices,
|
|
398
|
+
re as defaultFillColor,
|
|
399
|
+
X as faceSemantics,
|
|
400
|
+
A as flattenToFaces,
|
|
401
|
+
ce as parseCityJsonDocument,
|
|
402
|
+
le as parseCityJsonSeq,
|
|
403
|
+
ee as parseEpsgCode,
|
|
404
|
+
$ as parseLodPin,
|
|
405
|
+
z as parseSurfacesFlag,
|
|
406
|
+
k as resolveCrs
|
|
407
|
+
};
|