@nika-js/onlymap 0.4.6 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/THIRD-PARTY-LICENSES.md +2 -2
- package/dist/{LercDecode.es-B49qauXg.js → LercDecode.es-BNF7PeWz.js} +1 -1
- package/dist/{basemap-nJ_ODY7r.js → basemap-7GQL0KSL.js} +1 -1
- package/dist/cityjson-urQeujQv.js +407 -0
- package/dist/cityjson.d.ts +138 -0
- package/dist/data-layer.d.ts +21 -0
- package/dist/elements/om-map.d.ts +30 -0
- package/dist/{index-uDJbHjA9.js → index-7PZgoR0X.js} +1 -1
- package/dist/{index-CeOjfL2p.js → index-9W_UXlPB.js} +2 -2
- package/dist/{index-DjlGZov2.js → index-BKiYWYJz.js} +1 -1
- package/dist/index-CW1n5LdO.js +4006 -0
- package/dist/{index-o_yRv2lD.js → index-ULFXRQAo.js} +1 -1
- package/dist/{index-DSkgI4Oc.js → index-ppCRU8Eb.js} +11369 -11186
- package/dist/ir-snapshot.d.ts +2 -0
- package/dist/ir.d.ts +7 -0
- package/dist/layer-registry.d.ts +14 -0
- package/dist/{lerc-CaQOmDCP.js → lerc-C-D0ZLmp.js} +2 -2
- package/dist/license.d.ts +34 -8
- package/dist/onlymap.standalone.js +22520 -21925
- package/dist/onlymapjs.js +19 -19
- package/dist/quota-notice.d.ts +43 -0
- package/dist/raster-D43jYSib.js +4842 -0
- package/dist/runtime-core.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/docs/3d-assets.md +99 -1
- package/llms.txt +2 -2
- package/onlymapjs.html-data.json +12 -8
- package/package.json +2 -1
- package/skills/onlymapjs/SKILL.md +3 -1
- package/skills/onlymapjs/references/patterns.md +4 -0
- package/skills/onlymapjs/references/syntax.md +85 -16
- package/skills/onlymapjs/references/testing.md +1 -1
- package/dist/raster-DMq8FgiV.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, tiled XYZ/vector (`TileLayer`/`MVTLayer`); 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 595 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,9 +111,11 @@ 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 |
|
|
118
|
+
| **Tiled layer (XYZ / vector)** | `<om-layer type="TileLayer" data="…/{z}/{x}/{y}.png">` or `type="MVTLayer"` | a `{z}/{x}/{y}` `data` template is deck's tile URL — passed through, never fetched as rows; raster `TileLayer` gets a built-in `BitmapLayer` renderer, `MVTLayer` self-renders vector tiles (`get-*` accessors apply to tile features) |
|
|
117
119
|
| **Draw store** | `data="draw:sketch"` | live in-memory GeoJSON feature store written by `<om-widget type="draw" target="sketch">` |
|
|
118
120
|
|
|
119
121
|
Private endpoints: `OmMap.configureData({ headers, credentials, fetch })` — applied to every fetch including refreshes. Credentials stay out of markup by design. Full guide: [docs/live-data.md](docs/live-data.md).
|
|
@@ -263,7 +265,7 @@ Plus `OmMap.snapshotIR(html)` to lock down what a manifest *means* in a snapshot
|
|
|
263
265
|
|
|
264
266
|
## Free tier & licensing
|
|
265
267
|
|
|
266
|
-
**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
|
|
268
|
+
**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).
|
|
267
269
|
|
|
268
270
|
A license key lifts all limits and removes the badge:
|
|
269
271
|
|
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.
|
|
@@ -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-ppCRU8Eb.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;
|
|
@@ -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
|
+
};
|
|
@@ -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 {};
|
package/dist/data-layer.d.ts
CHANGED
|
@@ -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
|
|
@@ -119,6 +129,17 @@ export interface DataRequestConfig {
|
|
|
119
129
|
export declare function configureData(config: DataRequestConfig): void;
|
|
120
130
|
/** `data="draw:sketch"` — the reserved scheme for a widget-fed sketch layer (never collides with http/ws/inline). */
|
|
121
131
|
export declare function isDrawUrl(url: string): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* A tile URL template (spec: "Tiled layers"). deck's `TileLayer`/`MVTLayer`
|
|
134
|
+
* (and any `TileLayer` subclass) take their `data` prop as a `{z}/{x}/{y}`
|
|
135
|
+
* template STRING — a template deck expands per tile, NOT a document to fetch
|
|
136
|
+
* and parse into rows. OnlyMapJS otherwise routes every `data` attribute
|
|
137
|
+
* through getData() (fetch → parse), so a template would be fetched literally
|
|
138
|
+
* (`…/{z}/{x}/{y}.png`) and 404; this classifier lets the parser pass it
|
|
139
|
+
* through to deck untouched instead. Same `{z}/{x}/{y}` convention terrain and
|
|
140
|
+
* basemap tile URLs already use.
|
|
141
|
+
*/
|
|
142
|
+
export declare function isTileTemplateUrl(url: string): boolean;
|
|
122
143
|
/**
|
|
123
144
|
* Push a new sketch snapshot into a `draw:<name>` store (the draw widget's
|
|
124
145
|
* DrawSession.onChange calls this). A FRESH array reference is stored so
|