@nika-js/onlymap 0.7.6 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +3 -1
  3. package/dist/{LercDecode.es-sRuTJq5Y.js → LercDecode.es--DH6_OFE.js} +1 -1
  4. package/dist/{basemap-BCGMmc2F.js → basemap-2ZZ_9gFn.js} +528 -502
  5. package/dist/basemap.d.ts +15 -0
  6. package/dist/cartograph/api.d.ts +45 -0
  7. package/dist/cartograph/atlas.d.ts +50 -0
  8. package/dist/cartograph/core-loader.d.ts +35 -0
  9. package/dist/cartograph/cvd.d.ts +106 -0
  10. package/dist/cartograph/element-base.d.ts +26 -0
  11. package/dist/cartograph/elements/om-atlas.d.ts +47 -0
  12. package/dist/cartograph/elements/om-cartograph.d.ts +76 -0
  13. package/dist/cartograph/elements/om-frame.d.ts +111 -0
  14. package/dist/cartograph/elements/om-graticule.d.ts +24 -0
  15. package/dist/cartograph/elements/om-image.d.ts +12 -0
  16. package/dist/cartograph/elements/om-legend.d.ts +16 -0
  17. package/dist/cartograph/elements/om-north.d.ts +11 -0
  18. package/dist/cartograph/elements/om-scalebar.d.ts +13 -0
  19. package/dist/cartograph/elements/om-shape.d.ts +16 -0
  20. package/dist/cartograph/elements/om-text.d.ts +9 -0
  21. package/dist/cartograph/georef.d.ts +126 -0
  22. package/dist/cartograph/graticule.d.ts +70 -0
  23. package/dist/cartograph/html-data.d.ts +27 -0
  24. package/dist/cartograph/index.d.ts +1 -0
  25. package/dist/cartograph/layout.d.ts +23 -0
  26. package/dist/cartograph/legend.d.ts +75 -0
  27. package/dist/cartograph/live-frame.d.ts +69 -0
  28. package/dist/cartograph/paint.d.ts +54 -0
  29. package/dist/cartograph/refs.d.ts +33 -0
  30. package/dist/cartograph/render.d.ts +64 -0
  31. package/dist/cartograph/scalebar.d.ts +91 -0
  32. package/dist/cartograph/schema.d.ts +38 -0
  33. package/dist/cartograph/standalone.d.ts +1 -0
  34. package/dist/cartograph/textlayout.d.ts +45 -0
  35. package/dist/cartograph/tokens.d.ts +45 -0
  36. package/dist/cartograph/url-actions.d.ts +26 -0
  37. package/dist/cartograph/validate.d.ts +38 -0
  38. package/dist/cartograph/zip.d.ts +22 -0
  39. package/dist/cartograph.css +1 -0
  40. package/dist/cartograph.js +2768 -0
  41. package/dist/cartograph.standalone.js +7640 -0
  42. package/dist/crs-DsDQ4Q4i.js +71 -0
  43. package/dist/download.d.ts +6 -0
  44. package/dist/feature-access.d.ts +14 -0
  45. package/dist/field-access.d.ts +1 -10
  46. package/dist/{geoparquet-CK9IjEjH.js → geoparquet-DDZzee5Z.js} +1 -1
  47. package/dist/{index-CNRYpb0x.js → index-BGOblzWN.js} +5134 -5070
  48. package/dist/index-CcLC9jE5.js +4798 -0
  49. package/dist/{index-CIEJyv5x.js → index-CeoVzd6t.js} +1 -1
  50. package/dist/{index-CW3bdfHB.js → index-Cz1VsWzO.js} +2 -2
  51. package/dist/{index-CN_tn36D.js → index-DkxFFflv.js} +1 -1
  52. package/dist/{index-C1ZQGJxa.js → index-DphcuoPv.js} +1 -1
  53. package/dist/index.d.ts +1 -1
  54. package/dist/{lerc-Bb2JZ92v.js → lerc-D7TfTy6l.js} +2 -2
  55. package/dist/onlymap.standalone.js +8820 -8730
  56. package/dist/onlymapjs.js +12 -11
  57. package/dist/{raster-DpYssEe7.js → raster-D_3YKFZz.js} +3 -3
  58. package/dist/{raster-pipeline-BVCB-0yq.js → raster-pipeline-D1yeoJo2.js} +1 -1
  59. package/dist/runtime-core.d.ts +30 -0
  60. package/dist/snapshot.d.ts +17 -0
  61. package/dist/units.d.ts +9 -0
  62. package/dist/version.d.ts +1 -1
  63. package/dist/{zarr-DNb4iqbL.js → zarr-DH5Ntt5U.js} +2 -2
  64. package/docs/cartograph.md +396 -0
  65. package/llms.txt +1 -0
  66. package/onlymapjs.html-data.json +974 -0
  67. package/package.json +13 -2
  68. package/skills/onlymapjs/SKILL.md +2 -2
  69. package/skills/onlymapjs/references/syntax.md +26 -2
@@ -0,0 +1,2768 @@
1
+ let $t = null, Z = null;
2
+ function ye(r) {
3
+ $t = r;
4
+ }
5
+ function Ze() {
6
+ return B !== null;
7
+ }
8
+ let B = null;
9
+ function H() {
10
+ return B;
11
+ }
12
+ function we() {
13
+ return B ? Promise.resolve(B) : Z || ($t ? (Z = Promise.resolve($t()).then((r) => (B = r, B)).catch((r) => {
14
+ throw Z = null, r;
15
+ }), Z) : Promise.reject(
16
+ new Error(
17
+ '[onlymapjs/cartograph] No map runtime loader is configured. Import the cartograph entry ("@nika-js/onlymap/cartograph" or its standalone build) rather than a submodule — the entry installs it.'
18
+ )
19
+ ));
20
+ }
21
+ const Je = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
22
+ __proto__: null,
23
+ isCoreLoaded: Ze,
24
+ loadCore: we,
25
+ peekCore: H,
26
+ setCoreLoader: ye
27
+ }, Symbol.toStringTag, { value: "Module" }));
28
+ function _t(r) {
29
+ return { scale: r / 96, pxPerMm: r / 25.4 };
30
+ }
31
+ function ft(r) {
32
+ return typeof r?.paintCartograph == "function";
33
+ }
34
+ const q = (r, t) => {
35
+ const e = r.getAttribute(t);
36
+ if (e == null || e.trim() === "") return null;
37
+ const n = Number(e);
38
+ return Number.isFinite(n) ? n : null;
39
+ };
40
+ function C(r) {
41
+ return {
42
+ x: q(r, "x") ?? 0,
43
+ y: q(r, "y") ?? 0,
44
+ w: q(r, "w") ?? 0,
45
+ h: q(r, "h") ?? 0,
46
+ rotation: q(r, "rotation") ?? 0,
47
+ z: q(r, "z")
48
+ };
49
+ }
50
+ function kt(r, t = 0) {
51
+ if (!r) return t;
52
+ const e = Number.parseFloat(r);
53
+ return Number.isFinite(e) ? e : t;
54
+ }
55
+ function L(r) {
56
+ if (!r) return null;
57
+ const t = r.trim().toLowerCase();
58
+ return t === "transparent" || t === "rgba(0, 0, 0, 0)" || t === "none" ? null : r;
59
+ }
60
+ const W = 96 / 25.4, Ot = {
61
+ A0: { widthMm: 841, heightMm: 1189 },
62
+ A1: { widthMm: 594, heightMm: 841 },
63
+ A2: { widthMm: 420, heightMm: 594 },
64
+ A3: { widthMm: 297, heightMm: 420 },
65
+ A4: { widthMm: 210, heightMm: 297 },
66
+ A5: { widthMm: 148, heightMm: 210 },
67
+ Letter: { widthMm: 215.9, heightMm: 279.4 },
68
+ Legal: { widthMm: 215.9, heightMm: 355.6 }
69
+ }, Ut = "A4";
70
+ function ct(r, t, e, n) {
71
+ const i = t != null ? Number(t) : NaN, s = e != null ? Number(e) : NaN;
72
+ if (Number.isFinite(i) && i > 0 && Number.isFinite(s) && s > 0) return { widthMm: i, heightMm: s };
73
+ const o = Ot[r ?? Ut] ?? Ot[Ut];
74
+ return n === "landscape" ? { widthMm: o.heightMm, heightMm: o.widthMm } : { ...o };
75
+ }
76
+ let tt = null;
77
+ function Qe(r) {
78
+ if (!tt) {
79
+ tt = new Uint32Array(256);
80
+ for (let e = 0; e < 256; e++) {
81
+ let n = e;
82
+ for (let i = 0; i < 8; i++) n = n & 1 ? 3988292384 ^ n >>> 1 : n >>> 1;
83
+ tt[e] = n >>> 0;
84
+ }
85
+ }
86
+ let t = 4294967295;
87
+ for (let e = 0; e < r.length; e++) t = tt[(t ^ r[e]) & 255] ^ t >>> 8;
88
+ return (t ^ 4294967295) >>> 0;
89
+ }
90
+ function tr(r) {
91
+ const t = Math.max(1980, r.getFullYear());
92
+ return {
93
+ time: r.getHours() << 11 | r.getMinutes() << 5 | Math.floor(r.getSeconds() / 2),
94
+ date: t - 1980 << 9 | r.getMonth() + 1 << 5 | r.getDate()
95
+ };
96
+ }
97
+ async function er(r, t = /* @__PURE__ */ new Date()) {
98
+ const e = new TextEncoder(), { time: n, date: i } = tr(t), s = [], o = [];
99
+ let a = 0;
100
+ for (const u of r) {
101
+ const m = u.data instanceof Blob ? new Uint8Array(await u.data.arrayBuffer()) : u.data, d = e.encode(u.name), h = Qe(m), f = new DataView(new ArrayBuffer(30));
102
+ f.setUint32(0, 67324752, !0), f.setUint16(4, 20, !0), f.setUint16(6, 2048, !0), f.setUint16(8, 0, !0), f.setUint16(10, n, !0), f.setUint16(12, i, !0), f.setUint32(14, h, !0), f.setUint32(18, m.length, !0), f.setUint32(22, m.length, !0), f.setUint16(26, d.length, !0), f.setUint16(28, 0, !0), s.push(new Uint8Array(f.buffer), d, m), o.push({ name: u.name, bytes: m, crc: h, offset: a }), a += 30 + d.length + m.length;
103
+ }
104
+ const l = a;
105
+ for (const u of o) {
106
+ const m = e.encode(u.name), d = new DataView(new ArrayBuffer(46));
107
+ d.setUint32(0, 33639248, !0), d.setUint16(4, 20, !0), d.setUint16(6, 20, !0), d.setUint16(8, 2048, !0), d.setUint16(10, 0, !0), d.setUint16(12, n, !0), d.setUint16(14, i, !0), d.setUint32(16, u.crc, !0), d.setUint32(20, u.bytes.length, !0), d.setUint32(24, u.bytes.length, !0), d.setUint16(28, m.length, !0), d.setUint16(30, 0, !0), d.setUint16(32, 0, !0), d.setUint16(34, 0, !0), d.setUint16(36, 0, !0), d.setUint32(38, 0, !0), d.setUint32(42, u.offset, !0), s.push(new Uint8Array(d.buffer), m), a += 46 + m.length;
108
+ }
109
+ const c = new DataView(new ArrayBuffer(22));
110
+ return c.setUint32(0, 101010256, !0), c.setUint16(4, 0, !0), c.setUint16(6, 0, !0), c.setUint16(8, o.length, !0), c.setUint16(10, o.length, !0), c.setUint32(12, a - l, !0), c.setUint32(16, l, !0), c.setUint16(20, 0, !0), s.push(new Uint8Array(c.buffer)), new Blob(s, { type: "application/zip" });
111
+ }
112
+ const dt = ["protanopia", "deuteranopia", "tritanopia", "achromatopsia"], Me = {
113
+ protan: "protanopia",
114
+ deutan: "deuteranopia",
115
+ tritan: "tritanopia",
116
+ achroma: "achromatopsia"
117
+ };
118
+ function Lt(r) {
119
+ const t = r?.trim().toLowerCase();
120
+ return !t || t === "none" ? null : dt.includes(t) ? t : Me[t] ?? null;
121
+ }
122
+ const rr = ["none", ...dt, ...Object.keys(Me)], Ft = {
123
+ protanopia: [0.11238, 0.88762, 0, 0.11238, 0.88762, 0, 401e-5, -401e-5, 1],
124
+ deuteranopia: [0.29275, 0.70725, 0, 0.29275, 0.70725, 0, -0.02234, 0.02234, 1],
125
+ tritanopia: [1, 0.14461, -0.14461, 0, 0.85659, 0.14341, 0, 0.85659, 0.14341],
126
+ achromatopsia: [0.2126, 0.7152, 0.0722, 0.2126, 0.7152, 0.0722, 0.2126, 0.7152, 0.0722]
127
+ }, D = (r) => {
128
+ const t = r / 255;
129
+ return t <= 0.04045 ? t / 12.92 : ((t + 0.055) / 1.055) ** 2.4;
130
+ }, at = (r) => {
131
+ const t = Math.min(1, Math.max(0, r));
132
+ return 255 * (t <= 31308e-7 ? t * 12.92 : 1.055 * t ** (1 / 2.4) - 0.055);
133
+ };
134
+ function nr(r) {
135
+ const t = r.trim().toLowerCase(), e = /^#([0-9a-f]{3}|[0-9a-f]{6})$/.exec(t);
136
+ if (e) {
137
+ const i = e[1], s = (o) => parseInt(o.length === 1 ? o + o : o, 16);
138
+ return i.length === 3 ? { r: s(i[0]), g: s(i[1]), b: s(i[2]) } : { r: s(i.slice(0, 2)), g: s(i.slice(2, 4)), b: s(i.slice(4, 6)) };
139
+ }
140
+ const n = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/.exec(t);
141
+ return n ? { r: Number(n[1]), g: Number(n[2]), b: Number(n[3]) } : null;
142
+ }
143
+ function jt(r, t) {
144
+ const e = Ft[t], [n, i, s] = [D(r.r), D(r.g), D(r.b)];
145
+ return {
146
+ r: at(e[0] * n + e[1] * i + e[2] * s),
147
+ g: at(e[3] * n + e[4] * i + e[5] * s),
148
+ b: at(e[6] * n + e[7] * i + e[8] * s)
149
+ };
150
+ }
151
+ function zt({ r, g: t, b: e }) {
152
+ const [n, i, s] = [D(r), D(t), D(e)], o = (n * 0.4124564 + i * 0.3575761 + s * 0.1804375) / 0.95047, a = n * 0.2126729 + i * 0.7151522 + s * 0.072175, l = (n * 0.0193339 + i * 0.119192 + s * 0.9503041) / 1.08883, c = (h) => h > 216 / 24389 ? Math.cbrt(h) : 841 / 108 * h + 4 / 29, [u, m, d] = [c(o), c(a), c(l)];
153
+ return [116 * m - 16, 500 * (u - m), 200 * (m - d)];
154
+ }
155
+ function Dt(r, t) {
156
+ const [e, n, i] = zt(r), [s, o, a] = zt(t), l = Math.PI / 180, c = 180 / Math.PI, u = (Math.hypot(n, i) + Math.hypot(o, a)) / 2, m = 0.5 * (1 - Math.sqrt(u ** 7 / (u ** 7 + 25 ** 7))), [d, h] = [n * (1 + m), o * (1 + m)], [f, p] = [Math.hypot(d, i), Math.hypot(h, a)], g = (U, Q) => {
157
+ if (U === 0 && Q === 0) return 0;
158
+ const X = Math.atan2(Q, U) * c;
159
+ return X >= 0 ? X : X + 360;
160
+ }, [b, w] = [g(d, i), g(h, a)], y = s - e, S = p - f;
161
+ let M = 0;
162
+ f * p !== 0 && (M = w - b, M > 180 ? M -= 360 : M < -180 && (M += 360));
163
+ const F = 2 * Math.sqrt(f * p) * Math.sin(M * l / 2), _ = (e + s) / 2, $ = (f + p) / 2;
164
+ let N = b + w;
165
+ f * p !== 0 && (Math.abs(b - w) > 180 && (N += b + w < 360 ? 360 : -360), N /= 2);
166
+ const T = 1 - 0.17 * Math.cos((N - 30) * l) + 0.24 * Math.cos(2 * N * l) + 0.32 * Math.cos((3 * N + 6) * l) - 0.2 * Math.cos((4 * N - 63) * l), v = 1 + 0.015 * (_ - 50) ** 2 / Math.sqrt(20 + (_ - 50) ** 2), A = 1 + 0.045 * $, k = 1 + 0.015 * $ * T, E = -2 * Math.sqrt($ ** 7 / ($ ** 7 + 25 ** 7)) * Math.sin(60 * Math.exp(-(((N - 275) / 25) ** 2)) * l);
167
+ return Math.sqrt((y / v) ** 2 + (S / A) ** 2 + (F / k) ** 2 + E * (S / A) * (F / k));
168
+ }
169
+ const ir = 10;
170
+ function or(r, t = ir) {
171
+ const e = /* @__PURE__ */ new Map();
172
+ for (const a of r) {
173
+ const l = nr(a.color);
174
+ if (!l) continue;
175
+ const c = a.layer ?? "", u = e.get(c);
176
+ u ? u.push({ entry: a, rgb: l }) : e.set(c, [{ entry: a, rgb: l }]);
177
+ }
178
+ const n = [], i = /* @__PURE__ */ new Map();
179
+ for (const a of dt)
180
+ for (const l of e.values())
181
+ for (let c = 0; c < l.length; c++)
182
+ for (let u = c + 1; u < l.length; u++) {
183
+ if (Dt(l[c].rgb, l[u].rgb) < t) continue;
184
+ const d = Dt(jt(l[c].rgb, a), jt(l[u].rgb, a));
185
+ d < t && (n.push({ type: a, a: l[c].entry, b: l[u].entry, distance: d }), i.set(a, (i.get(a) ?? 0) + 1));
186
+ }
187
+ let s = null, o = 0;
188
+ for (const [a, l] of i)
189
+ l > o && (o = l, s = a);
190
+ return { collapses: n, worstType: s };
191
+ }
192
+ function sr(r, t, e, n) {
193
+ if (t <= 0 || e <= 0) return !0;
194
+ let i;
195
+ try {
196
+ i = r.getImageData(0, 0, t, e);
197
+ } catch {
198
+ return !1;
199
+ }
200
+ const s = new Float64Array(256);
201
+ for (let m = 0; m < 256; m++) s[m] = D(m);
202
+ const o = 1024, a = new Float64Array(o + 1);
203
+ for (let m = 0; m <= o; m++) a[m] = at(m / o);
204
+ const l = (m) => {
205
+ const d = Math.min(1, Math.max(0, m)) * o, h = Math.floor(d);
206
+ return h >= o ? a[o] : a[h] + (a[h + 1] - a[h]) * (d - h);
207
+ }, c = Ft[n], u = i.data;
208
+ for (let m = 0; m < u.length; m += 4) {
209
+ const d = s[u[m]], h = s[u[m + 1]], f = s[u[m + 2]];
210
+ u[m] = l(c[0] * d + c[1] * h + c[2] * f), u[m + 1] = l(c[3] * d + c[4] * h + c[5] * f), u[m + 2] = l(c[6] * d + c[7] * h + c[8] * f);
211
+ }
212
+ return r.putImageData(i, 0, 0), !0;
213
+ }
214
+ const ve = (r) => `om-carto-cvd-${r}`;
215
+ function ar(r) {
216
+ if (r.getElementById("om-carto-cvd-filters")) return;
217
+ const t = r.createElementNS("http://www.w3.org/2000/svg", "svg");
218
+ t.id = "om-carto-cvd-filters", t.setAttribute("aria-hidden", "true"), t.setAttribute("width", "0"), t.setAttribute("height", "0"), t.style.cssText = "position:absolute;width:0;height:0;overflow:hidden;";
219
+ for (const e of dt) {
220
+ const n = r.createElementNS("http://www.w3.org/2000/svg", "filter");
221
+ n.id = ve(e), n.setAttribute("color-interpolation-filters", "linearRGB");
222
+ const i = r.createElementNS("http://www.w3.org/2000/svg", "feColorMatrix");
223
+ i.setAttribute("type", "matrix");
224
+ const s = Ft[e];
225
+ i.setAttribute("values", [s[0], s[1], s[2], 0, 0, s[3], s[4], s[5], 0, 0, s[6], s[7], s[8], 0, 0, 0, 0, 0, 1, 0].join(" ")), n.appendChild(i), t.appendChild(n);
226
+ }
227
+ r.body.appendChild(t);
228
+ }
229
+ const Ae = 16384, Se = 134217728;
230
+ async function lr(r) {
231
+ const t = [], e = [...r.querySelectorAll("img")];
232
+ return await Promise.all(
233
+ e.map(async (n) => {
234
+ if (!(n.complete && n.naturalWidth > 0))
235
+ try {
236
+ await n.decode();
237
+ } catch {
238
+ t.push(`Image did not load and was skipped: ${n.getAttribute("src") ?? "(no src)"}`);
239
+ }
240
+ })
241
+ ), t;
242
+ }
243
+ function cr(r) {
244
+ return [...r.children].filter(
245
+ (t) => !t.hasAttribute("hidden") && !t.hasAttribute("data-om-carto-marks") && !t.hasAttribute("data-om-carto-flattened") && t.tagName.toLowerCase() !== "style" && t.tagName.toLowerCase() !== "script"
246
+ ).map((t, e) => ({ el: t, index: e, z: C(t).z ?? 0 })).sort((t, e) => t.z - e.z || t.index - e.index).map((t) => t.el);
247
+ }
248
+ async function It(r, t = {}) {
249
+ const e = r ?? document.querySelector("om-cartograph");
250
+ if (!e) throw new Error("[onlymapjs/cartograph] renderCartograph: no <om-cartograph> to render.");
251
+ const n = [], i = t.dpi ?? (Number(e.getAttribute("dpi")) || 300);
252
+ if (!Number.isFinite(i) || i <= 0) throw new Error(`[onlymapjs/cartograph] renderCartograph: invalid dpi ${t.dpi}.`);
253
+ const { widthMm: s, heightMm: o } = ct(
254
+ e.getAttribute("size"),
255
+ e.getAttribute("width"),
256
+ e.getAttribute("height"),
257
+ e.getAttribute("orientation")
258
+ ), a = Math.max(0, Number(e.getAttribute("bleed")) || 0), l = s + a * 2, c = o + a * 2, u = t.maxDimension ?? Ae, m = t.maxArea ?? Se;
259
+ let d = i;
260
+ const h = Math.min(u / (l / 25.4), u / (c / 25.4)), f = Math.sqrt(m / (l / 25.4 * (c / 25.4))), p = Math.min(h, f);
261
+ d > p && (d = Math.floor(p), n.push(`Requested ${i} dpi exceeds this browser's canvas budget for a ${l.toFixed(0)}×${c.toFixed(0)}mm page; rendered at ${d} dpi instead.`)), n.push(...await lr(e)), typeof document < "u" && document.fonts?.ready && await document.fonts.ready;
262
+ const g = _t(d), b = Math.max(1, Math.round(l * g.pxPerMm)), w = Math.max(1, Math.round(c * g.pxPerMm)), y = cr(e);
263
+ for (const v of y)
264
+ if (!(!ft(v) || !v.prepareCartograph))
265
+ try {
266
+ await v.prepareCartograph(_t(d));
267
+ } catch (A) {
268
+ v.discardCartograph?.(), n.push(`${v.tagName.toLowerCase()}${v.id ? `#${v.id}` : ""} could not be captured: ${A.message}`);
269
+ }
270
+ const S = document.createElement("canvas");
271
+ S.width = b, S.height = w;
272
+ const M = S.getContext("2d");
273
+ if (!M) throw new Error("[onlymapjs/cartograph] renderCartograph: could not get a 2D canvas context.");
274
+ const F = typeof getComputedStyle == "function" ? getComputedStyle(e) : null;
275
+ M.fillStyle = L(F?.backgroundColor) ?? "#ffffff", M.fillRect(0, 0, b, w);
276
+ for (const v of y) {
277
+ if (!ft(v)) continue;
278
+ const A = C(v);
279
+ if (M.save(), M.translate((A.x + a) * g.pxPerMm, (A.y + a) * g.pxPerMm), A.rotation) {
280
+ const k = A.w / 2 * g.pxPerMm, E = A.h / 2 * g.pxPerMm;
281
+ M.translate(k, E), M.rotate(A.rotation * Math.PI / 180), M.translate(-k, -E);
282
+ }
283
+ try {
284
+ v.paintCartograph(M, g);
285
+ } catch (k) {
286
+ n.push(`${v.tagName.toLowerCase()}${v.id ? `#${v.id}` : ""} failed to render: ${k.message}`);
287
+ }
288
+ M.restore();
289
+ }
290
+ const _ = e.querySelector(":scope > [data-om-carto-marks]");
291
+ for (const v of _?.querySelectorAll("line") ?? []) {
292
+ const A = Number(v.getAttribute("x1")), k = Number(v.getAttribute("y1")), E = Number(v.getAttribute("x2")), U = Number(v.getAttribute("y2"));
293
+ [A, k, E, U].every(Number.isFinite) && (M.save(), M.strokeStyle = v.getAttribute("stroke") || "#000000", M.lineWidth = (Number(v.getAttribute("stroke-width")) || 0.2) * g.pxPerMm, M.beginPath(), M.moveTo((A + a) * g.pxPerMm, (k + a) * g.pxPerMm), M.lineTo((E + a) * g.pxPerMm, (U + a) * g.pxPerMm), M.stroke(), M.restore());
294
+ }
295
+ const $ = Lt(t.cvd === "page" ? e.getAttribute("cvd") : t.cvd);
296
+ $ && !sr(M, b, w, $) && n.push("Colour-vision simulation was skipped: a cross-origin image on the page makes the export canvas unreadable.");
297
+ for (const v of y) ft(v) && v.discardCartograph?.();
298
+ const N = t.format === "jpeg" ? "image/jpeg" : "image/png";
299
+ N === "image/jpeg" && e.querySelector("om-text") && n.push("JPEG compresses text edges — prefer PNG for a page carrying labels.");
300
+ const T = await new Promise((v) => S.toBlob(v, N, t.quality));
301
+ if (!T) throw new Error("[onlymapjs/cartograph] renderCartograph: the canvas produced no image (it may have exceeded a browser limit).");
302
+ return { blob: T, widthPx: b, heightPx: w, dpi: d, warnings: n };
303
+ }
304
+ async function Ce(r, t = {}) {
305
+ const e = r ?? document.querySelector("om-cartograph");
306
+ if (!e) throw new Error("[onlymapjs/cartograph] renderCartographAtlas: no <om-cartograph> to render.");
307
+ const n = e.querySelector("om-atlas") ?? null;
308
+ if (!n) throw new Error("[onlymapjs/cartograph] renderCartographAtlas: this cartograph has no <om-atlas>.");
309
+ if (n.problem)
310
+ throw new Error(`[onlymapjs/cartograph] renderCartographAtlas: the atlas layer cannot drive pages (${n.problem}).`);
311
+ const i = n.count;
312
+ if (i === 0) throw new Error("[onlymapjs/cartograph] renderCartographAtlas: the atlas layer produced no features.");
313
+ const s = t.format === "jpeg" ? "jpg" : "png", o = n.filenames(s), a = [], l = n.index;
314
+ try {
315
+ if (t.page != null) {
316
+ const m = Math.trunc(t.page);
317
+ if (!Number.isInteger(m) || m < 0 || m >= i)
318
+ throw new Error(`[onlymapjs/cartograph] renderCartographAtlas: page ${t.page} is out of range (this atlas has ${i}).`);
319
+ n.seek(m);
320
+ const d = await Gt(e, t, a);
321
+ return { blob: d.blob, filenames: [o[m]], pages: 1, dpi: d.dpi, warnings: a };
322
+ }
323
+ const c = [];
324
+ let u = t.dpi ?? 300;
325
+ for (let m = 0; m < i; m++) {
326
+ n.seek(m);
327
+ const d = await Gt(e, t, a);
328
+ u = d.dpi, c.push({ name: o[m], data: d.blob }), t.onPage?.(m + 1, i);
329
+ }
330
+ return { blob: await er(c), filenames: o, pages: i, dpi: u, warnings: a };
331
+ } finally {
332
+ n.seek(l);
333
+ }
334
+ }
335
+ async function Gt(r, t, e) {
336
+ for (const i of r.querySelectorAll("om-frame"))
337
+ await i.mapElement?.whenSettled?.({ timeout: 2e4 });
338
+ const n = await It(r, t);
339
+ return e.push(...n.warnings), n;
340
+ }
341
+ const ur = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
342
+ __proto__: null,
343
+ MAX_CANVAS_AREA: Se,
344
+ MAX_CANVAS_DIMENSION: Ae,
345
+ renderCartograph: It,
346
+ renderCartographAtlas: Ce
347
+ }, Symbol.toStringTag, { value: "Module" }));
348
+ function qt(r, t, e = {}) {
349
+ const n = URL.createObjectURL(r), i = document.createElement("a");
350
+ i.href = n, i.download = t, document.body.appendChild(i), i.click(), i.remove();
351
+ const s = e.revokeAfterMs ?? 0;
352
+ s > 0 ? setTimeout(() => URL.revokeObjectURL(n), s) : URL.revokeObjectURL(n);
353
+ }
354
+ const hr = 3e4;
355
+ async function Wt(r, t = hr) {
356
+ let e = !1;
357
+ const n = new Promise(
358
+ (s) => setTimeout(() => {
359
+ e = !0, s();
360
+ }, t)
361
+ ), i = (async () => {
362
+ const s = [...r.querySelectorAll("om-frame")];
363
+ await Promise.all(s.map((a) => a.ready ?? Promise.resolve()));
364
+ const o = [...r.querySelectorAll("img")];
365
+ await Promise.all(
366
+ o.map(async (a) => {
367
+ a.complete || await new Promise((l) => {
368
+ a.addEventListener("load", () => l(), { once: !0 }), a.addEventListener("error", () => l(), { once: !0 });
369
+ });
370
+ })
371
+ ), typeof document < "u" && document.fonts?.ready && document.fonts.status !== "loaded" && await document.fonts.ready;
372
+ })();
373
+ return await Promise.race([i, n]), { timedOut: e };
374
+ }
375
+ async function mr(r, t = {}) {
376
+ if (!r.hasAttribute("allow-url-actions") || typeof document < "u" && document.documentElement?.dataset?.omHost === "editor") return null;
377
+ const e = t.search ?? (typeof location < "u" ? location.search : "");
378
+ if (!e) return null;
379
+ const n = new URLSearchParams(e), i = n.get("cvd");
380
+ i && r.setAttribute("cvd", i);
381
+ const s = n.get("export");
382
+ if (s) {
383
+ await Wt(r, t.timeoutMs);
384
+ const o = s === "jpeg" ? "jpeg" : "png", a = Number(n.get("dpi")) || void 0, l = r.getAttribute("cartograph-id") || "cartograph", c = n.get("page");
385
+ if (c != null) {
386
+ const m = r.querySelector("om-atlas");
387
+ if (!m) throw new Error(`?page=${c} needs an <om-atlas>; this cartograph is a single sheet.`);
388
+ const d = Number(c);
389
+ if (!Number.isInteger(d) || d < 1) throw new Error(`?page=${c} is not a page number (they start at 1).`);
390
+ m.refresh?.();
391
+ const h = await Ce(r, { dpi: a, format: o, page: d - 1 });
392
+ for (const f of h.warnings) console.warn(`[onlymapjs/cartograph] ${f}`);
393
+ return qt(h.blob, h.filenames[0] ?? `${l}.${o === "jpeg" ? "jpg" : "png"}`, { revokeAfterMs: 1e4 }), "export";
394
+ }
395
+ const u = await It(r, { dpi: a, format: o });
396
+ for (const m of u.warnings) console.warn(`[onlymapjs/cartograph] ${m}`);
397
+ return qt(u.blob, `${l}.${o === "jpeg" ? "jpg" : "png"}`, { revokeAfterMs: 1e4 }), "export";
398
+ }
399
+ return n.get("print") === "1" ? (await Wt(r, t.timeoutMs), typeof r.print == "function" ? await r.print({ dpi: Number(n.get("dpi")) || void 0 }) : typeof window < "u" && window.print(), "print") : i ? "cvd" : null;
400
+ }
401
+ function dr() {
402
+ if (typeof document > "u") return;
403
+ const r = () => {
404
+ for (const t of document.querySelectorAll("om-cartograph[allow-url-actions]"))
405
+ mr(t).catch((e) => console.warn(`[onlymapjs/cartograph] URL action failed: ${e.message}`));
406
+ };
407
+ document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", r, { once: !0 }) : r();
408
+ }
409
+ const xe = typeof window < "u" && typeof document < "u" && typeof customElements < "u", fr = xe ? HTMLElement : class {
410
+ };
411
+ function x(r, t) {
412
+ xe && (customElements.get(r) || customElements.define(r, t));
413
+ }
414
+ function pr(r) {
415
+ if (typeof DOMParser > "u")
416
+ throw new Error(
417
+ "[onlymapjs] Parsing a manifest string needs a DOM implementation (DOMParser). In node, run under a DOM test environment — e.g. vitest with `environment: 'happy-dom'` (or jsdom) — or pass an already-parsed Element instead of a string."
418
+ );
419
+ return new DOMParser().parseFromString(r, "text/html");
420
+ }
421
+ class R extends fr {
422
+ renderQueued = !1;
423
+ connectedCallback() {
424
+ this.scheduleRender();
425
+ }
426
+ attributeChangedCallback() {
427
+ this.isConnected && this.scheduleRender();
428
+ }
429
+ /** Ask for a re-render — used when something this element DEPENDS on changed. */
430
+ requestCartographRender() {
431
+ this.scheduleRender();
432
+ }
433
+ scheduleRender() {
434
+ this.renderQueued || (this.renderQueued = !0, queueMicrotask(() => {
435
+ this.renderQueued = !1, this.isConnected && this.renderNow();
436
+ }));
437
+ }
438
+ /** Write the placed-element inline styles from the mm placement attributes. */
439
+ applyPlacement() {
440
+ const t = (l) => {
441
+ const c = this.getAttribute(l);
442
+ if (c == null || c === "") return null;
443
+ const u = Number(c);
444
+ return Number.isFinite(u) ? u : null;
445
+ }, e = t("x"), n = t("y"), i = t("w"), s = t("h");
446
+ this.style.position = "absolute", e != null && (this.style.left = `${e}mm`), n != null && (this.style.top = `${n}mm`), i != null && (this.style.width = `${i}mm`), s != null && (this.style.height = `${s}mm`);
447
+ const o = t("rotation");
448
+ o != null && o !== 0 ? (this.style.transform = `rotate(${o}deg)`, this.style.transformOrigin = "center") : this.style.removeProperty("transform");
449
+ const a = t("z");
450
+ a != null && Number.isInteger(a) ? this.style.zIndex = String(a) : this.style.removeProperty("z-index");
451
+ }
452
+ }
453
+ const pt = "cartograph/2", qn = ["default", "dark", "sepia", "blueprint", "minimal"], Bt = ["A0", "A1", "A2", "A3", "A4", "A5", "Letter", "Legal"], gr = /* @__PURE__ */ new Map([
454
+ ["projection", "live frames render Web Mercator (EPSG:3857) only — Globe support is design-only; use a static frame for other projections"]
455
+ ]), j = ["id", "x", "y", "w", "h", "rotation", "z", "locked", "hidden"], P = {
456
+ "om-cartograph": ["cartograph-id", "format", "size", "width", "height", "orientation", "theme", "title", "attribution", "notes", "cvd", "bleed", "crop-marks", "safe-zone", "dpi", "flatten", "color-profile", "allow-url-actions"],
457
+ "om-frame": [...j, "src", "mode", "center", "zoom", "bearing", "pitch", "dpi", "crs", "corners", "crs-def", "overview-of", "overview-stroke", "atlas-fit", "atlas-margin", "frame-stroke", "frame-bg"],
458
+ "om-legend": [...j, "for", "title", "columns", "derived"],
459
+ "om-legend-row": ["layer", "match", "label", "hide", "order", "color", "shape"],
460
+ // NOTE: the discriminator on furniture is `kind`, never `style` — `style` is
461
+ // the global HTML attribute the runtime itself writes for mm placement, so
462
+ // `style="double"` would be erased on the first render. `kind` also matches
463
+ // om-text/om-shape, so the whole vocabulary has ONE discriminator word.
464
+ "om-scalebar": [...j, "for", "units", "segments", "segment-length", "kind"],
465
+ "om-north": [...j, "for", "kind"],
466
+ "om-graticule": [...j, "for", "crs", "crs-def", "interval", "interval-x", "interval-y", "kind", "labels", "format"],
467
+ "om-text": [...j, "kind", "font-size", "weight", "align", "font", "color", "bg"],
468
+ "om-shape": [...j, "kind", "fill", "fill-opacity", "stroke", "stroke-width", "radius"],
469
+ "om-image": [...j, "src", "fit"],
470
+ "om-atlas": ["id", "for", "layer", "filter", "sort", "filename", "page-name"]
471
+ }, gt = Object.keys(P), br = ["metric", "imperial", "nautical"], yr = 0.3048, wr = 1609.344, Mr = 1852;
472
+ function vr(r) {
473
+ return r != null && br.includes(r) ? r : null;
474
+ }
475
+ function $e(r) {
476
+ if (!isFinite(r) || r <= 0) return 0;
477
+ const t = Math.pow(10, Math.floor(Math.log10(r))), e = r / t;
478
+ return (e >= 5 ? 5 : e >= 2 ? 2 : 1) * t;
479
+ }
480
+ const Vt = {
481
+ // Finest first; the largest tier whose snapped length is ≥ 1 unit wins.
482
+ metric: [
483
+ { metersPerUnit: 1, label: "m" },
484
+ { metersPerUnit: 1e3, label: "km" }
485
+ ],
486
+ imperial: [
487
+ { metersPerUnit: yr, label: "ft" },
488
+ { metersPerUnit: wr, label: "mi" }
489
+ ],
490
+ nautical: [{ metersPerUnit: Mr, label: "nmi" }]
491
+ };
492
+ function ke({ groundMetersPerMm: r, maxWidthMm: t, segments: e = 4, units: n = "metric", segmentLengthMeters: i }) {
493
+ if (!Number.isFinite(r) || r <= 0 || !Number.isFinite(t) || t <= 0) return null;
494
+ const s = Number.isInteger(e) && e > 0 ? e : 4, o = Vt[n] ?? Vt.metric, a = t / s * r;
495
+ let l, c;
496
+ if (i != null && Number.isFinite(i) && i > 0)
497
+ l = i, c = Ht(o, l);
498
+ else {
499
+ c = Ht(o, a);
500
+ const f = $e(a / c.metersPerUnit);
501
+ if (f <= 0) return null;
502
+ l = f * c.metersPerUnit;
503
+ }
504
+ const u = l / r;
505
+ if (!Number.isFinite(u) || u <= 0) return null;
506
+ const m = l / c.metersPerUnit, d = m < 1 ? 2 : m < 10 && !Number.isInteger(m) ? 1 : 0, h = Array.from({ length: s + 1 }, (f, p) => (p * m).toFixed(d));
507
+ return {
508
+ segmentMeters: l,
509
+ segmentMm: u,
510
+ segments: s,
511
+ totalMm: u * s,
512
+ labels: h,
513
+ unitLabel: c.label,
514
+ // 1 mm on the page is groundMetersPerMm metres on the ground; the RF is
515
+ // that ratio in consistent units (mm : mm).
516
+ scaleDenominator: r * 1e3
517
+ };
518
+ }
519
+ function Ar(r, t, e) {
520
+ const n = Math.min(2.6, Math.max(1.6, e * 0.34)), i = 0.2, s = n * 1.25, o = Math.max(0.6, Math.min(2.2, e - s - 0.4)), a = 0, l = r.segmentMm, c = [], u = [];
521
+ if (t === "single" || t === "double") {
522
+ const g = t === "double" ? 2 : 1, b = o / g;
523
+ for (let w = 0; w < g; w++)
524
+ for (let y = 0; y < r.segments; y++)
525
+ c.push({
526
+ x: y * l,
527
+ y: a + w * b,
528
+ w: l,
529
+ h: b,
530
+ // "double" is a checkerboard: the second row's phase is inverted.
531
+ filled: (y + w) % 2 === 0
532
+ });
533
+ } else {
534
+ u.push({ x1: 0, y1: a + o, x2: r.totalMm, y2: a + o });
535
+ const g = t === "ticks" ? r.segments : 1;
536
+ for (let b = 0; b <= g; b++) {
537
+ const w = b / g * r.totalMm;
538
+ u.push({ x1: w, y1: a, x2: w, y2: a + o });
539
+ }
540
+ }
541
+ const m = r.labels.length - 1, d = r.labels.map((g, b) => ({
542
+ // The end label carries the unit so the bar reads "0 2 4 6 8 km".
543
+ text: b === m ? `${g} ${r.unitLabel}` : g,
544
+ // Keep the end label inside the drawn width rather than hanging off it.
545
+ x: b === m ? r.totalMm : b * l,
546
+ y: a + o + n,
547
+ anchor: b === 0 ? "start" : b === m ? "end" : "middle",
548
+ index: b
549
+ })), h = (g) => g.length * n * 0.62, f = [];
550
+ let p = -1 / 0;
551
+ for (const g of d) {
552
+ const b = h(g.text) / 2, w = g.anchor === "start" ? g.x : g.anchor === "end" ? g.x - b * 2 : g.x - b, y = w + h(g.text);
553
+ if (!(!(g.index === 0 || g.index === m) && w < p + n * 0.4)) {
554
+ if (g.index === m && w < p + n * 0.4)
555
+ for (; f.length > 1 && f[f.length - 1].x + h(f[f.length - 1].text) / 2 > w - n * 0.4; ) f.pop();
556
+ f.push({ text: g.text, x: g.x, y: g.y, anchor: g.anchor }), p = y;
557
+ }
558
+ }
559
+ return { bars: c, strokes: u, labels: f, fontMm: n, strokeMm: i, segmentMeters: r.segmentMeters, unitLabel: r.unitLabel, scaleDenominator: r.scaleDenominator };
560
+ }
561
+ function Ht(r, t) {
562
+ let e = r[0];
563
+ for (const n of r)
564
+ t / n.metersPerUnit >= 1 && (e = n);
565
+ return e;
566
+ }
567
+ const Sr = " ";
568
+ function Cr(r) {
569
+ return !Number.isFinite(r) || r <= 0 ? "" : Math.round(r).toString().replace(/\B(?=(\d{3})+(?!\d))/g, Sr);
570
+ }
571
+ const Ee = /\{\{\s*([\w.]+)\s*\}\}/g;
572
+ function xr(r, t) {
573
+ if (r.startsWith("atlas.")) return t.atlas?.[r.slice(6)];
574
+ if (r.startsWith("frame.")) {
575
+ const e = r.slice(6), n = e.lastIndexOf(".");
576
+ return n <= 0 ? void 0 : t.frames?.get(e.slice(0, n))?.[e.slice(n + 1)];
577
+ }
578
+ if ((r === "scale" || r === "crs") && t.soleFrameId) {
579
+ const n = t.frames?.get(t.soleFrameId)?.[r];
580
+ if (n !== void 0) return n;
581
+ }
582
+ return t.page[r];
583
+ }
584
+ function Ne(r, t) {
585
+ const e = [];
586
+ return { text: r.replace(Ee, (i, s) => {
587
+ const o = xr(s, t);
588
+ return o === void 0 ? (e.push(s), i) : o;
589
+ }), unresolved: e };
590
+ }
591
+ function $r(r) {
592
+ return [...r.matchAll(Ee)].map((t) => t[1]);
593
+ }
594
+ const Yt = /* @__PURE__ */ new WeakMap();
595
+ function kr(r, t) {
596
+ const e = [], n = r.ownerDocument ?? (typeof document < "u" ? document : null);
597
+ if (!n) return e;
598
+ const i = n.createTreeWalker(
599
+ r,
600
+ 4
601
+ /* NodeFilter.SHOW_TEXT */
602
+ ), s = [];
603
+ for (let o = i.nextNode(); o; o = i.nextNode()) s.push(o);
604
+ for (const o of s) {
605
+ let a = Yt.get(o);
606
+ if (a === void 0) {
607
+ if (a = o.data, !a.includes("{{")) continue;
608
+ Yt.set(o, a);
609
+ }
610
+ const l = Ne(a, t);
611
+ o.data !== l.text && (o.data = l.text), e.push(...l.unresolved);
612
+ }
613
+ return e;
614
+ }
615
+ function Er(r = /* @__PURE__ */ new Date()) {
616
+ return `${r.getFullYear()}-${String(r.getMonth() + 1).padStart(2, "0")}-${String(r.getDate()).padStart(2, "0")}`;
617
+ }
618
+ const Et = "data-om-carto-unlinked";
619
+ function Pe(r) {
620
+ return r.closest("om-cartograph");
621
+ }
622
+ function Te(r, t) {
623
+ const e = Pe(r);
624
+ if (!e || !t) return null;
625
+ for (const n of e.querySelectorAll("om-frame"))
626
+ if (n.id === t) return n;
627
+ return null;
628
+ }
629
+ function Le(r) {
630
+ return [...r.querySelectorAll("om-frame")];
631
+ }
632
+ function Nr(r) {
633
+ const t = Pe(r);
634
+ if (!t) return null;
635
+ const e = Le(t);
636
+ return e.length === 1 ? e[0] : null;
637
+ }
638
+ function O(r) {
639
+ const t = r.getAttribute("for");
640
+ return t ? Te(r, t) : Nr(r);
641
+ }
642
+ function G(r, t) {
643
+ let e = r.querySelector(`:scope > [${Et}]`);
644
+ if (!e) {
645
+ for (const n of [...r.children])
646
+ n.tagName.toLowerCase() !== "om-legend-row" && n.remove();
647
+ e = r.ownerDocument.createElement("div"), e.setAttribute(Et, ""), e.style.cssText = "font:italic 2.6mm/1.3 system-ui,sans-serif;color:var(--om-carto-ink);opacity:.7;", r.appendChild(e);
648
+ }
649
+ e.textContent !== t && (e.textContent = t);
650
+ }
651
+ function Pr(r) {
652
+ r.querySelector(`:scope > [${Et}]`)?.remove();
653
+ }
654
+ const Kt = "data-om-carto-page", Xt = "data-om-carto-marks", bt = "data-om-carto-credit", yt = "Built with OnlyMap by NIKA · free for non-commercial use", et = 2, Zt = 1.2, wt = 2.4, Jt = 1.5, Mt = 1.2, Qt = 0.3, te = 0.8, vt = "http://www.w3.org/2000/svg", Tr = 6e4;
655
+ class Fe extends R {
656
+ static observedAttributes = [...P["om-cartograph"]];
657
+ frameListenerBound = !1;
658
+ /** Fields of the atlas page currently displayed, for {{atlas.<field>}}. */
659
+ atlasFields;
660
+ creditObserver = null;
661
+ unsubscribeLicense = null;
662
+ /** The resolved page box, mm — furniture and export read this, never the attributes. */
663
+ get pageSize() {
664
+ return ct(this.getAttribute("size"), this.getAttribute("width"), this.getAttribute("height"), this.getAttribute("orientation"));
665
+ }
666
+ /** Bleed in mm (0 when unset) — extends the printed sheet past the trim. */
667
+ get bleedMm() {
668
+ const t = Number(this.getAttribute("bleed"));
669
+ return Number.isFinite(t) && t > 0 ? t : 0;
670
+ }
671
+ connectedCallback() {
672
+ super.connectedCallback(), this.frameListenerBound || (this.frameListenerBound = !0, this.addEventListener("om-frame-ready", () => {
673
+ this.atlas?.refresh(), this.refreshTokens(), this.refreshFurniture(), this.renderCredit();
674
+ })), !this.creditObserver && typeof MutationObserver < "u" && (this.creditObserver = new MutationObserver(() => {
675
+ this.querySelector(`:scope > [${bt}]`) || this.renderCredit();
676
+ }), this.creditObserver.observe(this, { childList: !0 }));
677
+ }
678
+ disconnectedCallback() {
679
+ this.creditObserver?.disconnect(), this.creditObserver = null, this.unsubscribeLicense?.(), this.unsubscribeLicense = null;
680
+ }
681
+ /**
682
+ * The free-tier credit (issue #41 §6, decided 2026-09-11): a cartograph is
683
+ * a print/export artifact, and `snapshot()` deliberately excludes the map
684
+ * badge — so without this line a free-plan sheet would ship credit-free.
685
+ * Suppressed when the author placed their OWN OnlyMap credit in any
686
+ * `om-text` (the licensing condition is the credit, not this element), or
687
+ * when a loaded core reports a paid plan (`hideBadge`). A static-only page
688
+ * never loads the core and keeps the credit — key verification is the map
689
+ * runtime's job (issue #41 §9: the om-map carries the key).
690
+ */
691
+ renderCredit() {
692
+ const t = H();
693
+ t && !this.unsubscribeLicense && typeof t.subscribeLicense == "function" && (this.unsubscribeLicense = t.subscribeLicense(() => this.renderCredit()));
694
+ const e = t ? t.getEntitlements().hideBadge : !1, n = [...this.querySelectorAll("om-text")].some((o) => /onlymap/i.test(o.textContent ?? "")), i = this.querySelector(`:scope > [${bt}]`);
695
+ if (e || n) {
696
+ i?.remove();
697
+ return;
698
+ }
699
+ if (i) {
700
+ this.armCreditPainter(i);
701
+ return;
702
+ }
703
+ const s = this.ownerDocument.createElement("div");
704
+ s.setAttribute(bt, ""), s.setAttribute("z", "2147483644"), s.textContent = yt, s.style.cssText = `position:absolute;left:${et}mm;bottom:${Zt}mm;z-index:2147483644;font:${wt}mm/${Jt} system-ui,sans-serif;color:#374151;background:rgba(255,255,255,.72);padding:${Qt}mm ${Mt}mm;border-radius:${te}mm;pointer-events:none;`, this.armCreditPainter(s), this.appendChild(s);
705
+ }
706
+ /** Give the runtime-owned DOM credit the same canvas paint contract as authored furniture. */
707
+ armCreditPainter(t) {
708
+ t.paintCartograph = (e, n) => {
709
+ const i = (c) => c * n.pxPerMm, o = wt * Jt + Qt * 2, a = this.pageSize.heightMm - Zt - o;
710
+ e.save(), e.font = `${i(wt)}px system-ui, sans-serif`;
711
+ const l = e.measureText(yt).width / n.pxPerMm + Mt * 2;
712
+ e.fillStyle = "rgba(255,255,255,.72)", e.beginPath(), typeof e.roundRect == "function" ? e.roundRect(i(et), i(a), i(l), i(o), i(te)) : e.rect(i(et), i(a), i(l), i(o)), e.fill(), e.fillStyle = "#374151", e.textAlign = "left", e.textBaseline = "middle", e.fillText(yt, i(et + Mt), i(a + o / 2)), e.restore();
713
+ };
714
+ }
715
+ /**
716
+ * Print this page at `dpi`.
717
+ *
718
+ * A live map is a WebGL canvas, and WebGL canvases print blank or at screen
719
+ * resolution — the drawing buffer is not preserved, and the print rasterizer
720
+ * never re-renders it. So every live frame is swapped for a print-resolution
721
+ * capture first, and restored on `afterprint`. This is also why an
722
+ * unassisted Ctrl+P cannot produce a high-DPI page: the print dialog opens
723
+ * synchronously and captures are asynchronous.
724
+ */
725
+ async print(t = {}) {
726
+ const e = t.dpi ?? (Number(this.getAttribute("dpi")) || 300), n = [...this.querySelectorAll("om-frame")];
727
+ let i = null;
728
+ if (this.hasAttribute("flatten") && (i = await this.buildFlattenedImage(e).catch(() => null), i && this.appendChild(i)), !i) for (const a of n) await a.enterPrintMode?.(e);
729
+ let s = !1;
730
+ const o = () => {
731
+ if (!s) {
732
+ s = !0, i && URL.revokeObjectURL(i.src), i?.remove(), i = null;
733
+ for (const a of n) a.exitPrintMode?.();
734
+ window.removeEventListener("afterprint", o);
735
+ }
736
+ };
737
+ window.addEventListener("afterprint", o);
738
+ try {
739
+ window.print();
740
+ } finally {
741
+ s || setTimeout(o, Tr);
742
+ }
743
+ }
744
+ /** Render the whole page to one image, for `flatten`. */
745
+ async buildFlattenedImage(t) {
746
+ const { renderCartograph: e } = await Promise.resolve().then(() => ur), { blob: n } = await e(this, { dpi: t }), i = this.ownerDocument.createElement("img");
747
+ i.src = URL.createObjectURL(n), i.setAttribute("data-om-carto-flattened", "");
748
+ const s = this.bleedMm;
749
+ return i.style.cssText = `position:absolute;left:${-s}mm;top:${-s}mm;width:calc(100% + ${s * 2}mm);height:calc(100% + ${s * 2}mm);z-index:2147483645;`, await i.decode().catch(() => {
750
+ }), i;
751
+ }
752
+ /** The page's atlas controller, if it has one. */
753
+ get atlas() {
754
+ return this.querySelector("om-atlas");
755
+ }
756
+ /** Called by the atlas when it seeks — the source for {{atlas.<field>}}. */
757
+ setAtlasFields(t) {
758
+ this.atlasFields = t, this.refreshTokens();
759
+ }
760
+ /** Re-render every furniture element — called when a frame's georeference appears. */
761
+ refreshFurniture() {
762
+ for (const t of this.querySelectorAll("om-scalebar, om-north, om-graticule, om-legend, om-frame[overview-of]"))
763
+ t.requestCartographRender?.();
764
+ }
765
+ /** Values every `{{token}}` on this page resolves against. */
766
+ tokenContext() {
767
+ const t = /* @__PURE__ */ new Map(), e = Le(this);
768
+ for (const s of e) {
769
+ const o = s.georef, a = o?.groundMetersPerMm() ?? 0;
770
+ t.set(s.id, {
771
+ scale: a > 0 ? Cr(a * 1e3) : void 0,
772
+ crs: o?.crs
773
+ });
774
+ }
775
+ const n = { page: {}, frames: t, soleFrameId: e.length === 1 ? e[0].id : null, atlas: this.atlasFields }, i = (s) => Ne(this.getAttribute(s) ?? "", n).text;
776
+ return {
777
+ page: {
778
+ title: i("title"),
779
+ attribution: i("attribution"),
780
+ date: Er(),
781
+ notes: i("notes")
782
+ },
783
+ frames: t,
784
+ // Bare {{scale}}/{{crs}} only bind when there is exactly one frame —
785
+ // with two, there is no honest answer (the validator says so).
786
+ soleFrameId: e.length === 1 ? e[0].id : null,
787
+ atlas: this.atlasFields
788
+ };
789
+ }
790
+ /** Re-resolve `{{tokens}}` in every text block on the page. */
791
+ refreshTokens() {
792
+ for (const t of this.querySelectorAll("om-text"))
793
+ t.resolveTokens?.();
794
+ }
795
+ renderNow() {
796
+ const { widthMm: t, heightMm: e } = this.pageSize, n = this.bleedMm;
797
+ this.style.display = "block", this.style.position = "relative", this.style.width = `${t}mm`, this.style.height = `${e}mm`, this.style.margin = n > 0 ? `${n}mm` : "", this.style.overflow = n > 0 ? "visible" : "hidden";
798
+ let i = this.querySelector(`style[${Kt}]`);
799
+ i || (i = document.createElement("style"), i.setAttribute(Kt, ""), this.prepend(i));
800
+ const s = t + n * 2, o = e + n * 2;
801
+ i.textContent = `@page { size: ${s}mm ${o}mm; margin: 0; }
802
+ @media print { html, body { margin: 0; padding: 0; } om-cartograph { break-after: page; } }`, this.renderPrintMarks(t, e, n), this.applyCvd(), this.refreshTokens(), this.renderCredit();
803
+ }
804
+ /**
805
+ * Crop marks and the safe-zone guide.
806
+ *
807
+ * Crop marks tell a trimmer where the sheet is cut, so they live OUTSIDE the
808
+ * trim box, in the bleed. The safe zone is the opposite: an on-screen guide
809
+ * showing where content is at risk of being trimmed off, and it must never
810
+ * reach paper or an exported image — a guide printed on the deliverable is
811
+ * worse than no guide.
812
+ */
813
+ renderPrintMarks(t, e, n) {
814
+ const i = this.hasAttribute("crop-marks") && n > 0, s = Number(this.getAttribute("safe-zone")), o = Number.isFinite(s) && s > 0, a = this.querySelector(`:scope > [${Xt}]`);
815
+ if (!i && !o) {
816
+ a?.remove();
817
+ return;
818
+ }
819
+ const l = this.ownerDocument, c = a ?? l.createElementNS(vt, "svg");
820
+ if (c.setAttribute(Xt, ""), c.setAttribute("viewBox", `${-n} ${-n} ${t + n * 2} ${e + n * 2}`), c.setAttribute("width", `${t + n * 2}mm`), c.setAttribute("height", `${e + n * 2}mm`), c.style.cssText = `position:absolute;left:${-n}mm;top:${-n}mm;pointer-events:none;z-index:2147483646;`, c.replaceChildren(), i) {
821
+ const u = Math.min(n, 4), m = Math.min(n * 0.35, 1.5), d = [];
822
+ for (const h of [0, t])
823
+ for (const f of [0, e]) {
824
+ const p = h === 0 ? -1 : 1, g = f === 0 ? -1 : 1;
825
+ d.push([h + p * m, f, h + p * (m + u), f]), d.push([h, f + g * m, h, f + g * (m + u)]);
826
+ }
827
+ for (const [h, f, p, g] of d) {
828
+ const b = l.createElementNS(vt, "line");
829
+ b.setAttribute("x1", String(h)), b.setAttribute("y1", String(f)), b.setAttribute("x2", String(p)), b.setAttribute("y2", String(g)), b.setAttribute("stroke", "#000"), b.setAttribute("stroke-width", "0.15"), c.appendChild(b);
830
+ }
831
+ }
832
+ if (o) {
833
+ const u = l.createElementNS(vt, "rect");
834
+ u.setAttribute("x", String(s)), u.setAttribute("y", String(s)), u.setAttribute("width", String(Math.max(0, t - s * 2))), u.setAttribute("height", String(Math.max(0, e - s * 2))), u.setAttribute("fill", "none"), u.setAttribute("stroke", "#22aaff"), u.setAttribute("stroke-width", "0.2"), u.setAttribute("stroke-dasharray", "2 1.5"), u.setAttribute("data-om-carto-screen-only", ""), c.appendChild(u);
835
+ }
836
+ a || this.appendChild(c);
837
+ }
838
+ /**
839
+ * Colour-vision simulation over the whole page — a review aid, so an author
840
+ * can see what a red/green categorical scheme does to roughly 1 in 12 male
841
+ * readers BEFORE it is printed, when nothing can be toggled back.
842
+ *
843
+ * CSS cannot express a colour matrix, so the filters are real SVG elements,
844
+ * injected only when a page actually asks for one.
845
+ */
846
+ applyCvd() {
847
+ const t = Lt(this.getAttribute("cvd"));
848
+ if (!t) {
849
+ this.style.removeProperty("filter");
850
+ return;
851
+ }
852
+ ar(this.ownerDocument), this.style.filter = `url(#${ve(t)})`;
853
+ }
854
+ }
855
+ x("om-cartograph", Fe);
856
+ function Lr({ text: r, maxWidth: t, fontSizePx: e, lineHeightPx: n, measure: i }) {
857
+ const s = n > 0 ? n : e * 1.2, o = [];
858
+ for (const c of r.split(/\r?\n/)) {
859
+ const u = c.split(/\s+/).filter((h) => h !== "");
860
+ if (u.length === 0) {
861
+ o.push({ text: "", baselineY: 0, width: 0 });
862
+ continue;
863
+ }
864
+ const m = (h) => {
865
+ let f = h;
866
+ for (; i(f) > t && f.length > 1; ) {
867
+ let p = f.length - 1;
868
+ for (; p > 1 && i(f.slice(0, p)) > t; ) p--;
869
+ o.push({ text: f.slice(0, p), baselineY: 0, width: i(f.slice(0, p)) }), f = f.slice(p);
870
+ }
871
+ return f;
872
+ };
873
+ let d = "";
874
+ for (const h of u) {
875
+ const f = d === "" ? h : `${d} ${h}`;
876
+ d === "" ? d = m(h) : i(f) > t ? (o.push({ text: d, baselineY: 0, width: i(d) }), d = m(h)) : d = f;
877
+ }
878
+ o.push({ text: d, baselineY: 0, width: i(d) });
879
+ }
880
+ const a = e * 0.8;
881
+ let l = 0;
882
+ return o.forEach((c, u) => {
883
+ c.baselineY = a + u * s, c.width > l && (l = c.width);
884
+ }), { lines: o, lineHeight: s, maxWidth: l };
885
+ }
886
+ function Wn(r) {
887
+ if (typeof document > "u") return null;
888
+ const e = document.createElement("canvas").getContext("2d");
889
+ return e ? (e.font = r, (n) => e.measureText(n).width) : null;
890
+ }
891
+ function Fr(r, t, e) {
892
+ return r === "center" ? (t - e) / 2 : r === "end" || r === "right" ? t - e : 0;
893
+ }
894
+ class Ie extends R {
895
+ static observedAttributes = [...P["om-text"]];
896
+ renderNow() {
897
+ this.applyPlacement(), this.style.display = "block", this.style.boxSizing = "border-box", this.style.overflow = "hidden";
898
+ const t = this.getAttribute("font-size");
899
+ t != null && Number.isFinite(Number(t)) && Number(t) > 0 ? this.style.fontSize = `${Number(t)}pt` : this.style.removeProperty("font-size");
900
+ const e = this.getAttribute("weight");
901
+ e ? this.style.fontWeight = e : this.style.removeProperty("font-weight");
902
+ const n = this.getAttribute("align");
903
+ n === "start" || n === "center" || n === "end" ? this.style.textAlign = n : this.style.removeProperty("text-align");
904
+ const i = this.getAttribute("font");
905
+ i ? this.style.fontFamily = i : this.style.removeProperty("font-family");
906
+ const s = this.getAttribute("color");
907
+ s ? this.style.color = s : this.style.removeProperty("color");
908
+ const o = this.getAttribute("bg");
909
+ o ? this.style.background = o : this.style.removeProperty("background"), this.resolveTokens();
910
+ }
911
+ /** Re-run token substitution from the authored templates. */
912
+ resolveTokens() {
913
+ const t = this.closest("om-cartograph");
914
+ t && typeof t.tokenContext == "function" && kr(this, t.tokenContext());
915
+ }
916
+ paintCartograph(t, e) {
917
+ const { w: n, h: i } = C(this), s = n * e.pxPerMm, o = i * e.pxPerMm;
918
+ if (s <= 0 || o <= 0) return;
919
+ const a = typeof getComputedStyle == "function" ? getComputedStyle(this) : null, l = L(a?.backgroundColor);
920
+ l && (t.fillStyle = l, t.fillRect(0, 0, s, o));
921
+ const c = (this.textContent ?? "").trim();
922
+ if (c === "") return;
923
+ const u = kt(a?.fontSize, 12), m = a?.lineHeight === "normal" ? u * 1.4 : kt(a?.lineHeight, u * 1.4), d = a?.fontFamily || "system-ui, sans-serif", h = a?.fontWeight || "400", f = u * e.scale, p = m * e.scale, g = `${h} ${f}px ${d}`;
924
+ t.save(), t.font = g, t.fillStyle = L(a?.color) ?? "#000", t.textBaseline = "alphabetic", t.textAlign = "left";
925
+ const b = Lr({
926
+ text: c,
927
+ maxWidth: s,
928
+ fontSizePx: f,
929
+ lineHeightPx: p,
930
+ measure: (y) => t.measureText(y).width
931
+ }), w = a?.textAlign || "start";
932
+ for (const y of b.lines) {
933
+ if (y.baselineY > o + p) break;
934
+ t.fillText(y.text, Fr(w, s, y.width), y.baselineY);
935
+ }
936
+ t.restore();
937
+ }
938
+ }
939
+ x("om-text", Ie);
940
+ const Ir = 63710087714e-4;
941
+ let Rr = Ir;
942
+ const rt = Math.PI / 180;
943
+ function Re(r, t, e = Rr) {
944
+ const n = (t[1] - r[1]) * rt, i = (t[0] - r[0]) * rt, s = r[1] * rt, o = t[1] * rt, a = Math.sin(n / 2) ** 2 + Math.cos(s) * Math.cos(o) * Math.sin(i / 2) ** 2;
945
+ return 2 * e * Math.asin(Math.min(1, Math.sqrt(a)));
946
+ }
947
+ const ut = 6378137, ee = Math.PI / 180, Nt = 180 / Math.PI, re = 85.0511287798066;
948
+ function _r([r, t]) {
949
+ const e = Math.max(-re, Math.min(re, t));
950
+ return [ut * r * ee, ut * Math.log(Math.tan(Math.PI / 4 + e * ee / 2))];
951
+ }
952
+ function Or([r, t]) {
953
+ return [r / ut * Nt, (2 * Math.atan(Math.exp(t / ut)) - Math.PI / 2) * Nt];
954
+ }
955
+ const _e = { code: "EPSG:3857", forward: _r, inverse: Or }, Ur = { code: "EPSG:4326", forward: ([r, t]) => [r, t], inverse: ([r, t]) => [r, t] };
956
+ function Y(r) {
957
+ if (r == null) return null;
958
+ const t = r.trim();
959
+ if (t === "") return null;
960
+ const e = /^(?:epsg:{1,2})?(\d{4,6})$/i.exec(t);
961
+ return e ? `EPSG:${e[1]}` : t.toUpperCase();
962
+ }
963
+ function K(r) {
964
+ const t = Y(r) ?? "EPSG:3857";
965
+ return t === "EPSG:3857" || t === "EPSG:900913" ? _e : t === "EPSG:4326" || t === "CRS:84" || t === "OGC:CRS84" ? Ur : null;
966
+ }
967
+ async function Oe(r, t) {
968
+ const e = K(r);
969
+ if (e && !t) return e;
970
+ const n = Y(r) ?? "EPSG:3857", i = t?.trim() || await jr(n);
971
+ if (!i) return null;
972
+ const s = (await import("./index-CcLC9jE5.js")).default, a = s(i, "+proj=longlat +datum=WGS84 +no_defs");
973
+ return {
974
+ code: n,
975
+ forward: (l) => a.inverse(l),
976
+ inverse: (l) => a.forward(l)
977
+ };
978
+ }
979
+ function ne(r, t) {
980
+ return !Number.isInteger(r) || r < 1 || r > 60 ? null : `+proj=utm +zone=${r}${t === "S" ? " +south" : ""} +datum=WGS84 +units=m +no_defs`;
981
+ }
982
+ async function jr(r) {
983
+ const t = Number(/^EPSG:(\d+)$/.exec(r)?.[1]);
984
+ if (!Number.isFinite(t)) return null;
985
+ if (t >= 32601 && t <= 32660) return ne(t - 32600, "N");
986
+ if (t >= 32701 && t <= 32760) return ne(t - 32700, "S");
987
+ const { CRS_DEFS: e } = await import("./crs-DsDQ4Q4i.js");
988
+ return e[t]?.def ?? null;
989
+ }
990
+ function zr(r, t) {
991
+ const e = t.length, n = r.map((i, s) => [...i, t[s]]);
992
+ for (let i = 0; i < e; i++) {
993
+ let s = i;
994
+ for (let o = i + 1; o < e; o++) Math.abs(n[o][i]) > Math.abs(n[s][i]) && (s = o);
995
+ if (Math.abs(n[s][i]) < 1e-12) return null;
996
+ [n[i], n[s]] = [n[s], n[i]];
997
+ for (let o = 0; o < e; o++) {
998
+ if (o === i) continue;
999
+ const a = n[o][i] / n[i][i];
1000
+ if (a !== 0)
1001
+ for (let l = i; l <= e; l++) n[o][l] -= a * n[i][l];
1002
+ }
1003
+ }
1004
+ return n.map((i, s) => i[e] / i[s]);
1005
+ }
1006
+ function Dr(r, t) {
1007
+ if (r.length !== 4 || t.length !== 4) return null;
1008
+ const e = r.reduce((m, d) => m + d[0], 0) / 4, n = r.reduce((m, d) => m + d[1], 0) / 4, i = r.reduce((m, d) => m + Math.hypot(d[0] - e, d[1] - n), 0) / 4, s = i > 1e-12 ? Math.SQRT2 / i : 1, o = (m) => [(m[0] - e) * s, (m[1] - n) * s], a = [], l = [];
1009
+ for (let m = 0; m < 4; m++) {
1010
+ const [d, h] = o(r[m]), [f, p] = t[m];
1011
+ a.push([d, h, 1, 0, 0, 0, -d * f, -h * f]), l.push(f), a.push([0, 0, 0, d, h, 1, -d * p, -h * p]), l.push(p);
1012
+ }
1013
+ const c = zr(a, l);
1014
+ if (!c || c.some((m) => !Number.isFinite(m))) return null;
1015
+ const u = (m) => [m[0] / s + e, m[1] / s + n];
1016
+ return { matrix: [c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], 1], normalize: o, denormalize: u };
1017
+ }
1018
+ function ie(r, [t, e]) {
1019
+ const n = r[6] * t + r[7] * e + r[8];
1020
+ return Math.abs(n) < 1e-12 ? null : [(r[0] * t + r[1] * e + r[2]) / n, (r[3] * t + r[4] * e + r[5]) / n];
1021
+ }
1022
+ function Gr(r) {
1023
+ const [t, e, n, i, s, o, a, l, c] = r, u = t * (s * c - o * l) - e * (i * c - o * a) + n * (i * l - s * a);
1024
+ return !Number.isFinite(u) || Math.abs(u) < 1e-15 ? null : [
1025
+ (s * c - o * l) / u,
1026
+ (n * l - e * c) / u,
1027
+ (e * o - n * s) / u,
1028
+ (o * a - i * c) / u,
1029
+ (t * c - n * a) / u,
1030
+ (n * i - t * o) / u,
1031
+ (i * l - s * a) / u,
1032
+ (e * a - t * l) / u,
1033
+ (t * s - e * i) / u
1034
+ ];
1035
+ }
1036
+ function oe(r, t) {
1037
+ let e = r;
1038
+ for (; e - t > 180; ) e -= 360;
1039
+ for (; t - e > 180; ) e += 360;
1040
+ return e;
1041
+ }
1042
+ const qr = (r) => {
1043
+ let t = ((r + 180) % 360 + 360) % 360 - 180;
1044
+ return t === -180 && (t = 180), t;
1045
+ };
1046
+ function ht({ corners: r, widthMm: t, heightMm: e, crsForward: n }) {
1047
+ const i = n ?? _e, s = r[0][0], o = r.map(([h, f]) => [oe(h, s), f]), a = o.map((h) => i.forward(h)), l = [
1048
+ [0, 0],
1049
+ [t, 0],
1050
+ [t, e],
1051
+ [0, e]
1052
+ ], c = t > 0 && e > 0 ? Dr(a, l) : null, u = c ? Gr(c.matrix) : null, m = !c || !u, d = {
1053
+ crs: i.code,
1054
+ corners: o,
1055
+ widthMm: t,
1056
+ heightMm: e,
1057
+ degenerate: m,
1058
+ project(h) {
1059
+ if (!c) return null;
1060
+ const f = i.forward([oe(h[0], s), h[1]]), p = ie(c.matrix, c.normalize(f));
1061
+ return p && Number.isFinite(p[0]) && Number.isFinite(p[1]) ? { xMm: p[0], yMm: p[1] } : null;
1062
+ },
1063
+ unproject(h, f) {
1064
+ if (!c || !u) return null;
1065
+ const p = ie(u, [h, f]);
1066
+ if (!p) return null;
1067
+ const g = i.inverse(c.denormalize(p));
1068
+ return Number.isFinite(g[0]) && Number.isFinite(g[1]) ? [qr(g[0]), g[1]] : null;
1069
+ },
1070
+ groundMetersPerMm() {
1071
+ if (m || t <= 0) return 0;
1072
+ const h = d.unproject(0, e / 2), f = d.unproject(t, e / 2);
1073
+ return !h || !f ? 0 : Re(h, f) / t;
1074
+ }
1075
+ };
1076
+ return d;
1077
+ }
1078
+ function mt(r) {
1079
+ if (r == null || r.trim() === "") return null;
1080
+ let t;
1081
+ try {
1082
+ t = JSON.parse(r);
1083
+ } catch {
1084
+ const n = r.trim().split(/[\s,]+/).map(Number);
1085
+ if (n.length !== 8 || n.some((i) => !Number.isFinite(i))) return null;
1086
+ t = [
1087
+ [n[0], n[1]],
1088
+ [n[2], n[3]],
1089
+ [n[4], n[5]],
1090
+ [n[6], n[7]]
1091
+ ];
1092
+ }
1093
+ if (!Array.isArray(t)) return null;
1094
+ if (t.length === 8 && t.every((n) => typeof n == "number" && Number.isFinite(n))) {
1095
+ const n = t;
1096
+ return [n[1], n[3], n[5], n[7]].some((i) => i < -90 || i > 90) ? null : [
1097
+ [n[0], n[1]],
1098
+ [n[2], n[3]],
1099
+ [n[4], n[5]],
1100
+ [n[6], n[7]]
1101
+ ];
1102
+ }
1103
+ if (t.length !== 4) return null;
1104
+ const e = [];
1105
+ for (const n of t) {
1106
+ if (!Array.isArray(n) || n.length < 2) return null;
1107
+ const [i, s] = n;
1108
+ if (typeof i != "number" || typeof s != "number" || !Number.isFinite(i) || !Number.isFinite(s) || s < -90 || s > 90) return null;
1109
+ e.push([i, s]);
1110
+ }
1111
+ return e;
1112
+ }
1113
+ function Wr(r) {
1114
+ if (r.degenerate) return null;
1115
+ const t = r.unproject(r.widthMm / 2, r.heightMm / 2);
1116
+ if (!t) return null;
1117
+ const n = [t[0], Math.min(89.9, Math.max(-89.9, t[1] + 0.01))], i = r.project(t), s = r.project(n);
1118
+ if (!i || !s) return null;
1119
+ const o = s.xMm - i.xMm, a = s.yMm - i.yMm;
1120
+ return Math.abs(o) < 1e-12 && Math.abs(a) < 1e-12 ? null : (Math.atan2(o, -a) * Nt % 360 + 360) % 360;
1121
+ }
1122
+ function Br(r) {
1123
+ if (r.degenerate) return null;
1124
+ const { widthMm: t, heightMm: e } = r, n = [];
1125
+ for (const o of [0.02, 0.5, 0.98]) {
1126
+ const a = r.unproject(0, e * o), l = r.unproject(t, e * o);
1127
+ if (!a || !l) return null;
1128
+ n.push(Re(a, l) / t);
1129
+ }
1130
+ const i = Math.min(...n), s = Math.max(...n);
1131
+ return i > 0 ? (s - i) / i : null;
1132
+ }
1133
+ function Bn(r, t, e, n) {
1134
+ return [
1135
+ [r, n],
1136
+ [e, n],
1137
+ [e, t],
1138
+ [r, t]
1139
+ ];
1140
+ }
1141
+ const Vr = (r) => r.toLowerCase().startsWith("on");
1142
+ function Hr(r, t) {
1143
+ const e = (n) => {
1144
+ const i = n.tagName.toLowerCase();
1145
+ if (i === "script") {
1146
+ if ((n.getAttribute("type") ?? "").toLowerCase() !== "application/json") return null;
1147
+ const a = t.createElement("script");
1148
+ return a.setAttribute("type", "application/json"), a.textContent = n.textContent, a;
1149
+ }
1150
+ if (i === "style" || i === "link" || i === "iframe" || i === "object" || i === "embed") return null;
1151
+ const s = t.createElement(i);
1152
+ for (const o of n.attributes)
1153
+ Vr(o.name) || /^\s*javascript:/i.test(o.value) || s.setAttribute(o.name, o.value);
1154
+ for (const o of n.childNodes)
1155
+ if (o.nodeType === 3) s.appendChild(t.createTextNode(o.textContent ?? ""));
1156
+ else if (o.nodeType === 1) {
1157
+ const a = e(o);
1158
+ a && s.appendChild(a);
1159
+ }
1160
+ return s;
1161
+ };
1162
+ return e(r);
1163
+ }
1164
+ async function Yr(r, t = document) {
1165
+ const e = await fetch(r, { credentials: "same-origin" });
1166
+ if (!e.ok) throw new Error(`fetching ${r} failed with HTTP ${e.status}`);
1167
+ const n = await e.text(), s = new DOMParser().parseFromString(n, "text/html").querySelector("om-map");
1168
+ if (!s) throw new Error(`${r} contains no <om-map>`);
1169
+ const o = Hr(s, t);
1170
+ if (!o) throw new Error(`${r} could not be adopted`);
1171
+ return o;
1172
+ }
1173
+ function lt(r) {
1174
+ const t = r.getAttribute("center"), e = r.getAttribute("zoom");
1175
+ if (!t || e == null || e.trim() === "") return null;
1176
+ const n = Number(e);
1177
+ if (!Number.isFinite(n)) return null;
1178
+ let i;
1179
+ try {
1180
+ i = JSON.parse(t);
1181
+ } catch {
1182
+ const a = t.split(/[\s,]+/).map(Number);
1183
+ i = a.length === 2 ? a : null;
1184
+ }
1185
+ if (!Array.isArray(i) || i.length < 2) return null;
1186
+ const [s, o] = i;
1187
+ return !Number.isFinite(s) || !Number.isFinite(o) ? null : {
1188
+ center: [s, o],
1189
+ zoom: n,
1190
+ bearing: Number(r.getAttribute("bearing")) || 0,
1191
+ pitch: Number(r.getAttribute("pitch")) || 0
1192
+ };
1193
+ }
1194
+ function se(r, t, e, n) {
1195
+ if (!(e > 0) || !(n > 0)) return null;
1196
+ const i = new r.WebMercatorViewport({
1197
+ width: e,
1198
+ height: n,
1199
+ longitude: t.center[0],
1200
+ latitude: t.center[1],
1201
+ zoom: t.zoom,
1202
+ bearing: t.bearing,
1203
+ pitch: t.pitch
1204
+ }), s = [
1205
+ [0, 0],
1206
+ [e, 0],
1207
+ [e, n],
1208
+ [0, n]
1209
+ ], o = [];
1210
+ for (const a of s) {
1211
+ const l = i.unproject(a);
1212
+ if (!l || !Number.isFinite(l[0]) || !Number.isFinite(l[1]) || Math.abs(l[1]) > 85.06) return null;
1213
+ o.push([l[0], l[1]]);
1214
+ }
1215
+ return o;
1216
+ }
1217
+ async function Kr(r) {
1218
+ const t = await we(), e = r.querySelector(":scope > om-map");
1219
+ if (e) return { core: t, map: e };
1220
+ const n = r.getAttribute("src");
1221
+ if (!n) throw new Error("a live frame needs src= or an inline <om-map> child");
1222
+ const i = await Yr(n, r.ownerDocument);
1223
+ return { core: t, map: i };
1224
+ }
1225
+ function At(r, t) {
1226
+ r.setAttribute("center", JSON.stringify(t.center)), r.setAttribute("zoom", String(t.zoom)), r.setAttribute("bearing", String(t.bearing)), r.setAttribute("pitch", String(t.pitch)), Xr(r, t);
1227
+ }
1228
+ function Xr(r, t) {
1229
+ const e = r;
1230
+ typeof e.setViewInternal == "function" ? e.setViewInternal({ longitude: t.center[0], latitude: t.center[1], zoom: t.zoom, pitch: t.pitch, bearing: t.bearing }) : typeof e.flyTo == "function" && e.flyTo(t.center, t.zoom, { duration: 0 });
1231
+ }
1232
+ const St = "data-om-carto-placeholder", Ct = "data-om-carto-overview", ae = "http://www.w3.org/2000/svg", le = 24, ce = 0.25;
1233
+ class Ue extends R {
1234
+ static observedAttributes = [...P["om-frame"]];
1235
+ /** Resolved non-builtin CRS planes, so a re-render does not re-await proj4. */
1236
+ resolvedForward = null;
1237
+ resolvedForwardKey = null;
1238
+ resolveToken = 0;
1239
+ lastError = null;
1240
+ readyFired = !1;
1241
+ readySettled = !1;
1242
+ readyPromise = null;
1243
+ readyResolve = null;
1244
+ /**
1245
+ * Settles when the frame has either rendered its map or given up on it —
1246
+ * mirroring `<om-map>`'s own `ready`. It RESOLVES on error rather than
1247
+ * rejecting, because every caller (export, print, the URL triggers) wants
1248
+ * "stop waiting", and a frame that failed still shows a visible placeholder
1249
+ * that belongs on the page.
1250
+ */
1251
+ get ready() {
1252
+ return this.readyPromise || (this.readyPromise = new Promise((t) => {
1253
+ this.readyResolve = t;
1254
+ }), this.readySettled && this.settleReady()), this.readyPromise;
1255
+ }
1256
+ settleReady() {
1257
+ this.readySettled = !0, this.readyResolve?.(), this.readyResolve = null;
1258
+ }
1259
+ /** Live-mode state. */
1260
+ liveMap = null;
1261
+ liveCorners = null;
1262
+ mountToken = 0;
1263
+ mounting = !1;
1264
+ mountingSource = null;
1265
+ mountedSource = null;
1266
+ liveMapOwned = !1;
1267
+ /** A capture standing in for the live map during export/print. */
1268
+ captureImage = null;
1269
+ printing = !1;
1270
+ get mode() {
1271
+ return this.getAttribute("mode") === "static" ? "static" : "live";
1272
+ }
1273
+ /** The mounted `<om-map>`, once a live frame has one. */
1274
+ get mapElement() {
1275
+ return this.liveMap;
1276
+ }
1277
+ /**
1278
+ * The frame's georeference, or null when it has none yet (malformed corners,
1279
+ * a CRS still resolving, a live map not yet mounted, a view too pitched to
1280
+ * georeference). Recomputed on demand — the fit is an 8×8 solve, far cheaper
1281
+ * than caching it correctly.
1282
+ */
1283
+ get georef() {
1284
+ const { w: t, h: e } = C(this);
1285
+ if (t <= 0 || e <= 0) return null;
1286
+ if (this.mode === "live")
1287
+ return this.liveCorners ? ht({ corners: this.liveCorners, widthMm: t, heightMm: e }) : null;
1288
+ const n = mt(this.getAttribute("corners"));
1289
+ if (!n) return null;
1290
+ const i = Y(this.getAttribute("crs")) ?? "EPSG:3857", s = this.getAttribute("crs-def");
1291
+ let o = s ? null : K(i);
1292
+ return !o && this.resolvedForwardKey === `${i}|${s ?? ""}` && (o = this.resolvedForward), o ? ht({ corners: n, widthMm: t, heightMm: e, crsForward: o }) : null;
1293
+ }
1294
+ /** The raster a static frame draws — its first <img> child, if any. */
1295
+ get raster() {
1296
+ return this.querySelector(":scope > img");
1297
+ }
1298
+ disconnectedCallback() {
1299
+ this.abandonMount();
1300
+ }
1301
+ /**
1302
+ * Bump the generation token so a mount already in flight cannot adopt its
1303
+ * map into this frame when it lands. The token is the whole cancellation
1304
+ * protocol, so it lives in one place — three copies of it is three chances
1305
+ * for a stale map to arrive after a src= change.
1306
+ */
1307
+ abandonMount() {
1308
+ this.mountToken++, this.mounting = !1, this.mountingSource = null;
1309
+ }
1310
+ renderNow() {
1311
+ this.applyPlacement(), this.style.display = "block", this.style.overflow = "hidden", this.style.boxSizing = "border-box";
1312
+ const t = this.getAttribute("frame-stroke");
1313
+ this.style.border = "0", this.style.boxShadow = `inset 0 0 0 ${ce}mm ${t || "var(--om-carto-frame-stroke)"}`;
1314
+ const e = this.getAttribute("frame-bg");
1315
+ this.style.background = e || "var(--om-carto-frame-bg)", this.mode === "static" ? (this.suspendLiveMode(), this.renderStatic()) : this.renderLive(), this.renderOverview();
1316
+ }
1317
+ // -------------------------------------------------------------------------
1318
+ // Static
1319
+ // -------------------------------------------------------------------------
1320
+ renderStatic() {
1321
+ const t = this.raster;
1322
+ if (!t) {
1323
+ this.showPlaceholder("no-raster", "Static frame has no <img> child to place.");
1324
+ return;
1325
+ }
1326
+ t.style.position = "absolute", t.style.inset = "0", t.style.width = "100%", t.style.height = "100%", t.style.objectFit = "fill", t.style.display = "block";
1327
+ const e = Y(this.getAttribute("crs")) ?? "EPSG:3857", n = this.getAttribute("crs-def"), i = `${e}|${n ?? ""}`;
1328
+ if ((n != null || K(e) == null) && this.resolvedForwardKey !== i) {
1329
+ const o = ++this.resolveToken;
1330
+ this.resolvedForward = null, this.resolvedForwardKey = null, Oe(e, n).then((a) => {
1331
+ if (o === this.resolveToken) {
1332
+ if (!a) {
1333
+ this.showPlaceholder("unknown-crs", `Unknown CRS "${e}" — supply crs-def="+proj=…".`);
1334
+ return;
1335
+ }
1336
+ this.resolvedForward = a, this.resolvedForwardKey = i, this.scheduleRender();
1337
+ }
1338
+ }).catch(() => {
1339
+ o === this.resolveToken && this.showPlaceholder("unknown-crs", `Could not resolve CRS "${e}".`);
1340
+ });
1341
+ return;
1342
+ }
1343
+ if (!mt(this.getAttribute("corners"))) {
1344
+ this.showPlaceholder("bad-corners", 'Static frame needs corners="[[lng,lat] × 4]" (tl, tr, br, bl).');
1345
+ return;
1346
+ }
1347
+ this.clearPlaceholder(), this.signalStaticReady(t);
1348
+ }
1349
+ // -------------------------------------------------------------------------
1350
+ // Live
1351
+ // -------------------------------------------------------------------------
1352
+ renderLive() {
1353
+ const t = lt(this);
1354
+ if (!t) {
1355
+ this.showPlaceholder("no-camera", 'Live frame needs its own camera — center="[lng, lat]" and zoom=.');
1356
+ return;
1357
+ }
1358
+ this.updateLiveCorners(t);
1359
+ const e = this.liveSource();
1360
+ if (this.liveMap)
1361
+ if (this.mountedSource !== e)
1362
+ this.releaseLiveMap();
1363
+ else {
1364
+ At(this.liveMap, t), this.sizeLiveMap();
1365
+ return;
1366
+ }
1367
+ if (this.mounting) {
1368
+ if (this.mountingSource === e) return;
1369
+ this.abandonMount();
1370
+ }
1371
+ this.mountLive(e);
1372
+ }
1373
+ /** Inline maps are identified by node identity; fetched maps by their src=. */
1374
+ liveSource() {
1375
+ return [...this.children].find(
1376
+ (e) => e.tagName.toLowerCase() === "om-map" && (!this.liveMapOwned || e !== this.liveMap)
1377
+ ) ?? `src:${this.getAttribute("src") ?? ""}`;
1378
+ }
1379
+ /** Leave live mode without deleting an authored inline map. */
1380
+ suspendLiveMode() {
1381
+ this.mounting && this.abandonMount(), this.captureImage = null, this.liveMap && (this.liveMapOwned ? this.releaseLiveMap() : this.liveMap.hidden = !0);
1382
+ }
1383
+ releaseLiveMap() {
1384
+ this.liveMapOwned ? this.liveMap?.remove() : this.liveMap && (this.liveMap.hidden = !0), this.liveMap = null, this.liveMapOwned = !1, this.mountedSource = null, this.liveCorners = null, this.captureImage = null;
1385
+ }
1386
+ updateLiveCorners(t = lt(this)) {
1387
+ if (!t) return;
1388
+ const e = H();
1389
+ if (!e) return;
1390
+ const { w: n, h: i } = C(this), s = se(e, t, n * W, i * W);
1391
+ this.liveCorners = s, s ? this.lastError === "degenerate-view" && this.clearPlaceholder() : this.showPlaceholder(
1392
+ "degenerate-view",
1393
+ "This camera cannot be georeferenced — the frame's top edge reaches the horizon. Reduce pitch below ~60°."
1394
+ );
1395
+ }
1396
+ async mountLive(t) {
1397
+ const e = ++this.mountToken;
1398
+ this.mounting = !0, this.mountingSource = t;
1399
+ try {
1400
+ const { core: n, map: i } = await Kr(this);
1401
+ if (e !== this.mountToken || !this.isConnected || this.mode !== "live" || this.liveSource() !== t) return;
1402
+ const s = lt(this);
1403
+ if (!s) return;
1404
+ At(i, s);
1405
+ const o = i;
1406
+ o.style.position = "absolute", o.style.inset = "0", o.style.display = "block", this.liveMapOwned = i.parentElement !== this, this.liveMapOwned && this.appendChild(i), i.setAttribute("data-om-carto-frame", ""), this.liveMap = o, this.mountedSource = t, this.sizeLiveMap(), this.clearPlaceholder();
1407
+ const { w: a, h: l } = C(this);
1408
+ if (this.liveCorners = se(n, s, a * W, l * W), await o.ready, e !== this.mountToken || (At(i, s), await o.whenSettled?.({ timeout: 2e4 }), e !== this.mountToken)) return;
1409
+ this.signalReady();
1410
+ } catch (n) {
1411
+ if (e !== this.mountToken) return;
1412
+ const i = n.message ?? String(n);
1413
+ this.showPlaceholder(i.includes("src=") || i.includes("no <om-map>") ? "no-source" : "load-failed", `Live frame could not load: ${i}`);
1414
+ } finally {
1415
+ e === this.mountToken && (this.mounting = !1, this.mountingSource = null);
1416
+ }
1417
+ }
1418
+ sizeLiveMap() {
1419
+ this.liveMap && (this.liveMap.style.width = "100%", this.liveMap.style.height = "100%", this.liveMap.hidden = this.printing);
1420
+ }
1421
+ // -------------------------------------------------------------------------
1422
+ // Locator inset (overview-of)
1423
+ // -------------------------------------------------------------------------
1424
+ /**
1425
+ * Draw another frame's footprint on this one. The outline is DENSIFIED —
1426
+ * sampled along each edge and projected point by point — because a
1427
+ * rectangle in one projection is a curve in another, and four straight
1428
+ * segments would visibly cut the corners on a wide-extent inset.
1429
+ */
1430
+ renderOverview() {
1431
+ const t = this.getAttribute("overview-of"), e = this.querySelector(`:scope > [${Ct}]`);
1432
+ if (!t) {
1433
+ e?.remove();
1434
+ return;
1435
+ }
1436
+ const n = Te(this, t), i = this.georef, s = n?.georef;
1437
+ if (!n || !i || !s) {
1438
+ e?.remove();
1439
+ return;
1440
+ }
1441
+ const { w: o, h: a } = C(this), l = [], [c, u, m, d] = s.corners, h = [
1442
+ [c, u],
1443
+ [u, m],
1444
+ [m, d],
1445
+ [d, c]
1446
+ ];
1447
+ for (const [b, w] of h)
1448
+ for (let y = 0; y < le; y++) {
1449
+ const S = y / le, M = [b[0] + (w[0] - b[0]) * S, b[1] + (w[1] - b[1]) * S], F = i.project(M);
1450
+ F && l.push(`${F.xMm.toFixed(3)},${F.yMm.toFixed(3)}`);
1451
+ }
1452
+ if (l.length < 3) {
1453
+ e?.remove();
1454
+ return;
1455
+ }
1456
+ const f = this.ownerDocument, p = e ?? f.createElementNS(ae, "svg");
1457
+ p.setAttribute(Ct, ""), p.setAttribute("width", `${o}mm`), p.setAttribute("height", `${a}mm`), p.setAttribute("viewBox", `0 0 ${o} ${a}`), p.style.cssText = "position:absolute;inset:0;pointer-events:none;", p.replaceChildren();
1458
+ const g = f.createElementNS(ae, "polygon");
1459
+ g.setAttribute("points", l.join(" ")), g.setAttribute("fill", "none"), g.setAttribute("stroke", this.getAttribute("overview-stroke") || "#e63946"), g.setAttribute("stroke-width", "0.4"), p.appendChild(g), e || this.appendChild(p);
1460
+ }
1461
+ // -------------------------------------------------------------------------
1462
+ // Readiness and errors
1463
+ // -------------------------------------------------------------------------
1464
+ signalStaticReady(t) {
1465
+ this.readyFired || (t.complete && t.naturalWidth > 0 ? this.signalReady() : (t.addEventListener("load", () => this.signalReady(), { once: !0 }), t.addEventListener(
1466
+ "error",
1467
+ () => this.showPlaceholder("raster-decode", `Could not load the frame's raster (${t.getAttribute("src") ?? "no src"}).`),
1468
+ { once: !0 }
1469
+ )));
1470
+ }
1471
+ signalReady() {
1472
+ this.readyFired || (this.readyFired = !0, this.settleReady(), this.dispatchEvent(new CustomEvent("om-frame-ready", { bubbles: !0, detail: { id: this.id, mode: this.mode } })));
1473
+ }
1474
+ /**
1475
+ * Frames fail loudly and visibly. A silent empty box on a printed page is
1476
+ * the worst outcome — it looks like a design choice.
1477
+ */
1478
+ showPlaceholder(t, e) {
1479
+ let n = this.querySelector(`:scope > [${St}]`);
1480
+ n || (n = document.createElement("div"), n.setAttribute(St, ""), n.style.cssText = "position:absolute;inset:0;display:grid;place-content:center;padding:4mm;text-align:center;font:italic 3mm/1.4 system-ui,sans-serif;color:var(--om-carto-ink);opacity:.75;", this.appendChild(n)), n.textContent !== e && (n.textContent = e), this.lastError !== t && (this.lastError = t, this.settleReady(), this.dispatchEvent(new CustomEvent("om-frame-error", { bubbles: !0, detail: { id: this.id, reason: t, message: e } })));
1481
+ }
1482
+ clearPlaceholder() {
1483
+ this.querySelector(`:scope > [${St}]`)?.remove(), this.lastError = null;
1484
+ }
1485
+ // -------------------------------------------------------------------------
1486
+ // Capture, export and print
1487
+ // -------------------------------------------------------------------------
1488
+ /**
1489
+ * Capture this frame's map at `scale` captured pixels per CSS pixel.
1490
+ * Static frames need no capture (their raster IS the picture); a live frame
1491
+ * re-renders at the requested resolution through the core.
1492
+ */
1493
+ async captureAt(t) {
1494
+ if (this.mode !== "live" || !this.liveMap) return null;
1495
+ const e = await this.liveMap.snapshot({ scale: t }), n = new Image();
1496
+ return n.decoding = "sync", n.src = e, await n.decode().catch(() => {
1497
+ }), n;
1498
+ }
1499
+ /**
1500
+ * Point this frame's camera at a lon/lat box — the atlas's per-page refit.
1501
+ * Solves for the zoom that fits the box in the frame's own pixel size, in
1502
+ * the Mercator plane (where the projection is linear), then writes the
1503
+ * camera attributes so everything downstream — corners, scale bar, tokens —
1504
+ * follows from the same source as any authored camera.
1505
+ */
1506
+ fitBounds(t) {
1507
+ const [e, n, i, s] = t, { w: o, h: a } = C(this);
1508
+ if (o <= 0 || a <= 0) return;
1509
+ const l = H();
1510
+ if (!l) return;
1511
+ const c = new l.WebMercatorViewport({ width: o * W, height: a * W, longitude: 0, latitude: 0, zoom: 0 }).fitBounds([
1512
+ [e, n],
1513
+ [i, s]
1514
+ ]);
1515
+ [c.longitude, c.latitude, c.zoom].every(Number.isFinite) && (this.setAttribute("center", JSON.stringify([Number(c.longitude.toFixed(8)), Number(c.latitude.toFixed(8))])), this.setAttribute("zoom", String(Number(c.zoom.toFixed(4)))), this.updateLiveCorners());
1516
+ }
1517
+ /** Export hook: capture before the page paints (see render.ts). */
1518
+ async prepareCartograph(t) {
1519
+ this.mode === "live" && (this.captureImage = await this.captureAt(t.scale));
1520
+ }
1521
+ /** Export hook: forget the capture, so a failed pre-pass never reuses it. */
1522
+ discardCartograph() {
1523
+ this.captureImage = null;
1524
+ }
1525
+ /**
1526
+ * Swap the live canvas for a print-resolution bitmap. A WebGL canvas prints
1527
+ * blank or at screen resolution — the drawing buffer is not preserved and
1528
+ * the print rasterizer does not re-render it — so the printed page shows a
1529
+ * captured `<img>` instead, restored afterwards.
1530
+ */
1531
+ async enterPrintMode(t) {
1532
+ if (this.mode !== "live" || !this.liveMap) return;
1533
+ const e = await this.captureAt(t / 96);
1534
+ e && (this.printing = !0, e.setAttribute("data-om-carto-print", ""), e.style.cssText = "position:absolute;inset:0;width:100%;height:100%;object-fit:fill;display:block;", this.appendChild(e), this.liveMap.hidden = !0);
1535
+ }
1536
+ exitPrintMode() {
1537
+ this.printing = !1, this.querySelector(":scope > img[data-om-carto-print]")?.remove(), this.liveMap && (this.liveMap.hidden = !1);
1538
+ }
1539
+ paintCartograph(t, e) {
1540
+ const { w: n, h: i } = C(this), s = n * e.pxPerMm, o = i * e.pxPerMm;
1541
+ if (s <= 0 || o <= 0) return;
1542
+ const a = typeof getComputedStyle == "function" ? getComputedStyle(this) : null, l = L(a?.backgroundColor);
1543
+ l && (t.fillStyle = l, t.fillRect(0, 0, s, o));
1544
+ const c = this.mode === "static" ? this.raster : this.captureImage;
1545
+ c && c.complete && c.naturalWidth > 0 && t.drawImage(c, 0, 0, s, o), this.paintOverview(t, e);
1546
+ const u = this.getAttribute("frame-stroke") || L(a?.getPropertyValue("--om-carto-frame-stroke")?.trim()) || "#333333", m = ce * e.pxPerMm;
1547
+ m > 0 && (t.strokeStyle = u, t.lineWidth = m, t.strokeRect(m / 2, m / 2, s - m, o - m));
1548
+ }
1549
+ paintOverview(t, e) {
1550
+ const n = this.querySelector(`:scope > [${Ct}] polygon`), i = n?.getAttribute("points");
1551
+ i && (t.save(), t.strokeStyle = n.getAttribute("stroke") || "#e63946", t.lineWidth = Number(n.getAttribute("stroke-width") || 0.4) * e.pxPerMm, t.beginPath(), i.split(/\s+/).forEach((s, o) => {
1552
+ const [a, l] = s.split(",").map(Number);
1553
+ !Number.isFinite(a) || !Number.isFinite(l) || (o === 0 ? t.moveTo(a * e.pxPerMm, l * e.pxPerMm) : t.lineTo(a * e.pxPerMm, l * e.pxPerMm));
1554
+ }), t.closePath(), t.stroke(), t.restore());
1555
+ }
1556
+ }
1557
+ x("om-frame", Ue);
1558
+ const nt = "http://www.w3.org/2000/svg", Zr = ["single", "double", "line", "ticks"];
1559
+ class je extends R {
1560
+ static observedAttributes = [...P["om-scalebar"]];
1561
+ /** The geometry currently drawn — reused by the canvas exporter. */
1562
+ get drawing() {
1563
+ const e = O(this)?.georef;
1564
+ if (!e) return null;
1565
+ const { w: n, h: i } = C(this), s = ke({
1566
+ groundMetersPerMm: e.groundMetersPerMm(),
1567
+ maxWidthMm: n,
1568
+ segments: Number(this.getAttribute("segments")) || void 0,
1569
+ units: this.unitSystem,
1570
+ segmentLengthMeters: this.getAttribute("segment-length") ? Number(this.getAttribute("segment-length")) : null
1571
+ });
1572
+ return s ? Ar(s, this.barStyle, i) : null;
1573
+ }
1574
+ get barStyle() {
1575
+ const t = this.getAttribute("kind");
1576
+ return Zr.includes(t) ? t : "single";
1577
+ }
1578
+ get unitSystem() {
1579
+ return vr(this.getAttribute("units")) ?? "metric";
1580
+ }
1581
+ renderNow() {
1582
+ if (this.applyPlacement(), this.style.display = "block", !O(this)) {
1583
+ const n = this.getAttribute("for");
1584
+ G(this, n ? `Scale bar references no frame with id "${n}".` : "Scale bar has no frame to measure (set for=).");
1585
+ return;
1586
+ }
1587
+ const e = this.drawing;
1588
+ if (!e) {
1589
+ G(this, "Scale bar cannot derive a scale from this frame yet.");
1590
+ return;
1591
+ }
1592
+ this.renderSvg(e);
1593
+ }
1594
+ renderSvg(t) {
1595
+ const { w: e, h: n } = C(this), i = this.ownerDocument, s = i.createElementNS(nt, "svg");
1596
+ s.setAttribute("width", `${e}mm`), s.setAttribute("height", `${n}mm`), s.setAttribute("viewBox", `0 0 ${e} ${n}`), s.setAttribute("part", "scalebar"), s.style.display = "block", s.style.overflow = "visible";
1597
+ const o = "var(--om-carto-scalebar-ink)";
1598
+ for (const a of t.bars) {
1599
+ const l = i.createElementNS(nt, "rect");
1600
+ l.setAttribute("x", String(a.x)), l.setAttribute("y", String(a.y)), l.setAttribute("width", String(a.w)), l.setAttribute("height", String(a.h)), l.setAttribute("fill", a.filled ? o : "none"), l.setAttribute("stroke", o), l.setAttribute("stroke-width", String(t.strokeMm)), s.appendChild(l);
1601
+ }
1602
+ for (const a of t.strokes) {
1603
+ const l = i.createElementNS(nt, "line");
1604
+ l.setAttribute("x1", String(a.x1)), l.setAttribute("y1", String(a.y1)), l.setAttribute("x2", String(a.x2)), l.setAttribute("y2", String(a.y2)), l.setAttribute("stroke", o), l.setAttribute("stroke-width", String(t.strokeMm)), s.appendChild(l);
1605
+ }
1606
+ for (const a of t.labels) {
1607
+ const l = i.createElementNS(nt, "text");
1608
+ l.setAttribute("x", String(a.x)), l.setAttribute("y", String(a.y)), l.setAttribute("font-size", String(t.fontMm)), l.setAttribute("fill", o), l.setAttribute("text-anchor", a.anchor), l.setAttribute("font-family", "var(--om-carto-font)"), l.textContent = a.text, s.appendChild(l);
1609
+ }
1610
+ this.replaceChildren(s);
1611
+ }
1612
+ paintCartograph(t, e) {
1613
+ const n = this.drawing;
1614
+ if (!n) return;
1615
+ const i = typeof getComputedStyle == "function" ? getComputedStyle(this) : null, s = L(i?.color) ?? "#000", o = (l) => l * e.pxPerMm;
1616
+ t.strokeStyle = s, t.fillStyle = s, t.lineWidth = o(n.strokeMm);
1617
+ for (const l of n.bars)
1618
+ l.filled && t.fillRect(o(l.x), o(l.y), o(l.w), o(l.h)), t.strokeRect(o(l.x), o(l.y), o(l.w), o(l.h));
1619
+ t.beginPath();
1620
+ for (const l of n.strokes)
1621
+ t.moveTo(o(l.x1), o(l.y1)), t.lineTo(o(l.x2), o(l.y2));
1622
+ t.stroke();
1623
+ const a = i?.fontFamily || "system-ui, sans-serif";
1624
+ t.font = `${o(n.fontMm)}px ${a}`, t.textBaseline = "alphabetic";
1625
+ for (const l of n.labels)
1626
+ t.textAlign = l.anchor === "middle" ? "center" : l.anchor === "end" ? "right" : "left", t.fillText(l.text, o(l.x), o(l.y));
1627
+ }
1628
+ }
1629
+ x("om-scalebar", je);
1630
+ const J = "http://www.w3.org/2000/svg";
1631
+ class ze extends R {
1632
+ static observedAttributes = [...P["om-north"]];
1633
+ /** Degrees clockwise from the page's "up", or null when unknowable. */
1634
+ get bearing() {
1635
+ const t = O(this)?.georef;
1636
+ return t ? Wr(t) : null;
1637
+ }
1638
+ get indicatorKind() {
1639
+ return this.getAttribute("kind") === "rose" ? "rose" : "arrow";
1640
+ }
1641
+ renderNow() {
1642
+ if (this.applyPlacement(), this.style.display = "block", !O(this)) {
1643
+ const l = this.getAttribute("for");
1644
+ G(this, l ? `North indicator references no frame with id "${l}".` : "North indicator has no frame (set for=).");
1645
+ return;
1646
+ }
1647
+ const e = this.bearing;
1648
+ if (e == null) {
1649
+ G(this, "North indicator cannot derive a direction from this frame yet.");
1650
+ return;
1651
+ }
1652
+ const { w: n, h: i } = C(this), s = this.ownerDocument, o = s.createElementNS(J, "svg");
1653
+ o.setAttribute("width", `${n}mm`), o.setAttribute("height", `${i}mm`), o.setAttribute("viewBox", `0 0 ${n} ${i}`), o.style.display = "block";
1654
+ const a = s.createElementNS(J, "g");
1655
+ a.setAttribute("transform", `rotate(${e.toFixed(3)} ${n / 2} ${i / 2})`);
1656
+ for (const l of this.buildGeometry(s, n, i)) a.appendChild(l);
1657
+ o.appendChild(a), this.replaceChildren(o);
1658
+ }
1659
+ buildGeometry(t, e, n) {
1660
+ const i = "var(--om-carto-compass-ink)", s = [], o = e / 2, a = Math.min(3, n * 0.24), l = n - a * 1.2;
1661
+ if (this.indicatorKind === "rose") {
1662
+ const f = Math.min(e, l) / 2, p = t.createElementNS(J, "circle");
1663
+ p.setAttribute("cx", String(o)), p.setAttribute("cy", String(l / 2)), p.setAttribute("r", String(f)), p.setAttribute("fill", "none"), p.setAttribute("stroke", i), p.setAttribute("stroke-width", "0.2"), s.push(p);
1664
+ }
1665
+ const c = 0, u = l, m = Math.min(e * 0.3, 4), d = t.createElementNS(J, "path");
1666
+ d.setAttribute("d", `M ${o} ${c} L ${o + m} ${u} L ${o} ${u - m * 0.55} L ${o - m} ${u} Z`), d.setAttribute("fill", i), s.push(d);
1667
+ const h = t.createElementNS(J, "text");
1668
+ return h.setAttribute("x", String(o)), h.setAttribute("y", String(n)), h.setAttribute("font-size", String(a)), h.setAttribute("text-anchor", "middle"), h.setAttribute("fill", i), h.setAttribute("font-family", "var(--om-carto-font)"), h.textContent = "N", s.push(h), s;
1669
+ }
1670
+ paintCartograph(t, e) {
1671
+ const n = this.bearing;
1672
+ if (n == null) return;
1673
+ const { w: i, h: s } = C(this), o = typeof getComputedStyle == "function" ? getComputedStyle(this) : null, a = L(o?.color) ?? "#000", l = (h) => h * e.pxPerMm, c = Math.min(3, s * 0.24), u = s - c * 1.2, m = i / 2, d = Math.min(i * 0.3, 4);
1674
+ t.save(), t.translate(l(m), l(s / 2)), t.rotate(n * Math.PI / 180), t.translate(-l(m), -l(s / 2)), t.fillStyle = a, t.strokeStyle = a, this.indicatorKind === "rose" && (t.lineWidth = l(0.2), t.beginPath(), t.arc(l(m), l(u / 2), l(Math.min(i, u) / 2), 0, Math.PI * 2), t.stroke()), t.beginPath(), t.moveTo(l(m), 0), t.lineTo(l(m + d), l(u)), t.lineTo(l(m), l(u - d * 0.55)), t.lineTo(l(m - d), l(u)), t.closePath(), t.fill(), t.font = `${l(c)}px ${o?.fontFamily || "system-ui, sans-serif"}`, t.textAlign = "center", t.textBaseline = "alphabetic", t.fillText("N", l(m), l(s)), t.restore();
1675
+ }
1676
+ }
1677
+ x("om-north", ze);
1678
+ const it = "http://www.w3.org/2000/svg", Jr = ["rect", "ellipse", "line", "arrow"];
1679
+ class De extends R {
1680
+ static observedAttributes = [...P["om-shape"]];
1681
+ get kind() {
1682
+ const t = this.getAttribute("kind");
1683
+ return Jr.includes(t) ? t : "rect";
1684
+ }
1685
+ get fill() {
1686
+ return this.getAttribute("fill") || "var(--om-carto-shape-fill)";
1687
+ }
1688
+ get stroke() {
1689
+ return this.getAttribute("stroke") || "var(--om-carto-shape-stroke)";
1690
+ }
1691
+ get strokeWidthMm() {
1692
+ const t = this.getAttribute("stroke-width");
1693
+ if (t == null || t.trim() === "") return 0.3;
1694
+ const e = Number(t);
1695
+ return Number.isFinite(e) && e >= 0 ? e : 0.3;
1696
+ }
1697
+ get fillOpacity() {
1698
+ const t = this.getAttribute("fill-opacity");
1699
+ if (t == null || t.trim() === "") return 1;
1700
+ const e = Number(t);
1701
+ return Number.isFinite(e) && e >= 0 && e <= 1 ? e : 1;
1702
+ }
1703
+ get radiusMm() {
1704
+ const t = Number(this.getAttribute("radius"));
1705
+ return Number.isFinite(t) && t > 0 ? t : 0;
1706
+ }
1707
+ renderNow() {
1708
+ this.applyPlacement(), this.style.display = "block";
1709
+ const { w: t, h: e } = C(this), n = this.ownerDocument, i = n.createElementNS(it, "svg");
1710
+ i.setAttribute("width", `${t}mm`), i.setAttribute("height", `${e}mm`), i.setAttribute("viewBox", `0 0 ${t} ${e}`), i.style.display = "block", i.style.overflow = "visible";
1711
+ const s = this.strokeWidthMm / 2, o = this.kind;
1712
+ let a;
1713
+ o === "ellipse" ? (a = n.createElementNS(it, "ellipse"), a.setAttribute("cx", String(t / 2)), a.setAttribute("cy", String(e / 2)), a.setAttribute("rx", String(Math.max(0, t / 2 - s))), a.setAttribute("ry", String(Math.max(0, e / 2 - s)))) : o === "line" || o === "arrow" ? (a = n.createElementNS(it, "path"), a.setAttribute("d", o === "arrow" ? this.arrowPath(t, e) : `M 0 0 L ${t} ${e}`)) : (a = n.createElementNS(it, "rect"), a.setAttribute("x", String(s)), a.setAttribute("y", String(s)), a.setAttribute("width", String(Math.max(0, t - this.strokeWidthMm))), a.setAttribute("height", String(Math.max(0, e - this.strokeWidthMm))), this.radiusMm > 0 && a.setAttribute("rx", String(this.radiusMm)));
1714
+ const l = o === "rect" || o === "ellipse" || o === "arrow";
1715
+ a.setAttribute("fill", l ? this.fill : "none"), l && a.setAttribute("fill-opacity", String(this.fillOpacity)), a.setAttribute("stroke", this.stroke), a.setAttribute("stroke-width", String(this.strokeWidthMm)), i.appendChild(a), this.replaceChildren(i);
1716
+ }
1717
+ /** A line from the box's top-left to its bottom-right, with a solid head. */
1718
+ arrowPath(t, e) {
1719
+ const n = Math.min(t, e) * 0.28, i = Math.atan2(e, t), s = t, o = e, a = { x: s - n * Math.cos(i - 0.4), y: o - n * Math.sin(i - 0.4) }, l = { x: s - n * Math.cos(i + 0.4), y: o - n * Math.sin(i + 0.4) };
1720
+ return `M 0 0 L ${s} ${o} M ${s} ${o} L ${a.x} ${a.y} L ${l.x} ${l.y} Z`;
1721
+ }
1722
+ paintCartograph(t, e) {
1723
+ const { w: n, h: i } = C(this);
1724
+ if (n <= 0 || i <= 0) return;
1725
+ const s = typeof getComputedStyle == "function" ? getComputedStyle(this) : null, o = (d) => d * e.pxPerMm, a = this.getAttribute("fill") || s?.getPropertyValue("--om-carto-shape-fill")?.trim() || "#cccccc", l = this.getAttribute("stroke") || s?.getPropertyValue("--om-carto-shape-stroke")?.trim() || "#333333", c = o(this.strokeWidthMm), u = c / 2, m = this.kind;
1726
+ if (t.save(), t.globalAlpha = this.fillOpacity, t.fillStyle = a, t.strokeStyle = l, t.lineWidth = c, m === "ellipse")
1727
+ t.beginPath(), t.ellipse(o(n / 2), o(i / 2), Math.max(0, o(n / 2) - u), Math.max(0, o(i / 2) - u), 0, 0, Math.PI * 2), t.fill(), t.globalAlpha = 1, c > 0 && t.stroke();
1728
+ else if (m === "line" || m === "arrow") {
1729
+ if (t.globalAlpha = 1, t.beginPath(), t.moveTo(0, 0), t.lineTo(o(n), o(i)), c > 0 && t.stroke(), m === "arrow") {
1730
+ const d = Math.min(o(n), o(i)) * 0.28, h = Math.atan2(o(i), o(n));
1731
+ t.beginPath(), t.moveTo(o(n), o(i)), t.lineTo(o(n) - d * Math.cos(h - 0.4), o(i) - d * Math.sin(h - 0.4)), t.lineTo(o(n) - d * Math.cos(h + 0.4), o(i) - d * Math.sin(h + 0.4)), t.closePath(), t.fillStyle = l, t.fill();
1732
+ }
1733
+ } else {
1734
+ const d = o(this.radiusMm);
1735
+ t.beginPath(), d > 0 && typeof t.roundRect == "function" ? t.roundRect(u, u, o(n) - c, o(i) - c, d) : t.rect(u, u, o(n) - c, o(i) - c), t.fill(), t.globalAlpha = 1, c > 0 && t.stroke();
1736
+ }
1737
+ t.restore();
1738
+ }
1739
+ }
1740
+ x("om-shape", De);
1741
+ const ue = "data-om-carto-owned", Qr = ["contain", "cover", "fill"];
1742
+ class Ge extends R {
1743
+ static observedAttributes = [...P["om-image"]];
1744
+ get fit() {
1745
+ const t = this.getAttribute("fit");
1746
+ return Qr.includes(t ?? "") ? t : "contain";
1747
+ }
1748
+ get image() {
1749
+ return this.querySelector(":scope > img");
1750
+ }
1751
+ renderNow() {
1752
+ this.applyPlacement(), this.style.display = "block", this.style.overflow = "hidden";
1753
+ const t = this.getAttribute("src");
1754
+ let e = this.image;
1755
+ !e && t && (e = this.ownerDocument.createElement("img"), e.setAttribute(ue, ""), e.alt = "", e.crossOrigin = "anonymous", this.appendChild(e)), e && (t && e.hasAttribute(ue) && e.getAttribute("src") !== t && e.setAttribute("src", t), e.style.width = "100%", e.style.height = "100%", e.style.objectFit = this.fit, e.style.display = "block");
1756
+ }
1757
+ paintCartograph(t, e) {
1758
+ const n = this.image;
1759
+ if (!n || !n.complete || n.naturalWidth === 0) return;
1760
+ const { w: i, h: s } = C(this), o = i * e.pxPerMm, a = s * e.pxPerMm;
1761
+ if (o <= 0 || a <= 0) return;
1762
+ const l = this.fit;
1763
+ if (l === "fill") {
1764
+ t.drawImage(n, 0, 0, o, a);
1765
+ return;
1766
+ }
1767
+ const c = n.naturalWidth / n.naturalHeight, u = o / a, m = l === "contain" ? c > u : c < u, d = m ? o : a * c, h = m ? o / c : a;
1768
+ l === "cover" && (t.save(), t.beginPath(), t.rect(0, 0, o, a), t.clip()), t.drawImage(n, (o - d) / 2, (a - h) / 2, d, h), l === "cover" && t.restore();
1769
+ }
1770
+ }
1771
+ x("om-image", Ge);
1772
+ const tn = /* @__PURE__ */ new Set(["PathLayer", "LineLayer", "ArcLayer", "TripsLayer", "GreatCircleLayer", "PathStyleLayer"]), en = /* @__PURE__ */ new Set(["ScatterplotLayer", "IconLayer", "TextLayer", "PointCloudLayer", "HeatmapLayer", "ScreenGridLayer"]), rn = /* @__PURE__ */ new Set(["COGLayer", "ZarrLayer", "BitmapLayer", "TerrainLayer", "ImageOverlay"]);
1773
+ function nn(r) {
1774
+ const t = r.type ?? "";
1775
+ if (rn.has(t)) return "ramp";
1776
+ if (tn.has(t)) return "line";
1777
+ if (en.has(t)) return "point";
1778
+ if (t === "GeoJsonLayer") {
1779
+ const e = r.props ?? {}, n = e.getFillColor !== void 0, i = e.getLineColor !== void 0;
1780
+ if (n && !i) return "polygon";
1781
+ if (i && !n || e.filled === !1 && e.stroked !== !1) return "line";
1782
+ }
1783
+ return "polygon";
1784
+ }
1785
+ function on(r) {
1786
+ const t = r.meta?.legend;
1787
+ return r.meta?.color ?? (t?.kind === "ramp" ? t.colors[0] : t?.entries[0]?.color) ?? "#999";
1788
+ }
1789
+ const xt = 12;
1790
+ function sn(r, t = []) {
1791
+ const e = [];
1792
+ for (const n of r) {
1793
+ if (n.props?.visible === !1) continue;
1794
+ const i = t.filter((a) => a.layer === n.id), s = i.find((a) => a.match == null);
1795
+ if (s?.hide) continue;
1796
+ const o = an(n.meta?.legend);
1797
+ e.push({
1798
+ layerId: n.id,
1799
+ label: s?.label ?? n.meta?.label ?? n.id,
1800
+ color: s?.color ?? on(n),
1801
+ shape: s?.shape ?? nn(n),
1802
+ legend: ln(o, i),
1803
+ order: s?.order
1804
+ });
1805
+ }
1806
+ return e.map((n, i) => ({ row: n, index: i })).sort((n, i) => {
1807
+ const s = n.row.order ?? Number.POSITIVE_INFINITY, o = i.row.order ?? Number.POSITIVE_INFINITY;
1808
+ return s - o || n.index - i.index;
1809
+ }).map((n) => n.row);
1810
+ }
1811
+ function an(r) {
1812
+ if (!r || r.kind !== "categories" || r.entries.length <= xt) return r;
1813
+ const t = r.entries.slice(0, xt), e = r.entries.length - xt;
1814
+ return {
1815
+ ...r,
1816
+ entries: [...t, { color: r.defaultColor ?? "#999", label: `+${e} more` }]
1817
+ };
1818
+ }
1819
+ function ln(r, t) {
1820
+ const e = t.filter((i) => i.match != null);
1821
+ if (!r || r.kind === "ramp" || e.length === 0) return r;
1822
+ const n = [];
1823
+ for (const i of r.entries) {
1824
+ const s = e.find((o) => o.match === i.label);
1825
+ s?.hide || n.push({ color: s?.color ?? i.color, label: s?.label ?? i.label });
1826
+ }
1827
+ return { ...r, entries: n };
1828
+ }
1829
+ function cn(r) {
1830
+ const t = [];
1831
+ for (const e of r.querySelectorAll(":scope > om-legend-row")) {
1832
+ const n = e.getAttribute("layer");
1833
+ if (!n) continue;
1834
+ const i = e.getAttribute("order"), s = i == null || i.trim() === "" ? void 0 : Number(i);
1835
+ t.push({
1836
+ layer: n,
1837
+ match: e.getAttribute("match") ?? void 0,
1838
+ label: e.getAttribute("label") ?? void 0,
1839
+ hide: e.hasAttribute("hide"),
1840
+ order: s != null && Number.isFinite(s) ? s : void 0,
1841
+ color: e.getAttribute("color") ?? void 0,
1842
+ shape: e.getAttribute("shape") ?? void 0
1843
+ });
1844
+ }
1845
+ return t;
1846
+ }
1847
+ function qe(r) {
1848
+ const t = [];
1849
+ for (const e of r.querySelectorAll(":scope > om-legend-row")) {
1850
+ if (e.hasAttribute("hide")) continue;
1851
+ const n = e.getAttribute("order"), i = n == null || n.trim() === "" ? void 0 : Number(n);
1852
+ t.push({
1853
+ layerId: e.getAttribute("layer") ?? "",
1854
+ label: e.getAttribute("label") ?? e.getAttribute("layer") ?? "",
1855
+ color: e.getAttribute("color") ?? "#999",
1856
+ shape: e.getAttribute("shape") ?? "polygon",
1857
+ order: i != null && Number.isFinite(i) ? i : void 0
1858
+ });
1859
+ }
1860
+ return t.sort((e, n) => (e.order ?? Number.POSITIVE_INFINITY) - (n.order ?? Number.POSITIVE_INFINITY));
1861
+ }
1862
+ const he = "data-om-carto-legend-rows";
1863
+ class We extends R {
1864
+ static observedAttributes = [...P["om-legend"]];
1865
+ get derived() {
1866
+ return this.getAttribute("derived") !== "false";
1867
+ }
1868
+ /** The rows this legend currently shows — recomputed on demand, never stored. */
1869
+ get rows() {
1870
+ if (!this.derived) return qe(this);
1871
+ const n = O(this)?.mapElement?.getLayers?.();
1872
+ return n ? sn(n, cn(this)) : [];
1873
+ }
1874
+ renderNow() {
1875
+ if (this.applyPlacement(), this.style.display = "block", this.style.boxSizing = "border-box", this.style.overflow = "hidden", this.derived && !O(this)) {
1876
+ const e = this.getAttribute("for");
1877
+ G(this, e ? `Legend references no frame with id "${e}".` : "Legend has no frame to derive from (set for=).");
1878
+ return;
1879
+ }
1880
+ const t = this.rows;
1881
+ if (t.length === 0 && this.derived) {
1882
+ G(this, "Legend is waiting for its frame's layers.");
1883
+ return;
1884
+ }
1885
+ this.renderRows(t);
1886
+ }
1887
+ renderRows(t) {
1888
+ const e = this.ownerDocument;
1889
+ this.querySelector(`:scope > [${he}]`)?.remove(), Pr(this);
1890
+ const n = e.createElement("div");
1891
+ n.setAttribute(he, ""), n.style.cssText = "padding:2mm;font-size:2.6mm;line-height:1.35;";
1892
+ const i = this.getAttribute("title");
1893
+ if (i) {
1894
+ const o = e.createElement("div");
1895
+ o.textContent = i, o.style.cssText = "font-weight:600;margin-bottom:1.2mm;color:var(--om-carto-legend-title-ink);", n.appendChild(o);
1896
+ }
1897
+ const s = Number(this.getAttribute("columns"));
1898
+ Number.isFinite(s) && s > 1 && (n.style.columnCount = String(Math.floor(s)), n.style.columnGap = "3mm");
1899
+ for (const o of t)
1900
+ n.appendChild(this.buildRow(e, o)), o.legend && n.appendChild(this.buildSymbology(e, o));
1901
+ this.appendChild(n);
1902
+ }
1903
+ buildRow(t, e) {
1904
+ const n = t.createElement("div");
1905
+ n.style.cssText = "display:flex;align-items:center;gap:1.5mm;margin:0.6mm 0;break-inside:avoid;", n.appendChild(this.buildSwatch(t, e.color, e.shape));
1906
+ const i = t.createElement("span");
1907
+ return i.textContent = e.label, n.appendChild(i), n;
1908
+ }
1909
+ /** The swatch geometry carries meaning: an area, a line and a point read differently. */
1910
+ buildSwatch(t, e, n) {
1911
+ const i = t.createElement("span"), s = "display:inline-block;flex:none;width:3.2mm;height:3.2mm;";
1912
+ return n === "line" ? i.style.cssText = `${s}height:0.9mm;background:${e};` : n === "point" ? i.style.cssText = `${s}border-radius:50%;background:${e};` : n === "ramp" ? i.style.cssText = `${s}width:6mm;background:linear-gradient(to right, ${e}, transparent);` : i.style.cssText = `${s}background:${e};border:0.15mm solid rgba(0,0,0,.35);`, i;
1913
+ }
1914
+ buildSymbology(t, e) {
1915
+ const n = t.createElement("div");
1916
+ n.style.cssText = "margin:0.4mm 0 1.6mm 4.7mm;break-inside:avoid;";
1917
+ const i = e.legend, s = t.createElement("div");
1918
+ if (s.textContent = i.field, s.style.cssText = "font-size:2.1mm;opacity:.7;margin-bottom:0.5mm;", n.appendChild(s), i.kind === "ramp") {
1919
+ const o = t.createElement("div");
1920
+ o.style.cssText = `height:2.4mm;background:linear-gradient(to right, ${i.colors.join(", ")});border:0.15mm solid rgba(0,0,0,.25);`, n.appendChild(o);
1921
+ const a = t.createElement("div");
1922
+ return a.style.cssText = "display:flex;justify-content:space-between;font-size:2.1mm;opacity:.75;", a.append(ot(t, i.domain[0].toLocaleString("en-US")), ot(t, i.domain[1].toLocaleString("en-US"))), n.appendChild(a), n;
1923
+ }
1924
+ for (const o of i.entries) {
1925
+ const a = t.createElement("div");
1926
+ a.style.cssText = "display:flex;align-items:center;gap:1.2mm;margin:0.3mm 0;font-size:2.3mm;", a.appendChild(this.buildSwatch(t, o.color, "polygon")), a.appendChild(ot(t, o.label)), n.appendChild(a);
1927
+ }
1928
+ if (i.kind === "categories" && i.defaultColor) {
1929
+ const o = t.createElement("div");
1930
+ o.style.cssText = "display:flex;align-items:center;gap:1.2mm;margin:0.3mm 0;font-size:2.3mm;", o.appendChild(this.buildSwatch(t, i.defaultColor, "polygon")), o.appendChild(ot(t, "other")), n.appendChild(o);
1931
+ }
1932
+ return n;
1933
+ }
1934
+ paintCartograph(t, e) {
1935
+ const { w: n, h: i } = C(this), s = n * e.pxPerMm, o = i * e.pxPerMm;
1936
+ if (s <= 0 || o <= 0) return;
1937
+ const a = typeof getComputedStyle == "function" ? getComputedStyle(this) : null, l = L(a?.backgroundColor);
1938
+ l && (t.fillStyle = l, t.fillRect(0, 0, s, o));
1939
+ const c = L(a?.borderTopColor), u = kt(a?.borderTopWidth) * e.scale;
1940
+ c && u > 0 && (t.strokeStyle = c, t.lineWidth = u, t.strokeRect(u / 2, u / 2, s - u, o - u));
1941
+ const m = L(a?.color) ?? "#111", d = a?.fontFamily || "system-ui, sans-serif", h = (N) => N * e.pxPerMm;
1942
+ let f = h(2);
1943
+ const p = this.getAttribute("title");
1944
+ p && (t.fillStyle = m, t.font = `600 ${h(2.6)}px ${d}`, t.textBaseline = "top", t.fillText(p, h(2), f), f += h(4));
1945
+ const g = this.rows, b = Number(this.getAttribute("columns")), w = Math.min(g.length || 1, Number.isFinite(b) && b > 1 ? Math.floor(b) : 1), y = Math.max(1, Math.ceil(g.length / w)), S = 3, M = Math.max(1, (n - 4 - S * (w - 1)) / w), F = f;
1946
+ let _ = -1, $ = 2;
1947
+ t.font = `${h(2.6)}px ${d}`;
1948
+ for (const [N, T] of g.entries()) {
1949
+ const v = Math.min(w - 1, Math.floor(N / y));
1950
+ if (v !== _ && (_ = v, $ = 2 + _ * (M + S), f = _ === 0 ? F : h(2)), f > o || (t.fillStyle = T.color, T.shape === "line" ? t.fillRect(h($), f + h(1.2), h(3.2), h(0.9)) : T.shape === "point" ? (t.beginPath(), t.arc(h($ + 1.6), f + h(1.6), h(1.6), 0, Math.PI * 2), t.fill()) : t.fillRect(h($), f, h(3.2), h(3.2)), t.fillStyle = m, t.textBaseline = "top", t.fillText(T.label, h($ + 4.7), f + h(0.3)), f += h(4.2), !T.legend)) continue;
1951
+ const A = T.legend;
1952
+ if (t.save(), t.font = `${h(2.1)}px ${d}`, t.globalAlpha = 0.7, t.fillText(A.field, h($ + 2.7), f), t.globalAlpha = 1, f += h(2.8), A.kind === "ramp") {
1953
+ const k = Math.min(24, Math.max(1, M - 2.7)), E = $ + 2.7, U = t.createLinearGradient(h(E), 0, h(E + k), 0);
1954
+ A.colors.forEach((X, Xe) => U.addColorStop(A.colors.length === 1 ? 0 : Xe / (A.colors.length - 1), X)), t.fillStyle = U, t.fillRect(h(E), f, h(k), h(2.4)), f += h(3.4), t.fillStyle = m, t.font = `${h(2.1)}px ${d}`, t.fillText(A.domain[0].toLocaleString("en-US"), h(E), f);
1955
+ const Q = A.domain[1].toLocaleString("en-US");
1956
+ t.textAlign = "right", t.fillText(Q, h(E + k), f), t.textAlign = "left", f += h(3.2);
1957
+ } else {
1958
+ const k = [...A.entries];
1959
+ A.kind === "categories" && A.defaultColor && k.push({ color: A.defaultColor, label: "other" }), t.font = `${h(2.3)}px ${d}`;
1960
+ for (const E of k) {
1961
+ if (f > o) break;
1962
+ t.fillStyle = E.color, t.fillRect(h($ + 2.7), f, h(2.6), h(2.6)), t.fillStyle = m, t.fillText(E.label, h($ + 6.3), f + h(0.2)), f += h(3.2);
1963
+ }
1964
+ f += h(1);
1965
+ }
1966
+ t.restore(), t.font = `${h(2.6)}px ${d}`;
1967
+ }
1968
+ }
1969
+ }
1970
+ const ot = (r, t) => {
1971
+ const e = r.createElement("span");
1972
+ return e.textContent = t, e;
1973
+ };
1974
+ x("om-legend", We);
1975
+ const me = 48, Be = {
1976
+ toLngLat: (r, t) => [r, t],
1977
+ fromLngLat: (r) => [r[0], r[1]]
1978
+ };
1979
+ function de(r, t = 5) {
1980
+ return !Number.isFinite(r) || r <= 0 ? 0 : $e(r / Math.max(1, t));
1981
+ }
1982
+ function un({ georef: r, plane: t = Be, interval: e, intervalX: n, intervalY: i, labels: s = "outside", format: o }) {
1983
+ if (r.degenerate) return null;
1984
+ const a = r.corners.map((y) => t.fromLngLat(y)), l = a.map((y) => y[0]), c = a.map((y) => y[1]);
1985
+ if (l.some((y) => !Number.isFinite(y)) || c.some((y) => !Number.isFinite(y))) return null;
1986
+ const u = Math.min(...l), m = Math.max(...l), d = Math.min(...c), h = Math.max(...c), f = fe(n ?? e, de(m - u)), p = fe(i ?? e, de(h - d));
1987
+ if (f <= 0 || p <= 0 || (m - u) / f > 500 || (h - d) / p > 500) return null;
1988
+ const g = [], b = [], w = o ?? mn;
1989
+ for (let y = Math.ceil(u / f) * f; y <= m + 1e-9; y += f) {
1990
+ const S = pe(r, t, y, d, h, "x");
1991
+ S && (g.push(S), s !== "none" && be(b, S, r, w(y, "x"), "x", s));
1992
+ }
1993
+ for (let y = Math.ceil(d / p) * p; y <= h + 1e-9; y += p) {
1994
+ const S = pe(r, t, y, u, m, "y");
1995
+ S && (g.push(S), s !== "none" && be(b, S, r, w(y, "y"), "y", s));
1996
+ }
1997
+ return { lines: g, labels: b, intervalX: f, intervalY: p };
1998
+ }
1999
+ const fe = (r, t) => r != null && Number.isFinite(r) && r > 0 ? r : t;
2000
+ function pe(r, t, e, n, i, s) {
2001
+ const o = [];
2002
+ for (let l = 0; l <= me; l++) {
2003
+ const c = n + (i - n) * l / me, u = s === "x" ? t.toLngLat(e, c) : t.toLngLat(c, e);
2004
+ if (!Number.isFinite(u[0]) || !Number.isFinite(u[1])) continue;
2005
+ const m = r.project(u);
2006
+ m && o.push(m);
2007
+ }
2008
+ const a = hn(o, r.widthMm, r.heightMm);
2009
+ return a.length >= 2 ? { points: a, value: e, axis: s } : null;
2010
+ }
2011
+ function hn(r, t, e) {
2012
+ const n = (a) => a.xMm >= -0.01 && a.xMm <= t + 0.01 && a.yMm >= -0.01 && a.yMm <= e + 0.01, i = r.findIndex(n);
2013
+ if (i === -1) return [];
2014
+ let s = r.length - 1;
2015
+ for (; s > i && !n(r[s]); ) s--;
2016
+ return r.slice(Math.max(0, i - 1), Math.min(r.length, s + 2)).map((a) => ({ xMm: ge(a.xMm, 0, t), yMm: ge(a.yMm, 0, e) }));
2017
+ }
2018
+ const ge = (r, t, e) => Math.min(e, Math.max(t, r));
2019
+ function be(r, t, e, n, i, s) {
2020
+ if (i === "x") {
2021
+ const o = t.points.reduce((a, l) => l.yMm > a.yMm ? l : a);
2022
+ if (Math.abs(o.yMm - e.heightMm) > 0.5) return;
2023
+ r.push({ text: n, xMm: o.xMm, yMm: e.heightMm + (s === "outside" ? 3 : -1), edge: "bottom", axis: i });
2024
+ } else {
2025
+ const o = t.points.reduce((a, l) => l.xMm < a.xMm ? l : a);
2026
+ if (Math.abs(o.xMm) > 0.5) return;
2027
+ r.push({ text: n, xMm: s === "outside" ? -1.2 : 1.2, yMm: o.yMm, edge: "left", axis: i });
2028
+ }
2029
+ }
2030
+ function mn(r, t) {
2031
+ if (Math.abs(r) >= 1e3) return Math.round(r).toLocaleString("en-US");
2032
+ const e = Math.abs(r) < 1 ? 3 : Math.abs(r) < 10 ? 2 : 1;
2033
+ return `${Number(r.toFixed(e))}°`;
2034
+ }
2035
+ function dn(r, t) {
2036
+ const e = t === "x" ? r < 0 ? "W" : "E" : r < 0 ? "S" : "N", n = Math.abs(r), i = Math.floor(n), s = (n - i) * 60, o = Math.floor(s), a = Math.round((s - o) * 60), l = a === 60 ? o + 1 : o, c = l === 60 ? i + 1 : i, u = l === 60 ? 0 : l, m = a === 60 ? 0 : a;
2037
+ return `${c}°${String(u).padStart(2, "0")}′${String(m).padStart(2, "0")}″${e}`;
2038
+ }
2039
+ const st = "http://www.w3.org/2000/svg", fn = ["solid", "cross", "markers"], z = 1.4;
2040
+ class Ve extends R {
2041
+ static observedAttributes = [...P["om-graticule"]];
2042
+ resolvedPlane = null;
2043
+ resolvedPlaneKey = null;
2044
+ resolveToken = 0;
2045
+ get kind() {
2046
+ const t = this.getAttribute("kind");
2047
+ return fn.includes(t) ? t : "solid";
2048
+ }
2049
+ get labelPlacement() {
2050
+ const t = this.getAttribute("labels");
2051
+ return t === "none" || t === "inside" ? t : "outside";
2052
+ }
2053
+ /** The grid this element currently draws, or null when it cannot yet. */
2054
+ get graticule() {
2055
+ const t = O(this)?.georef;
2056
+ if (!t) return null;
2057
+ const e = this.gridPlane();
2058
+ if (!e) return null;
2059
+ const n = (i) => {
2060
+ const s = Number(this.getAttribute(i));
2061
+ return Number.isFinite(s) && s > 0 ? s : null;
2062
+ };
2063
+ return un({
2064
+ georef: t,
2065
+ plane: e,
2066
+ interval: n("interval"),
2067
+ intervalX: n("interval-x"),
2068
+ intervalY: n("interval-y"),
2069
+ labels: this.labelPlacement,
2070
+ format: this.getAttribute("format") === "dms" ? dn : void 0
2071
+ });
2072
+ }
2073
+ /**
2074
+ * The coordinate system the lattice is walked in. Geographic needs nothing;
2075
+ * a projected grid needs proj4, which resolves asynchronously and triggers
2076
+ * a re-render when it lands.
2077
+ */
2078
+ gridPlane() {
2079
+ const t = Y(this.getAttribute("crs"));
2080
+ if (!t || t === "EPSG:4326" || t === "CRS:84" || t === "OGC:CRS84") return Be;
2081
+ const e = `${t}|${this.getAttribute("crs-def") ?? ""}`;
2082
+ if (this.resolvedPlaneKey === e) return this.resolvedPlane;
2083
+ const n = K(t);
2084
+ if (n) {
2085
+ const s = { toLngLat: (o, a) => n.inverse([o, a]), fromLngLat: (o) => n.forward(o) };
2086
+ return this.resolvedPlane = s, this.resolvedPlaneKey = e, s;
2087
+ }
2088
+ const i = ++this.resolveToken;
2089
+ return Oe(t, this.getAttribute("crs-def")).then((s) => {
2090
+ i !== this.resolveToken || !s || (this.resolvedPlane = { toLngLat: (o, a) => s.inverse([o, a]), fromLngLat: (o) => s.forward(o) }, this.resolvedPlaneKey = e, this.scheduleRender());
2091
+ }).catch(() => {
2092
+ }), null;
2093
+ }
2094
+ renderNow() {
2095
+ if (this.applyPlacement(), this.style.display = "block", this.style.pointerEvents = "none", !O(this)) {
2096
+ const n = this.getAttribute("for");
2097
+ G(this, n ? `Graticule references no frame with id "${n}".` : "Graticule has no frame to cover (set for=).");
2098
+ return;
2099
+ }
2100
+ const e = this.graticule;
2101
+ if (!e) {
2102
+ this.replaceChildren();
2103
+ return;
2104
+ }
2105
+ this.renderSvg(e);
2106
+ }
2107
+ renderSvg(t) {
2108
+ const { w: e, h: n } = C(this), i = this.ownerDocument, s = i.createElementNS(st, "svg");
2109
+ s.setAttribute("width", `${e}mm`), s.setAttribute("height", `${n}mm`), s.setAttribute("viewBox", `0 0 ${e} ${n}`), s.style.cssText = "display:block;overflow:visible;";
2110
+ const o = "var(--om-carto-graticule-ink)", a = this.kind;
2111
+ for (const l of t.lines)
2112
+ if (a === "solid") {
2113
+ const c = i.createElementNS(st, "polyline");
2114
+ c.setAttribute("points", l.points.map((u) => `${u.xMm.toFixed(3)},${u.yMm.toFixed(3)}`).join(" ")), c.setAttribute("fill", "none"), c.setAttribute("stroke", o), c.setAttribute("stroke-width", "0.15"), s.appendChild(c);
2115
+ } else
2116
+ for (const c of this.tickPoints(l, a, t)) {
2117
+ const u = i.createElementNS(st, "path");
2118
+ u.setAttribute("d", `M ${c.xMm - z} ${c.yMm} H ${c.xMm + z} M ${c.xMm} ${c.yMm - z} V ${c.yMm + z}`), u.setAttribute("stroke", o), u.setAttribute("stroke-width", "0.15"), s.appendChild(u);
2119
+ }
2120
+ for (const l of t.labels) {
2121
+ const c = i.createElementNS(st, "text");
2122
+ c.setAttribute("x", String(l.xMm)), c.setAttribute("y", String(l.yMm)), c.setAttribute("font-size", "2.2"), c.setAttribute("fill", o), c.setAttribute("font-family", "var(--om-carto-font)"), c.setAttribute("text-anchor", l.edge === "left" ? "end" : "middle"), c.textContent = l.text, s.appendChild(c);
2123
+ }
2124
+ this.replaceChildren(s);
2125
+ }
2126
+ /** Intersections of this line with the perpendicular family. */
2127
+ tickPoints(t, e, n) {
2128
+ if (e === "markers") return [t.points[0], t.points[t.points.length - 1]];
2129
+ const i = n.lines.filter((o) => o.axis !== t.axis), s = [];
2130
+ for (const o of i) {
2131
+ const a = pn(t.points, o.points);
2132
+ a && s.push(a);
2133
+ }
2134
+ return s;
2135
+ }
2136
+ paintCartograph(t, e) {
2137
+ const n = this.graticule;
2138
+ if (!n) return;
2139
+ const i = typeof getComputedStyle == "function" ? getComputedStyle(this) : null, s = L(i?.color) ?? "#ffffff", o = (l) => l * e.pxPerMm, a = this.kind;
2140
+ t.save(), t.strokeStyle = s, t.fillStyle = s, t.lineWidth = o(0.15);
2141
+ for (const l of n.lines)
2142
+ if (a === "solid")
2143
+ t.beginPath(), l.points.forEach((c, u) => u === 0 ? t.moveTo(o(c.xMm), o(c.yMm)) : t.lineTo(o(c.xMm), o(c.yMm))), t.stroke();
2144
+ else
2145
+ for (const c of this.tickPoints(l, a, n))
2146
+ t.beginPath(), t.moveTo(o(c.xMm - z), o(c.yMm)), t.lineTo(o(c.xMm + z), o(c.yMm)), t.moveTo(o(c.xMm), o(c.yMm - z)), t.lineTo(o(c.xMm), o(c.yMm + z)), t.stroke();
2147
+ t.font = `${o(2.2)}px ${i?.fontFamily || "system-ui, sans-serif"}`, t.textBaseline = "alphabetic";
2148
+ for (const l of n.labels)
2149
+ t.textAlign = l.edge === "left" ? "right" : "center", t.fillText(l.text, o(l.xMm), o(l.yMm));
2150
+ t.restore();
2151
+ }
2152
+ }
2153
+ function pn(r, t) {
2154
+ let e = null, n = Number.POSITIVE_INFINITY;
2155
+ for (const i of r)
2156
+ for (const s of t) {
2157
+ const o = (i.xMm - s.xMm) ** 2 + (i.yMm - s.yMm) ** 2;
2158
+ o < n && (n = o, e = i);
2159
+ }
2160
+ return n <= 1 ? e : null;
2161
+ }
2162
+ x("om-graticule", Ve);
2163
+ function gn(r, t) {
2164
+ if (r == null || typeof r != "object") return;
2165
+ const e = r.properties;
2166
+ return e && t in e ? e[t] : r[t];
2167
+ }
2168
+ function Pt(r, t) {
2169
+ if (Array.isArray(r)) {
2170
+ if (typeof r[0] == "number") {
2171
+ t.push([r[0], r[1]]);
2172
+ return;
2173
+ }
2174
+ for (const e of r) Pt(e, t);
2175
+ }
2176
+ }
2177
+ function bn(r) {
2178
+ return r ? r.tileData != null ? "tiled" : r.asset != null ? "asset" : null : "empty";
2179
+ }
2180
+ function I(r, t) {
2181
+ return gn(r, t);
2182
+ }
2183
+ function Tt(r) {
2184
+ const t = {}, e = (i) => {
2185
+ for (const [s, o] of Object.entries(i))
2186
+ o == null || typeof o == "object" || (t[s] = String(o));
2187
+ };
2188
+ e(r);
2189
+ const n = r.properties;
2190
+ return n && typeof n == "object" && e(n), t;
2191
+ }
2192
+ function yn(r) {
2193
+ let t = 1 / 0, e = 1 / 0, n = -1 / 0, i = -1 / 0;
2194
+ const s = [], o = r.geometry;
2195
+ if (o?.coordinates != null) Pt(o.coordinates, s);
2196
+ else if (Array.isArray(r.coordinates)) Pt(r.coordinates, s);
2197
+ else {
2198
+ const a = Number(I(r, "lng") ?? I(r, "longitude") ?? I(r, "lon") ?? I(r, "x")), l = Number(I(r, "lat") ?? I(r, "latitude") ?? I(r, "y"));
2199
+ Number.isFinite(a) && Number.isFinite(l) && s.push([a, l]);
2200
+ }
2201
+ for (const [a, l] of s)
2202
+ !Number.isFinite(a) || !Number.isFinite(l) || (t = Math.min(t, a), n = Math.max(n, a), e = Math.min(e, l), i = Math.max(i, l));
2203
+ return !Number.isFinite(t) || !Number.isFinite(e) ? null : [t, e, n, i];
2204
+ }
2205
+ function wn(r, t) {
2206
+ const [e, n, i, s] = r, o = Math.max(i - e, 1e-4), a = Math.max(s - n, 1e-4), l = o * t, c = a * t;
2207
+ return [e - l, n - c, i + l, s + c];
2208
+ }
2209
+ const Vn = ([r, t, e, n]) => Math.max(0, e - r) * Math.max(0, n - t);
2210
+ function Mn({ features: r, filter: t, sort: e }) {
2211
+ let n = t ? r.filter((s) => vn(t, s)) : [...r];
2212
+ const i = An(e);
2213
+ return i.length > 0 && (n = [...n].sort((s, o) => {
2214
+ for (const { field: a, descending: l } of i) {
2215
+ const c = I(s, a), u = I(o, a);
2216
+ if (c == null && u == null) continue;
2217
+ if (c == null) return 1;
2218
+ if (u == null) return -1;
2219
+ const m = Sn(c, u);
2220
+ if (m !== 0) return l ? -m : m;
2221
+ }
2222
+ return 0;
2223
+ })), n;
2224
+ }
2225
+ function vn(r, t) {
2226
+ try {
2227
+ return !!r(t);
2228
+ } catch {
2229
+ return !1;
2230
+ }
2231
+ }
2232
+ function An(r) {
2233
+ return r ? r.split(",").map((t) => t.trim()).filter(Boolean).map((t) => t.startsWith("-") ? { field: t.slice(1), descending: !0 } : { field: t, descending: !1 }) : [];
2234
+ }
2235
+ function Sn(r, t) {
2236
+ return typeof r == "number" && typeof t == "number" ? r - t : String(r).localeCompare(String(t), void 0, { numeric: !0, sensitivity: "base" });
2237
+ }
2238
+ function Cn(r) {
2239
+ const t = r.replace(/[\x00-\x1f<>:"/\\|?*]+/g, "-").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, 100);
2240
+ return t === "" ? "page" : t;
2241
+ }
2242
+ function xn(r, t, e) {
2243
+ const n = /* @__PURE__ */ new Map();
2244
+ return t.map((i, s) => {
2245
+ const o = Tt(i), a = (r ?? "page-{{atlas.index}}").replace(/\{\{\s*atlas\.([\w]+)\s*\}\}/g, (u, m) => m === "index" ? String(s + 1) : m === "count" ? String(t.length) : o[m] ?? u), l = Cn(a), c = n.get(l) ?? 0;
2246
+ return n.set(l, c + 1), `${c === 0 ? l : `${l}-${c + 1}`}.${e}`;
2247
+ });
2248
+ }
2249
+ class He extends R {
2250
+ static observedAttributes = [...P["om-atlas"]];
2251
+ /**
2252
+ * Pages are SNAPSHOTTED when iteration first starts. A streaming or polling
2253
+ * layer would otherwise change the page count halfway through an export,
2254
+ * producing an archive whose contents do not match its own index.
2255
+ */
2256
+ pages = null;
2257
+ currentIndex = 0;
2258
+ rejection = null;
2259
+ /** The frame whose layer drives the iteration — the same `for=` rule every furniture element follows. */
2260
+ get frame() {
2261
+ return O(this);
2262
+ }
2263
+ get count() {
2264
+ return this.ensurePages().length;
2265
+ }
2266
+ get index() {
2267
+ return this.currentIndex;
2268
+ }
2269
+ get current() {
2270
+ return this.ensurePages()[this.currentIndex] ?? null;
2271
+ }
2272
+ /** Why this atlas cannot run, if it cannot. */
2273
+ get problem() {
2274
+ return this.ensurePages(), this.rejection;
2275
+ }
2276
+ /** Export filenames, one per page, already de-duplicated. */
2277
+ filenames(t = "png") {
2278
+ return xn(this.getAttribute("filename"), this.ensurePages(), t);
2279
+ }
2280
+ /** Human page labels, for an editor's stepper and PDF bookmarks. */
2281
+ pageName(t) {
2282
+ const e = this.ensurePages()[t];
2283
+ if (!e) return `Page ${t + 1}`;
2284
+ const n = Tt(e), i = this.getAttribute("page-name");
2285
+ return i ? i.replace(
2286
+ /\{\{\s*atlas\.([\w]+)\s*\}\}/g,
2287
+ (s, o) => o === "index" ? String(t + 1) : o === "count" ? String(this.count) : n[o] ?? s
2288
+ ) : n.name ?? n.title ?? `Page ${t + 1}`;
2289
+ }
2290
+ /**
2291
+ * Recompute the page list from the layer as it stands now, and apply the
2292
+ * current page. Applying is part of refreshing because the page list first
2293
+ * becomes non-empty when the frame mounts its map: without it, page 1 would
2294
+ * never be applied and a cartograph would print `{{atlas.*}}` literally with
2295
+ * the authored camera, until something else happened to seek.
2296
+ */
2297
+ refresh() {
2298
+ this.pages = null, this.rejection = null, this.ensurePages().length > 0 && this.applyPage();
2299
+ }
2300
+ ensurePages() {
2301
+ if (this.pages) return this.pages;
2302
+ const e = this.frame?.mapElement?.getLayers?.(), n = this.getAttribute("layer"), i = e?.find((a) => a.id === n), s = e ? bn(i) : null;
2303
+ if (!e || s)
2304
+ return this.rejection = e ? s : null, this.pages = [];
2305
+ const o = this.materialize(i);
2306
+ return this.pages = Mn({ features: o, filter: this.compileFilter(), sort: this.getAttribute("sort") }), this.currentIndex = Math.min(this.currentIndex, Math.max(0, this.pages.length - 1)), this.pages;
2307
+ }
2308
+ /**
2309
+ * Rows from the layer, with the layer's OWN declarative filters applied —
2310
+ * the coherence rule. An atlas that paged over unfiltered rows would print
2311
+ * sheets for features the map itself is hiding.
2312
+ */
2313
+ materialize(t) {
2314
+ const e = H(), n = t.data;
2315
+ if (!n) return [];
2316
+ const i = e?.asRows ? e.asRows(n) : Array.isArray(n) ? n : [], s = [t.filter, t.categoryFilter].filter(Boolean);
2317
+ return s.length === 0 ? i : i.filter(
2318
+ (o) => s.every((a) => {
2319
+ if (!a.field) return !0;
2320
+ const l = I(o, a.field);
2321
+ if (a.range) {
2322
+ const c = Number(l);
2323
+ return Number.isFinite(c) && c >= a.range[0] && c <= a.range[1];
2324
+ }
2325
+ return a.categories ? a.categories.includes(l) : !0;
2326
+ })
2327
+ );
2328
+ }
2329
+ /** `filter=` uses the CORE expression language — the syntax authors already know from om-layer. */
2330
+ compileFilter() {
2331
+ const t = this.getAttribute("filter");
2332
+ if (!t) return;
2333
+ const e = H();
2334
+ if (e?.compileExpression)
2335
+ try {
2336
+ const n = e.compileExpression(t, { shape: "geojson", paramName: "d" });
2337
+ return (i) => !!n.fn(i);
2338
+ } catch (n) {
2339
+ console.warn(`[onlymapjs/cartograph] <om-atlas filter="${t}"> did not compile: ${n.message}`);
2340
+ return;
2341
+ }
2342
+ }
2343
+ /** Move to a page: refit the following frames, re-resolve tokens, announce it. */
2344
+ seek(t) {
2345
+ const e = this.ensurePages();
2346
+ e.length !== 0 && (this.currentIndex = Math.min(Math.max(0, Math.trunc(t)), e.length - 1), this.applyPage());
2347
+ }
2348
+ next() {
2349
+ this.seek(this.currentIndex + 1);
2350
+ }
2351
+ applyPage() {
2352
+ const t = this.current;
2353
+ if (!t) return;
2354
+ const e = this.closest("om-cartograph");
2355
+ for (const n of e?.querySelectorAll("om-frame") ?? []) {
2356
+ if (n.getAttribute("atlas-fit") !== "feature") continue;
2357
+ const i = yn(t);
2358
+ if (!i) continue;
2359
+ const s = Number(n.getAttribute("atlas-margin")), o = wn(i, Number.isFinite(s) && s > 0 ? s : 0.15);
2360
+ n.fitBounds?.(o);
2361
+ }
2362
+ e?.setAtlasFields({ ...Tt(t), index: String(this.currentIndex + 1), count: String(this.count) }), e?.refreshFurniture(), this.dispatchEvent(
2363
+ new CustomEvent("om-atlas-page", {
2364
+ bubbles: !0,
2365
+ detail: { index: this.currentIndex, count: this.count, name: this.pageName(this.currentIndex), feature: t }
2366
+ })
2367
+ );
2368
+ }
2369
+ renderNow() {
2370
+ this.style.display = "none", this.refresh();
2371
+ }
2372
+ }
2373
+ x("om-atlas", He);
2374
+ const $n = /* @__PURE__ */ new Set(["class", "style", "id", "hidden", "title", "lang", "dir", "slot", "part", "role", "tabindex"]), kn = /* @__PURE__ */ new Set(["style", "script", "img", "template", "om-map"]), En = ["data-om-carto-credit", "data-om-carto-marks", "data-om-carto-flattened"], Nn = /* @__PURE__ */ new Set(["om-text", "om-legend", "om-scalebar", "om-north", "om-shape", "om-image"]), Pn = /* @__PURE__ */ new Set(["om-frame", "om-legend", "om-scalebar", "om-north", "om-graticule", "om-text", "om-shape", "om-image"]), Tn = 0.02, Ln = (r) => Math.max(0, Number(r.getAttribute("bleed")) || 0);
2375
+ function Ye(r) {
2376
+ const t = r.tagName.toLowerCase();
2377
+ return r.id ? `${t}#${r.id}` : t;
2378
+ }
2379
+ function V(r, t) {
2380
+ const e = r.getAttribute(t);
2381
+ if (e == null || e.trim() === "") return null;
2382
+ const n = Number(e);
2383
+ return Number.isFinite(n) ? n : null;
2384
+ }
2385
+ function Fn(r, t) {
2386
+ return r.x < t.x + t.w && t.x < r.x + r.w && r.y < t.y + t.h && t.y < r.y + r.h;
2387
+ }
2388
+ function In(r, t) {
2389
+ const e = r.getAttribute("style");
2390
+ if (!e) return null;
2391
+ const n = new RegExp(`(?:^|;)\\s*${t}\\s*:\\s*(-?[\\d.]+)mm`, "i").exec(e);
2392
+ return n ? Number(n[1]) : null;
2393
+ }
2394
+ function Ke(r) {
2395
+ const t = [], e = (o, a, l, c, u) => {
2396
+ t.push({ severity: o, element: Ye(a), ...u ? { attribute: u } : {}, message: l, fix: c });
2397
+ }, n = [...r.querySelectorAll("om-cartograph")];
2398
+ if (n.length === 0 && r.tagName?.toLowerCase() === "om-cartograph" && n.push(r), n.length === 0)
2399
+ return { valid: !0, errors: [], warnings: [] };
2400
+ for (const o of n) Rn(o, e);
2401
+ const i = t.filter((o) => o.severity === "error"), s = t.filter((o) => o.severity === "warning");
2402
+ return { valid: i.length === 0, errors: i, warnings: s };
2403
+ }
2404
+ function Rn(r, t) {
2405
+ const e = r.getAttribute("size"), n = V(r, "width") != null && V(r, "height") != null;
2406
+ e && !Bt.includes(e) && !n && t(
2407
+ "error",
2408
+ r,
2409
+ `Unknown page size "${e}" and no width/height to fall back on.`,
2410
+ `Use one of ${Bt.join(", ")}, or give width= and height= in millimetres.`,
2411
+ "size"
2412
+ );
2413
+ const i = r.getAttribute("cvd");
2414
+ i && i.trim().toLowerCase() !== "none" && !Lt(i) && t("warning", r, `Unknown cvd="${i}" — no simulation applied.`, `Use one of ${rr.join(", ")}.`, "cvd"), r.hasAttribute("crop-marks") && Ln(r) <= 0 && t("warning", r, "crop-marks needs bleed to draw into — trim marks sit outside the page box.", 'Add bleed="3" (mm), or drop crop-marks.', "crop-marks");
2415
+ const s = V(r, "safe-zone");
2416
+ if (s != null && s > 0) {
2417
+ const h = ct(r.getAttribute("size"), r.getAttribute("width"), r.getAttribute("height"), r.getAttribute("orientation"));
2418
+ s * 2 >= Math.min(h.widthMm, h.heightMm) && t("warning", r, `safe-zone=${s}mm leaves no usable area on a ${h.widthMm} × ${h.heightMm} mm page.`, "Use a smaller inset — 5–10mm is typical.", "safe-zone");
2419
+ }
2420
+ const o = r.getAttribute("format");
2421
+ o == null ? t("warning", r, "No format= on the cartograph root.", `Add format="${pt}" so future runtimes can migrate this document.`, "format") : o !== pt && t("warning", r, `Unrecognised document format "${o}".`, `This build understands "${pt}".`, "format");
2422
+ const { widthMm: a, heightMm: l } = ct(e, r.getAttribute("width"), r.getAttribute("height"), r.getAttribute("orientation")), c = Math.max(0, V(r, "bleed") ?? 0), u = /* @__PURE__ */ new Set(), m = [...r.querySelectorAll("om-frame")];
2423
+ for (const h of m)
2424
+ h.id ? u.add(h.id) : t("error", h, "Frame has no id.", "Give every frame an id so furniture can reference it with for=.", "id");
2425
+ for (const h of r.children) {
2426
+ if (En.some((p) => h.hasAttribute(p))) continue;
2427
+ const f = h.tagName.toLowerCase();
2428
+ !gt.includes(f) && !kn.has(f) && t("error", h, `<${f}> is not a cartograph element.`, `Use one of ${gt.join(", ")}, or move this content inside an <om-text>.`);
2429
+ }
2430
+ const d = [];
2431
+ for (const h of [r, ...r.querySelectorAll("*")]) {
2432
+ const f = h.tagName.toLowerCase();
2433
+ if (gt.includes(f) && !h.closest("om-map")) {
2434
+ if (_n(h, f, t), Pn.has(f)) {
2435
+ const p = C(h);
2436
+ On(h, p, a, l, c, t), Nn.has(f) && !h.hasAttribute("hidden") && d.push({ el: h, box: p });
2437
+ }
2438
+ f === "om-frame" && Un(h, t), f === "om-legend" && jn(h, m, t), f === "om-atlas" && zn(h, m, t), f === "om-scalebar" && Dn(h, m, t), f === "om-text" && Gn(h, m, t);
2439
+ for (const p of ["for", "overview-of"]) {
2440
+ const g = h.getAttribute(p);
2441
+ g && !u.has(g) && t("error", h, `${p}="${g}" references no frame on this page.`, `Point it at one of: ${[...u].join(", ") || "(no frames)"}.`, p);
2442
+ }
2443
+ }
2444
+ }
2445
+ for (let h = 0; h < d.length; h++)
2446
+ for (let f = h + 1; f < d.length; f++) {
2447
+ const p = d[h], g = d[f];
2448
+ p.box.w <= 0 || p.box.h <= 0 || g.box.w <= 0 || g.box.h <= 0 || Fn(p.box, g.box) && t(
2449
+ "warning",
2450
+ p.el,
2451
+ `Overlaps ${Ye(g.el)} on the page.`,
2452
+ "Move one of them — furniture overlapping furniture usually means a stale layout (frames may sit under anything)."
2453
+ );
2454
+ }
2455
+ }
2456
+ function _n(r, t, e) {
2457
+ const n = new Set(P[t] ?? []);
2458
+ for (const i of r.attributes) {
2459
+ const s = i.name.toLowerCase();
2460
+ if (n.has(s) || $n.has(s) || s.startsWith("data-") || s.startsWith("aria-")) continue;
2461
+ const o = gr.get(s);
2462
+ if (o) {
2463
+ e("warning", r, `Attribute "${s}" has no effect: ${o}`, "Remove it, or use the documented alternative.", s);
2464
+ continue;
2465
+ }
2466
+ e("warning", r, `Unknown attribute "${s}" on <${t}> — likely a typo.`, `Valid attributes: ${[...n].join(", ")}.`, s);
2467
+ }
2468
+ }
2469
+ function On(r, t, e, n, i, s) {
2470
+ if (t.w <= 0 || t.h <= 0) {
2471
+ s("error", r, `Element has no size (w=${t.w}, h=${t.h}).`, "Give w= and h= in millimetres — both greater than zero.", t.w <= 0 ? "w" : "h");
2472
+ return;
2473
+ }
2474
+ const o = -i, a = e + i, l = n + i;
2475
+ (t.x < o || t.y < o || t.x + t.w > a + 1e-6 || t.y + t.h > l + 1e-6) && s(
2476
+ "error",
2477
+ r,
2478
+ `Element box (${t.x}, ${t.y}, ${t.w} × ${t.h} mm) falls outside the ${e} × ${n} mm page${i ? ` (+${i}mm bleed)` : ""}.`,
2479
+ "Move or resize it so the whole box sits on the sheet."
2480
+ );
2481
+ const c = [
2482
+ ["left", t.x],
2483
+ ["top", t.y],
2484
+ ["width", t.w],
2485
+ ["height", t.h]
2486
+ ];
2487
+ for (const [u, m] of c) {
2488
+ const d = In(r, u);
2489
+ d != null && Math.abs(d - m) > 0.01 && s(
2490
+ "error",
2491
+ r,
2492
+ `Inline style ${u}:${d}mm disagrees with the placement attribute (${m}mm).`,
2493
+ "The attributes are authoritative — make the inline style match, or drop it and let the runtime write it.",
2494
+ "style"
2495
+ );
2496
+ }
2497
+ }
2498
+ function Un(r, t) {
2499
+ if ((r.getAttribute("mode") === "static" ? "static" : "live") === "static") {
2500
+ const n = r.getAttribute("corners"), i = mt(n);
2501
+ i || t(
2502
+ "error",
2503
+ r,
2504
+ n == null ? "Static frame has no corners=." : `Static frame has malformed corners= ("${n}").`,
2505
+ 'Give four lon/lat pairs in tl, tr, br, bl order — corners="[[lng,lat],[lng,lat],[lng,lat],[lng,lat]]".',
2506
+ "corners"
2507
+ ), r.querySelector(":scope > img") || t("error", r, "Static frame has no <img> child to place.", "Add the georeferenced raster as an <img> child — it is also what renders with JavaScript disabled.");
2508
+ const s = C(r), o = K(r.getAttribute("crs"));
2509
+ if (i && o && s.w > 0 && s.h > 0) {
2510
+ const a = ht({ corners: i, widthMm: s.w, heightMm: s.h, crsForward: o });
2511
+ if (a.degenerate)
2512
+ t("error", r, "Frame corners are degenerate (collinear or coincident) — nothing can be placed on this frame.", "Check the corner order: tl, tr, br, bl.", "corners");
2513
+ else {
2514
+ const l = Br(a);
2515
+ l != null && l > Tn && t(
2516
+ "warning",
2517
+ r,
2518
+ `Ground scale varies ${(l * 100).toFixed(1)}% across this frame, so a single scale bar is only true near its anchor.`,
2519
+ "Shrink the frame's latitude span, or drop the scale bar in favour of a stated representative fraction."
2520
+ );
2521
+ }
2522
+ }
2523
+ } else {
2524
+ lt(r) || t(
2525
+ "error",
2526
+ r,
2527
+ "Live frame has no valid camera.",
2528
+ 'Give it center="[lng,lat]" and an explicit numeric zoom= (zoom 0 is valid).',
2529
+ r.hasAttribute("center") ? "zoom" : "center"
2530
+ ), !r.getAttribute("src") && !r.querySelector(":scope > om-map") && t("error", r, "Live frame has no map source.", "Give it src= or add an inline <om-map> child.", "src");
2531
+ for (const i of ["crs", "corners"])
2532
+ r.hasAttribute(i) && t(
2533
+ "warning",
2534
+ r,
2535
+ `${i}= is ignored on a live frame — live frames are always EPSG:3857 and derive their corners from the camera.`,
2536
+ 'Remove it, or set mode="static" if this frame is a georeferenced raster.',
2537
+ i
2538
+ );
2539
+ const n = Y(r.getAttribute("crs"));
2540
+ n && n !== "EPSG:3857" && t("error", r, `Live frame declares ${n}, but live frames render EPSG:3857.`, 'Use mode="static" with a georeferenced raster for other projections.', "crs");
2541
+ }
2542
+ }
2543
+ function Rt(r, t) {
2544
+ const e = r.getAttribute("for");
2545
+ return e ? t.find((n) => n.id === e) : t.length === 1 ? t[0] : void 0;
2546
+ }
2547
+ function jn(r, t, e) {
2548
+ const n = r.getAttribute("derived") !== "false", i = Rt(r, t);
2549
+ n && i?.getAttribute("mode") === "static" && e(
2550
+ "warning",
2551
+ r,
2552
+ "This legend derives from a static frame, which has no layers to derive from.",
2553
+ 'Write the rows as <om-legend-row color= label= shape=> children and set derived="false".',
2554
+ "derived"
2555
+ );
2556
+ const s = qe(r);
2557
+ if (s.length < 2) return;
2558
+ const o = or(s.map((a) => ({ label: a.label, color: a.color, layer: a.layerId })));
2559
+ if (o.collapses.length > 0 && o.worstType) {
2560
+ const a = o.collapses.filter((c) => c.type === o.worstType), l = a.slice(0, 3).map((c) => `"${c.a.label}"/"${c.b.label}"`).join(", ");
2561
+ e(
2562
+ "warning",
2563
+ r,
2564
+ `Under ${o.worstType}, ${a.length === 1 ? "one legend pair becomes" : `${a.length} legend pairs become`} indistinguishable: ${l}.`,
2565
+ "Pick colours that differ in lightness as well as hue, or add a pattern or label to separate them."
2566
+ );
2567
+ }
2568
+ }
2569
+ function zn(r, t, e) {
2570
+ const n = r.getAttribute("layer");
2571
+ n || e("error", r, "Atlas has no layer= to iterate.", 'Name a layer inside the frame, e.g. layer="parcels".', "layer");
2572
+ const i = r.getAttribute("for"), s = Rt(r, t);
2573
+ if (!s) {
2574
+ !i && t.length !== 1 && e("error", r, `Atlas does not say which frame drives it, and the page has ${t.length} frames.`, 'Add for="<frame id>".', "for");
2575
+ return;
2576
+ }
2577
+ if (s.getAttribute("mode") === "static") {
2578
+ e(
2579
+ "error",
2580
+ r,
2581
+ "An atlas cannot iterate a static frame — a raster has no features.",
2582
+ "Point the atlas at a live frame whose map carries the layer.",
2583
+ "for"
2584
+ );
2585
+ return;
2586
+ }
2587
+ const o = s.querySelector(":scope > om-map");
2588
+ if (o && n) {
2589
+ const a = [...o.querySelectorAll("om-layer")].find((c) => c.id === n);
2590
+ if (!a) {
2591
+ e("error", r, `Atlas layer "${n}" is not in frame #${s.id}'s map.`, "Check the layer id, or point the atlas at the frame that has it.", "layer");
2592
+ return;
2593
+ }
2594
+ const l = a.getAttribute("data") ?? "";
2595
+ /\{z\}|\{x\}|\{y\}/.test(l) && e(
2596
+ "error",
2597
+ r,
2598
+ `Atlas layer "${n}" is tiled, so its features live on a server rather than in this document.`,
2599
+ "Iterate a layer whose data is loaded locally (GeoJSON, CSV, inline JSON).",
2600
+ "layer"
2601
+ );
2602
+ }
2603
+ }
2604
+ function Dn(r, t, e) {
2605
+ const n = V(r, "segment-length");
2606
+ if (n == null) return;
2607
+ const i = Rt(r, t);
2608
+ if (!i) return;
2609
+ const s = mt(i.getAttribute("corners")), o = K(i.getAttribute("crs")), a = C(i);
2610
+ if (!s || !o || a.w <= 0 || a.h <= 0) return;
2611
+ const l = ht({ corners: s, widthMm: a.w, heightMm: a.h, crsForward: o }), c = C(r), u = ke({
2612
+ groundMetersPerMm: l.groundMetersPerMm(),
2613
+ maxWidthMm: c.w,
2614
+ segments: V(r, "segments") ?? void 0,
2615
+ segmentLengthMeters: n
2616
+ });
2617
+ u && u.totalMm > c.w + 1e-6 && e(
2618
+ "warning",
2619
+ r,
2620
+ `segment-length=${n}m makes the bar ${u.totalMm.toFixed(1)}mm wide, wider than its ${c.w}mm box.`,
2621
+ "Widen the bar's box, use fewer segments, or drop segment-length and let it snap to a 1-2-5 length that fits.",
2622
+ "segment-length"
2623
+ );
2624
+ }
2625
+ function Gn(r, t, e) {
2626
+ const n = r.textContent ?? "", i = /* @__PURE__ */ new Set(["title", "attribution", "date", "notes", "scale", "crs"]), s = new Set(t.map((o) => o.id).filter(Boolean));
2627
+ for (const o of $r(n))
2628
+ if (!o.startsWith("atlas.")) {
2629
+ if (o.startsWith("frame.")) {
2630
+ const a = o.slice(6), l = a.lastIndexOf("."), c = l > 0 ? a.slice(0, l) : "", u = l > 0 ? a.slice(l + 1) : "";
2631
+ s.has(c) ? u !== "scale" && u !== "crs" && e("warning", r, `{{${o}}} — frames provide scale and crs.`, `Use {{frame.${c}.scale}} or {{frame.${c}.crs}}.`) : e("warning", r, `{{${o}}} references no frame with id "${c}".`, `Frames on this page: ${[...s].join(", ") || "(none)"}.`);
2632
+ continue;
2633
+ }
2634
+ if ((o === "scale" || o === "crs") && t.length !== 1) {
2635
+ e(
2636
+ "warning",
2637
+ r,
2638
+ `{{${o}}} is ambiguous with ${t.length} frames on the page.`,
2639
+ `Name the frame: {{frame.<id>.${o}}}.`
2640
+ );
2641
+ continue;
2642
+ }
2643
+ i.has(o) || e("warning", r, `Unknown token {{${o}}} — it will render literally.`, `Known tokens: ${[...i].join(", ")}, frame.<id>.scale, frame.<id>.crs, atlas.<field>.`);
2644
+ }
2645
+ }
2646
+ function Hn(r) {
2647
+ return Ke(pr(r));
2648
+ }
2649
+ async function Yn(r) {
2650
+ const t = Ke(r), e = [...r.querySelectorAll("om-frame")].filter((a) => a.querySelector(":scope > om-map"));
2651
+ if (e.length === 0) return t;
2652
+ const { loadCore: n } = await Promise.resolve().then(() => Je), i = await n(), s = [...t.errors], o = [...t.warnings];
2653
+ for (const a of e) {
2654
+ const l = a.id ? `om-frame#${a.id}` : "om-frame", c = i.validateManifest(a);
2655
+ for (const u of c.errors) s.push({ ...u, element: `${l} > ${u.element}` });
2656
+ for (const u of c.warnings) o.push({ ...u, element: `${l} > ${u.element}` });
2657
+ }
2658
+ return { valid: s.length === 0, errors: s, warnings: o };
2659
+ }
2660
+ function Kn() {
2661
+ x("om-cartograph", Fe), x("om-text", Ie), x("om-frame", Ue), x("om-scalebar", je), x("om-north", ze), x("om-shape", De), x("om-image", Ge), x("om-legend", We), x("om-graticule", Ve), x("om-atlas", He);
2662
+ }
2663
+ ye(() => import("@nika-js/onlymap"));
2664
+ dr();
2665
+ export {
2666
+ P as CARTOGRAPH_ELEMENT_ATTRS,
2667
+ pt as CARTOGRAPH_FORMAT,
2668
+ gt as CARTOGRAPH_TAG_NAMES,
2669
+ qn as CARTOGRAPH_THEMES,
2670
+ ir as COLLAPSE_THRESHOLD,
2671
+ Ft as CVD_MATRICES,
2672
+ dt as CVD_TYPES,
2673
+ rr as CVD_VALUES,
2674
+ Be as GEOGRAPHIC_PLANE,
2675
+ gr as INERT_RESERVED_CARTOGRAPH_ATTRS,
2676
+ Se as MAX_CANVAS_AREA,
2677
+ Ae as MAX_CANVAS_DIMENSION,
2678
+ xt as MAX_CATEGORY_ENTRIES,
2679
+ He as OmAtlasElement,
2680
+ Fe as OmCartographElement,
2681
+ Ue as OmFrameElement,
2682
+ Ve as OmGraticuleElement,
2683
+ Ge as OmImageElement,
2684
+ We as OmLegendElement,
2685
+ ze as OmNorthElement,
2686
+ je as OmScalebarElement,
2687
+ De as OmShapeElement,
2688
+ Ie as OmTextElement,
2689
+ Ot as PAGE_PRESETS,
2690
+ Bt as PAGE_SIZE_NAMES,
2691
+ j as PLACED_ATTR_NAMES,
2692
+ W as PX_PER_MM,
2693
+ Sr as SCALE_GROUP_SEPARATOR,
2694
+ hr as URL_ACTION_TIMEOUT_MS,
2695
+ Fr as alignOffset,
2696
+ Le as allFrames,
2697
+ sr as applyCvdToCanvas,
2698
+ kr as applyTokens,
2699
+ mr as applyUrlActions,
2700
+ Tt as atlasFields,
2701
+ Vn as boundsArea,
2702
+ C as boxMm,
2703
+ un as buildGraticule,
2704
+ Mn as buildPages,
2705
+ ke as buildScalebar,
2706
+ Ar as buildScalebarDrawing,
2707
+ K as builtinCrsForward,
2708
+ Wn as canvasMeasurer,
2709
+ Pr as clearUnlinked,
2710
+ Bn as cornersFromBounds,
2711
+ se as cornersFromCamera,
2712
+ ht as createFrameGeoref,
2713
+ er as createZip,
2714
+ Kn as defineCartographElements,
2715
+ Dt as deltaE00,
2716
+ sn as deriveLegendRows,
2717
+ ar as ensureCvdFilters,
2718
+ yn as featureBounds,
2719
+ I as fieldOf,
2720
+ Te as findFrame,
2721
+ dn as formatDms,
2722
+ Cr as formatScaleDenominator,
2723
+ dr as installUrlActions,
2724
+ Ze as isCoreLoaded,
2725
+ Er as isoDate,
2726
+ Lr as layoutText,
2727
+ or as lintLegendColours,
2728
+ we as loadCore,
2729
+ _r as mercatorForward,
2730
+ Or as mercatorInverse,
2731
+ de as niceInterval,
2732
+ $e as niceNumberBelow,
2733
+ Y as normalizeCrs,
2734
+ Wr as northBearingDegrees,
2735
+ Pe as ownerCartograph,
2736
+ wn as padBounds,
2737
+ _t as paintEnvForDpi,
2738
+ nr as parseColor,
2739
+ mt as parseCorners,
2740
+ An as parseSort,
2741
+ H as peekCore,
2742
+ lt as readCamera,
2743
+ qe as readLiteralRows,
2744
+ cn as readOverrides,
2745
+ O as referencedFrame,
2746
+ bn as rejectionFor,
2747
+ It as renderCartograph,
2748
+ Ce as renderCartographAtlas,
2749
+ Oe as resolveCrsForward,
2750
+ Lt as resolveCvdType,
2751
+ xn as resolveFilenames,
2752
+ ct as resolvePageSize,
2753
+ xr as resolveToken,
2754
+ Ne as resolveTokens,
2755
+ on as rowColorFor,
2756
+ Hr as sanitizeAdoptedMap,
2757
+ Cn as sanitizeFilename,
2758
+ Br as scaleVariation,
2759
+ ye as setCoreLoader,
2760
+ jt as simulate,
2761
+ nn as swatchShapeFor,
2762
+ $r as tokensIn,
2763
+ ne as utmDef,
2764
+ Ke as validateCartograph,
2765
+ Yn as validateCartographDeep,
2766
+ Hn as validateCartographString,
2767
+ Wt as whenPageReady
2768
+ };