@nika-js/onlymap 0.6.0 → 0.6.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/CHANGELOG.md +15 -1
- package/README.md +18 -6
- package/THIRD-PARTY-LICENSES.md +27 -0
- package/dist/{LercDecode.es-fbo6h5rt.js → LercDecode.es-CJnypw8j.js} +1 -1
- package/dist/{basemap-CprSan9q.js → basemap-DrQ0-eyR.js} +1 -1
- package/dist/cityjson-D_V5GY8b.js +332 -0
- package/dist/crs-Ciu7Xs7a.js +108 -0
- package/dist/crs.d.ts +50 -0
- package/dist/ctx.d.ts +8 -1
- package/dist/elements/om-map.d.ts +26 -2
- package/dist/elements/om-overlay.d.ts +1 -1
- package/dist/elements/om-widget.d.ts +4 -0
- package/dist/feature-colors.d.ts +41 -0
- package/dist/{geoparquet-Bm8pfxyo.js → geoparquet-Dix4lTNy.js} +1 -1
- package/dist/html-data.d.ts +1 -1
- package/dist/ifc-Dc-TdSJr.js +877 -0
- package/dist/ifc.d.ts +406 -0
- package/dist/{index-Bz-rbmhj.js → index-1UgNlfGR.js} +20892 -18886
- package/dist/{index-GOwMjqm6.js → index-BQMjW5w0.js} +1 -1
- package/dist/{index-DSRIZR81.js → index-B_1PPJgC.js} +1 -1
- package/dist/{index-CCQttJzO.js → index-CAuT5j9Y.js} +2 -2
- package/dist/{index-UzmNhuS0.js → index-CXPaeisL.js} +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/layer-registry.d.ts +14 -0
- package/dist/layers/bim-layer.d.ts +104 -0
- package/dist/layers/feature-mesh-layer.d.ts +288 -0
- package/dist/layers/gltf-scene-walk.d.ts +55 -0
- package/dist/{lerc-CuifOkoA.js → lerc-CbTjQ7uI.js} +2 -2
- package/dist/meshopt_simplifier-Co6uRDDA.js +370 -0
- package/dist/onlymap.standalone.js +57996 -54706
- package/dist/onlymapjs.js +69 -67
- package/dist/programmatic.d.ts +22 -2
- package/dist/{raster-dysYIQ_k.js → raster-0b0nSHUh.js} +2 -2
- package/dist/{raster-pipeline-ZdCOGfqV.js → raster-pipeline-D8siq7-4.js} +1 -1
- package/dist/react/om-overlay.d.ts +9 -0
- package/dist/react.js +144 -144
- package/dist/runtime-core.d.ts +110 -3
- package/dist/selection.d.ts +33 -0
- package/dist/site-placement.d.ts +85 -0
- package/dist/testing.d.ts +6 -2
- package/dist/tile3d-metadata.d.ts +185 -0
- package/dist/version.d.ts +1 -1
- package/dist/widget-registry.d.ts +11 -0
- package/dist/widgets/ifc-clash.d.ts +18 -0
- package/dist/widgets/ifc.d.ts +41 -0
- package/dist/{zarr-OewK7k2K.js → zarr-DCYro_Vs.js} +79 -66
- package/docs/testing.md +2 -2
- package/llms.txt +3 -3
- package/onlymapjs.html-data.json +152 -0
- package/package.json +3 -1
- package/skills/onlymapjs/SKILL.md +4 -2
- package/skills/onlymapjs/references/react.md +1 -1
- package/skills/onlymapjs/references/syntax.md +298 -11
- package/dist/cityjson-urQeujQv.js +0 -407
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ur, d as lr, R as zt, e as fr, m as dr, p as St, f as hr, g as pr, h as mr } from "./raster-pipeline-
|
|
2
|
-
import { ap as gr } from "./index-
|
|
1
|
+
import { A as ur, d as lr, R as zt, e as fr, m as dr, p as St, f as hr, g as pr, h as mr } from "./raster-pipeline-D8siq7-4.js";
|
|
2
|
+
import { ap as gr } from "./index-1UgNlfGR.js";
|
|
3
3
|
import $t from "./index-CW1n5LdO.js";
|
|
4
4
|
var Et;
|
|
5
5
|
function h(e, t, n) {
|
|
@@ -48,7 +48,7 @@ class zn extends Error {
|
|
|
48
48
|
}
|
|
49
49
|
(Et = globalThis).__zod_globalConfig ?? (Et.__zod_globalConfig = {});
|
|
50
50
|
const et = globalThis.__zod_globalConfig;
|
|
51
|
-
function
|
|
51
|
+
function M(e) {
|
|
52
52
|
return et;
|
|
53
53
|
}
|
|
54
54
|
function Sn(e) {
|
|
@@ -396,7 +396,7 @@ const st = (e) => (t, n, r, o) => {
|
|
|
396
396
|
if (i instanceof Promise)
|
|
397
397
|
throw new ee();
|
|
398
398
|
if (i.issues.length) {
|
|
399
|
-
const a = new (o?.Err ?? e)(i.issues.map((c) => D(c, s,
|
|
399
|
+
const a = new (o?.Err ?? e)(i.issues.map((c) => D(c, s, M())));
|
|
400
400
|
throw $n(a, o?.callee), a;
|
|
401
401
|
}
|
|
402
402
|
return i.value;
|
|
@@ -404,7 +404,7 @@ const st = (e) => (t, n, r, o) => {
|
|
|
404
404
|
const s = r ? { ...r, async: !0 } : { async: !0 };
|
|
405
405
|
let i = t._zod.run({ value: n, issues: [] }, s);
|
|
406
406
|
if (i instanceof Promise && (i = await i), i.issues.length) {
|
|
407
|
-
const a = new (o?.Err ?? e)(i.issues.map((c) => D(c, s,
|
|
407
|
+
const a = new (o?.Err ?? e)(i.issues.map((c) => D(c, s, M())));
|
|
408
408
|
throw $n(a, o?.callee), a;
|
|
409
409
|
}
|
|
410
410
|
return i.value;
|
|
@@ -414,14 +414,14 @@ const st = (e) => (t, n, r, o) => {
|
|
|
414
414
|
throw new ee();
|
|
415
415
|
return s.issues.length ? {
|
|
416
416
|
success: !1,
|
|
417
|
-
error: new (e ?? In)(s.issues.map((i) => D(i, o,
|
|
417
|
+
error: new (e ?? In)(s.issues.map((i) => D(i, o, M())))
|
|
418
418
|
} : { success: !0, data: s.value };
|
|
419
419
|
}, Cr = /* @__PURE__ */ Pe(Zn), je = (e) => async (t, n, r) => {
|
|
420
420
|
const o = r ? { ...r, async: !0 } : { async: !0 };
|
|
421
421
|
let s = t._zod.run({ value: n, issues: [] }, o);
|
|
422
422
|
return s instanceof Promise && (s = await s), s.issues.length ? {
|
|
423
423
|
success: !1,
|
|
424
|
-
error: new e(s.issues.map((i) => D(i, o,
|
|
424
|
+
error: new e(s.issues.map((i) => D(i, o, M())))
|
|
425
425
|
} : { success: !0, data: s.value };
|
|
426
426
|
}, Pr = /* @__PURE__ */ je(Zn), jr = (e) => (t, n, r) => {
|
|
427
427
|
const o = r ? { ...r, direction: "backward" } : { direction: "backward" };
|
|
@@ -429,7 +429,7 @@ const st = (e) => (t, n, r, o) => {
|
|
|
429
429
|
}, xr = (e) => (t, n, r) => st(e)(t, n, r), Rr = (e) => async (t, n, r) => {
|
|
430
430
|
const o = r ? { ...r, direction: "backward" } : { direction: "backward" };
|
|
431
431
|
return it(e)(t, n, o);
|
|
432
|
-
},
|
|
432
|
+
}, Lr = (e) => async (t, n, r) => it(e)(t, n, r), Mr = (e) => (t, n, r) => {
|
|
433
433
|
const o = r ? { ...r, direction: "backward" } : { direction: "backward" };
|
|
434
434
|
return Pe(e)(t, n, o);
|
|
435
435
|
}, Dr = (e) => (t, n, r) => Pe(e)(t, n, r), Ur = (e) => async (t, n, r) => {
|
|
@@ -925,9 +925,9 @@ const Io = {
|
|
|
925
925
|
t.pattern ?? (t.pattern = Jr), z.init(e, t);
|
|
926
926
|
}), Ro = /* @__PURE__ */ h("$ZodULID", (e, t) => {
|
|
927
927
|
t.pattern ?? (t.pattern = Vr), z.init(e, t);
|
|
928
|
-
}),
|
|
928
|
+
}), Lo = /* @__PURE__ */ h("$ZodXID", (e, t) => {
|
|
929
929
|
t.pattern ?? (t.pattern = Wr), z.init(e, t);
|
|
930
|
-
}),
|
|
930
|
+
}), Mo = /* @__PURE__ */ h("$ZodKSUID", (e, t) => {
|
|
931
931
|
t.pattern ?? (t.pattern = Gr), z.init(e, t);
|
|
932
932
|
}), Do = /* @__PURE__ */ h("$ZodISODateTime", (e, t) => {
|
|
933
933
|
t.pattern ?? (t.pattern = uo(t)), z.init(e, t);
|
|
@@ -1046,7 +1046,7 @@ const Qo = /* @__PURE__ */ h("$ZodJWT", (e, t) => {
|
|
|
1046
1046
|
continue: !t.abort
|
|
1047
1047
|
});
|
|
1048
1048
|
};
|
|
1049
|
-
}),
|
|
1049
|
+
}), Ln = /* @__PURE__ */ h("$ZodNumber", (e, t) => {
|
|
1050
1050
|
$.init(e, t), e._zod.pattern = e._zod.bag.pattern ?? Cn, e._zod.parse = (n, r) => {
|
|
1051
1051
|
if (t.coerce)
|
|
1052
1052
|
try {
|
|
@@ -1066,7 +1066,7 @@ const Qo = /* @__PURE__ */ h("$ZodJWT", (e, t) => {
|
|
|
1066
1066
|
}), n;
|
|
1067
1067
|
};
|
|
1068
1068
|
}), es = /* @__PURE__ */ h("$ZodNumberFormat", (e, t) => {
|
|
1069
|
-
go.init(e, t),
|
|
1069
|
+
go.init(e, t), Ln.init(e, t);
|
|
1070
1070
|
}), ts = /* @__PURE__ */ h("$ZodUnknown", (e, t) => {
|
|
1071
1071
|
$.init(e, t), e._zod.parse = (n) => n;
|
|
1072
1072
|
}), ns = /* @__PURE__ */ h("$ZodNever", (e, t) => {
|
|
@@ -1120,7 +1120,7 @@ function ve(e, t, n, r, o, s) {
|
|
|
1120
1120
|
}
|
|
1121
1121
|
e.value === void 0 ? i && (t.value[n] = void 0) : t.value[n] = e.value;
|
|
1122
1122
|
}
|
|
1123
|
-
function
|
|
1123
|
+
function Mn(e) {
|
|
1124
1124
|
const t = Object.keys(e.shape);
|
|
1125
1125
|
for (const r of t)
|
|
1126
1126
|
if (!e.shape?.[r]?._zod?.traits?.has("$ZodType"))
|
|
@@ -1165,7 +1165,7 @@ const os = /* @__PURE__ */ h("$ZodObject", (e, t) => {
|
|
|
1165
1165
|
}
|
|
1166
1166
|
});
|
|
1167
1167
|
}
|
|
1168
|
-
const r = tt(() =>
|
|
1168
|
+
const r = tt(() => Mn(t));
|
|
1169
1169
|
v(e._zod, "propValues", () => {
|
|
1170
1170
|
const a = t.shape, c = {};
|
|
1171
1171
|
for (const u in a) {
|
|
@@ -1200,7 +1200,7 @@ const os = /* @__PURE__ */ h("$ZodObject", (e, t) => {
|
|
|
1200
1200
|
};
|
|
1201
1201
|
}), ss = /* @__PURE__ */ h("$ZodObjectJIT", (e, t) => {
|
|
1202
1202
|
os.init(e, t);
|
|
1203
|
-
const n = e._zod.parse, r = tt(() =>
|
|
1203
|
+
const n = e._zod.parse, r = tt(() => Mn(t)), o = (d) => {
|
|
1204
1204
|
const p = new To(["shape", "payload", "ctx"]), g = r.value, k = (O) => {
|
|
1205
1205
|
const y = It(O);
|
|
1206
1206
|
return `shape[${y}]._zod.run({ value: input[${y}], issues: [] }, ctx)`;
|
|
@@ -1301,7 +1301,7 @@ function At(e, t, n, r) {
|
|
|
1301
1301
|
code: "invalid_union",
|
|
1302
1302
|
input: t.value,
|
|
1303
1303
|
inst: n,
|
|
1304
|
-
errors: e.map((s) => s.issues.map((i) => D(i, r,
|
|
1304
|
+
errors: e.map((s) => s.issues.map((i) => D(i, r, M())))
|
|
1305
1305
|
}), t);
|
|
1306
1306
|
}
|
|
1307
1307
|
const is = /* @__PURE__ */ h("$ZodUnion", (e, t) => {
|
|
@@ -1501,7 +1501,7 @@ const us = /* @__PURE__ */ h("$ZodRecord", (e, t) => {
|
|
|
1501
1501
|
n.issues.push({
|
|
1502
1502
|
code: "invalid_key",
|
|
1503
1503
|
origin: "record",
|
|
1504
|
-
issues: l.issues.map((p) => D(p, r,
|
|
1504
|
+
issues: l.issues.map((p) => D(p, r, M())),
|
|
1505
1505
|
input: u,
|
|
1506
1506
|
path: [u],
|
|
1507
1507
|
inst: e
|
|
@@ -1540,7 +1540,7 @@ const us = /* @__PURE__ */ h("$ZodRecord", (e, t) => {
|
|
|
1540
1540
|
t.mode === "loose" ? n.value[a] = o[a] : n.issues.push({
|
|
1541
1541
|
code: "invalid_key",
|
|
1542
1542
|
origin: "record",
|
|
1543
|
-
issues: c.issues.map((f) => D(f, r,
|
|
1543
|
+
issues: c.issues.map((f) => D(f, r, M())),
|
|
1544
1544
|
input: a,
|
|
1545
1545
|
path: [a],
|
|
1546
1546
|
inst: e
|
|
@@ -1621,10 +1621,10 @@ const ms = /* @__PURE__ */ h("$ZodPrefault", (e, t) => {
|
|
|
1621
1621
|
return n ? new Set([...n].filter((r) => r !== void 0)) : void 0;
|
|
1622
1622
|
}), e._zod.parse = (n, r) => {
|
|
1623
1623
|
const o = t.innerType._zod.run(n, r);
|
|
1624
|
-
return o instanceof Promise ? o.then((s) =>
|
|
1624
|
+
return o instanceof Promise ? o.then((s) => Lt(s, e)) : Lt(o, e);
|
|
1625
1625
|
};
|
|
1626
1626
|
});
|
|
1627
|
-
function
|
|
1627
|
+
function Lt(e, t) {
|
|
1628
1628
|
return !e.issues.length && e.value === void 0 && e.issues.push({
|
|
1629
1629
|
code: "invalid_type",
|
|
1630
1630
|
expected: "nonoptional",
|
|
@@ -1640,13 +1640,13 @@ const ys = /* @__PURE__ */ h("$ZodCatch", (e, t) => {
|
|
|
1640
1640
|
return o instanceof Promise ? o.then((s) => (n.value = s.value, s.issues.length && (n.value = t.catchValue({
|
|
1641
1641
|
...n,
|
|
1642
1642
|
error: {
|
|
1643
|
-
issues: s.issues.map((i) => D(i, r,
|
|
1643
|
+
issues: s.issues.map((i) => D(i, r, M()))
|
|
1644
1644
|
},
|
|
1645
1645
|
input: n.value
|
|
1646
1646
|
}), n.issues = [], n.fallback = !0), n)) : (n.value = o.value, o.issues.length && (n.value = t.catchValue({
|
|
1647
1647
|
...n,
|
|
1648
1648
|
error: {
|
|
1649
|
-
issues: o.issues.map((s) => D(s, r,
|
|
1649
|
+
issues: o.issues.map((s) => D(s, r, M()))
|
|
1650
1650
|
},
|
|
1651
1651
|
input: n.value
|
|
1652
1652
|
}), n.issues = [], n.fallback = !0), n);
|
|
@@ -1669,10 +1669,10 @@ const ws = /* @__PURE__ */ h("$ZodReadonly", (e, t) => {
|
|
|
1669
1669
|
if (r.direction === "backward")
|
|
1670
1670
|
return t.innerType._zod.run(n, r);
|
|
1671
1671
|
const o = t.innerType._zod.run(n, r);
|
|
1672
|
-
return o instanceof Promise ? o.then(
|
|
1672
|
+
return o instanceof Promise ? o.then(Mt) : Mt(o);
|
|
1673
1673
|
};
|
|
1674
1674
|
});
|
|
1675
|
-
function
|
|
1675
|
+
function Mt(e) {
|
|
1676
1676
|
return e.value = Object.freeze(e.value), e;
|
|
1677
1677
|
}
|
|
1678
1678
|
const bs = /* @__PURE__ */ h("$ZodCustom", (e, t) => {
|
|
@@ -1894,7 +1894,7 @@ function Rs(e, t) {
|
|
|
1894
1894
|
});
|
|
1895
1895
|
}
|
|
1896
1896
|
// @__NO_SIDE_EFFECTS__
|
|
1897
|
-
function
|
|
1897
|
+
function Ls(e, t) {
|
|
1898
1898
|
return new e({
|
|
1899
1899
|
type: "string",
|
|
1900
1900
|
format: "ipv6",
|
|
@@ -1904,7 +1904,7 @@ function Ms(e, t) {
|
|
|
1904
1904
|
});
|
|
1905
1905
|
}
|
|
1906
1906
|
// @__NO_SIDE_EFFECTS__
|
|
1907
|
-
function
|
|
1907
|
+
function Ms(e, t) {
|
|
1908
1908
|
return new e({
|
|
1909
1909
|
type: "string",
|
|
1910
1910
|
format: "cidrv4",
|
|
@@ -2044,7 +2044,7 @@ function Bt(e, t) {
|
|
|
2044
2044
|
});
|
|
2045
2045
|
}
|
|
2046
2046
|
// @__NO_SIDE_EFFECTS__
|
|
2047
|
-
function
|
|
2047
|
+
function Le(e, t) {
|
|
2048
2048
|
return new jn({
|
|
2049
2049
|
check: "less_than",
|
|
2050
2050
|
...m(t),
|
|
@@ -2062,7 +2062,7 @@ function Jt(e, t) {
|
|
|
2062
2062
|
});
|
|
2063
2063
|
}
|
|
2064
2064
|
// @__NO_SIDE_EFFECTS__
|
|
2065
|
-
function
|
|
2065
|
+
function Me(e, t) {
|
|
2066
2066
|
return new xn({
|
|
2067
2067
|
check: "greater_than",
|
|
2068
2068
|
...m(t),
|
|
@@ -2625,11 +2625,11 @@ const pi = (e, t = {}) => (n) => {
|
|
|
2625
2625
|
function Ri(e) {
|
|
2626
2626
|
return /* @__PURE__ */ Vs(xi, e);
|
|
2627
2627
|
}
|
|
2628
|
-
const
|
|
2628
|
+
const Li = /* @__PURE__ */ h("ZodISODate", (e, t) => {
|
|
2629
2629
|
Uo.init(e, t), E.init(e, t);
|
|
2630
2630
|
});
|
|
2631
|
-
function
|
|
2632
|
-
return /* @__PURE__ */ Ws(
|
|
2631
|
+
function Mi(e) {
|
|
2632
|
+
return /* @__PURE__ */ Ws(Li, e);
|
|
2633
2633
|
}
|
|
2634
2634
|
const Di = /* @__PURE__ */ h("ZodISOTime", (e, t) => {
|
|
2635
2635
|
Fo.init(e, t), E.init(e, t);
|
|
@@ -2674,7 +2674,7 @@ const Ji = (e, t) => {
|
|
|
2674
2674
|
});
|
|
2675
2675
|
}, x = /* @__PURE__ */ h("ZodError", Ji, {
|
|
2676
2676
|
Parent: Error
|
|
2677
|
-
}), Vi = /* @__PURE__ */ st(x), Wi = /* @__PURE__ */ it(x), Gi = /* @__PURE__ */ Pe(x), Ki = /* @__PURE__ */ je(x), Yi = /* @__PURE__ */ jr(x), qi = /* @__PURE__ */ xr(x), Xi = /* @__PURE__ */ Rr(x), Hi = /* @__PURE__ */
|
|
2677
|
+
}), Vi = /* @__PURE__ */ st(x), Wi = /* @__PURE__ */ it(x), Gi = /* @__PURE__ */ Pe(x), Ki = /* @__PURE__ */ je(x), Yi = /* @__PURE__ */ jr(x), qi = /* @__PURE__ */ xr(x), Xi = /* @__PURE__ */ Rr(x), Hi = /* @__PURE__ */ Lr(x), Qi = /* @__PURE__ */ Mr(x), ea = /* @__PURE__ */ Dr(x), ta = /* @__PURE__ */ Ur(x), na = /* @__PURE__ */ Fr(x), Wt = /* @__PURE__ */ new WeakMap();
|
|
2678
2678
|
function fe(e, t, n) {
|
|
2679
2679
|
const r = Object.getPrototypeOf(e);
|
|
2680
2680
|
let o = Wt.get(r);
|
|
@@ -2770,7 +2770,7 @@ const I = /* @__PURE__ */ h("ZodType", (e, t) => ($.init(e, t), Object.assign(e[
|
|
|
2770
2770
|
return Xt(this, Ca(n));
|
|
2771
2771
|
},
|
|
2772
2772
|
default(n) {
|
|
2773
|
-
return
|
|
2773
|
+
return La(this, n);
|
|
2774
2774
|
},
|
|
2775
2775
|
prefault(n) {
|
|
2776
2776
|
return Da(this, n);
|
|
@@ -2859,9 +2859,9 @@ const I = /* @__PURE__ */ h("ZodType", (e, t) => ($.init(e, t), Object.assign(e[
|
|
|
2859
2859
|
}
|
|
2860
2860
|
});
|
|
2861
2861
|
}), ra = /* @__PURE__ */ h("ZodString", (e, t) => {
|
|
2862
|
-
at.init(e, t), Kn.init(e, t), e.email = (n) => e.check(/* @__PURE__ */ Ss(oa, n)), e.url = (n) => e.check(/* @__PURE__ */ Zs(sa, n)), e.jwt = (n) => e.check(/* @__PURE__ */ Js(ba, n)), e.emoji = (n) => e.check(/* @__PURE__ */ Os(ia, n)), e.guid = (n) => e.check(/* @__PURE__ */ Ft(Gt, n)), e.uuid = (n) => e.check(/* @__PURE__ */ $s(ye, n)), e.uuidv4 = (n) => e.check(/* @__PURE__ */ Es(ye, n)), e.uuidv6 = (n) => e.check(/* @__PURE__ */ Ts(ye, n)), e.uuidv7 = (n) => e.check(/* @__PURE__ */ Is(ye, n)), e.nanoid = (n) => e.check(/* @__PURE__ */ As(aa, n)), e.guid = (n) => e.check(/* @__PURE__ */ Ft(Gt, n)), e.cuid = (n) => e.check(/* @__PURE__ */ Ns(ca, n)), e.cuid2 = (n) => e.check(/* @__PURE__ */ Cs(ua, n)), e.ulid = (n) => e.check(/* @__PURE__ */ Ps(la, n)), e.base64 = (n) => e.check(/* @__PURE__ */ Us(ya, n)), e.base64url = (n) => e.check(/* @__PURE__ */ Fs(_a, n)), e.xid = (n) => e.check(/* @__PURE__ */ js(fa, n)), e.ksuid = (n) => e.check(/* @__PURE__ */ xs(da, n)), e.ipv4 = (n) => e.check(/* @__PURE__ */ Rs(ha, n)), e.ipv6 = (n) => e.check(/* @__PURE__ */
|
|
2862
|
+
at.init(e, t), Kn.init(e, t), e.email = (n) => e.check(/* @__PURE__ */ Ss(oa, n)), e.url = (n) => e.check(/* @__PURE__ */ Zs(sa, n)), e.jwt = (n) => e.check(/* @__PURE__ */ Js(ba, n)), e.emoji = (n) => e.check(/* @__PURE__ */ Os(ia, n)), e.guid = (n) => e.check(/* @__PURE__ */ Ft(Gt, n)), e.uuid = (n) => e.check(/* @__PURE__ */ $s(ye, n)), e.uuidv4 = (n) => e.check(/* @__PURE__ */ Es(ye, n)), e.uuidv6 = (n) => e.check(/* @__PURE__ */ Ts(ye, n)), e.uuidv7 = (n) => e.check(/* @__PURE__ */ Is(ye, n)), e.nanoid = (n) => e.check(/* @__PURE__ */ As(aa, n)), e.guid = (n) => e.check(/* @__PURE__ */ Ft(Gt, n)), e.cuid = (n) => e.check(/* @__PURE__ */ Ns(ca, n)), e.cuid2 = (n) => e.check(/* @__PURE__ */ Cs(ua, n)), e.ulid = (n) => e.check(/* @__PURE__ */ Ps(la, n)), e.base64 = (n) => e.check(/* @__PURE__ */ Us(ya, n)), e.base64url = (n) => e.check(/* @__PURE__ */ Fs(_a, n)), e.xid = (n) => e.check(/* @__PURE__ */ js(fa, n)), e.ksuid = (n) => e.check(/* @__PURE__ */ xs(da, n)), e.ipv4 = (n) => e.check(/* @__PURE__ */ Rs(ha, n)), e.ipv6 = (n) => e.check(/* @__PURE__ */ Ls(pa, n)), e.cidrv4 = (n) => e.check(/* @__PURE__ */ Ms(ma, n)), e.cidrv6 = (n) => e.check(/* @__PURE__ */ Ds(ga, n)), e.e164 = (n) => e.check(/* @__PURE__ */ Bs(wa, n)), e.datetime = (n) => e.check(Ri(n)), e.date = (n) => e.check(Mi(n)), e.time = (n) => e.check(Ui(n)), e.duration = (n) => e.check(Bi(n));
|
|
2863
2863
|
});
|
|
2864
|
-
function
|
|
2864
|
+
function L(e) {
|
|
2865
2865
|
return /* @__PURE__ */ zs(ra, e);
|
|
2866
2866
|
}
|
|
2867
2867
|
const E = /* @__PURE__ */ h("ZodStringFormat", (e, t) => {
|
|
@@ -2885,9 +2885,9 @@ const E = /* @__PURE__ */ h("ZodStringFormat", (e, t) => {
|
|
|
2885
2885
|
}), la = /* @__PURE__ */ h("ZodULID", (e, t) => {
|
|
2886
2886
|
Ro.init(e, t), E.init(e, t);
|
|
2887
2887
|
}), fa = /* @__PURE__ */ h("ZodXID", (e, t) => {
|
|
2888
|
-
Mo.init(e, t), E.init(e, t);
|
|
2889
|
-
}), da = /* @__PURE__ */ h("ZodKSUID", (e, t) => {
|
|
2890
2888
|
Lo.init(e, t), E.init(e, t);
|
|
2889
|
+
}), da = /* @__PURE__ */ h("ZodKSUID", (e, t) => {
|
|
2890
|
+
Mo.init(e, t), E.init(e, t);
|
|
2891
2891
|
}), ha = /* @__PURE__ */ h("ZodIPv4", (e, t) => {
|
|
2892
2892
|
Jo.init(e, t), E.init(e, t);
|
|
2893
2893
|
}), pa = /* @__PURE__ */ h("ZodIPv6", (e, t) => {
|
|
@@ -2905,24 +2905,24 @@ const E = /* @__PURE__ */ h("ZodStringFormat", (e, t) => {
|
|
|
2905
2905
|
}), ba = /* @__PURE__ */ h("ZodJWT", (e, t) => {
|
|
2906
2906
|
Qo.init(e, t), E.init(e, t);
|
|
2907
2907
|
}), Yn = /* @__PURE__ */ h("ZodNumber", (e, t) => {
|
|
2908
|
-
|
|
2908
|
+
Ln.init(e, t), I.init(e, t), e._zod.processJSONSchema = (r, o, s) => yi(e, r, o), fe(e, "ZodNumber", {
|
|
2909
2909
|
gt(r, o) {
|
|
2910
2910
|
return this.check(/* @__PURE__ */ Jt(r, o));
|
|
2911
2911
|
},
|
|
2912
2912
|
gte(r, o) {
|
|
2913
|
-
return this.check(/* @__PURE__ */
|
|
2913
|
+
return this.check(/* @__PURE__ */ Me(r, o));
|
|
2914
2914
|
},
|
|
2915
2915
|
min(r, o) {
|
|
2916
|
-
return this.check(/* @__PURE__ */
|
|
2916
|
+
return this.check(/* @__PURE__ */ Me(r, o));
|
|
2917
2917
|
},
|
|
2918
2918
|
lt(r, o) {
|
|
2919
2919
|
return this.check(/* @__PURE__ */ Bt(r, o));
|
|
2920
2920
|
},
|
|
2921
2921
|
lte(r, o) {
|
|
2922
|
-
return this.check(/* @__PURE__ */
|
|
2922
|
+
return this.check(/* @__PURE__ */ Le(r, o));
|
|
2923
2923
|
},
|
|
2924
2924
|
max(r, o) {
|
|
2925
|
-
return this.check(/* @__PURE__ */
|
|
2925
|
+
return this.check(/* @__PURE__ */ Le(r, o));
|
|
2926
2926
|
},
|
|
2927
2927
|
int(r) {
|
|
2928
2928
|
return this.check(Se(r));
|
|
@@ -2934,13 +2934,13 @@ const E = /* @__PURE__ */ h("ZodStringFormat", (e, t) => {
|
|
|
2934
2934
|
return this.check(/* @__PURE__ */ Jt(0, r));
|
|
2935
2935
|
},
|
|
2936
2936
|
nonnegative(r) {
|
|
2937
|
-
return this.check(/* @__PURE__ */
|
|
2937
|
+
return this.check(/* @__PURE__ */ Me(0, r));
|
|
2938
2938
|
},
|
|
2939
2939
|
negative(r) {
|
|
2940
2940
|
return this.check(/* @__PURE__ */ Bt(0, r));
|
|
2941
2941
|
},
|
|
2942
2942
|
nonpositive(r) {
|
|
2943
|
-
return this.check(/* @__PURE__ */
|
|
2943
|
+
return this.check(/* @__PURE__ */ Le(0, r));
|
|
2944
2944
|
},
|
|
2945
2945
|
multipleOf(r, o) {
|
|
2946
2946
|
return this.check(/* @__PURE__ */ Vt(r, o));
|
|
@@ -3090,7 +3090,7 @@ const Kt = /* @__PURE__ */ h("ZodRecord", (e, t) => {
|
|
|
3090
3090
|
function Aa(e, t, n) {
|
|
3091
3091
|
return !t || !t._zod ? new Kt({
|
|
3092
3092
|
type: "record",
|
|
3093
|
-
keyType:
|
|
3093
|
+
keyType: L(),
|
|
3094
3094
|
valueType: e,
|
|
3095
3095
|
...m(t)
|
|
3096
3096
|
}) : new Kt({
|
|
@@ -3191,7 +3191,7 @@ function qt(e) {
|
|
|
3191
3191
|
const Ra = /* @__PURE__ */ h("ZodDefault", (e, t) => {
|
|
3192
3192
|
ps.init(e, t), I.init(e, t), e._zod.processJSONSchema = (n, r, o) => Ai(e, n, r, o), e.unwrap = () => e._zod.def.innerType, e.removeDefault = e.unwrap;
|
|
3193
3193
|
});
|
|
3194
|
-
function
|
|
3194
|
+
function La(e, t) {
|
|
3195
3195
|
return new Ra({
|
|
3196
3196
|
type: "default",
|
|
3197
3197
|
innerType: e,
|
|
@@ -3200,11 +3200,11 @@ function Ma(e, t) {
|
|
|
3200
3200
|
}
|
|
3201
3201
|
});
|
|
3202
3202
|
}
|
|
3203
|
-
const
|
|
3203
|
+
const Ma = /* @__PURE__ */ h("ZodPrefault", (e, t) => {
|
|
3204
3204
|
ms.init(e, t), I.init(e, t), e._zod.processJSONSchema = (n, r, o) => Ni(e, n, r, o), e.unwrap = () => e._zod.def.innerType;
|
|
3205
3205
|
});
|
|
3206
3206
|
function Da(e, t) {
|
|
3207
|
-
return new
|
|
3207
|
+
return new Ma({
|
|
3208
3208
|
type: "prefault",
|
|
3209
3209
|
innerType: e,
|
|
3210
3210
|
get defaultValue() {
|
|
@@ -3264,9 +3264,9 @@ function Ya(e, t) {
|
|
|
3264
3264
|
const qa = {
|
|
3265
3265
|
custom: "custom"
|
|
3266
3266
|
}, Xa = ct([
|
|
3267
|
-
J({ "proj:code":
|
|
3268
|
-
J({ "proj:wkt2":
|
|
3269
|
-
J({ "proj:projjson": Aa(
|
|
3267
|
+
J({ "proj:code": L().regex(/^[A-Z]+:[0-9]+$/) }),
|
|
3268
|
+
J({ "proj:wkt2": L() }),
|
|
3269
|
+
J({ "proj:projjson": Aa(L(), Ke()) })
|
|
3270
3270
|
]), Qn = $e([
|
|
3271
3271
|
A(),
|
|
3272
3272
|
A(),
|
|
@@ -3275,23 +3275,23 @@ const qa = {
|
|
|
3275
3275
|
A(),
|
|
3276
3276
|
A()
|
|
3277
3277
|
]), er = $e([Se().min(1), Se().min(1)]), Ha = J({
|
|
3278
|
-
asset:
|
|
3279
|
-
derived_from:
|
|
3278
|
+
asset: L(),
|
|
3279
|
+
derived_from: L().optional(),
|
|
3280
3280
|
transform: J({
|
|
3281
3281
|
scale: le(A()).optional(),
|
|
3282
3282
|
translation: le(A()).optional()
|
|
3283
3283
|
}).optional(),
|
|
3284
|
-
resampling_method:
|
|
3284
|
+
resampling_method: L().optional(),
|
|
3285
3285
|
// spatial: overrides per level — required here when not at group level
|
|
3286
3286
|
"spatial:transform": Qn.optional(),
|
|
3287
3287
|
"spatial:shape": er.optional()
|
|
3288
3288
|
}).passthrough(), Qa = J({
|
|
3289
3289
|
multiscales: J({
|
|
3290
3290
|
layout: le(Ha).min(1),
|
|
3291
|
-
resampling_method:
|
|
3291
|
+
resampling_method: L().optional()
|
|
3292
3292
|
})
|
|
3293
3293
|
}), ec = J({
|
|
3294
|
-
"spatial:dimensions": le(
|
|
3294
|
+
"spatial:dimensions": le(L()).min(2).max(3),
|
|
3295
3295
|
"spatial:transform": Qn.optional(),
|
|
3296
3296
|
"spatial:shape": er.optional(),
|
|
3297
3297
|
"spatial:bbox": ct([
|
|
@@ -3305,7 +3305,7 @@ const qa = {
|
|
|
3305
3305
|
A()
|
|
3306
3306
|
])
|
|
3307
3307
|
]).optional(),
|
|
3308
|
-
"spatial:transform_type":
|
|
3308
|
+
"spatial:transform_type": L().optional(),
|
|
3309
3309
|
"spatial:registration": qn(["node", "pixel"]).optional(),
|
|
3310
3310
|
multiscales: Qa.shape.multiscales.optional()
|
|
3311
3311
|
}), tc = ec.superRefine((e, t) => {
|
|
@@ -4778,15 +4778,15 @@ function Rc(e, t) {
|
|
|
4778
4778
|
n instanceof Promise ? n = n.then((o) => r(o)) : n = r(n);
|
|
4779
4779
|
return n;
|
|
4780
4780
|
}
|
|
4781
|
-
function
|
|
4781
|
+
function Lc(e, ...t) {
|
|
4782
4782
|
return Rc(e, t);
|
|
4783
4783
|
}
|
|
4784
4784
|
async function ar(e) {
|
|
4785
4785
|
let t = e.store.arrayExtensions;
|
|
4786
|
-
return t?.length ? await
|
|
4786
|
+
return t?.length ? await Lc(e, ...t) : e;
|
|
4787
4787
|
}
|
|
4788
|
-
let pe =
|
|
4789
|
-
function
|
|
4788
|
+
let pe = Mc();
|
|
4789
|
+
function Mc() {
|
|
4790
4790
|
let e = /* @__PURE__ */ new WeakMap();
|
|
4791
4791
|
function t(n) {
|
|
4792
4792
|
let r = e.get(n) ?? { v2: 0, v3: 0 };
|
|
@@ -5428,8 +5428,20 @@ const ku = async (e, t) => {
|
|
|
5428
5428
|
function _e(e) {
|
|
5429
5429
|
return JSON.stringify([e.src ?? "", e.variable ?? "", e.select ?? "", e.bounds ?? null, e.crs ?? "", e.spatialDims ?? ""]);
|
|
5430
5430
|
}
|
|
5431
|
+
class zu extends _u {
|
|
5432
|
+
static layerName = "OmZarrTileLayer";
|
|
5433
|
+
renderLayers() {
|
|
5434
|
+
const t = super.renderLayers();
|
|
5435
|
+
if (!t) return t;
|
|
5436
|
+
const r = {
|
|
5437
|
+
updateTriggers: { ...t.props.updateTriggers, getTileData: this.props.selectTrigger },
|
|
5438
|
+
onViewportLoad: () => requestAnimationFrame(() => this.setNeedsRedraw())
|
|
5439
|
+
};
|
|
5440
|
+
return t.clone(r);
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5431
5443
|
const kn = /* @__PURE__ */ new Map();
|
|
5432
|
-
async function
|
|
5444
|
+
async function Su(e) {
|
|
5433
5445
|
const t = new URL(e.src ?? "", typeof document < "u" ? document.baseURI : void 0).href, n = new oc(t);
|
|
5434
5446
|
let r = n;
|
|
5435
5447
|
try {
|
|
@@ -5446,7 +5458,7 @@ async function zu(e) {
|
|
|
5446
5458
|
}
|
|
5447
5459
|
return { node: o, variable: e.variable };
|
|
5448
5460
|
}
|
|
5449
|
-
class
|
|
5461
|
+
class Zu extends gr {
|
|
5450
5462
|
static layerName = "OmZarrLayer";
|
|
5451
5463
|
static defaultProps = {
|
|
5452
5464
|
src: "",
|
|
@@ -5470,7 +5482,7 @@ class Iu extends gr {
|
|
|
5470
5482
|
if (!t.src) throw new Error('ZarrLayer requires a src (the ".zarr" store URL).');
|
|
5471
5483
|
const r = bu(t.select), o = JSON.stringify([t.src, t.variable ?? "", t.bounds ?? null, t.crs ?? "", t.spatialDims ?? ""]);
|
|
5472
5484
|
let s = kn.get(o);
|
|
5473
|
-
if (s || (s = await
|
|
5485
|
+
if (s || (s = await Su(t), kn.set(o, s)), n !== _e(this.props)) return;
|
|
5474
5486
|
this.setState({ status: "ready", node: s.node, variable: s.variable, metadata: s.metadata, selection: r, loadKey: n });
|
|
5475
5487
|
} catch (r) {
|
|
5476
5488
|
console.error(`[onlymapjs] ZarrLayer src="${t.src}" failed to load:`, r), n === _e(this.props) && this.setState({ status: "error", loadKey: n });
|
|
@@ -5485,13 +5497,14 @@ class Iu extends gr {
|
|
|
5485
5497
|
colormap: this.props.colormap,
|
|
5486
5498
|
nodataOverride: this.props.nodataOverride
|
|
5487
5499
|
};
|
|
5488
|
-
return new
|
|
5500
|
+
return new zu(
|
|
5489
5501
|
this.getSubLayerProps({
|
|
5490
5502
|
id: "zarr",
|
|
5491
5503
|
node: n,
|
|
5492
5504
|
...r !== void 0 ? { variable: r } : {},
|
|
5493
5505
|
...o !== void 0 ? { metadata: o } : {},
|
|
5494
5506
|
selection: s,
|
|
5507
|
+
selectTrigger: this.props.select,
|
|
5495
5508
|
getTileData: ku,
|
|
5496
5509
|
renderTile: (a) => pr(a, i),
|
|
5497
5510
|
opacity: this.props.opacity
|
|
@@ -5500,7 +5513,7 @@ class Iu extends gr {
|
|
|
5500
5513
|
}
|
|
5501
5514
|
}
|
|
5502
5515
|
export {
|
|
5503
|
-
|
|
5516
|
+
Zu as OmZarrLayer,
|
|
5504
5517
|
vu as buildManualGeoZarrAttrs,
|
|
5505
5518
|
bu as parseZarrSelection
|
|
5506
5519
|
};
|
package/docs/testing.md
CHANGED
|
@@ -11,7 +11,7 @@ This guide is the complete workflow. It assumes a page like this (a manifest in
|
|
|
11
11
|
get-position="[$lon, $lat]"
|
|
12
12
|
get-fill-color="scale($magnitude, sequential, ['#fee8c8','#b30000'], domain=[0,7])"
|
|
13
13
|
filter-field="magnitude" filter-range="[0, 10]" pickable></om-layer>
|
|
14
|
-
<om-overlay id="detail" anchor-from="selection" visible="false">
|
|
14
|
+
<om-overlay id="detail" anchor-from="selection" selection-type="click" visible="false">
|
|
15
15
|
<div><b>{{place}}</b> — M {{magnitude}}</div>
|
|
16
16
|
</om-overlay>
|
|
17
17
|
<om-behavior on="click" layer="quakes" action="show-overlay" target="detail"></om-behavior>
|
|
@@ -111,7 +111,7 @@ it("panning away empties viewport-scoped widgets", async () => {
|
|
|
111
111
|
});
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
The harness API: `pick` (synthetic picks fed through the exact code path real deck.gl picks take — columnar layers pick object-less by index, exactly like live), `clearSelection` (hover-off
|
|
114
|
+
The harness API: `pick` (synthetic picks fed through the exact code path real deck.gl picks take — columnar layers pick object-less by index, exactly like live), `clearSelection` (an empty pick — default kind `"hover"`, a hover-off that runs the tooltip auto-hide path; pass `"click"` for a click on empty space, the gesture that dismisses a `selection-type="click"` popup), `mapPoint` (a click/hover map coordinate through the real onMapPoint path — fires `om-map-point`, drives the draw controller), `emit` (any action, same payload contract as `ctx.emit`/`data-emit`), `setView`, `layers()` (the live IR), `flush`, `unmount`. Every verb settles the library's internal batching before resolving — **you never write a sleep**.
|
|
115
115
|
|
|
116
116
|
**Remote data:** mock `fetch` and the harness waits for it via the readiness signal:
|
|
117
117
|
|
package/llms.txt
CHANGED
|
@@ -28,13 +28,13 @@ OnlyMapJS is NOT raw deck.gl and NOT generic HTML/JSX. The rules below are the d
|
|
|
28
28
|
|
|
29
29
|
## Element vocabulary
|
|
30
30
|
|
|
31
|
-
- `<om-map center="[lng, lat]" zoom="11" pitch="55" bearing="20" basemap="positron">` — the root. Give it a height (`om-map { display:block; height:100vh }` with `html,body{height:100%}`, or a sized container) — a custom element is display:inline by default and collapses to zero size; with none set, the library falls back to display:block + a 400px floor so a bare map still shows (any height you DO set wins over the floor, including one below 400px), and a collapsed map logs a console warning. A map hidden on purpose (`hidden`, or inside a `display:none` panel) stays hidden and does not warn. `basemap` accepts a free preset (`liberty`, `bright`, `positron`, `dark-matter`, `voyager`, `osm` — no keys; or `maptiler-streets`/`maptiler-dataviz`/`maptiler-satellite` with `basemap-key="…"` or `OmMap.configureBasemap({ maptilerKey })`), `maplibre` (bare demo style), a style URL (e.g. a MapTiler-customized style; any scheme fetch supports, including desktop asset protocols like Tauri's, query strings fine), or `none` (standalone canvas). The attribute is LIVE — writing it switches the basemap in place (camera + layers survive); the `set-basemap {basemap}` action and `<om-widget type="basemap-switcher" options="positron dark-matter osm">` do the same. Register more via `OmMap.registerBasemap(name, { style })`. Required provider attribution renders automatically (`attribution="false"` opts out). `pitch`/`bearing` tilt the initial camera (use for 3D content). Terrain: `terrain="terrarium"` (keyless AWS DEM; also `maptiler-terrain` with a key, a raw `{z}/{x}/{y}` DEM URL + `terrain-decoder="terrarium|mapbox-rgb|<JSON>"`, or `off`) raises a 3D surface — geographic layers DRAPE onto it automatically (per-layer `terrain="drape|offset|off"` overrides; 3D-model layers sit ON it), `terrain-exaggeration` scales relief, `terrain-texture` drapes imagery; terrain REPLACES an active basemap while on (restored when off); `set-terrain` action, `terrain` watch token, `OmMap.registerTerrain(name, {...})` for more sources. Scene lighting for 3D content: `lighting="daylight|studio|flat|custom"` (absent = deck defaults) with `lighting-ambient`/`lighting-sun`/`lighting-sun-azimuth`/`lighting-sun-elevation`/`lighting-camera` overrides and `lighting-sun-date` (ISO/epoch — solar-position sun computed at the map center, wins over azimuth/elevation); attribute-backed (undoable, live-editable), story-steppable via the `set-lighting {lighting, sunAzimuth, ...}` action (`lighting="default"` removes the attribute set; a bare preset is a clean reset — stale overrides clear), native UI via `<om-widget type="lighting">` (preset radios + tuning sliders), widget watch token `lighting`. GeoJsonLayer extrudes polygons declaratively: `extruded get-elevation="$height"` (+ `wireframe`). `widgets-hidden` attribute (or the `set-widgets-visible {visible}` action / `<om-widget type="widgets-toggle">` button) hides every widget WITHOUT destroying state — attribution never hides (license); transient (not an undo step) but story-steppable, so a step can clear chrome for a cinematic take. Slots auto-dim while an open `<om-overlay>` popup covers them (position stability over the popup dodging; `widgets-dim="off"` disables). `validate` attribute enables live validation + on-page error panel. Include a `map-id="<random UUID>"` on complete pages (identifies the map artifact for usage telemetry — not a visitor id; the page author deletes it to opt out); `telemetry="off"` disables usage telemetry + library-error reporting for the map (global: `OmMap.configureTelemetry({ disabled: true })`; schema: docs/telemetry.md). Free plan limits (HOSTED http(s) pages only — a dev context (localhost, file://, any non-web scheme) lifts every cap while the attribution badge stays; the exemption is technical convenience, not a license grant — commercial deployment incl. packaged apps still requires a key): 5 layers, 25k rows per layer — layers past a limit don't render and validation says why; a `license-key="om_live_…"` attribute (publishable, origin-restricted) or `OmMap.configureLicense(key)` lifts limits and removes the corner badge. When a user's map exceeds free limits, say so and point at the validation message rather than silently restructuring their data. Events on the element: `om-map-ready` (boot complete; `await mapEl.ready` is the promise twin), `om-validation-error`, `om-view-changed` (camera settled after a move, debounced; `detail = {longitude, latitude, zoom, pitch, bearing, origin}` where `origin` is `"user"` for gesture bursts vs `"programmatic"` for API/story moves — the camera-persistence hook, and the echo-suppression signal when syncing camera state to an app store), `om-map-point` (`detail = {coordinate: [lng,lat]|null, kind}` — every click/hover map coordinate incl. empty-map clicks, for custom capture tools beyond the draw widget), `om-tileset-load` (`detail = {layerId, tileset}` — a Tile3DLayer's live deck Tileset3D, for tools needing the real tileset like region export). `MapController` mirrors these as `onViewChange`/`onMapPoint`/`onTilesetLoad` options. `await mapEl.snapshot()` returns a canvas-only PNG dataURL of the scene (basemap + layers at device pixels; `{as:"blob"}` for files) — DOM widgets/overlays/attribution are NOT captured, so exports must render provider credits themselves.
|
|
32
|
-
- `<om-layer id="..." type="ScatterplotLayer" data="./points.json">` — any deck.gl layer class by `type` (all 33 bundled, plus the native `COGLayer` raster type), plus `PopupLayer` (WebGL badges/labels at scale: `layout="badge|pin-label|card"`, `min-zoom`/`max-zoom`). Every type's full attribute list ships in the package's `onlymapjs.html-data.json` — consult it instead of guessing attribute names or reading the minified dist. TextLayer's default font atlas covers ASCII only: set `character-set` when label text carries other glyphs (`—`, `·`, accents) or deck warns and renders them blank. External layer classes register via `OmMap.registerLayer({type, deckClass, props})` — build them on `@nika-js/onlymap/deck` (the bundled `CompositeLayer`/`TileLayer`/… re-exports), never a separately-installed deck.gl (different class hierarchy, breaks in the renderer); function-valued props ride the subclass's `static defaultProps`; register at module top level BEFORE the manifest mounts (see docs/custom-layers.md). Data: `data` URL (JSON, GeoJSON, CSV/TSV `.csv` — parsed to typed columns — Shapefile `.shp` (+`.dbf` attributes) and KML `.kml` as GeoJSON features, GPX `.gpx` (waypoints/tracks/routes → features tagged `_gpxKind`; a `#waypoints`/`#tracks`/`#routes` URL fragment selects one part), FlatGeobuf `.fgb` (cloud-native binary vector, whole-file decode), GeoParquet `.parquet`/`.geoparquet` (cloud-native columnar vector — all-Point files stay columnar like Arrow, lines/polygons become GeoJSON features; requires the file's `geo` metadata with WKB geometry, and CRS84/EPSG:4326 — a projected CRS is a loud error telling you to reproject, not a silent misplacement; snappy/gzip/zstd row-group compression handled), CityJSON `.city.json` / CityJSONSeq `.city.jsonl` (semantic 3D city models — 3DBAG, PLATEAU — decoded to one of two shapes by the `data` URL, no CityJSON layer type: default → extruded footprints, `type="GeoJsonLayer" extruded get-elevation="$roof_height"`; `?om-surfaces=1` → one row PER FACE at its own real per-vertex height so a pitched LoD2.2 roof actually looks pitched, `type="SolidPolygonLayer" get-polygon="$polygon" full3d` (`extruded` stays at its ordinary `false` default) (flat-shaded — deck.gl only lights the `extruded` shader path — each row also carrying `surface_type`: RoofSurface/WallSurface/GroundSurface, and `fill_color`: a ninja-viewer-style default color per surface_type/cityobject_type, verified against cityjson-threejs-loader's own default palette — `get-fill-color` on `SolidPolygonLayer` reads it automatically when left unauthored, no color attribute required, and an authored `get-fill-color` still overrides it); derived properties (both modes) `roof_height` (area-weighted mean roof height above ground), `eaves_height`, `ridge_height`, `ground_height`, `roof_area`, `surface_count`, `lod`, `cityobject_id`, `cityobject_type`, `parent_id` win over same-named source attributes, plus surfaces-mode-only `polygon`/`outline`/`surface_type`/`fill_color` (`outline` is the face's outer ring flattened and closed — bind a companion `type="PathLayer" get-path="$outline"` layer to it for visible face edges, since surfaces mode is flat-shaded and `SolidPolygonLayer`'s own `wireframe` prop is a no-op when unextruded — always pair one, matching `filter-field`/`filter-range` to the fill layer), and a parent Building's attributes are inherited by its BuildingPart rows; national grids NL/CH/DE/JP/AT/SG reproject automatically including axis order, other EPSG codes fail with an error naming the code; highest LoD wins, pin one with `?om-lod=1.2` (combine as `?om-lod=1.2&om-surfaces=1`, cached independently); `.city.jsonl` fills in as it downloads in either mode — see docs/3d-assets.md), or Arrow IPC `.arrow`/`.feather` — large point datasets stay columnar, GeoArrow line/polygon geometry becomes GeoJSON features, zstd-compressed IPC is handled; other formats plug in via `OmMap.registerFormat({match, parse})`; data URLs accept any scheme the runtime's fetch supports — desktop webviews (Tauri, Electron) pass asset-protocol URLs straight in), inline `<script type="application/json">` (row arrays or column-oriented `{"columns": {"lon": [...], "lat": [...]}}`; must be a DIRECT child of the `<om-layer>`, and when present it wins — omit the `data` attribute), or `wss://` streaming (`key="mmsi"` upserts entities in place, `flush="250ms"` coalesces bursts, `source="name"` selects a `OmMap.registerSource` decoder plugin), or a polled REST snapshot (`refresh="5s"` re-fetches and replaces — for live endpoints that return the full current state). TILED layers: a `{z}/{x}/{y}` `data` template is deck's tile URL for `TileLayer`/`MVTLayer` (NOT rows) — passed through to deck verbatim, never fetched/parsed, so `<om-layer type="TileLayer" data="…/{z}/{x}/{y}.png">` works (raster gets a built-in BitmapLayer sublayer) and `type="MVTLayer" data="…/{z}/{x}/{y}.pbf"` self-renders vector tiles with `get-*` accessors applying to each decoded feature's properties; a tiled layer has no local rows so `ctx.data`/`ctx.stats`/`filter-*` don't apply. Authenticated endpoints: call `OmMap.configureData({ headers: {...} })` in a script — never put tokens in attributes. `$field` accessors work identically on all of them — never write column-index code yourself. One columnar restriction: the `js` full-JS opt-in is not allowed on Arrow/columnar layers (validation will tell you; use `$field` accessors instead). For 3D models use `type="ScenegraphLayer"` with `scenegraph="./model.glb"` (required) and `get-orientation="[0, $heading, 90]"` — the roll of 90 stands Y-up glTF models upright; see docs/3d-assets.md. GeoTIFF/COG rasters use `type="COGLayer"` with `src="./dem.tif"` (NOT `data` — rasters stream tiles by Range request, they are not parsed rows): `min`/`max` set the rescale window (default 0–255; ALWAYS set them for float/16-bit data like DEMs), `colormap` picks a bundled ramp for single-band sources (gray, viridis, plasma, inferno, magma, cividis, rdylgn, rdbu, spectral, terrain, jet, turbo), `nodata` overrides the source sentinel (renders transparent); plain 8-bit RGB COGs need no styling attributes; restretch/recolor are GPU uniforms (no refetch) and the legend ramp derives from colormap+min/max automatically. Sources must be Cloud-Optimized (`gdal_translate -of COG` otherwise).
|
|
31
|
+
- `<om-map center="[lng, lat]" zoom="11" pitch="55" bearing="20" basemap="positron">` — the root. Give it a height (`om-map { display:block; height:100vh }` with `html,body{height:100%}`, or a sized container) — a custom element is display:inline by default and collapses to zero size; with none set, the library falls back to display:block + a 400px floor so a bare map still shows (any height you DO set wins over the floor, including one below 400px), and a collapsed map logs a console warning. A map hidden on purpose (`hidden`, or inside a `display:none` panel) stays hidden and does not warn. `basemap` accepts a free preset (`liberty`, `bright`, `positron`, `dark-matter`, `voyager`, `osm` — no keys; or `maptiler-streets`/`maptiler-dataviz`/`maptiler-satellite` with `basemap-key="…"` or `OmMap.configureBasemap({ maptilerKey })`), `maplibre` (bare demo style), a style URL (e.g. a MapTiler-customized style; any scheme fetch supports, including desktop asset protocols like Tauri's, query strings fine), or `none` (standalone canvas). The attribute is LIVE — writing it switches the basemap in place (camera + layers survive); the `set-basemap {basemap}` action and `<om-widget type="basemap-switcher" options="positron dark-matter osm">` do the same. Register more via `OmMap.registerBasemap(name, { style })`. Required provider attribution renders automatically (`attribution="false"` opts out). `pitch`/`bearing` tilt the initial camera (use for 3D content). Terrain: `terrain="terrarium"` (keyless AWS DEM; also `mapterhorn` — keyless, CARTO Positron drape by default — `maptiler-terrain` with a key, a raw `{z}/{x}/{y}` DEM URL + `terrain-decoder="terrarium|mapbox-rgb|<JSON>"`, or `off`) raises a 3D surface — geographic layers DRAPE onto it automatically (per-layer `terrain="drape|offset|off"` overrides; 3D-model layers sit ON it), `terrain-exaggeration` scales relief, `terrain-texture` drapes imagery; terrain REPLACES an active basemap while on (restored when off); `set-terrain` action, `terrain` watch token, `OmMap.registerTerrain(name, {...})` for more sources. a georeferenced BIM model REQUIRES the map to author `terrain` explicitly — any value including an explicit `terrain="off"` (flat-ground siting); a model that resolves real elevation (IfcMapConversion + OrthogonalHeight) on a map with no `terrain` attribute raises an ERROR through the validation channel at load time (the library never writes attributes for you — no auto-terrain). Scene lighting for 3D content: `lighting="daylight|studio|flat|custom"` (absent = deck defaults) with `lighting-ambient`/`lighting-sun`/`lighting-sun-azimuth`/`lighting-sun-elevation`/`lighting-camera` overrides and `lighting-sun-date` (ISO/epoch — solar-position sun computed at the map center, wins over azimuth/elevation); attribute-backed (undoable, live-editable), story-steppable via the `set-lighting {lighting, sunAzimuth, ...}` action (`lighting="default"` removes the attribute set; a bare preset is a clean reset — stale overrides clear), native UI via `<om-widget type="lighting">` (preset radios + tuning sliders), widget watch token `lighting`. GeoJsonLayer extrudes polygons declaratively: `extruded get-elevation="$height"` (+ `wireframe`). `widgets-hidden` attribute (or the `set-widgets-visible {visible}` action / `<om-widget type="widgets-toggle">` button) hides every widget WITHOUT destroying state — attribution never hides (license); transient (not an undo step) but story-steppable, so a step can clear chrome for a cinematic take. Slots auto-dim while an open `<om-overlay>` popup covers them (position stability over the popup dodging; `widgets-dim="off"` disables). `validate` attribute enables live validation + on-page error panel. Include a `map-id="<random UUID>"` on complete pages (identifies the map artifact for usage telemetry — not a visitor id; the page author deletes it to opt out); `telemetry="off"` disables usage telemetry + library-error reporting for the map (global: `OmMap.configureTelemetry({ disabled: true })`; schema: docs/telemetry.md). Free plan limits (HOSTED http(s) pages only — a dev context (localhost, file://, any non-web scheme) lifts every cap while the attribution badge stays; the exemption is technical convenience, not a license grant — commercial deployment incl. packaged apps still requires a key): 5 layers, 25k rows per layer — layers past a limit don't render and validation says why; a `license-key="om_live_…"` attribute (publishable, origin-restricted) or `OmMap.configureLicense(key)` lifts limits and removes the corner badge. When a user's map exceeds free limits, say so and point at the validation message rather than silently restructuring their data. Events on the element: `om-map-ready` (boot complete; `await mapEl.ready` is the promise twin), `om-validation-error`, `om-view-changed` (camera settled after a move, debounced; `detail = {longitude, latitude, zoom, pitch, bearing, origin}` where `origin` is `"user"` for gesture bursts vs `"programmatic"` for API/story moves — the camera-persistence hook, and the echo-suppression signal when syncing camera state to an app store), `om-map-point` (`detail = {coordinate: [lng,lat]|null, kind}` — every click/hover map coordinate incl. empty-map clicks, for custom capture tools beyond the draw widget), `om-tileset-load` (`detail = {layerId, tileset}` — a Tile3DLayer's live deck Tileset3D, for tools needing the real tileset like region export). `MapController` mirrors these as `onViewChange`/`onMapPoint`/`onTilesetLoad` options. `await mapEl.snapshot()` returns a canvas-only PNG dataURL of the scene (basemap + layers at device pixels; `{as:"blob"}` for files) — DOM widgets/overlays/attribution are NOT captured, so exports must render provider credits themselves.
|
|
32
|
+
- `<om-layer id="..." type="ScatterplotLayer" data="./points.json">` — any deck.gl layer class by `type` (37 layer types total: 32 bundled deck.gl core/geo/aggregation/mesh layers, plus the native `COGLayer`/`ZarrLayer` raster types, `ImageOverlay` for georeferenced drone JPEGs, and `BIMLayer` for BIM source files), plus `PopupLayer` (WebGL badges/labels at scale: `layout="badge|pin-label|card"`, `min-zoom`/`max-zoom`). Every type's full attribute list ships in the package's `onlymapjs.html-data.json` — consult it instead of guessing attribute names or reading the minified dist. TextLayer's default font atlas covers ASCII only: set `character-set` when label text carries other glyphs (`—`, `·`, accents) or deck warns and renders them blank. External layer classes register via `OmMap.registerLayer({type, deckClass, props})` — build them on `@nika-js/onlymap/deck` (the bundled `CompositeLayer`/`TileLayer`/… re-exports), never a separately-installed deck.gl (different class hierarchy, breaks in the renderer); function-valued props ride the subclass's `static defaultProps`; register at module top level BEFORE the manifest mounts (see docs/custom-layers.md). Data: `data` URL (JSON, GeoJSON, CSV/TSV `.csv` — parsed to typed columns — Shapefile `.shp` (+`.dbf` attributes) and KML `.kml` as GeoJSON features, GPX `.gpx` (waypoints/tracks/routes → features tagged `_gpxKind`; a `#waypoints`/`#tracks`/`#routes` URL fragment selects one part), FlatGeobuf `.fgb` (cloud-native binary vector, whole-file decode), GeoParquet `.parquet`/`.geoparquet` (cloud-native columnar vector — all-Point files stay columnar like Arrow, lines/polygons become GeoJSON features; requires the file's `geo` metadata with WKB geometry, and CRS84/EPSG:4326 — a projected CRS is a loud error telling you to reproject, not a silent misplacement; snappy/gzip/zstd row-group compression handled), CityJSON `.city.json` / CityJSONSeq `.city.jsonl` (semantic 3D city models — 3DBAG, PLATEAU — decoded to one of two shapes by the `data` URL, no CityJSON layer type: default → extruded footprints, `type="GeoJsonLayer" extruded get-elevation="$roof_height"`; `?om-surfaces=1` → one row PER FACE at its own real per-vertex height so a pitched LoD2.2 roof actually looks pitched, `type="SolidPolygonLayer" get-polygon="$polygon" full3d` (`extruded` stays at its ordinary `false` default) (flat-shaded — deck.gl only lights the `extruded` shader path — each row also carrying `surface_type`: RoofSurface/WallSurface/GroundSurface, and `fill_color`: a ninja-viewer-style default color per surface_type/cityobject_type, verified against cityjson-threejs-loader's own default palette — `get-fill-color` on `SolidPolygonLayer` reads it automatically when left unauthored, no color attribute required, and an authored `get-fill-color` still overrides it); derived properties (both modes) `roof_height` (area-weighted mean roof height above ground), `eaves_height`, `ridge_height`, `ground_height`, `roof_area`, `surface_count`, `lod`, `cityobject_id`, `cityobject_type`, `parent_id` win over same-named source attributes, plus surfaces-mode-only `polygon`/`outline`/`surface_type`/`fill_color` (`outline` is the face's outer ring flattened and closed — bind a companion `type="PathLayer" get-path="$outline"` layer to it for visible face edges, since surfaces mode is flat-shaded and `SolidPolygonLayer`'s own `wireframe` prop is a no-op when unextruded — always pair one, matching `filter-field`/`filter-range` to the fill layer), and a parent Building's attributes are inherited by its BuildingPart rows; national grids NL/CH/DE/JP/AT/SG reproject automatically including axis order, other EPSG codes fail with an error naming the code; highest LoD wins, pin one with `?om-lod=1.2` (combine as `?om-lod=1.2&om-surfaces=1`, cached independently); `.city.jsonl` fills in as it downloads in either mode — see docs/3d-assets.md), or Arrow IPC `.arrow`/`.feather` — large point datasets stay columnar, GeoArrow line/polygon geometry becomes GeoJSON features, zstd-compressed IPC is handled; other formats plug in via `OmMap.registerFormat({match, parse})`; data URLs accept any scheme the runtime's fetch supports — desktop webviews (Tauri, Electron) pass asset-protocol URLs straight in), inline `<script type="application/json">` (row arrays or column-oriented `{"columns": {"lon": [...], "lat": [...]}}`; must be a DIRECT child of the `<om-layer>`, and when present it wins — omit the `data` attribute), or `wss://` streaming (`key="mmsi"` upserts entities in place, `flush="250ms"` coalesces bursts, `source="name"` selects a `OmMap.registerSource` decoder plugin), or a polled REST snapshot (`refresh="5s"` re-fetches and replaces — for live endpoints that return the full current state). TILED layers: a `{z}/{x}/{y}` `data` template is deck's tile URL for `TileLayer`/`MVTLayer` (NOT rows) — passed through to deck verbatim, never fetched/parsed, so `<om-layer type="TileLayer" data="…/{z}/{x}/{y}.png">` works (raster gets a built-in BitmapLayer sublayer) and `type="MVTLayer" data="…/{z}/{x}/{y}.pbf"` self-renders vector tiles with `get-*` accessors applying to each decoded feature's properties; a tiled layer has no local rows so `ctx.data`/`ctx.stats`/`filter-*` don't apply. Authenticated endpoints: call `OmMap.configureData({ headers: {...} })` in a script — never put tokens in attributes. `$field` accessors work identically on all of them — never write column-index code yourself. One columnar restriction: the `js` full-JS opt-in is not allowed on Arrow/columnar layers (validation will tell you; use `$field` accessors instead). For 3D models use `type="ScenegraphLayer"` with `scenegraph="./model.glb"` (required) and `get-orientation="[0, $heading, 90]"` — the roll of 90 stands Y-up glTF models upright; see docs/3d-assets.md. 3D Tiles use `type="Tile3DLayer"` with `tileset="…/tileset.json"` (NOT `data`). For BIM/photogrammetry, `pickable` alone picks a whole TILE — add `pick-features` to pick an individual ELEMENT (a wall, a window, one IFC product), and the `selection` then carries that element's `featureId`/`properties`/`class` from the tile's own `EXT_mesh_features` + `EXT_structural_metadata` (`feature-id-property` selects the ID set, default `_FEATURE_ID_0`). `feature-styles` recolours/fades/highlights by feature ID — an array indexed BY id of `{color: [r,g,b], strength: 0-1, opacity: 0-1}`, live-settable, uploaded as a small lookup texture (no refetch). Use `strength` below 1 to tint rather than replace, or the model's own texture is hidden. Isolate/hide/ghost are DECLARATIVE and mirror the vector `filter-field`/`filter-categories` pair — `feature-filter-field="component"` names the metadata field, then `isolate-features='["Clock"]'` / `hide-features='["Windows","Skylight"]'` / `ghost-features='["Wall"]'` take JSON value lists matched against the tile's property table (`ghost-opacity` tunes the fade, default 0.18). `isolate-features` is EXCLUSIVE (anything unlisted is hidden); hiding is a shader discard so a hidden element also stops being pickable and whatever is behind it becomes selectable. They compose ONTO `feature-styles` (style table supplies colour, these supply visibility), and being attributes they are undoable and story-steppable — prefer them over computing a style table in page JS. Multi-material/multi-primitive models fan out correctly (glTF allows one material per primitive, so real IFC exports are usually many primitives); only genuinely instanced i3dm tiles stay tile-granularity. Limits to state rather than discover: texture-backed IDs — how photogrammetry classification ships — require `load-options='{"gltf":{"loadBuffers":true,"loadImages":true},"image":{"type":"data"}}'`, and omitting `image.type` makes the tileset take MINUTES to appear (loaders.gl otherwise reads the whole ID texture back through a canvas once per vertex); and `opacity` below 1 currently blanks the model. BIM WIDGETS (all need a `pick-features` layer): `<om-widget type="ifc-browser" layer="clinic" fields="ifcClass material container spatialPath" scale-fields="netVolume" rows="7">` groups the model by a property-table field, counts each value, and gives every row I/H/G buttons that WRITE `isolate-features`/`hide-features`/`ghost-features` (so it is a UI over the attributes — undoable and story-steppable; I is MULTI-SELECT, isolating the union of every pressed row or tree node, since `isolate-features` is a list and the layer matches it as a set) while keeping the companion outline layer's `filter-categories` in step; that same select also offers whichever TREES the file supports — Spatial (`spatialPath`), Type (`typePath`), System (`systemPath`) and Classification (`classificationPath`, built by walking `ReferencedSource`) — each expandable with counts aggregated upward and the same I/H/G on every node, so isolating a storey or a system or a CCS code reaches every element under it. Spatial is NOT privileged (on a real Danish project the classification tree covered 3,415 elements to spatial's 660). A tree is not a separate mode, just a group-by on a hierarchy column — LIST it in `fields` to offer it, or set `field="spatialPath"` to open on it — and `loadIfc` emits a hierarchy column ONLY when the file populates it, so a tree that would render empty is never offered; trees appear automatically when available (no need to list them in `fields`) and `field="spatialPath"` opens straight onto one. ONE browser per layer: `feature-filter-field` and the isolate/hide/ghost attributes are single-valued, so two instances on one layer clobber each other. The widget was renamed from `ifc-legend` (still registered as a deprecated alias) because it is a model browser, not a legend. non-physical classes (IfcSpace, IfcOpeningElement) are hidden unless `show-non-physical`, and `no-color` removes the colour select. `<om-widget type="feature-inspector" fields="ifcClass material container netVolume">` (renamed from `ifc-inspector`, kept as an alias) shows the picked element's properties. `<om-widget type="ifc-loader" layer="ifc" federate>` is a drop zone that parses `.ifc` files IN THE BROWSER and builds both layers itself; `federate` accepts SEVERAL models into one co-registered scene (one drop zone, one layer per model, each with a visibility toggle) rather than one widget per discipline, matching how coordination tools append models; a model that turns out to be georeferenced is AUTO-PLACED (its own coordinates/heading/scale written onto the layers the WIDGET created, camera flown there) — but the widget NEVER writes `<om-map>`'s own scene attributes (`basemap`, `terrain`): those are author-owned, and a georeferenced model on a map with neither raises a structured "no spatial context" warning instead of switching one on. COLOUR BY PROPERTY instead of hand-computing a `feature-styles` table: `feature-color-by` (categorical, `feature-palette` overrides the built-in cycle) or `feature-color-scale` (graduated over a numeric field), with `feature-color-strength` (default 0.85) controlling how hard the colour mixes over the model's own material. Setting NEITHER is the default and is meaningful — the model renders in its own IFC surface colours. A graduated ramp needs the field populated: Revit IFC2x3 exports often carry no `IfcElementQuantity`, so every `netVolume` is 0 and the ramp is flat. Widget scripts read the decoded property table with `ctx.features(layerId)` (undefined until the first tile carrying one lands) and re-render on the `features` watch token. `<om-widget type="ifc-clash" layers="arch mep" tolerance="0">` is the CLASH OVERLAY over two co-registered model layers: it flags element pairs whose bounding boxes interpenetrate, colours both sides via `feature-styles`, and flies to the centre of each overlap. v1 is an axis-aligned box test — fast and serverless, but it over-reports anything diagonal and says nothing about which clashes matter; zones/spaces/openings/proxies and same-class-same-name pairs are excluded as noise. The header carries an overlay on/off switch; an isolation mode select (None/Dim/Hide) sits above the results list and applies once a row is selected (with nothing selected, nothing is hidden), and clicking a row FOCUSES that clash (chosen pair at full strength, every other clashing element dropped to a faint tint, camera flown to the overlap centre) — without that, everything is highlighted and nothing is. Results are GROUPED by the side-A element with a count (one wall crossing four ducts is one row), and the two model selects appear only when more than two models are loaded. It CHECKS co-registration (matching `site-origin`) and says so when it fails, because two mis-registered models report zero clashes exactly like two clean ones. Persisting/sharing results is BCF's job and is out of scope. `<om-layer type="BIMLayer" src="./model.ifc">` is the declarative counterpart to `ifc-loader`/`loadIfc`: point it at a BIM source file (an .ifc today) and it runs the loader itself the moment src resolves — no pre-baked tileset, no site-origin/site-heading/site-scale (the file's own georeference is read and applied automatically; not wired up yet: an authored site-origin on a BIMLayer does not override it), and no separate PathLayer for the outline overlay (added automatically). `pick-features` defaults ON (unlike a plain Tile3DLayer); feature-filter-field/feature-styles/isolate-hide-ghost/feature-color-by/ghost-opacity all work unchanged, since BIMLayer forwards them to a real Tile3DLayer it builds internally. Known gap: the outline overlay does not yet follow isolate/hide/ghost the way the mesh does. Reach for BIMLayer when the model is fixed and known ahead of time; reach for ifc-loader when a visitor picks the file or several models need to federate. IN-BROWSER IFC: `loadIfc(bytes)` parses an `.ifc` with web-ifc (WASM, MPL-2.0, CDN-fetched on first use — NOT a package dependency; `configureIfc({wasmPath})` self-hosts) and returns `{tilesetUrl, edgesUrl, loadOptions, features (rows carry `ifcClass`/`name`/`material`/`container`/`netVolume`/`spatialPath`/`typePath`/`systemPath`/`classificationPath` — hierarchy columns joined by U+001F that the model trees navigate, each emitted only when the file populates it), lonLat, georeferenced, heading, scale, originSource, headingSource, stats, timings, bounds, revoke()}`; the output IS a tileset so picking/styling/`site-*`/isolate-hide-ghost work unchanged. CALL `revoke()` when swapping models — blob URLs are held by the document. FEDERATION: pass the first model's returned `origin` as `LoadIfcOptions.origin` for every later model of the same building, or each is centred on its own bounding box and they drift apart — a clash pass then finds nothing, which looks identical to a clean model. `ifc-loader` shares one origin AND one placement (`site-origin`/`site-heading`/`site-scale`) per map automatically — discipline files routinely declare IfcSite coordinates kilometres apart for the same building, so the first model loaded decides where it goes and the rest follow (`independent` opts out). Every element also carries a tile-local bounding box in the property table (`bboxMinE`/`bboxMinN`/`bboxMinU`/`bboxMaxE`/`bboxMaxN`/`bboxMaxU`). POSITION is read from the file preferring the trustworthy route: `IfcMapConversion` (a surveyed placement into a named projected CRS) WINS over `IfcSite.RefLatitude`/`RefLongitude`, which is very often an authoring default; `originSource` reports which was used, and anything other than `"map-conversion"` raises a structured `"warning"` through the same validation channel other `om-layer` errors use (the on-page panel with `validate` set, `om-validation-error`'s `detail.warnings`) for both `BIMLayer` and `ifc-loader`, once per layer — it never flips `valid` false, only flags the position may be off by tens of metres with no rotation correction applied; override with `site-origin`/`site-heading` or a proper `IfcMapConversion`. Un-projecting a map conversion is supported for WGS84 UTM zones (EPSG:326xx/327xx) and DECLINED with a warning for anything else — a guessed projection lands the model in another country while looking plausible. Eastings/northings are in the target CRS unit, frequently MILLIMETRES. Reading a file correctly and the model being somewhere sensible are SEPARATE problems: all three prepared samples declare placeholders (clinic on Revit's Boston default, which is a 1630 graveyard; duplex on a Chicago city-centre point; bridge — which does carry a real IfcMapConversion — into the mid-Pacific). `headingSource` distinguishes "map-conversion"/"true-north" (read) from "assumed" (file was silent) — report the assumption, never let it read as a measurement. `<om-map>` reads camera attributes ONCE at init, so `setAttribute("center", …)` after mount moves nothing: use `map.flyTo(lonLat, zoom)`. GEOREFERENCING is declarative on `Tile3DLayer` and `PathLayer`: `site-origin="[lng, lat]"` (or `[lng, lat, elevation]`) OVERRIDES the position baked into a tileset's root transform, `site-heading` is a bearing in degrees CLOCKWISE from true north (on its own it rotates the model where it stands), `site-scale` is a uniform multiplier; rotation and scale pivot on the model's own anchor, not the tileset origin. An IFC model is a PAIR of layers — the mesh tileset plus a `PathLayer` outline overlay whose paths are local east/north/up METRES — and both need the same three values or the building separates from its own edges. Never trust a model's declared position without looking at it: authoring tools ship a default project location that is indistinguishable from a survey (the buildingSMART Medical-Dental Clinic sample carries Revit's Boston default, the Duplex a Chicago city-centre point, so both land on occupied downtown blocks at an arbitrary rotation), `IfcMapConversion` is absent from most IFC2x3 exports, and `TrueNorth` is routinely unset. Editing `site-*` on a live Tile3DLayer reloads the tileset (deck.gl only reloads on a URL change); the PathLayer updates as a uniform. GeoTIFF/COG rasters use `type="COGLayer"` with `src="./dem.tif"` (NOT `data` — rasters stream tiles by Range request, they are not parsed rows): `min`/`max` set the rescale window (default 0–255; ALWAYS set them for float/16-bit data like DEMs), `colormap` picks a bundled ramp for single-band sources (gray, viridis, plasma, inferno, magma, cividis, rdylgn, rdbu, spectral, terrain, jet, turbo), `nodata` overrides the source sentinel (renders transparent); plain 8-bit RGB COGs need no styling attributes; restretch/recolor are GPU uniforms (no refetch) and the legend ramp derives from colormap+min/max automatically. Sources must be Cloud-Optimized (`gdal_translate -of COG` otherwise).
|
|
33
33
|
- Geotagged drone JPEGs are the library-owned `ImageOverlay` type, not row `data` and not a raw `BitmapLayer`: `<om-layer type="ImageOverlay" src="./photo.jpg" georeference="exif">`. It reads GPS/relative altitude/camera/focal length plus DJI gimbal metadata through the configured fetch policy, waits before `ready`, bakes yaw/roll, and computes visualization-grade flat-ground bounds. Unknown cameras need `sensor-width-mm` + `sensor-height-mm` (and `focal-length-mm` when EXIF lacks it). For collaborative/saved maps call `OmMap.resolveImageOverlay(fileOrUrl)`, upload its returned `image`, then reconstruct using `src` + the returned explicit `bounds` (no EXIF fetch). Use `COGLayer` for large orthomosaics; see docs/image-overlays.md.
|
|
34
34
|
- Chunked N-dimensional Zarr / GeoZarr rasters (climate/weather grids, datacubes) are the library-owned `ZarrLayer` type (built on `@developmentseed/deck.gl-zarr` + zarrita, a lazy chunk): `<om-layer type="ZarrLayer" src="./x.zarr" variable="temp" select="time=0" colormap="viridis" min="…" max="…">`. `src` not `data` (chunks stream through the layer's reader, never parsed rows). Pick the `variable` and pin EVERY non-spatial dimension in `select` ("init_time=0, lead_time=0, ensemble_member=0"); the two spatial dims are handled for you (a 2-D array needs no select). A GeoZarr-compliant store georeferences itself; a plain Zarr needs manual `bounds="[w,s,e,n]"` + `crs="EPSG:4326"` + `spatial-dims="<yName> <xName>"` (bounds without crs+spatial-dims is a validation error). `min`/`max`/`colormap`/`nodata` and the auto legend reuse the exact COGLayer raster pipeline. Beware store chunking: a dataset chunked coarsely over non-spatial dims (e.g. all forecast steps in one chunk) decodes far more than the pinned frame needs. `src` may be any absolute URL (`https://…/store.zarr`) — an external/remote store works with no server setup (a static host serves Zarr's extensionless chunk keys natively), but zarrita fetches it directly from the browser so the store MUST send CORS headers (`Access-Control-Allow-Origin`), and it must be PUBLIC — authenticated stores are not yet supported (ZarrLayer uses zarrita's own fetch, not `OmMap.configureData`).
|
|
35
35
|
- `<om-widget type="legend|layer-switcher|basemap-switcher|lighting|zoom-controls|undo-redo|scale-bar|attribution|filter|vega-lite|measure" position="bottom-end">` — static UI panels. `position` takes one of 8 managed slots (logical, RTL-aware: `top-start|top-center|top-end|center-start|center-end|bottom-start|bottom-center|bottom-end`; legacy corners `top-left` etc. alias) — same-slot widgets stack with flush edges and a shared gap (never overlap); provider attribution joins `bottom-end` and the license badge joins `bottom-start` as in-flow members, so required chrome never covers a widget; `order="1"` orders within a slot; adjacent compact button widgets (zoom-controls, undo-redo, widgets-toggle) auto-merge into ONE control group with dividers (`cluster="false"` opts a widget out); `position="manual"` renders a plain block you place with your own CSS (even outside the map). At map widths ≤640px managed widgets auto-fold into one accessible drawer per map side; `fold="never"` exempts an essential widget, `widgets-fold="off"` opts the map out, `--om-widget-fold-breakpoint` changes the threshold. Layout tokens on `<om-map>`: `widget-style="gap:10 opacity:0.9 inset:16"` (keys inset/gap/inset-x/-y/gap-x/-y/opacity/radius/size, px except opacity) or the `--om-widget-inset-x/-y/-gap-x/-gap-y/-opacity/-radius/-fold-breakpoint` custom properties. Built-ins are themeable from page CSS via custom properties (they inherit through the shadow root): `om-map { --om-widget-bg: #111827; --om-widget-fg: #f9fafb; }` — full set: `--om-widget-bg/-fg/-muted/-border/-hover-bg/-accent`; scope to a single widget with an `om-widget[type=legend]` selector instead. `measure` is a geodesic ruler: `modes="distance area"` (default both), `units="metric|imperial|nautical"` — click the map to place points, live per-segment + total labels render on the map, and it dispatches an `om-measure` event (`detail = {mode, units, totalMeters, segments, areaMeters2, perimeterMeters, poleWarning}`); it reuses the draw capture stack (measure/draw mutually exclusive) and its geometry is ephemeral (never saved, not an undo step). `scale-bar` now takes the same `units`. No `type` + HTML + `<script type="om/widget">` = custom widget with `ctx` (`ctx.layers`, `ctx.data(id)`, `ctx.dataInViewport(id)`, `ctx.stats(id, field)`, `ctx.viewport`, `ctx.selection`, `ctx.emit(action, payload)`), `this.watch = ['data:<layerId>', 'viewport', 'selection', 'layers', 'history', 'basemap']` (`layers` also fires on visibility/filter changes; `basemap` on basemap switches; `history` on undo/redo availability), `this.$(sel)`, `vegaEmbed`/`d3` as globals.
|
|
36
36
|
- Custom-widget event emission (the #1 custom-widget bug — a widget that renders but does nothing): a widget DRIVES the map ONLY by EMITTING a registered action; it never mutates the map or dispatches its own `CustomEvent`. Two ways: (1) declarative `data-emit="<action>"` + `data-*` payload keys on an element (fires on click, or change for form controls whose `.value` is auto-added; `data-*` values are STRINGS — use `ctx.emit` for numeric/array payloads like a slider's range); (2) `ctx.emit(action, payload)` for typed payloads, wired INSIDE `render` (so `ctx` is in scope) by assigning `.oninput`/`.onclick` — e.g. a day slider: `this.render = (ctx) => { this.$("#day").oninput = e => ctx.emit("filter-layer", { layer: "quakes", field: "day", range: [+e.target.value, +e.target.value] }); }`. Actions + payloads: `filter-layer {layer, field?, range:[min,max]}`, `toggle-layer {layer, visible?}`, `fly-to {center:[lng,lat], zoom?, duration?}`, `zoom-to-feature {layer, featureId}`, `set-basemap {basemap}`, `highlight-feature {layer, featureId}`, `show-overlay`/`hide-overlay {target}`, `story-play`/`story-pause`/`story-seek {story, t?}`, `undo`/`redo`, `zoom-in`/`zoom-out`, `set-widgets-visible {visible}`; register more with `OmMap.registerAction(name, handler)`. NEVER inline `onclick=`/`oninput=` — `ctx` isn't a global and CSP blocks them, so it silently fires nothing (validation errors on it). For a plain value/time slider prefer the built-in `<om-widget type="filter" layer=… field=…>` — it wires `filter-layer` for you; hand-author only for bespoke UI.
|
|
37
|
-
- `<om-overlay id="..." anchor-from="selection">` — rich geo-anchored HTML (≤ ~20 per map). Anchors: `anchor="[lng, lat]"` (static), `anchor-from="selection"` (follows picks), or `anchor-layer="regions" anchor-feature-id="mission"` (anchored to a feature's own geometry — bbox center — no coordinates in markup; `{{field}}` interpolates that feature's attributes). `{{field}}` interpolates the picked feature HTML-escaped; `{{{field}}}` is raw (avoid). For labels on many features use `PopupLayer`, not overlays.
|
|
37
|
+
- `<om-overlay id="..." anchor-from="selection">` — rich geo-anchored HTML (≤ ~20 per map). Anchors: `anchor="[lng, lat]"` (static), `anchor-from="selection"` (follows picks), or `anchor-layer="regions" anchor-feature-id="mission"` (anchored to a feature's own geometry — bbox center — no coordinates in markup; `{{field}}` interpolates that feature's attributes). Selection-anchored overlays scope with `layer="…"` (one layer's picks only) and `selection-type="click"|"hover"` (one pick type only) — a click-opened popup should ALWAYS set `selection-type="click"`, else merely hovering any pickable feature drags it there and re-templates it against the hovered object; with it, hover is inert and a click on empty space still dismisses. `{{field}}` interpolates the picked feature HTML-escaped; `{{{field}}}` is raw (avoid). For labels on many features use `PopupLayer`, not overlays.
|
|
38
38
|
- `<om-behavior on="click|hover|drag|load|data-loaded" layer="..." action="...">` — declarative interaction. Built-in actions: `show-overlay`, `hide-overlay`, `show-tooltip`, `hide-tooltip`, `toggle-layer`, `filter-layer`, `highlight-feature`, `zoom-to-feature`, `set-basemap`, `undo`, `redo`. One payload contract everywhere: `{ layer, target, feature, featureId, coordinate }`.
|
|
39
39
|
- Undo/redo is built in: user-facing manifest changes (layer toggles, filter changes, basemap switches, element add/remove, drawn sketches) are recorded automatically — the manifest is the state. `<om-widget type="undo-redo">` renders the buttons; Cmd/Ctrl-Z, Shift-Cmd/Ctrl-Z, and Ctrl-Y work on any map (text inputs keep their native undo). Camera moves, hover effects, and story playback are deliberately NOT undo steps. Widget scripts: `ctx.history.canUndo/canRedo` with watch token `history`; `ctx.emit("undo")`/`ctx.emit("redo")`.
|
|
40
40
|
- `<om-fallback>` — static no-JS fallback, direct child of `<om-map>` (one per map, no attributes, plain HTML content — links allowed). Shown ONLY where scripts never run (chat-app/email file previews — iOS QuickLook renders HTML attachments with JS off — file managers, sandboxed webviews); hidden automatically once the map boots. GOOD PRACTICE: include one on every complete page, especially pages that may be shared as a file ("This interactive map requires JavaScript — open this file in a web browser", plus a hosted-version link when one exists). Without one, the stylesheet shows a generic text-only banner. The gate is pure CSS (`om-map:not(:defined)` in onlymapjs.css), so the CSS must load without JS — a real `<link rel="stylesheet">` or inlined `<style>` on no-build pages; a bundler-emitted stylesheet is fine in npm projects.
|