@edugis-org/webmapx 0.1.6 → 0.1.8

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 (60) hide show
  1. package/dist-lib/{decorate-CWgUV1hU.js → decorate-DnZTfaod.js} +6 -6
  2. package/dist-lib/dist-BIbq3o-p.js +213 -0
  3. package/dist-lib/{dist-Dm6b7XCs.js → dist-CKAaoSoW.js} +2743 -2491
  4. package/dist-lib/{dist-kKlmcBXq.js → dist-CWCNMXMK.js} +1 -1
  5. package/dist-lib/{dist-Ha9LQCut.js → dist-D-5Gmgwo.js} +1 -1
  6. package/dist-lib/{layer-discovery-afWzu5hY.js → layer-discovery-DFP-WHAh.js} +1 -1
  7. package/dist-lib/{leaflet-adapter-D9djjrKv.js → leaflet-adapter-A0rte-c7.js} +1 -1
  8. package/dist-lib/{maplibre-adapter-TFc3e0G9.js → maplibre-adapter-Cy6yX6o7.js} +1 -1
  9. package/dist-lib/ol-tilegrid-Ct4my3IN.js +24 -0
  10. package/dist-lib/openlayers-adapter-B8WtV1tn.js +1462 -0
  11. package/dist-lib/{toast-Cm28o9U6.js → toast-BsoXOdeA.js} +1 -1
  12. package/dist-lib/{webmapx-3d-tool-D4CTD2gB.js → webmapx-3d-tool-CLppA7mK.js} +13 -13
  13. package/dist-lib/{webmapx-base-tool-Dm9NAWLD.js → webmapx-base-tool-DfRa7TlD.js} +1 -1
  14. package/dist-lib/{webmapx-config-edit-tool-BgcSkmmg.js → webmapx-config-edit-tool-BztWspia.js} +21 -21
  15. package/dist-lib/{webmapx-coordinates-tool-B3cuq_hG.js → webmapx-coordinates-tool-EOqZodjr.js} +20 -20
  16. package/dist-lib/{webmapx-core-bundle-BDImi1RE.js → webmapx-core-bundle-CH-5vYXU.js} +607 -1519
  17. package/dist-lib/webmapx-draw-tool-DxQgUF1Q.js +2117 -0
  18. package/dist-lib/{webmapx-geolocation-tool-Rw3-Iad1.js → webmapx-geolocation-tool-DIMh2kIr.js} +26 -25
  19. package/dist-lib/{webmapx-import-layer-tool-DRYviHd5.js → webmapx-import-layer-tool-DMiC1TpU.js} +16 -16
  20. package/dist-lib/{webmapx-info-tool-BJA157cy.js → webmapx-info-tool-D-XbAU9J.js} +56 -56
  21. package/dist-lib/{webmapx-language-osmvector-M5y_lwOg.js → webmapx-language-osmvector-BAw9TR-M.js} +12 -11
  22. package/dist-lib/{webmapx-measure-tool-BXhMJFC6.js → webmapx-measure-tool-CXlg11s8.js} +34 -34
  23. package/dist-lib/{webmapx-modal-tool-eF6Naluv.js → webmapx-modal-tool-Cs7LRrgW.js} +3 -3
  24. package/dist-lib/{webmapx-plugin-tool-D2Hghf9n.js → webmapx-plugin-tool-qe2yTrWB.js} +7 -7
  25. package/dist-lib/{webmapx-print-tool-ob1bOsR5.js → webmapx-print-tool-DrK9sLC7.js} +27 -26
  26. package/dist-lib/{webmapx-search-tool-Cv8BrYvY.js → webmapx-search-tool-H7NisgWH.js} +11 -11
  27. package/dist-lib/webmapx-settings-EHSm-AGU.js +167 -0
  28. package/dist-lib/{webmapx-truearea-tool-CMB4Orm-.js → webmapx-truearea-tool-CSU9mE1D.js} +26 -26
  29. package/dist-lib/{webmapx-view-mode-tool-CUpLNjOj.js → webmapx-view-mode-tool-D1QyQfq8.js} +9 -9
  30. package/dist-lib/webmapx.css +1 -1
  31. package/dist-lib/webmapx.js +22 -21
  32. package/package.json +1 -1
  33. package/dist-lib/WMTS-DCN4zX0-.js +0 -1169
  34. package/dist-lib/alert-GeHlqlN8.js +0 -310
  35. package/dist-lib/button-DFdGkRPQ.js +0 -741
  36. package/dist-lib/checkbox-QoR4S8tV.js +0 -284
  37. package/dist-lib/chunk.36O46B5H-B6ZL7Sm1.js +0 -77
  38. package/dist-lib/chunk.3RPBFEDE-BFO1fHVm.js +0 -138
  39. package/dist-lib/chunk.5JY5FUCG-DTXsslmx.js +0 -1090
  40. package/dist-lib/chunk.6CTB5ZDJ-DjZrBd6Y.js +0 -99
  41. package/dist-lib/chunk.AJ3ENQ5C-Ci7Gm2b6.js +0 -175
  42. package/dist-lib/chunk.LD4M4QGE-CiCfhE8r.js +0 -8
  43. package/dist-lib/chunk.NYIIDP5N-BikXIStD.js +0 -99
  44. package/dist-lib/chunk.RWUUFNUL-DFztA4uV.js +0 -43
  45. package/dist-lib/chunk.SI4ACBFK-CLb9VfMG.js +0 -61
  46. package/dist-lib/chunk.YHLNUJ7P-D-kanrCf.js +0 -503
  47. package/dist-lib/decorators-B35AgiCU.js +0 -351
  48. package/dist-lib/dist-c1PlDAd1.js +0 -2359
  49. package/dist-lib/divider-CPm675yY.js +0 -41
  50. package/dist-lib/icon-CEOgWlro.js +0 -9
  51. package/dist-lib/icon-button-Da_nBTy3.js +0 -408
  52. package/dist-lib/input-CeGntPlT.js +0 -590
  53. package/dist-lib/ol-tilegrid-9VtyxaLG.js +0 -64
  54. package/dist-lib/openlayers-adapter-DVW1KCRv.js +0 -13307
  55. package/dist-lib/option-CBxl1mZP.js +0 -1106
  56. package/dist-lib/rbush-C8k41T4z.js +0 -254
  57. package/dist-lib/spinner-DysxdNG9.js +0 -6
  58. package/dist-lib/tooltip-Cucn1SiD.js +0 -197
  59. package/dist-lib/webmapx-draw-tool-DooAV8cF.js +0 -4336
  60. package/dist-lib/webmapx-settings-DDEJ8aoV.js +0 -479
@@ -0,0 +1,2117 @@
1
+ import { t as e } from "./decorate-DnZTfaod.js";
2
+ import { a as t, i as n, t as r } from "./zip.js-DVhmtjxZ.js";
3
+ import { t as i } from "./webmapx-modal-tool-Cs7LRrgW.js";
4
+ import { r as a } from "./map-layer-registry-2cmkiRDK.js";
5
+ import { LitElement as o, css as s, html as c } from "lit";
6
+ import { customElement as l, property as u, query as d, state as f } from "lit/decorators.js";
7
+ import "@shoelace-style/shoelace/dist/components/button/button.js";
8
+ import "@shoelace-style/shoelace/dist/components/icon/icon.js";
9
+ import "@shoelace-style/shoelace/dist/components/dialog/dialog.js";
10
+ import "@shoelace-style/shoelace/dist/components/input/input.js";
11
+ import "@shoelace-style/shoelace/dist/components/icon-button/icon-button.js";
12
+ import "@shoelace-style/shoelace/dist/components/tooltip/tooltip.js";
13
+ import "@shoelace-style/shoelace/dist/components/radio-group/radio-group.js";
14
+ import "@shoelace-style/shoelace/dist/components/radio/radio.js";
15
+ import "@shoelace-style/shoelace/dist/components/select/select.js";
16
+ import "@shoelace-style/shoelace/dist/components/option/option.js";
17
+ import "@shoelace-style/shoelace/dist/components/color-picker/color-picker.js";
18
+ //#region src/components/webmapx-draw-layer-dialog.ts
19
+ var p = {
20
+ Point: [
21
+ "string",
22
+ "number",
23
+ "longitude",
24
+ "latitude",
25
+ "linkURL",
26
+ "imageURL",
27
+ "create-time",
28
+ "update-time"
29
+ ],
30
+ LineString: [
31
+ "string",
32
+ "number",
33
+ "length",
34
+ "linkURL",
35
+ "imageURL",
36
+ "create-time",
37
+ "update-time"
38
+ ],
39
+ Polygon: [
40
+ "string",
41
+ "number",
42
+ "area",
43
+ "perimeter",
44
+ "longitude",
45
+ "latitude",
46
+ "linkURL",
47
+ "imageURL",
48
+ "create-time",
49
+ "update-time"
50
+ ]
51
+ }, m = [{
52
+ name: "id",
53
+ type: "number"
54
+ }, {
55
+ name: "name",
56
+ type: "string"
57
+ }], h = {
58
+ Point: "#0f62fe",
59
+ LineString: "#0f62fe",
60
+ Polygon: "#0f62fe"
61
+ };
62
+ function g(e) {
63
+ return {
64
+ id: `layer-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
65
+ name: "",
66
+ type: e,
67
+ color: h[e],
68
+ properties: m.map((e) => ({ ...e }))
69
+ };
70
+ }
71
+ var _ = class extends o {
72
+ constructor(...e) {
73
+ super(...e), this.geometryType = "Point", this.existingLayers = [], this.mapLayers = [], this.step = "select", this.selectedId = "new", this.layer = g("Point"), this.nameError = !1, this.newPropName = "", this.newPropType = "string", this.allowedAttributes = null;
74
+ }
75
+ static {
76
+ this.styles = s`
77
+ :host { display: block; }
78
+
79
+ sl-dialog::part(panel) {
80
+ min-width: min(420px, 90vw);
81
+ }
82
+
83
+ .layer-list {
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: 0.25rem;
87
+ max-height: 220px;
88
+ overflow-y: auto;
89
+ margin-bottom: 1rem;
90
+ }
91
+
92
+ .layer-option {
93
+ display: flex;
94
+ align-items: center;
95
+ gap: 0.5rem;
96
+ padding: 0.4rem 0.6rem;
97
+ border-radius: 4px;
98
+ cursor: pointer;
99
+ border: 1px solid transparent;
100
+ font-size: 0.9rem;
101
+ }
102
+
103
+ .layer-option:hover { background: var(--sl-color-neutral-100); }
104
+
105
+ .layer-option.selected {
106
+ background: var(--sl-color-primary-100);
107
+ border-color: var(--sl-color-primary-400);
108
+ }
109
+
110
+ .color-dot {
111
+ width: 12px; height: 12px;
112
+ border-radius: 50%;
113
+ flex-shrink: 0;
114
+ }
115
+
116
+ .new-icon { color: var(--sl-color-neutral-500); font-size: 1rem; }
117
+
118
+ .prop-table {
119
+ width: 100%;
120
+ border-collapse: collapse;
121
+ font-size: 0.85rem;
122
+ margin-top: 0.5rem;
123
+ }
124
+
125
+ .prop-table th {
126
+ text-align: left;
127
+ background: var(--sl-color-neutral-100);
128
+ padding: 0.25rem 0.4rem;
129
+ border-bottom: 1px solid var(--sl-color-neutral-200);
130
+ }
131
+
132
+ .prop-table td {
133
+ padding: 0.2rem 0.4rem;
134
+ border-bottom: 1px solid var(--sl-color-neutral-100);
135
+ vertical-align: middle;
136
+ }
137
+
138
+ .prop-table tr:last-child td { border-bottom: none; }
139
+
140
+ .prop-row-auto td { color: var(--sl-color-neutral-400); font-style: italic; }
141
+
142
+ .type-computed { color: var(--sl-color-primary-600); font-size: 0.75rem; }
143
+
144
+ .add-row td { background: var(--sl-color-neutral-50); }
145
+
146
+ .add-row sl-input,
147
+ .add-row sl-select { font-size: 0.85rem; }
148
+
149
+ .name-input-row {
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 0.5rem;
153
+ margin-bottom: 0.75rem;
154
+ }
155
+
156
+ .color-swatch {
157
+ width: 32px; height: 32px;
158
+ border-radius: 4px;
159
+ border: 1px solid var(--sl-color-neutral-300);
160
+ cursor: pointer;
161
+ flex-shrink: 0;
162
+ }
163
+
164
+ .footer {
165
+ display: flex;
166
+ justify-content: flex-end;
167
+ gap: 0.5rem;
168
+ margin-top: 1rem;
169
+ }
170
+
171
+ .error-msg {
172
+ color: var(--sl-color-danger-600);
173
+ font-size: 0.8rem;
174
+ margin-top: 0.25rem;
175
+ }
176
+ `;
177
+ }
178
+ open() {
179
+ this.step = "select", this.selectedId = this.existingLayers.length === 0 ? "new" : this.existingLayers[0].id, this.layer = g(this.geometryType), this.newPropName = "", this.newPropType = "string", this.nameError = !1, this.dialog?.show();
180
+ }
181
+ close() {
182
+ this.dialog?.hide();
183
+ }
184
+ selectOption(e) {
185
+ this.selectedId = e;
186
+ }
187
+ goToDetail() {
188
+ if (this.selectedId === "new") this.layer = g(this.geometryType), this.allowedAttributes = null;
189
+ else {
190
+ let e = this.existingLayers.find((e) => e.id === this.selectedId);
191
+ if (e) this.layer = {
192
+ ...e,
193
+ properties: e.properties.map((e) => ({ ...e }))
194
+ }, this.allowedAttributes = null;
195
+ else {
196
+ let e = this.mapLayers.find((e) => e.layerId === this.selectedId);
197
+ this.layer = {
198
+ ...g(this.geometryType),
199
+ name: e?.label ?? this.selectedId,
200
+ properties: e?.properties?.map((e) => ({ ...e })) ?? m.map((e) => ({ ...e })),
201
+ borrowedSourceId: e?.sourceId
202
+ }, this.allowedAttributes = e?.allowedAttributes ?? null;
203
+ }
204
+ }
205
+ this.step = "detail", this.updateComplete.then(() => this.renderRoot.querySelector("sl-input[name=\"layername\"]")?.focus());
206
+ }
207
+ goBack() {
208
+ this.step = "select";
209
+ }
210
+ addProperty() {
211
+ this.newPropName.trim() && (this.layer = {
212
+ ...this.layer,
213
+ properties: [...this.layer.properties, {
214
+ name: this.newPropName.trim(),
215
+ type: this.newPropType
216
+ }]
217
+ }, this.newPropName = "", this.newPropType = "string");
218
+ }
219
+ removeProperty(e) {
220
+ let t = [...this.layer.properties];
221
+ t.splice(e, 1), this.layer = {
222
+ ...this.layer,
223
+ properties: t
224
+ };
225
+ }
226
+ confirm() {
227
+ let e = this.renderRoot.querySelector("sl-input[name=\"layername\"]")?.value?.trim() ?? this.layer.name;
228
+ if (!e) {
229
+ this.nameError = !0, this.requestUpdate();
230
+ return;
231
+ }
232
+ this.nameError = !1;
233
+ let t = this.newPropName.trim(), n = t ? [...this.layer.properties, {
234
+ name: t,
235
+ type: this.newPropType
236
+ }] : [...this.layer.properties], r = {
237
+ ...this.layer,
238
+ name: e,
239
+ properties: n
240
+ };
241
+ this.dispatchEvent(new CustomEvent("webmapx-draw-layer-confirm", {
242
+ detail: r,
243
+ bubbles: !0,
244
+ composed: !0
245
+ })), this.dialog.hide();
246
+ }
247
+ cancel() {
248
+ this.dispatchEvent(new CustomEvent("webmapx-draw-layer-cancel", {
249
+ bubbles: !0,
250
+ composed: !0
251
+ })), this.dialog.hide();
252
+ }
253
+ renderSelectStep() {
254
+ let e = this.geometryType === "LineString" ? "Line" : this.geometryType;
255
+ return c`
256
+ <div class="layer-list">
257
+ <div class="layer-option ${this.selectedId === "new" ? "selected" : ""}"
258
+ @click=${() => this.selectOption("new")}
259
+ @dblclick=${() => {
260
+ this.selectOption("new"), this.goToDetail();
261
+ }}>
262
+ <span class="new-icon">+</span>
263
+ <span>New ${e} layer</span>
264
+ </div>
265
+ ${this.existingLayers.map((e) => c`
266
+ <div class="layer-option ${this.selectedId === e.id ? "selected" : ""}"
267
+ @click=${() => this.selectOption(e.id)}
268
+ @dblclick=${() => {
269
+ this.selectOption(e.id), this.goToDetail();
270
+ }}>
271
+ <span class="color-dot" style="background:${e.color}"></span>
272
+ <span>${e.name}</span>
273
+ </div>
274
+ `)}
275
+ ${this.mapLayers.length > 0 ? c`
276
+ <div style="font-size:0.72rem;color:var(--sl-color-neutral-500);padding:0.4rem 0.2rem 0.1rem;text-transform:uppercase;letter-spacing:0.05em">Map layers</div>
277
+ ${this.mapLayers.map((e) => c`
278
+ <div class="layer-option ${this.selectedId === e.layerId ? "selected" : ""}"
279
+ @click=${() => this.selectOption(e.layerId)}
280
+ @dblclick=${() => {
281
+ this.selectOption(e.layerId), this.goToDetail();
282
+ }}>
283
+ <span class="new-icon" style="color:var(--sl-color-warning-600)">✎</span>
284
+ <span>${e.label}</span>
285
+ <span style="font-size:0.7rem;color:var(--sl-color-neutral-400);margin-left:auto">map layer</span>
286
+ </div>
287
+ `)}
288
+ ` : ""}
289
+ </div>
290
+ <div class="footer">
291
+ <sl-button @click=${this.cancel}>Cancel</sl-button>
292
+ <sl-button autofocus variant="primary" @click=${this.goToDetail}>Next →</sl-button>
293
+ </div>
294
+ `;
295
+ }
296
+ renderDetailStep() {
297
+ let e = p[this.geometryType], t = {
298
+ longitude: "longitude (auto)",
299
+ latitude: "latitude (auto)",
300
+ area: "area (auto)",
301
+ perimeter: "perimeter (auto)",
302
+ length: "length (auto)",
303
+ linkURL: "link URL",
304
+ imageURL: "image URL",
305
+ "create-time": "create-time (auto)",
306
+ "update-time": "update-time (auto)"
307
+ };
308
+ return c`
309
+ <div class="name-input-row">
310
+ <sl-input name="layername"
311
+ style="flex:1"
312
+ placeholder="Layer name"
313
+ aria-label="Layer name"
314
+ value=${this.layer.name}
315
+ @keydown=${(e) => e.key === "Enter" && this.renderRoot.querySelector("#new-prop-name")?.focus()}
316
+ ></sl-input>
317
+ <div class="color-swatch"
318
+ style="background:${this.layer.color}"
319
+ title="Layer color"
320
+ @click=${() => this.renderRoot.querySelector("input[type=color]")?.click()}>
321
+ </div>
322
+ <input type="color" style="display:none" .value=${this.layer.color}
323
+ @input=${(e) => {
324
+ this.layer = {
325
+ ...this.layer,
326
+ color: e.target.value
327
+ };
328
+ }}>
329
+ </div>
330
+ ${this.nameError ? c`<div class="error-msg">Enter a layer name.</div>` : ""}
331
+
332
+ <table class="prop-table">
333
+ <thead>
334
+ <tr><th>Property</th><th>Type</th><th style="width:2rem"></th></tr>
335
+ </thead>
336
+ <tbody>
337
+ ${this.layer.properties.map((e, t) => c`
338
+ <tr class="${e.name === "id" ? "prop-row-auto" : ""}">
339
+ <td>${e.name}</td>
340
+ <td class="${[
341
+ "string",
342
+ "number",
343
+ "linkURL",
344
+ "imageURL"
345
+ ].includes(e.type) ? "" : "type-computed"}">${e.type}${e.name === "id" ? " (auto)" : ""}</td>
346
+ <td>
347
+ ${t === 0 ? "" : c`
348
+ <sl-icon-button name="x" @click=${() => this.removeProperty(t)}></sl-icon-button>
349
+ `}
350
+ </td>
351
+ </tr>
352
+ `)}
353
+ <tr class="add-row">
354
+ <td>
355
+ ${this.allowedAttributes ? c`
356
+ <sl-select id="new-prop-name" size="small" aria-label="Property name"
357
+ .value=${this.newPropName}
358
+ @sl-change=${(e) => this.newPropName = e.target.value}>
359
+ <sl-option value="">— select —</sl-option>
360
+ ${this.allowedAttributes.filter((e) => !this.layer.properties.find((t) => t.name === e)).map((e) => c`<sl-option value=${e}>${e}</sl-option>`)}
361
+ </sl-select>
362
+ ` : c`
363
+ <sl-input id="new-prop-name" size="small" placeholder="property name" aria-label="Property name"
364
+ .value=${this.newPropName}
365
+ @sl-input=${(e) => this.newPropName = e.target.value}
366
+ @keydown=${(e) => e.key === "Enter" && this.addProperty()}>
367
+ </sl-input>
368
+ `}
369
+ </td>
370
+ <td>
371
+ <sl-select id="new-prop-type" size="small" .value=${this.newPropType}
372
+ @sl-change=${(e) => this.newPropType = e.target.value}>
373
+ ${e.map((e) => c`<sl-option value=${e}>${t[e] ?? e}</sl-option>`)}
374
+ </sl-select>
375
+ </td>
376
+ <td>
377
+ <sl-icon-button name="plus" @click=${this.addProperty}></sl-icon-button>
378
+ </td>
379
+ </tr>
380
+ </tbody>
381
+ </table>
382
+
383
+ <div class="footer">
384
+ <sl-button @click=${this.goBack}>← Back</sl-button>
385
+ <sl-button @click=${this.cancel}>Cancel</sl-button>
386
+ <sl-button variant="primary" @click=${this.confirm}>OK</sl-button>
387
+ </div>
388
+ `;
389
+ }
390
+ render() {
391
+ let e = this.geometryType === "LineString" ? "Line" : this.geometryType;
392
+ return c`
393
+ <sl-dialog label="${this.step === "select" ? `Select ${e} layer` : `Configure ${e} layer`}"
394
+ @sl-request-close=${(e) => {
395
+ e.detail?.source === "overlay" && this.cancel();
396
+ }}>
397
+ ${this.step === "select" ? this.renderSelectStep() : this.renderDetailStep()}
398
+ </sl-dialog>
399
+ `;
400
+ }
401
+ };
402
+ e([u({ type: String })], _.prototype, "geometryType", void 0), e([u({ attribute: !1 })], _.prototype, "existingLayers", void 0), e([u({ attribute: !1 })], _.prototype, "mapLayers", void 0), e([f()], _.prototype, "step", void 0), e([f()], _.prototype, "selectedId", void 0), e([f()], _.prototype, "layer", void 0), e([f()], _.prototype, "nameError", void 0), e([f()], _.prototype, "newPropName", void 0), e([f()], _.prototype, "newPropType", void 0), e([f()], _.prototype, "allowedAttributes", void 0), e([d("sl-dialog")], _.prototype, "dialog", void 0), _ = e([l("webmapx-draw-layer-dialog")], _);
403
+ //#endregion
404
+ //#region src/utils/snap-utils.ts
405
+ function v(e) {
406
+ return e.type === "Point" ? [e.coordinates] : e.type === "MultiPoint" || e.type === "LineString" ? e.coordinates : e.type === "MultiLineString" || e.type === "Polygon" ? e.coordinates.flat() : e.type === "MultiPolygon" ? e.coordinates.flat(2) : [];
407
+ }
408
+ function y(e) {
409
+ let t = [], n = (e) => {
410
+ for (let n = 0; n < e.length - 1; n++) t.push([e[n], e[n + 1]]);
411
+ };
412
+ return e.type === "LineString" ? n(e.coordinates) : e.type === "MultiLineString" ? e.coordinates.forEach((e) => n(e)) : e.type === "Polygon" ? e.coordinates.forEach((e) => n(e)) : e.type === "MultiPolygon" && e.coordinates.forEach((e) => e.forEach((e) => n(e))), t;
413
+ }
414
+ function b(e, t, n, r = {}) {
415
+ let i = r.threshold ?? 16, a = r.edgePenalty ?? 8, o = null, s = i;
416
+ for (let r of t) {
417
+ for (let t of v(r)) {
418
+ let r = n(t), i = Math.hypot(r[0] - e[0], r[1] - e[1]);
419
+ i < s && (s = i, o = t);
420
+ }
421
+ for (let [t, i] of y(r)) {
422
+ let r = n(t), c = n(i), l = c[0] - r[0], u = c[1] - r[1], d = l * l + u * u;
423
+ if (d === 0) continue;
424
+ let f = Math.max(0, Math.min(1, ((e[0] - r[0]) * l + (e[1] - r[1]) * u) / d)), p = Math.hypot(r[0] + f * l - e[0], r[1] + f * u - e[1]);
425
+ p + a < s && (s = p + a, o = [t[0] + f * (i[0] - t[0]), t[1] + f * (i[1] - t[1])]);
426
+ }
427
+ }
428
+ return o;
429
+ }
430
+ //#endregion
431
+ //#region src/components/webmapx-draw-tool.ts
432
+ var x = "webmapx-draw-rubber-source", S = "webmapx-draw-rubber-line", C = "webmapx-draw-vertex-source", w = "webmapx-draw-vertex-layer";
433
+ function T(e) {
434
+ return `webmapx-draw-src-${e}`;
435
+ }
436
+ function E(e) {
437
+ return `${e}-map`;
438
+ }
439
+ var D = "#888888", O = "#ff3b30", k = 10, A = "webmapx-draw-sel-source", j = "webmapx-draw-sel-point", M = "webmapx-draw-draft-source", N = "webmapx-draw-draft-points", P = "webmapx-draw-edit-vert-source", F = "webmapx-draw-edit-vert", I = "webmapx-draw-edit-mid-source", L = "webmapx-draw-edit-mid", R = "webmapx-draw-sel-vert-source", z = "webmapx-draw-sel-vert", B = 12, V = "webmapx-draw-snap-source", H = "webmapx-draw-snap-layer", U = 16, W = class extends i {
440
+ constructor(...e) {
441
+ super(...e), this.toolId = "draw", this.mode = "select", this.drawLayers = [], this.features = [], this.selectedFeatureId = null, this.helpText = "", this.pendingMode = null, this.uiVersion = 0, this.draftPoints = [], this.draftRedoStack = [], this.cursorPos = null, this.activeLayerIds = {}, this.history = [], this.historyIndex = -1, this.sharedLayersCreated = !1, this.createdDrawLayerIds = /* @__PURE__ */ new Set(), this.touchMQ = window.matchMedia("(pointer: coarse)"), this.isTouchDevice = this.touchMQ.matches, this.onTouchMQChange = (e) => {
442
+ this.isTouchDevice = e.matches;
443
+ }, this.snapEnabled = !0, this.altActive = !1, this.snapPos = null, this.lastCursorPx = null, this.editState = "none", this.editHandles = [], this.hoveredHandle = null, this.dragging = null, this.selectedHandle = null, this.featureDrag = null, this.unsubClick = null, this.unsubMove = null, this.unsubCtx = null, this.unsubDown = null, this.unsubUp = null, this.exportFilename = "draw-export", this.exportMode = "combined", this.onKeyDown = (e) => {
444
+ if (this.isRelevantTarget(e)) {
445
+ if (e.key === "Alt") {
446
+ e.preventDefault(), this.altActive || (this.altActive = !0, this.snapPos = null, this.updateRubberband(), this.updateSnapIndicator());
447
+ return;
448
+ }
449
+ if (!this.isTypingTarget(e)) {
450
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "z") {
451
+ e.preventDefault(), this.undoOrDraftBack();
452
+ return;
453
+ }
454
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "y") {
455
+ e.preventDefault(), this.redoOrDraftForward();
456
+ return;
457
+ }
458
+ (e.key === "Delete" || e.key === "Backspace") && (this.draftPoints.length > 0 ? (e.preventDefault(), this.removeLastDraftPoint()) : this.selectedHandle ? (e.preventDefault(), this.deleteSelectedVertex()) : this.selectedFeatureId && (e.preventDefault(), this.deleteSelected()));
459
+ }
460
+ }
461
+ }, this.onWindowBlur = () => {
462
+ this.altActive && (this.altActive = !1, (this.mode === "draw-point" || this.mode === "draw-line" || this.mode === "draw-polygon") && this.updateRubberband());
463
+ }, this.onKeyUp = (e) => {
464
+ e.key === "Alt" && (this.altActive = !1, (this.mode === "draw-point" || this.mode === "draw-line" || this.mode === "draw-polygon") && this.updateRubberband());
465
+ };
466
+ }
467
+ connectedCallback() {
468
+ super.connectedCallback(), this.touchMQ.addEventListener("change", this.onTouchMQChange);
469
+ }
470
+ get effectiveSnap() {
471
+ return this.snapEnabled && !this.altActive;
472
+ }
473
+ static {
474
+ this.styles = s`
475
+ :host {
476
+ display: flex;
477
+ flex-direction: column;
478
+ padding: var(--webmapx-tool-padding, 0);
479
+ min-width: 200px;
480
+ max-height: var(--webmapx-draw-tool-max-height, 100%);
481
+ overflow: hidden;
482
+ }
483
+
484
+ .scroll-content {
485
+ flex: 1;
486
+ overflow-y: auto;
487
+ min-height: 0;
488
+ }
489
+
490
+ .toolbar {
491
+ display: flex;
492
+ gap: 0.25rem;
493
+ flex-wrap: wrap;
494
+ margin-bottom: 0.5rem;
495
+ flex-shrink: 0;
496
+ }
497
+
498
+ sl-icon-button[active]::part(base) {
499
+ color: var(--sl-color-primary-600);
500
+ background: var(--sl-color-primary-100);
501
+ border-radius: 4px;
502
+ }
503
+
504
+ .help {
505
+ font-size: 0.8rem;
506
+ color: var(--sl-color-neutral-600);
507
+ margin-bottom: 0.5rem;
508
+ min-height: 2.5em;
509
+ }
510
+
511
+ .layers-section {
512
+ margin-top: 0.5rem;
513
+ }
514
+
515
+ .section-label {
516
+ font-size: 0.7rem;
517
+ text-transform: uppercase;
518
+ letter-spacing: 0.06em;
519
+ color: var(--sl-color-neutral-500);
520
+ margin-bottom: 0.25rem;
521
+ }
522
+
523
+ .layer-row {
524
+ display: flex;
525
+ align-items: center;
526
+ gap: 0.4rem;
527
+ padding: 0.2rem 0.4rem;
528
+ border-radius: 4px;
529
+ font-size: 0.85rem;
530
+ cursor: pointer;
531
+ }
532
+
533
+ .layer-row:hover {
534
+ background: var(--sl-color-neutral-100);
535
+ }
536
+
537
+ .remove-layer-btn {
538
+ font-size: 0.75rem;
539
+ opacity: 0;
540
+ transition: opacity 0.1s;
541
+ }
542
+
543
+ .layer-row:hover .remove-layer-btn {
544
+ opacity: 1;
545
+ }
546
+
547
+ .color-dot {
548
+ width: 10px; height: 10px;
549
+ border-radius: 50%;
550
+ flex-shrink: 0;
551
+ }
552
+
553
+ .layer-name { flex: 1; }
554
+
555
+ .layer-type {
556
+ font-size: 0.7rem;
557
+ color: var(--sl-color-neutral-500);
558
+ }
559
+
560
+ .features-section {
561
+ margin-top: 0.25rem;
562
+ max-height: 180px;
563
+ overflow-y: auto;
564
+ }
565
+
566
+ .feature-row {
567
+ display: flex;
568
+ align-items: center;
569
+ gap: 0.4rem;
570
+ padding: 0.2rem 0.6rem;
571
+ border-radius: 4px;
572
+ cursor: pointer;
573
+ font-size: 0.82rem;
574
+ }
575
+
576
+ .feature-row:hover { background: var(--sl-color-neutral-100); }
577
+ .feature-row.selected { background: var(--sl-color-primary-100); }
578
+
579
+ .divider {
580
+ width: 1px; height: 1.2rem;
581
+ background: var(--sl-color-neutral-200);
582
+ margin: 0 0.1rem;
583
+ }
584
+
585
+ .prop-row { display: flex; gap: 0.4rem; align-items: center; font-size: 0.82rem; margin-bottom: 0.2rem; }
586
+ .prop-label { width: 80px; color: var(--sl-color-neutral-500); flex-shrink: 0; }
587
+ .prop-value { flex: 1; min-width: 0; }
588
+ .prop-link { display: block; width: 100%; font-size: 0.75rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
589
+ .prop-img { max-width: 100%; max-height: 80px; border-radius: 3px; object-fit: cover; }
590
+ .prop-img-error { font-size: 0.75rem; color: var(--sl-color-danger-600, #c0392b); font-style: italic; }
591
+ .prop-url-wrap { flex: 1; min-width: 0; overflow: hidden; display: flex; flex-direction: column; gap: 0.2rem; }
592
+ `;
593
+ }
594
+ onActivate() {
595
+ if (this.adapter?.store.getState().mapLoaded) this.createSharedLayers();
596
+ else {
597
+ let e = this.adapter?.store.subscribe((t) => {
598
+ t.mapLoaded && (e?.(), this.createSharedLayers());
599
+ });
600
+ }
601
+ for (let e of this.drawLayers) this.createdDrawLayerIds.has(e.id) || (this.addMapLayersForDrawLayer(e), this.refreshDrawLayerSource(e.id)), e.borrowedSourceId && this.adapter?.getSource(e.borrowedSourceId)?.setData({
602
+ type: "FeatureCollection",
603
+ features: []
604
+ });
605
+ this.bindEvents(), window.addEventListener("keydown", this.onKeyDown, !0), window.addEventListener("keyup", this.onKeyUp), window.addEventListener("blur", this.onWindowBlur), this.setModeInternal("select");
606
+ }
607
+ onDeactivate() {
608
+ this.unbindEvents(), window.removeEventListener("keydown", this.onKeyDown, !0), window.removeEventListener("keyup", this.onKeyUp), window.removeEventListener("blur", this.onWindowBlur), this.altActive = !1;
609
+ for (let e of this.drawLayers) e.borrowedSourceId && this.restoreBorrowedLayer(e), this.suspendDrawLayerFromMap(e);
610
+ this.draftPoints = [], this.cursorPos = null, this.snapPos = null, this.lastCursorPx = null, this.dragging = null, this.featureDrag = null, this.selectedFeatureId = null, this.editState = "none", this.editHandles = [], this.hoveredHandle = null, this.adapter?.setPanEnabled(!0), this.adapter?.setDoubleClickZoomEnabled(!0), this.updateSelectedSource(), this.removeSharedLayers(), this.adapter?.setCursor("");
611
+ }
612
+ disconnectedCallback() {
613
+ this.touchMQ.removeEventListener("change", this.onTouchMQChange), this.removeAllMapLayers(), super.disconnectedCallback();
614
+ }
615
+ onMapAttached(e) {
616
+ super.onMapAttached(e);
617
+ }
618
+ onMapDetached() {
619
+ this.removeAllMapLayers(), super.onMapDetached();
620
+ }
621
+ createSharedLayers() {
622
+ if (!this.sharedLayersCreated) {
623
+ this.dispatch("webmapx-add-source", {
624
+ id: x,
625
+ config: {
626
+ type: "geojson",
627
+ data: {
628
+ type: "FeatureCollection",
629
+ features: []
630
+ }
631
+ }
632
+ }), this.dispatch("webmapx-add-source", {
633
+ id: C,
634
+ config: {
635
+ type: "geojson",
636
+ data: {
637
+ type: "FeatureCollection",
638
+ features: []
639
+ }
640
+ }
641
+ }), this.dispatch("webmapx-add-layer", {
642
+ id: S,
643
+ type: "line",
644
+ source: x,
645
+ metadata: {
646
+ isToolLayer: !0,
647
+ hideFromLegend: !0
648
+ },
649
+ paint: {
650
+ "line-color": "#0f62fe",
651
+ "line-width": 2,
652
+ "line-dasharray": [4, 4]
653
+ }
654
+ }), this.dispatch("webmapx-add-layer", {
655
+ id: w,
656
+ type: "circle",
657
+ source: C,
658
+ metadata: {
659
+ isToolLayer: !0,
660
+ hideFromLegend: !0
661
+ },
662
+ paint: {
663
+ "circle-radius": 8,
664
+ "circle-color": "transparent",
665
+ "circle-stroke-width": 2,
666
+ "circle-stroke-color": "#ff6600"
667
+ }
668
+ }), this.dispatch("webmapx-add-source", {
669
+ id: A,
670
+ config: {
671
+ type: "geojson",
672
+ data: {
673
+ type: "FeatureCollection",
674
+ features: []
675
+ }
676
+ }
677
+ }), this.dispatch("webmapx-add-layer", {
678
+ id: j,
679
+ type: "circle",
680
+ source: A,
681
+ metadata: {
682
+ isToolLayer: !0,
683
+ hideFromLegend: !0
684
+ },
685
+ paint: {
686
+ "circle-radius": k,
687
+ "circle-color": this.cssVar("--webmapx-draw-selected-color", O)
688
+ }
689
+ }), this.dispatch("webmapx-add-source", {
690
+ id: M,
691
+ config: {
692
+ type: "geojson",
693
+ data: {
694
+ type: "FeatureCollection",
695
+ features: []
696
+ }
697
+ }
698
+ }), this.dispatch("webmapx-add-layer", {
699
+ id: N,
700
+ type: "circle",
701
+ source: M,
702
+ metadata: {
703
+ isToolLayer: !0,
704
+ hideFromLegend: !0
705
+ },
706
+ paint: {
707
+ "circle-radius": 5,
708
+ "circle-color": "#fff",
709
+ "circle-stroke-width": 2,
710
+ "circle-stroke-color": "#0f62fe"
711
+ }
712
+ }), this.dispatch("webmapx-add-source", {
713
+ id: P,
714
+ config: {
715
+ type: "geojson",
716
+ data: {
717
+ type: "FeatureCollection",
718
+ features: []
719
+ }
720
+ }
721
+ }), this.dispatch("webmapx-add-layer", {
722
+ id: F,
723
+ type: "circle",
724
+ source: P,
725
+ metadata: {
726
+ isToolLayer: !0,
727
+ hideFromLegend: !0
728
+ },
729
+ paint: {
730
+ "circle-radius": 6,
731
+ "circle-color": "#fff",
732
+ "circle-stroke-width": 2,
733
+ "circle-stroke-color": "#0f62fe"
734
+ }
735
+ }), this.dispatch("webmapx-add-source", {
736
+ id: I,
737
+ config: {
738
+ type: "geojson",
739
+ data: {
740
+ type: "FeatureCollection",
741
+ features: []
742
+ }
743
+ }
744
+ }), this.dispatch("webmapx-add-layer", {
745
+ id: L,
746
+ type: "circle",
747
+ source: I,
748
+ metadata: {
749
+ isToolLayer: !0,
750
+ hideFromLegend: !0
751
+ },
752
+ paint: {
753
+ "circle-radius": 4,
754
+ "circle-color": "#fff",
755
+ "circle-stroke-width": 1.5,
756
+ "circle-stroke-color": "#0f62fe",
757
+ "circle-opacity": .7
758
+ }
759
+ }), this.dispatch("webmapx-add-source", {
760
+ id: R,
761
+ config: {
762
+ type: "geojson",
763
+ data: {
764
+ type: "FeatureCollection",
765
+ features: []
766
+ }
767
+ }
768
+ }), this.dispatch("webmapx-add-layer", {
769
+ id: z,
770
+ type: "circle",
771
+ source: R,
772
+ metadata: {
773
+ isToolLayer: !0,
774
+ hideFromLegend: !0
775
+ },
776
+ paint: {
777
+ "circle-radius": k,
778
+ "circle-color": this.cssVar("--webmapx-draw-selected-color", O),
779
+ "circle-stroke-width": 2,
780
+ "circle-stroke-color": "#fff"
781
+ }
782
+ }), this.dispatch("webmapx-add-source", {
783
+ id: V,
784
+ config: {
785
+ type: "geojson",
786
+ data: {
787
+ type: "FeatureCollection",
788
+ features: []
789
+ }
790
+ }
791
+ }), this.dispatch("webmapx-add-layer", {
792
+ id: H,
793
+ type: "circle",
794
+ source: V,
795
+ metadata: {
796
+ isToolLayer: !0,
797
+ hideFromLegend: !0
798
+ },
799
+ paint: {
800
+ "circle-radius": 7,
801
+ "circle-color": "#ffdd00",
802
+ "circle-stroke-width": 2,
803
+ "circle-stroke-color": "#fff",
804
+ "circle-opacity": .9
805
+ }
806
+ }), this.sharedLayersCreated = !0;
807
+ for (let e of this.drawLayers) this.addMapLayersForDrawLayer(e), this.refreshDrawLayerSource(e.id);
808
+ }
809
+ }
810
+ addMapLayersForDrawLayer(e) {
811
+ if (this.createdDrawLayerIds.has(e.id)) return;
812
+ let t = T(e.id);
813
+ this.dispatch("webmapx-add-source", {
814
+ id: t,
815
+ config: {
816
+ type: "geojson",
817
+ data: {
818
+ type: "FeatureCollection",
819
+ features: []
820
+ }
821
+ }
822
+ }), e.type === "Polygon" ? (this.dispatch("webmapx-add-layer", {
823
+ id: e.id,
824
+ type: "fill",
825
+ source: t,
826
+ title: e.name,
827
+ metadata: {
828
+ label: e.name,
829
+ legendRole: "overlay",
830
+ hideFromLegend: !0
831
+ },
832
+ paint: {
833
+ "fill-color": e.color,
834
+ "fill-opacity": .35
835
+ }
836
+ }), this.dispatch("webmapx-add-layer", {
837
+ id: `${e.id}-outline`,
838
+ type: "line",
839
+ source: t,
840
+ title: e.name,
841
+ metadata: {
842
+ label: e.name,
843
+ legendRole: "overlay",
844
+ hideFromLegend: !0
845
+ },
846
+ paint: {
847
+ "line-color": e.color,
848
+ "line-width": 2
849
+ }
850
+ })) : e.type === "LineString" ? this.dispatch("webmapx-add-layer", {
851
+ id: e.id,
852
+ type: "line",
853
+ source: t,
854
+ title: e.name,
855
+ metadata: {
856
+ label: e.name,
857
+ legendRole: "overlay",
858
+ hideFromLegend: !0
859
+ },
860
+ paint: {
861
+ "line-color": e.color,
862
+ "line-width": 2
863
+ }
864
+ }) : this.dispatch("webmapx-add-layer", {
865
+ id: e.id,
866
+ type: "circle",
867
+ source: t,
868
+ title: e.name,
869
+ metadata: {
870
+ label: e.name,
871
+ legendRole: "overlay",
872
+ hideFromLegend: !0
873
+ },
874
+ paint: {
875
+ "circle-radius": 6,
876
+ "circle-color": e.color,
877
+ "circle-stroke-width": 2,
878
+ "circle-stroke-color": "#fff"
879
+ }
880
+ }), this.createdDrawLayerIds.add(e.id), e.type === "Polygon" && this.createdDrawLayerIds.add(`${e.id}-outline`);
881
+ }
882
+ removeMapLayersForDrawLayer(e) {
883
+ e.type === "Polygon" && this.dispatch("webmapx-remove-layer", `${e.id}-outline`), this.dispatch("webmapx-remove-layer", e.id), this.dispatch("webmapx-remove-source", T(e.id)), this.adapter?.store && a(this.adapter.store, e.id), this.createdDrawLayerIds.delete(e.id), e.type === "Polygon" && this.createdDrawLayerIds.delete(`${e.id}-outline`);
884
+ }
885
+ removeSharedLayers() {
886
+ for (let e of [
887
+ S,
888
+ w,
889
+ j,
890
+ N,
891
+ F,
892
+ L,
893
+ z,
894
+ H
895
+ ]) this.dispatch("webmapx-remove-layer", e);
896
+ for (let e of [
897
+ x,
898
+ C,
899
+ A,
900
+ M,
901
+ P,
902
+ I,
903
+ R,
904
+ V
905
+ ]) this.dispatch("webmapx-remove-source", e);
906
+ this.sharedLayersCreated = !1;
907
+ }
908
+ removeAllMapLayers() {
909
+ this.removeSharedLayers();
910
+ for (let e of this.drawLayers) this.removeMapLayersForDrawLayer(e);
911
+ this.createdDrawLayerIds.clear();
912
+ }
913
+ refreshDrawLayerSource(e) {
914
+ let t = this.features.filter((t) => t.layerId === e).map((e) => ({
915
+ type: "Feature",
916
+ id: e.id,
917
+ geometry: {
918
+ type: e.type,
919
+ coordinates: e.coordinates
920
+ },
921
+ properties: e.properties
922
+ }));
923
+ this.dispatch("webmapx-set-source-data", {
924
+ id: T(e),
925
+ data: {
926
+ type: "FeatureCollection",
927
+ features: t
928
+ }
929
+ });
930
+ }
931
+ cssVar(e, t) {
932
+ return getComputedStyle(this).getPropertyValue(e).trim() || t;
933
+ }
934
+ get modKey() {
935
+ return /Mac|iPhone|iPad/.test(navigator.platform) ? "Cmd" : "Ctrl";
936
+ }
937
+ isTypingTarget(e) {
938
+ let t = e.composedPath()[0], n = t?.tagName?.toLowerCase();
939
+ return n === "input" || n === "textarea" || n === "sl-input" || n === "sl-textarea" || t?.isContentEditable === !0;
940
+ }
941
+ isRelevantTarget(e) {
942
+ let t = e.composedPath(), n = this.mapHost;
943
+ if (t.includes(this) || n && t.includes(n)) return !0;
944
+ let r = t[0];
945
+ return r === document.body || r === document.documentElement;
946
+ }
947
+ bindEvents() {
948
+ this.adapter && (this.unsubClick = this.adapter.events.on("click", (e) => this.handleClick(e)), this.unsubMove = this.adapter.events.on("pointer-move", (e) => this.handlePointerMove(e)), this.unsubCtx = this.adapter.events.on("contextmenu", (e) => this.handleContextMenu(e)), this.unsubDown = this.adapter.events.on("pointer-down", (e) => this.handlePointerDown(e)), this.unsubUp = this.adapter.events.on("pointer-up", (e) => this.handlePointerUp(e)));
949
+ }
950
+ unbindEvents() {
951
+ this.unsubClick?.(), this.unsubClick = null, this.unsubMove?.(), this.unsubMove = null, this.unsubCtx?.(), this.unsubCtx = null, this.unsubDown?.(), this.unsubDown = null, this.unsubUp?.(), this.unsubUp = null;
952
+ }
953
+ async requestDrawMode(e) {
954
+ let t = G(e);
955
+ if (!t) {
956
+ this.setModeInternal(e);
957
+ return;
958
+ }
959
+ if (this.activeLayerIds[t]) {
960
+ this.setModeInternal(e);
961
+ return;
962
+ }
963
+ await this.openLayerDialog(e);
964
+ }
965
+ async openLayerDialog(e) {
966
+ let t = G(e);
967
+ if (!t) return;
968
+ this.pendingMode = e;
969
+ let n = this.drawLayers.filter((e) => e.type === t && !e.borrowedSourceId), r = this.adapter ? await this.getEditableMapLayers(t) : [], i = this.activeLayerIds[t], a = this.drawLayers.find((e) => e.id === i && e.borrowedSourceId), o = a?.borrowedSourceId, s = o ? r.find((e) => e.sourceId === o)?.layerId : void 0;
970
+ if (a && s) {
971
+ let e = r.find((e) => e.layerId === s);
972
+ e && (e.properties = a.properties.map((e) => ({ ...e })));
973
+ }
974
+ this.layerDialog.geometryType = t, this.layerDialog.existingLayers = n, this.layerDialog.mapLayers = r, this.layerDialog.open(), s && (this.layerDialog.selectedId = s);
975
+ }
976
+ async getEditableMapLayers(e) {
977
+ if (!this.adapter) return [];
978
+ let t = this.adapter.store.getState().mapLayers ?? {}, n = /* @__PURE__ */ new Set(), r = [], i = this.activeLayerIds[e], a = new Set(this.drawLayers.filter((e) => e.borrowedSourceId && e.id !== i).map((e) => e.borrowedSourceId));
979
+ for (let [i, o] of Object.entries(t)) {
980
+ if (o.isToolLayer || this.createdDrawLayerIds.has(i)) continue;
981
+ let t = typeof o.sourceId == "string" ? o.sourceId : null;
982
+ if (!t || n.has(t) || a.has(t)) continue;
983
+ let s = this.adapter.getSourceData(t);
984
+ if (!s) continue;
985
+ let c = await this.resolveFeatureCollection(s);
986
+ if (typeof s == "string") {
987
+ let a = c?.features[0]?.geometry?.type, s = a ? this.geometryFamilyForGeoJSONType(a) : null, l = this.geometryFamilyForLayerType(o.layerType);
988
+ if ((s ?? l) !== e) continue;
989
+ n.add(t), r.push({
990
+ layerId: i,
991
+ sourceId: t,
992
+ label: o.label ?? i,
993
+ properties: o.properties ?? (c ? this.inferPropertyDefs(c) : void 0),
994
+ allowedAttributes: o.attributes?.allowedAttributes ?? void 0
995
+ });
996
+ continue;
997
+ }
998
+ let l = s.features[0]?.geometry?.type, u = l ? this.geometryFamilyForGeoJSONType(l) : null, d = this.geometryFamilyForLayerType(o.layerType);
999
+ (u ?? d) === e && (n.add(t), r.push({
1000
+ layerId: i,
1001
+ sourceId: t,
1002
+ label: o.label ?? i,
1003
+ properties: o.properties ?? this.inferPropertyDefs(s),
1004
+ allowedAttributes: o.attributes?.allowedAttributes ?? void 0
1005
+ }));
1006
+ }
1007
+ return r;
1008
+ }
1009
+ async resolveFeatureCollection(e) {
1010
+ if (typeof e != "string") return e;
1011
+ try {
1012
+ let t = await fetch(e);
1013
+ return t.ok ? await t.json() : null;
1014
+ } catch {
1015
+ return null;
1016
+ }
1017
+ }
1018
+ geometryFamilyForGeoJSONType(e) {
1019
+ return e === "Point" || e === "MultiPoint" ? "Point" : e === "LineString" || e === "MultiLineString" ? "LineString" : e === "Polygon" || e === "MultiPolygon" ? "Polygon" : null;
1020
+ }
1021
+ geometryFamilyForLayerType(e) {
1022
+ return e === "circle" ? "Point" : e === "line" ? "LineString" : e === "fill" ? "Polygon" : null;
1023
+ }
1024
+ inferPropertyDefs(e) {
1025
+ let t = e.features.find((e) => e.properties && Object.keys(e.properties).length > 0)?.properties;
1026
+ return t ? Object.entries(t).map(([e, t]) => ({
1027
+ name: e,
1028
+ type: typeof t == "number" ? "number" : "string"
1029
+ })) : [{
1030
+ name: "id",
1031
+ type: "number"
1032
+ }, {
1033
+ name: "name",
1034
+ type: "string"
1035
+ }];
1036
+ }
1037
+ setModeInternal(e) {
1038
+ switch (this.mode = e, this.draftPoints = [], this.cursorPos = null, this.snapPos = null, this.lastCursorPx = null, this.updateRubberband(), e) {
1039
+ case "select":
1040
+ this.adapter?.setDoubleClickZoomEnabled(!0), this.adapter?.setCursor(""), this.editState = "none", this.editHandles = [], this.updateEditHandles(), this.helpText = "Click a feature to select it.";
1041
+ break;
1042
+ case "draw-point":
1043
+ case "draw-line":
1044
+ case "draw-polygon":
1045
+ this.adapter?.setDoubleClickZoomEnabled(!1), this.editState = "none", this.editHandles = [], this.hoveredHandle = null, this.selectedFeatureId = null, this.updateEditHandles(), this.updateSelectedSource(), this.adapter?.setCursor("crosshair"), this.helpText = this.mode === "draw-point" ? "Click to place a point." : this.mode === "draw-line" ? "Click to add vertices. Right-click or double-click to finish." : "Click to add vertices. Click first point or double-click to close.";
1046
+ break;
1047
+ }
1048
+ }
1049
+ async handleLayerConfirm(e) {
1050
+ let t = e.detail, n = this.activeLayerIds[t.type];
1051
+ if (n && n !== t.id) {
1052
+ let e = this.drawLayers.find((e) => e.id === n);
1053
+ e && this.releaseLayer(e);
1054
+ }
1055
+ let r = this.drawLayers.findIndex((e) => e.id === t.id);
1056
+ if (r >= 0) this.drawLayers = this.drawLayers.map((e, n) => n === r ? t : e);
1057
+ else if (t.borrowedSourceId || (t = {
1058
+ ...t,
1059
+ borrowedSourceId: this.createPermLayer(t)
1060
+ }), this.drawLayers = [...this.drawLayers, t], this.addMapLayersForDrawLayer(t), t.borrowedSourceId && this.adapter) {
1061
+ let e = this.adapter.getSourceData(t.borrowedSourceId), n = e ? await this.resolveFeatureCollection(e) : null;
1062
+ if (n) {
1063
+ let e = [];
1064
+ for (let r of n.features) {
1065
+ if (!r.geometry || !K(r.geometry.type)) continue;
1066
+ let n = { ...r.properties };
1067
+ e.push({
1068
+ id: this.newId(),
1069
+ layerId: t.id,
1070
+ type: r.geometry.type,
1071
+ coordinates: r.geometry.coordinates,
1072
+ properties: n
1073
+ });
1074
+ }
1075
+ if (this.features = [...this.features, ...e], t.properties.length <= 2) {
1076
+ let e = this.inferPropertyDefs(n);
1077
+ t = {
1078
+ ...t,
1079
+ properties: e
1080
+ }, this.drawLayers = this.drawLayers.map((e) => e.id === t.id ? t : e);
1081
+ }
1082
+ }
1083
+ this.adapter.getSource(t.borrowedSourceId)?.setData({
1084
+ type: "FeatureCollection",
1085
+ features: []
1086
+ });
1087
+ }
1088
+ this.activeLayerIds[t.type] = t.id, this.refreshDrawLayerSource(t.id);
1089
+ let i = E(t.id);
1090
+ if (this.adapter?.store) {
1091
+ let e = this.adapter.store.getState().mapLayers ?? {}, n = e[i];
1092
+ n && this.adapter.store.dispatch({ mapLayers: {
1093
+ ...e,
1094
+ [i]: {
1095
+ ...n,
1096
+ properties: t.properties
1097
+ }
1098
+ } }, "MAP");
1099
+ }
1100
+ this.pendingMode &&= (this.setModeInternal(this.pendingMode), null);
1101
+ }
1102
+ restoreBorrowedLayer(e) {
1103
+ if (!e.borrowedSourceId || !this.adapter) return;
1104
+ let t = this.features.filter((t) => t.layerId === e.id).map((e) => ({
1105
+ type: "Feature",
1106
+ geometry: {
1107
+ type: e.type,
1108
+ coordinates: e.coordinates
1109
+ },
1110
+ properties: { ...e.properties }
1111
+ }));
1112
+ this.adapter.getSource(e.borrowedSourceId)?.setData({
1113
+ type: "FeatureCollection",
1114
+ features: t
1115
+ });
1116
+ }
1117
+ createPermLayer(e) {
1118
+ let t = `webmapx-perm-src-${e.id}`;
1119
+ this.dispatch("webmapx-add-source", {
1120
+ id: t,
1121
+ config: {
1122
+ type: "geojson",
1123
+ data: {
1124
+ type: "FeatureCollection",
1125
+ features: []
1126
+ }
1127
+ }
1128
+ });
1129
+ let n = E(e.id);
1130
+ return e.type === "Polygon" ? (this.dispatch("webmapx-add-layer", {
1131
+ id: n,
1132
+ type: "fill",
1133
+ source: t,
1134
+ title: e.name,
1135
+ metadata: {
1136
+ label: e.name,
1137
+ legendRole: "overlay",
1138
+ properties: e.properties
1139
+ },
1140
+ paint: {
1141
+ "fill-color": D,
1142
+ "fill-opacity": .35
1143
+ }
1144
+ }), this.dispatch("webmapx-add-layer", {
1145
+ id: `${n}-outline`,
1146
+ type: "line",
1147
+ source: t,
1148
+ title: e.name,
1149
+ metadata: {
1150
+ label: e.name,
1151
+ legendRole: "overlay",
1152
+ hideFromLegend: !0,
1153
+ properties: e.properties
1154
+ },
1155
+ paint: {
1156
+ "line-color": D,
1157
+ "line-width": 2
1158
+ }
1159
+ })) : e.type === "LineString" ? this.dispatch("webmapx-add-layer", {
1160
+ id: n,
1161
+ type: "line",
1162
+ source: t,
1163
+ title: e.name,
1164
+ metadata: {
1165
+ label: e.name,
1166
+ legendRole: "overlay",
1167
+ properties: e.properties
1168
+ },
1169
+ paint: {
1170
+ "line-color": D,
1171
+ "line-width": 2
1172
+ }
1173
+ }) : this.dispatch("webmapx-add-layer", {
1174
+ id: n,
1175
+ type: "circle",
1176
+ source: t,
1177
+ title: e.name,
1178
+ metadata: {
1179
+ label: e.name,
1180
+ legendRole: "overlay",
1181
+ properties: e.properties
1182
+ },
1183
+ paint: {
1184
+ "circle-radius": 5,
1185
+ "circle-color": D,
1186
+ "circle-stroke-width": 1,
1187
+ "circle-stroke-color": "#555"
1188
+ }
1189
+ }), t;
1190
+ }
1191
+ releaseBorrowedLayer(e) {
1192
+ if (e.borrowedSourceId) {
1193
+ this.restoreBorrowedLayer(e), this.removeMapLayersForDrawLayer(e), this.features = this.features.filter((t) => t.layerId !== e.id), this.drawLayers = this.drawLayers.filter((t) => t.id !== e.id);
1194
+ for (let [t, n] of Object.entries(this.activeLayerIds)) n === e.id && delete this.activeLayerIds[t];
1195
+ }
1196
+ }
1197
+ suspendDrawLayerFromMap(e) {
1198
+ e.type === "Polygon" && this.dispatch("webmapx-remove-layer", `${e.id}-outline`), this.dispatch("webmapx-remove-layer", e.id), this.dispatch("webmapx-remove-source", T(e.id)), this.adapter?.store && a(this.adapter.store, e.id), this.createdDrawLayerIds.delete(e.id), e.type === "Polygon" && this.createdDrawLayerIds.delete(`${e.id}-outline`);
1199
+ }
1200
+ releaseLayer(e) {
1201
+ this.releaseBorrowedLayer(e);
1202
+ }
1203
+ removeFromEditing(e) {
1204
+ let t = this.activeLayerIds[e.type] === e.id;
1205
+ this.releaseLayer(e), t && this.setModeInternal("select");
1206
+ }
1207
+ handleLayerCancel() {
1208
+ this.pendingMode = null;
1209
+ }
1210
+ handleClick(e) {
1211
+ let t = this.effectiveSnap && this.snapPos ? this.snapPos : e.coords, n = G(this.mode), r = n ? this.activeLayerIds[n] : null;
1212
+ if (!(!r && this.mode !== "select")) {
1213
+ if (this.mode === "draw-point") {
1214
+ this.commitFeature({
1215
+ id: this.newId(),
1216
+ layerId: r,
1217
+ type: "Point",
1218
+ coordinates: t,
1219
+ properties: this.defaultProperties(r)
1220
+ });
1221
+ return;
1222
+ }
1223
+ if (this.mode === "draw-line" || this.mode === "draw-polygon") {
1224
+ if (this.draftPoints.length >= 2) {
1225
+ let e = this.draftPoints[this.draftPoints.length - 1];
1226
+ if (this.withinPixelThreshold(t, e, 10) || this.mode === "draw-polygon" && this.withinPixelThreshold(t, this.draftPoints[0], 14)) {
1227
+ this.finishDraft(r);
1228
+ return;
1229
+ }
1230
+ }
1231
+ this.draftPoints.length === 0 && this.selectedFeatureId && (this.selectedFeatureId = null, this.editState = "none", this.editHandles = [], this.updateSelectedSource(), this.updateEditHandles()), this.draftPoints.push(t), this.draftRedoStack = [], this.uiVersion++, this.updateRubberband(), this.updateHelpTextDuring();
1232
+ return;
1233
+ }
1234
+ if (this.mode === "select") {
1235
+ let e = this.adapter.project(t), n = [e[0], e[1]], r = this.findFeatureAt(n, t);
1236
+ r && r.id === this.selectedFeatureId && this.editState === "selected" && (J(r.type) || Y(r.type)) ? this.enterEditMode(r.id) : r && r.id === this.selectedFeatureId && this.editState === "editing" || (r ? (this.selectedFeatureId = r.id, this.editState = q(r.type) ? "editing" : "selected", this.editState === "selected" ? this.helpText = "Click again to edit vertices." : q(r.type) && (this.helpText = "Drag to move point."), this.updateSelectedSource(), this.updateEditHandles(), this.requestUpdate()) : this.editState === "editing" ? (this.editState = "selected", this.updateEditHandles(), this.requestUpdate()) : (this.selectedFeatureId = null, this.editState = "none", this.updateSelectedSource(), this.updateEditHandles(), this.requestUpdate()));
1237
+ }
1238
+ }
1239
+ }
1240
+ handlePointerMove(e) {
1241
+ if (this.cursorPos = e.coords, this.featureDrag) {
1242
+ let t = this.features.find((e) => e.id === this.featureDrag.featureId);
1243
+ if (t) {
1244
+ let n = e.coords[0] - this.featureDrag.startCoords[0], r = e.coords[1] - this.featureDrag.startCoords[1];
1245
+ t.coordinates = this.translateCoordsGeoPreserving(this.featureDrag.origCoords, t.type, n, r, this.featureDrag.origCentroidLng, this.featureDrag.origCentroidLat), this.refreshDrawLayerSource(t.layerId), this.updateEditHandles(), this.updateSelectedSource();
1246
+ }
1247
+ return;
1248
+ }
1249
+ if (this.dragging) {
1250
+ let t = this.features.find((e) => e.id === this.dragging.handle.featureId);
1251
+ if (t) {
1252
+ let n = e.coords;
1253
+ if (this.effectiveSnap && this.features.length > 0) {
1254
+ let r = this.adapter.project(e.coords), i = this.computeSnapExcluding([r[0], r[1]], this.dragging.handle.featureId, q(t.type));
1255
+ this.snapPos = i, i && (n = i);
1256
+ } else this.snapPos = null;
1257
+ this.applyDragMove(t, this.dragging.handle, n), this.dragging.lastCoords = e.coords, this.refreshDrawLayerSource(t.layerId), this.updateEditHandles(), this.updateSelectedSource(), this.updateSnapIndicator();
1258
+ let r = this.dragging.handle;
1259
+ this.selectedHandle && r.kind === "vertex" && this.selectedHandle.featureId === r.featureId && this.selectedHandle.partIdx === r.partIdx && this.selectedHandle.ringIdx === r.ringIdx && this.selectedHandle.vertIdx === r.vertIdx && (this.selectedHandle.coords = r.coords, this.updateSelectedVertexSource());
1260
+ }
1261
+ return;
1262
+ }
1263
+ if (this.mode === "draw-point" || this.mode === "draw-line" || this.mode === "draw-polygon") {
1264
+ if (this.effectiveSnap && this.features.length > 0) {
1265
+ let t = this.adapter.project(e.coords), n = [t[0], t[1]];
1266
+ (!this.lastCursorPx || Math.hypot(n[0] - this.lastCursorPx[0], n[1] - this.lastCursorPx[1]) > .5) && (this.lastCursorPx = n, this.snapPos = this.computeSnap(n));
1267
+ } else this.snapPos = null;
1268
+ this.updateRubberband();
1269
+ return;
1270
+ }
1271
+ if (this.mode === "select") {
1272
+ let t = this.adapter.project(e.coords), n = this.findFeatureAt([t[0], t[1]], e.coords);
1273
+ this.editState === "selected" && this.selectedFeatureId ? this.adapter?.setCursor(n?.id === this.selectedFeatureId ? "grab" : "") : this.editState === "none" && this.adapter?.setCursor(n ? "pointer" : "");
1274
+ }
1275
+ if (this.mode === "select" && this.editState === "editing" && this.editHandles.length > 0) {
1276
+ let t = this.adapter.project(e.coords), n = this.findHandleAt([t[0], t[1]]);
1277
+ n !== this.hoveredHandle && (this.hoveredHandle = n, this.adapter?.setCursor(n ? "grab" : ""));
1278
+ }
1279
+ }
1280
+ handlePointerDown(e) {
1281
+ if (e.button !== 0) return;
1282
+ if (this.editState === "selected" && this.selectedFeatureId) {
1283
+ let t = [e.pixel[0], e.pixel[1]], n = this.findFeatureAt(t, e.coords);
1284
+ if (n && n.id === this.selectedFeatureId) {
1285
+ let t = this.centroid(n);
1286
+ this.featureDrag = {
1287
+ featureId: n.id,
1288
+ startCoords: e.coords,
1289
+ origCoords: JSON.parse(JSON.stringify(n.coordinates)),
1290
+ origCentroidLat: t[1],
1291
+ origCentroidLng: t[0]
1292
+ }, this.adapter?.setPanEnabled(!1), this.adapter?.setCursor("grabbing");
1293
+ }
1294
+ return;
1295
+ }
1296
+ if (this.editState !== "editing") return;
1297
+ let t = [e.pixel[0], e.pixel[1]], n = this.findHandleAt(t);
1298
+ if (!n) {
1299
+ this.selectedHandle && (this.selectedHandle = null, this.updateSelectedVertexSource());
1300
+ return;
1301
+ }
1302
+ if (this.selectedHandle = n.kind === "vertex" ? n : null, this.updateSelectedVertexSource(), this.dragging = {
1303
+ handle: n,
1304
+ lastCoords: e.coords
1305
+ }, this.adapter?.setPanEnabled(!1), this.adapter?.setCursor("grabbing"), n.kind === "midpoint") {
1306
+ let t = this.features.find((e) => e.id === n.featureId);
1307
+ if (t) {
1308
+ this.insertVertex(t, n);
1309
+ let r = n.afterVertIdx + 1, i = {
1310
+ kind: "vertex",
1311
+ featureId: n.featureId,
1312
+ partIdx: n.partIdx,
1313
+ ringIdx: n.ringIdx,
1314
+ vertIdx: r,
1315
+ coords: n.coords
1316
+ };
1317
+ this.dragging = {
1318
+ handle: i,
1319
+ lastCoords: e.coords
1320
+ }, this.selectedHandle = i, this.updateSelectedVertexSource(), this.refreshDrawLayerSource(t.layerId), this.updateEditHandles();
1321
+ }
1322
+ }
1323
+ }
1324
+ handlePointerUp(e) {
1325
+ if (this.featureDrag) {
1326
+ let e = this.features.find((e) => e.id === this.featureDrag.featureId);
1327
+ if (e) {
1328
+ let t = this.drawLayers.find((t) => t.id === e.layerId);
1329
+ t && this.computeSpecialProperties(e, t), this.pushHistory({
1330
+ type: "update",
1331
+ features: [{ ...e }]
1332
+ }), this.features = [...this.features], this.refreshDrawLayerSource(e.layerId);
1333
+ }
1334
+ this.featureDrag = null, this.adapter?.setPanEnabled(!0), this.adapter?.setCursor("");
1335
+ return;
1336
+ }
1337
+ if (!this.dragging) return;
1338
+ this.snapPos = null, this.updateSnapIndicator(), this.adapter?.setPanEnabled(!0), this.adapter?.setCursor(this.hoveredHandle ? "grab" : "");
1339
+ let t = this.features.find((e) => e.id === this.dragging.handle.featureId);
1340
+ if (t) {
1341
+ let e = this.drawLayers.find((e) => e.id === t.layerId);
1342
+ e && this.computeSpecialProperties(t, e), this.pushHistory({
1343
+ type: "update",
1344
+ features: [{ ...t }]
1345
+ }), this.features = [...this.features], this.refreshDrawLayerSource(t.layerId);
1346
+ }
1347
+ this.dragging = null;
1348
+ }
1349
+ handleContextMenu(e) {
1350
+ let t = G(this.mode), n = t ? this.activeLayerIds[t] : null;
1351
+ n && (this.mode === "draw-line" || this.mode === "draw-polygon") && this.finishDraft(n);
1352
+ }
1353
+ defaultProperties(e) {
1354
+ let t = this.drawLayers.find((t) => t.id === e);
1355
+ return t ? Object.fromEntries(t.properties.map((e) => [e.name, null])) : {};
1356
+ }
1357
+ finishDraft(e) {
1358
+ let t = this.draftPoints;
1359
+ if (this.mode === "draw-line" && t.length >= 2) this.commitFeature({
1360
+ id: this.newId(),
1361
+ layerId: e,
1362
+ type: "LineString",
1363
+ coordinates: t.map((e) => [e[0], e[1]]),
1364
+ properties: this.defaultProperties(e)
1365
+ }, t);
1366
+ else if (this.mode === "draw-polygon" && t.length >= 3) {
1367
+ let n = [...t.map((e) => [e[0], e[1]]), [t[0][0], t[0][1]]];
1368
+ this.commitFeature({
1369
+ id: this.newId(),
1370
+ layerId: e,
1371
+ type: "Polygon",
1372
+ coordinates: [n],
1373
+ properties: this.defaultProperties(e)
1374
+ }, t);
1375
+ }
1376
+ this.draftPoints = [], this.draftRedoStack = [], this.cursorPos = null, this.updateRubberband();
1377
+ }
1378
+ computeSnap(e) {
1379
+ return this.computeSnapExcluding(e, null, this.mode === "draw-point");
1380
+ }
1381
+ computeSnapExcluding(e, t, n) {
1382
+ return this.adapter ? b(e, this.features.filter((e) => e.id !== t && !(n && q(e.type))), (e) => {
1383
+ let t = this.adapter.project(e);
1384
+ return [t[0], t[1]];
1385
+ }, {
1386
+ threshold: U,
1387
+ edgePenalty: 8
1388
+ }) : null;
1389
+ }
1390
+ updateSnapIndicator() {
1391
+ if (!this.sharedLayersCreated) return;
1392
+ let e = this.effectiveSnap && this.snapPos ? [{
1393
+ type: "Feature",
1394
+ geometry: {
1395
+ type: "Point",
1396
+ coordinates: this.snapPos
1397
+ },
1398
+ properties: {}
1399
+ }] : [];
1400
+ this.dispatch("webmapx-set-source-data", {
1401
+ id: V,
1402
+ data: {
1403
+ type: "FeatureCollection",
1404
+ features: e
1405
+ }
1406
+ });
1407
+ }
1408
+ updateRubberband() {
1409
+ if (!this.sharedLayersCreated) return;
1410
+ let e = [], t = [], n = this.mode === "draw-point" || this.mode === "draw-line" || this.mode === "draw-polygon", r = this.effectiveSnap && this.snapPos ? this.snapPos : this.cursorPos;
1411
+ if (n && this.draftPoints.length > 0 && r) {
1412
+ let n = [...this.draftPoints.map((e) => [e[0], e[1]]), [r[0], r[1]]];
1413
+ e.push({
1414
+ type: "Feature",
1415
+ geometry: {
1416
+ type: "LineString",
1417
+ coordinates: n
1418
+ },
1419
+ properties: {}
1420
+ });
1421
+ for (let e of this.draftPoints) t.push({
1422
+ type: "Feature",
1423
+ geometry: {
1424
+ type: "Point",
1425
+ coordinates: [e[0], e[1]]
1426
+ },
1427
+ properties: {}
1428
+ });
1429
+ }
1430
+ this.dispatch("webmapx-set-source-data", {
1431
+ id: x,
1432
+ data: {
1433
+ type: "FeatureCollection",
1434
+ features: e
1435
+ }
1436
+ }), this.dispatch("webmapx-set-source-data", {
1437
+ id: M,
1438
+ data: {
1439
+ type: "FeatureCollection",
1440
+ features: t
1441
+ }
1442
+ });
1443
+ let i = n && this.effectiveSnap && this.snapPos ? [{
1444
+ type: "Feature",
1445
+ geometry: {
1446
+ type: "Point",
1447
+ coordinates: this.snapPos
1448
+ },
1449
+ properties: {}
1450
+ }] : [];
1451
+ this.dispatch("webmapx-set-source-data", {
1452
+ id: V,
1453
+ data: {
1454
+ type: "FeatureCollection",
1455
+ features: i
1456
+ }
1457
+ });
1458
+ }
1459
+ updateSelectedSource() {
1460
+ if (!this.sharedLayersCreated) return;
1461
+ let e = this.features.find((e) => e.id === this.selectedFeatureId), t = e && q(e.type) ? [{
1462
+ type: "Feature",
1463
+ id: e.id,
1464
+ geometry: {
1465
+ type: e.type,
1466
+ coordinates: e.coordinates
1467
+ },
1468
+ properties: {}
1469
+ }] : [];
1470
+ this.dispatch("webmapx-set-source-data", {
1471
+ id: A,
1472
+ data: {
1473
+ type: "FeatureCollection",
1474
+ features: t
1475
+ }
1476
+ });
1477
+ }
1478
+ enterEditMode(e) {
1479
+ this.editState = "editing", this.helpText = "Drag points to move. Drag midpoints to add a point. Click a point and press Delete to remove it. Click empty to exit.", this.updateEditHandles(), this.adapter?.setCursor("default"), this.requestUpdate();
1480
+ }
1481
+ computeHandles(e) {
1482
+ let t = [];
1483
+ if (e.type === "Point") return t.push({
1484
+ kind: "vertex",
1485
+ featureId: e.id,
1486
+ partIdx: 0,
1487
+ ringIdx: 0,
1488
+ vertIdx: 0,
1489
+ coords: e.coordinates
1490
+ }), t;
1491
+ if (e.type === "MultiPoint") return e.coordinates.forEach((n, r) => {
1492
+ t.push({
1493
+ kind: "vertex",
1494
+ featureId: e.id,
1495
+ partIdx: r,
1496
+ ringIdx: 0,
1497
+ vertIdx: 0,
1498
+ coords: n
1499
+ });
1500
+ }), t;
1501
+ let n = (n, r, i, a) => {
1502
+ let o = a ? n.length - 1 : n.length;
1503
+ for (let s = 0; s < o; s++) {
1504
+ if (t.push({
1505
+ kind: "vertex",
1506
+ featureId: e.id,
1507
+ partIdx: r,
1508
+ ringIdx: i,
1509
+ vertIdx: s,
1510
+ coords: n[s]
1511
+ }), !a && s === o - 1) continue;
1512
+ let c = (s + 1) % o, l = [(n[s][0] + n[c][0]) / 2, (n[s][1] + n[c][1]) / 2];
1513
+ t.push({
1514
+ kind: "midpoint",
1515
+ featureId: e.id,
1516
+ partIdx: r,
1517
+ ringIdx: i,
1518
+ afterVertIdx: s,
1519
+ coords: l
1520
+ });
1521
+ }
1522
+ };
1523
+ return e.type === "LineString" ? n(e.coordinates, 0, 0, !1) : e.type === "MultiLineString" ? e.coordinates.forEach((e, t) => n(e, t, 0, !1)) : e.type === "Polygon" ? e.coordinates.forEach((e, t) => n(e, 0, t, !0)) : e.type === "MultiPolygon" && e.coordinates.forEach((e, t) => {
1524
+ e.forEach((e, r) => n(e, t, r, !0));
1525
+ }), t;
1526
+ }
1527
+ updateEditHandles() {
1528
+ if (!this.sharedLayersCreated) return;
1529
+ let e = this.selectedFeatureId ? this.features.find((e) => e.id === this.selectedFeatureId) : null;
1530
+ if (!e) {
1531
+ this.editHandles = [], this.selectedHandle = null, this.updateSelectedVertexSource(), this.dispatch("webmapx-set-source-data", {
1532
+ id: P,
1533
+ data: {
1534
+ type: "FeatureCollection",
1535
+ features: []
1536
+ }
1537
+ }), this.dispatch("webmapx-set-source-data", {
1538
+ id: I,
1539
+ data: {
1540
+ type: "FeatureCollection",
1541
+ features: []
1542
+ }
1543
+ });
1544
+ return;
1545
+ }
1546
+ this.selectedHandle && this.selectedHandle.featureId !== e.id && (this.selectedHandle = null, this.updateSelectedVertexSource()), this.editHandles = this.computeHandles(e);
1547
+ let t = this.editState === "editing", n = this.editHandles.filter((e) => e.kind === "vertex").map((e) => ({
1548
+ type: "Feature",
1549
+ geometry: {
1550
+ type: "Point",
1551
+ coordinates: e.coords
1552
+ },
1553
+ properties: {}
1554
+ })), r = t ? this.editHandles.filter((e) => e.kind === "midpoint").map((e) => ({
1555
+ type: "Feature",
1556
+ geometry: {
1557
+ type: "Point",
1558
+ coordinates: e.coords
1559
+ },
1560
+ properties: {}
1561
+ })) : [];
1562
+ this.dispatch("webmapx-set-source-data", {
1563
+ id: P,
1564
+ data: {
1565
+ type: "FeatureCollection",
1566
+ features: n
1567
+ }
1568
+ }), this.dispatch("webmapx-set-source-data", {
1569
+ id: I,
1570
+ data: {
1571
+ type: "FeatureCollection",
1572
+ features: r
1573
+ }
1574
+ });
1575
+ }
1576
+ findHandleAt(e) {
1577
+ let t = null, n = B;
1578
+ for (let r of this.editHandles) {
1579
+ let i = this.adapter.project(r.coords), a = Math.hypot(e[0] - i[0], e[1] - i[1]);
1580
+ a < n && (n = a, t = r);
1581
+ }
1582
+ return t;
1583
+ }
1584
+ applyDragMove(e, t, n) {
1585
+ let r = JSON.parse(JSON.stringify(e.coordinates));
1586
+ if (t.kind !== "vertex") return;
1587
+ let { partIdx: i, ringIdx: a, vertIdx: o } = t;
1588
+ if (e.type === "Point") {
1589
+ e.coordinates = [n[0], n[1]], t.coords = n;
1590
+ return;
1591
+ } else if (e.type === "MultiPoint") r[i] = [n[0], n[1]];
1592
+ else if (e.type === "LineString") r[o] = [n[0], n[1]];
1593
+ else if (e.type === "MultiLineString") r[i][o] = [n[0], n[1]];
1594
+ else if (e.type === "Polygon") {
1595
+ r[a][o] = [n[0], n[1]];
1596
+ let e = r[a];
1597
+ o === 0 && (e[e.length - 1] = e[0]);
1598
+ } else if (e.type === "MultiPolygon") {
1599
+ r[i][a][o] = [n[0], n[1]];
1600
+ let e = r[i][a];
1601
+ o === 0 && (e[e.length - 1] = e[0]);
1602
+ }
1603
+ e.coordinates = r, t.coords = n;
1604
+ }
1605
+ translateCoordsGeoPreserving(e, t, n, r, i, a) {
1606
+ if (!isFinite(a) || !isFinite(i)) return this.translateCoords(e, t, n, r);
1607
+ let o = a + r, s = Math.cos(a * Math.PI / 180), c = Math.cos(o * Math.PI / 180), l = c > 1e-6 ? s / c : 1, u = i + n, d = (e) => [u + (e[0] - i) * l, e[1] + r];
1608
+ return t === "Point" ? d(e) : t === "MultiPoint" || t === "LineString" ? e.map(d) : t === "MultiLineString" ? e.map((e) => e.map(d)) : t === "Polygon" ? e.map((e) => e.map(d)) : t === "MultiPolygon" ? e.map((e) => e.map((e) => e.map(d))) : e;
1609
+ }
1610
+ translateCoords(e, t, n, r) {
1611
+ let i = (e) => [e[0] + n, e[1] + r];
1612
+ return t === "Point" ? i(e) : t === "MultiPoint" || t === "LineString" ? e.map(i) : t === "MultiLineString" ? e.map((e) => e.map(i)) : t === "Polygon" ? e.map((e) => e.map(i)) : t === "MultiPolygon" ? e.map((e) => e.map((e) => e.map(i))) : e;
1613
+ }
1614
+ insertVertex(e, t) {
1615
+ let n = JSON.parse(JSON.stringify(e.coordinates)), { partIdx: r, ringIdx: i, afterVertIdx: a } = t;
1616
+ e.type === "LineString" ? n.splice(a + 1, 0, [t.coords[0], t.coords[1]]) : e.type === "MultiLineString" ? n[r].splice(a + 1, 0, [t.coords[0], t.coords[1]]) : e.type === "Polygon" ? n[i].splice(a + 1, 0, [t.coords[0], t.coords[1]]) : e.type === "MultiPolygon" && n[r][i].splice(a + 1, 0, [t.coords[0], t.coords[1]]), e.coordinates = n, this.pushHistory({
1617
+ type: "update",
1618
+ features: [{ ...e }]
1619
+ });
1620
+ }
1621
+ updateSelectedVertexSource() {
1622
+ if (this.uiVersion++, !this.sharedLayersCreated) return;
1623
+ let e = this.selectedHandle ? [{
1624
+ type: "Feature",
1625
+ geometry: {
1626
+ type: "Point",
1627
+ coordinates: this.selectedHandle.coords
1628
+ },
1629
+ properties: {}
1630
+ }] : [];
1631
+ this.dispatch("webmapx-set-source-data", {
1632
+ id: R,
1633
+ data: {
1634
+ type: "FeatureCollection",
1635
+ features: e
1636
+ }
1637
+ });
1638
+ }
1639
+ deleteSelectedVertex() {
1640
+ let e = this.selectedHandle;
1641
+ if (!e) return;
1642
+ let t = this.features.find((t) => t.id === e.featureId);
1643
+ if (!t) return;
1644
+ let n = JSON.parse(JSON.stringify(t.coordinates)), { partIdx: r, ringIdx: i, vertIdx: a } = e;
1645
+ if (t.type === "LineString") {
1646
+ if (n.length <= 2) return;
1647
+ n.splice(a, 1);
1648
+ } else if (t.type === "MultiLineString") {
1649
+ if (n[r].length <= 2) return;
1650
+ n[r].splice(a, 1);
1651
+ } else if (t.type === "Polygon") {
1652
+ let e = n[i];
1653
+ if (e.length - 1 <= 3) return;
1654
+ a === 0 || a === e.length - 1 ? (e.splice(e.length - 1, 1), e.splice(0, 1), e.push([...e[0]])) : e.splice(a, 1);
1655
+ } else if (t.type === "MultiPolygon") {
1656
+ let e = n[r][i];
1657
+ if (e.length - 1 <= 3) return;
1658
+ a === 0 || a === e.length - 1 ? (e.splice(e.length - 1, 1), e.splice(0, 1), e.push([...e[0]])) : e.splice(a, 1);
1659
+ } else if (t.type === "MultiPoint") {
1660
+ if (n.length <= 1) return;
1661
+ n.splice(r, 1);
1662
+ } else return;
1663
+ t.coordinates = n;
1664
+ let o = this.drawLayers.find((e) => e.id === t.layerId);
1665
+ o && this.computeSpecialProperties(t, o), this.pushHistory({
1666
+ type: "update",
1667
+ features: [{ ...t }]
1668
+ }), this.features = [...this.features], this.refreshDrawLayerSource(t.layerId), this.selectedHandle = null, this.updateSelectedVertexSource(), this.updateEditHandles();
1669
+ }
1670
+ commitFeature(e, t) {
1671
+ let n = this.features.filter((t) => t.layerId === e.layerId).reduce((e, t) => {
1672
+ let n = parseInt(String(t.properties.id ?? 0), 10);
1673
+ return isNaN(n) ? e : Math.max(e, n);
1674
+ }, 0);
1675
+ e.properties.id = n + 1;
1676
+ let r = this.drawLayers.find((t) => t.id === e.layerId);
1677
+ if (r && this.computeSpecialProperties(e, r), r) for (let t of r.properties) t.type === "create-time" && (e.properties[t.name] = Date.now());
1678
+ this.pushHistory(t ? {
1679
+ type: "finish",
1680
+ features: [e],
1681
+ draftPoints: t.map((e) => [e[0], e[1]])
1682
+ } : {
1683
+ type: "add",
1684
+ features: [e]
1685
+ }), this.features = [...this.features, e], this.refreshDrawLayerSource(e.layerId), this.setModeInternal(this.mode), this.selectedFeatureId = e.id, this.editState = q(e.type) ? "editing" : "selected", this.updateSelectedSource(), this.updateEditHandles();
1686
+ }
1687
+ deleteSelected() {
1688
+ if (!this.selectedFeatureId) return;
1689
+ let e = this.features.filter((e) => e.id === this.selectedFeatureId);
1690
+ this.pushHistory({
1691
+ type: "delete",
1692
+ features: e
1693
+ }), this.features = this.features.filter((e) => e.id !== this.selectedFeatureId), new Set(e.map((e) => e.layerId)).forEach((e) => this.refreshDrawLayerSource(e)), this.selectedFeatureId = null, this.editState = "none", this.editHandles = [], this.adapter?.setCursor(""), this.updateSelectedSource(), this.updateEditHandles();
1694
+ }
1695
+ pushHistory(e) {
1696
+ this.history = this.history.slice(0, this.historyIndex + 1), this.history.push(e), this.historyIndex = this.history.length - 1, this.uiVersion++;
1697
+ }
1698
+ removeLastDraftPoint() {
1699
+ this.draftRedoStack.push(this.draftPoints.pop()), this.uiVersion++, this.updateRubberband(), this.updateHelpTextDuring();
1700
+ }
1701
+ undoOrDraftBack() {
1702
+ this.draftPoints.length > 0 ? this.removeLastDraftPoint() : this.undo();
1703
+ }
1704
+ redoOrDraftForward() {
1705
+ this.draftRedoStack.length > 0 ? (this.draftPoints.push(this.draftRedoStack.pop()), this.uiVersion++, this.updateRubberband(), this.updateHelpTextDuring()) : this.redo();
1706
+ }
1707
+ undo() {
1708
+ if (this.historyIndex < 0) return;
1709
+ let e = this.history[this.historyIndex--];
1710
+ this.uiVersion++;
1711
+ let t = /* @__PURE__ */ new Set();
1712
+ if (e.type === "add" || e.type === "finish") {
1713
+ let n = new Set(e.features.map((e) => e.id));
1714
+ this.features = this.features.filter((e) => !n.has(e.id)), e.features.forEach((e) => t.add(e.layerId));
1715
+ } else e.type === "delete" ? (this.features = [...this.features, ...e.features], e.features.forEach((e) => t.add(e.layerId))) : e.type === "update" && (this.features = this.features.map((n) => {
1716
+ let r = e.features.find((e) => e.id === n.id);
1717
+ return r ? (t.add(n.layerId), {
1718
+ ...n,
1719
+ coordinates: r.coordinates
1720
+ }) : n;
1721
+ }));
1722
+ if (this.selectedFeatureId = null, this.editState = "none", this.updateSelectedSource(), this.updateEditHandles(), t.forEach((e) => this.refreshDrawLayerSource(e)), e.type === "finish" && e.draftPoints) {
1723
+ let t = e.features[0];
1724
+ this.mode = t.type === "LineString" ? "draw-line" : "draw-polygon", this.draftPoints = e.draftPoints.map((e) => [e[0], e[1]]), this.draftRedoStack = [], this.cursorPos = this.draftPoints[this.draftPoints.length - 1], this.updateRubberband(), this.updateHelpTextDuring();
1725
+ }
1726
+ }
1727
+ redo() {
1728
+ if (this.historyIndex >= this.history.length - 1) return;
1729
+ let e = this.history[++this.historyIndex];
1730
+ this.uiVersion++;
1731
+ let t = /* @__PURE__ */ new Set();
1732
+ if (e.type === "add" || e.type === "finish") this.features = [...this.features, ...e.features], e.features.forEach((e) => t.add(e.layerId));
1733
+ else if (e.type === "delete") {
1734
+ let n = new Set(e.features.map((e) => e.id));
1735
+ this.features = this.features.filter((e) => !n.has(e.id)), e.features.forEach((e) => t.add(e.layerId));
1736
+ } else e.type === "update" && (this.features = this.features.map((n) => {
1737
+ let r = e.features.find((e) => e.id === n.id);
1738
+ return r ? (t.add(n.layerId), {
1739
+ ...n,
1740
+ coordinates: r.coordinates
1741
+ }) : n;
1742
+ }));
1743
+ if (t.forEach((e) => this.refreshDrawLayerSource(e)), e.type === "finish") {
1744
+ let t = e.features[0];
1745
+ this.draftPoints = [], this.draftRedoStack = [], this.cursorPos = null, this.selectedFeatureId = t.id, this.editState = q(t.type) ? "editing" : "selected", this.updateSelectedSource(), this.updateEditHandles(), this.updateRubberband();
1746
+ }
1747
+ }
1748
+ exportGeoJSON() {
1749
+ this.exportFilename = "draw-export", this.exportMode = "combined", this.exportDialog.show();
1750
+ }
1751
+ async doExport() {
1752
+ this.exportDialog.hide();
1753
+ let e = this.exportFilename.trim() || "draw-export";
1754
+ if (this.drawLayers.length <= 1 || this.exportMode === "combined") {
1755
+ let t = {
1756
+ type: "FeatureCollection",
1757
+ features: this.features.map((e) => ({
1758
+ type: "Feature",
1759
+ id: e.id,
1760
+ geometry: {
1761
+ type: e.type,
1762
+ coordinates: e.coordinates
1763
+ },
1764
+ properties: {
1765
+ ...e.properties,
1766
+ _layer: this.drawLayers.find((t) => t.id === e.layerId)?.name ?? e.layerId
1767
+ }
1768
+ }))
1769
+ };
1770
+ this.downloadBlob(new Blob([JSON.stringify(t, null, 2)], { type: "application/json" }), `${e}.geojson`);
1771
+ } else {
1772
+ let i = new r(new n("application/zip"));
1773
+ for (let e of this.drawLayers) {
1774
+ let n = {
1775
+ type: "FeatureCollection",
1776
+ features: this.features.filter((t) => t.layerId === e.id).map((e) => ({
1777
+ type: "Feature",
1778
+ id: e.id,
1779
+ geometry: {
1780
+ type: e.type,
1781
+ coordinates: e.coordinates
1782
+ },
1783
+ properties: { ...e.properties }
1784
+ }))
1785
+ }, r = e.name.replace(/[/\\?%*:|"<>]/g, "_");
1786
+ await i.add(`${r}.geojson`, new t(JSON.stringify(n, null, 2)));
1787
+ }
1788
+ this.downloadBlob(await i.close(), `${e}.zip`);
1789
+ }
1790
+ }
1791
+ downloadBlob(e, t) {
1792
+ let n = URL.createObjectURL(e), r = document.createElement("a");
1793
+ r.href = n, r.download = t, r.click(), URL.revokeObjectURL(n);
1794
+ }
1795
+ newId() {
1796
+ return `draw-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
1797
+ }
1798
+ computeSpecialProperties(e, t) {
1799
+ for (let n of t.properties) switch (n.type) {
1800
+ case "longitude":
1801
+ case "latitude": {
1802
+ let t = e.type === "Point" ? e.coordinates : this.centroid(e);
1803
+ e.properties[n.name] = n.type === "longitude" ? t[0] : t[1];
1804
+ break;
1805
+ }
1806
+ case "area":
1807
+ e.properties[n.name] = e.type === "Polygon" ? this.polygonArea(e.coordinates) : null;
1808
+ break;
1809
+ case "perimeter":
1810
+ case "length":
1811
+ e.properties[n.name] = e.type === "Polygon" ? this.ringLength(e.coordinates[0]) : e.type === "LineString" ? this.ringLength(e.coordinates, !1) : null;
1812
+ break;
1813
+ case "update-time":
1814
+ e.properties[n.name] = Date.now();
1815
+ break;
1816
+ }
1817
+ }
1818
+ centroid(e) {
1819
+ let t;
1820
+ switch (e.type) {
1821
+ case "Point":
1822
+ t = [e.coordinates];
1823
+ break;
1824
+ case "MultiPoint":
1825
+ t = e.coordinates;
1826
+ break;
1827
+ case "LineString":
1828
+ t = e.coordinates;
1829
+ break;
1830
+ case "MultiLineString":
1831
+ t = e.coordinates.flat();
1832
+ break;
1833
+ case "Polygon":
1834
+ t = e.coordinates[0];
1835
+ break;
1836
+ case "MultiPolygon":
1837
+ t = e.coordinates.flat(2);
1838
+ break;
1839
+ default: t = [];
1840
+ }
1841
+ if (t.length === 0) return [0, 0];
1842
+ let n = t.reduce((e, t) => [e[0] + t[0], e[1] + t[1]], [0, 0]);
1843
+ return [n[0] / t.length, n[1] / t.length];
1844
+ }
1845
+ haversineKm(e, t) {
1846
+ let n = (t[1] - e[1]) * Math.PI / 180, r = (t[0] - e[0]) * Math.PI / 180, i = Math.sin(n / 2) ** 2 + Math.cos(e[1] * Math.PI / 180) * Math.cos(t[1] * Math.PI / 180) * Math.sin(r / 2) ** 2;
1847
+ return 6371 * 2 * Math.atan2(Math.sqrt(i), Math.sqrt(1 - i));
1848
+ }
1849
+ ringLength(e, t = !0) {
1850
+ let n = 0, r = e.length - 1;
1851
+ for (let t = 0; t < r; t++) n += this.haversineKm(e[t], e[t + 1]);
1852
+ return Math.round(n * 1e3);
1853
+ }
1854
+ polygonArea(e) {
1855
+ let t = e[0], n = 0;
1856
+ for (let e = 0, r = t.length - 1; e < t.length; r = e++) n += (t[r][0] + t[e][0]) * (t[r][1] - t[e][1]);
1857
+ let r = Math.abs(n / 2), i = t[0][1] * Math.PI / 180;
1858
+ return Math.round(111320 * Math.cos(i) * r * 111320);
1859
+ }
1860
+ withinPixelThreshold(e, t, n) {
1861
+ if (!this.adapter) return !1;
1862
+ let r = this.adapter.project(e), i = this.adapter.project(t);
1863
+ return Math.hypot(r[0] - i[0], r[1] - i[1]) < n;
1864
+ }
1865
+ findFeatureAt(e, t) {
1866
+ for (let n of [...this.features].reverse()) if (n.type === "Point") {
1867
+ let t = this.adapter.project(n.coordinates);
1868
+ if (Math.hypot(t[0] - e[0], t[1] - e[1]) < 10) return n;
1869
+ } else if (n.type === "MultiPoint") for (let t of n.coordinates) {
1870
+ let r = this.adapter.project(t);
1871
+ if (Math.hypot(r[0] - e[0], r[1] - e[1]) < 10) return n;
1872
+ }
1873
+ else if (n.type === "LineString") {
1874
+ if (this.pixelNearPolyline(e, n.coordinates, 10)) return n;
1875
+ } else if (n.type === "MultiLineString") {
1876
+ if (n.coordinates.some((t) => this.pixelNearPolyline(e, t, 10))) return n;
1877
+ } else if (n.type === "Polygon") {
1878
+ if (this.pointInRing(t, n.coordinates[0]) || this.pixelNearPolyline(e, n.coordinates[0], 10)) return n;
1879
+ } else if (n.type === "MultiPolygon") for (let r of n.coordinates) {
1880
+ let i = r[0];
1881
+ if (i && (this.pointInRing(t, i) || this.pixelNearPolyline(e, i, 10))) return n;
1882
+ }
1883
+ return null;
1884
+ }
1885
+ pixelNearPolyline(e, t, n) {
1886
+ for (let r = 0; r < t.length - 1; r++) {
1887
+ let i = this.adapter.project(t[r]), a = this.adapter.project(t[r + 1]);
1888
+ if (this.distToSegment(e, i, a) < n) return !0;
1889
+ }
1890
+ return !1;
1891
+ }
1892
+ distToSegment(e, t, n) {
1893
+ let r = n[0] - t[0], i = n[1] - t[1];
1894
+ if (r === 0 && i === 0) return Math.hypot(e[0] - t[0], e[1] - t[1]);
1895
+ let a = Math.max(0, Math.min(1, ((e[0] - t[0]) * r + (e[1] - t[1]) * i) / (r * r + i * i)));
1896
+ return Math.hypot(e[0] - (t[0] + a * r), e[1] - (t[1] + a * i));
1897
+ }
1898
+ pointInRing(e, t) {
1899
+ let n = !1;
1900
+ for (let r = 0, i = t.length - 1; r < t.length; i = r++) {
1901
+ let a = t[r][0], o = t[r][1], s = t[i][0], c = t[i][1];
1902
+ o > e[1] != c > e[1] && e[0] < (s - a) * (e[1] - o) / (c - o) + a && (n = !n);
1903
+ }
1904
+ return n;
1905
+ }
1906
+ dispatch(e, t) {
1907
+ this.dispatchEvent(new CustomEvent(e, {
1908
+ detail: t,
1909
+ bubbles: !0,
1910
+ composed: !0
1911
+ }));
1912
+ }
1913
+ updateHelpTextDuring() {
1914
+ let e = this.draftPoints.length;
1915
+ this.mode === "draw-line" ? this.helpText = `${e} pt${e === 1 ? "" : "s"}. Double-click or right-click to finish.` : this.mode === "draw-polygon" && (this.helpText = e >= 3 ? `${e} pts. Click first point, double-click, or right-click to close.` : `${e} pt${e === 1 ? "" : "s"}. Need at least 3 to close.`);
1916
+ }
1917
+ render() {
1918
+ let e = this.features.find((e) => e.id === this.selectedFeatureId), t = e ? this.drawLayers.find((t) => t.id === e.layerId) : null;
1919
+ return c`
1920
+ <div class="toolbar">
1921
+ <sl-tooltip content="Select">
1922
+ <sl-icon-button name="cursor"
1923
+ ?active=${this.mode === "select"}
1924
+ @click=${() => this.requestDrawMode("select")}>
1925
+ </sl-icon-button>
1926
+ </sl-tooltip>
1927
+ <sl-tooltip content="Draw point">
1928
+ <sl-icon-button name="geo-fill"
1929
+ ?active=${this.mode === "draw-point"}
1930
+ @click=${() => this.requestDrawMode("draw-point")}>
1931
+ </sl-icon-button>
1932
+ </sl-tooltip>
1933
+ <sl-tooltip content="Draw line">
1934
+ <sl-icon-button name="slash-lg"
1935
+ ?active=${this.mode === "draw-line"}
1936
+ @click=${() => this.requestDrawMode("draw-line")}>
1937
+ </sl-icon-button>
1938
+ </sl-tooltip>
1939
+ <sl-tooltip content="Draw polygon">
1940
+ <sl-icon-button name="pentagon"
1941
+ ?active=${this.mode === "draw-polygon"}
1942
+ @click=${() => this.requestDrawMode("draw-polygon")}>
1943
+ </sl-icon-button>
1944
+ </sl-tooltip>
1945
+
1946
+ <div class="divider"></div>
1947
+
1948
+ <sl-tooltip content="Snap to points and edges (${this.snapEnabled ? "on" : "off"}) — hold Alt to toggle">
1949
+ <sl-icon-button name="magnet"
1950
+ ?active=${this.effectiveSnap}
1951
+ @click=${() => {
1952
+ this.snapEnabled = !this.snapEnabled, this.snapEnabled || (this.snapPos = null, this.updateRubberband());
1953
+ }}>
1954
+ </sl-icon-button>
1955
+ </sl-tooltip>
1956
+
1957
+ <div class="divider"></div>
1958
+
1959
+ <sl-tooltip content="Undo (${this.modKey}+Z)">
1960
+ <sl-icon-button name="arrow-counterclockwise"
1961
+ ?disabled=${this.historyIndex < 0 && this.draftPoints.length === 0}
1962
+ @click=${() => this.undoOrDraftBack()}>
1963
+ </sl-icon-button>
1964
+ </sl-tooltip>
1965
+ <sl-tooltip content="Redo (${this.modKey}+Y)">
1966
+ <sl-icon-button name="arrow-clockwise"
1967
+ ?disabled=${this.historyIndex >= this.history.length - 1 && this.draftRedoStack.length === 0}
1968
+ @click=${() => this.redoOrDraftForward()}>
1969
+ </sl-icon-button>
1970
+ </sl-tooltip>
1971
+
1972
+ <div class="divider"></div>
1973
+
1974
+ <sl-tooltip content=${this.draftPoints.length > 0 ? "Remove last point" : this.selectedHandle ? "Delete selected point" : "Delete selected"}>
1975
+ <sl-icon-button name="trash"
1976
+ ?disabled=${this.draftPoints.length === 0 && !this.selectedFeatureId && !this.selectedHandle}
1977
+ @click=${() => this.draftPoints.length > 0 ? this.removeLastDraftPoint() : this.selectedHandle ? this.deleteSelectedVertex() : this.deleteSelected()}>
1978
+ </sl-icon-button>
1979
+ </sl-tooltip>
1980
+ <sl-tooltip content="Export GeoJSON">
1981
+ <sl-icon-button name="download"
1982
+ ?disabled=${this.features.length === 0}
1983
+ @click=${() => this.exportGeoJSON()}>
1984
+ </sl-icon-button>
1985
+ </sl-tooltip>
1986
+ </div>
1987
+
1988
+ <div class="scroll-content">
1989
+ <div class="help">${this.helpText}</div>
1990
+
1991
+ ${this.isTouchDevice && (this.mode === "draw-line" || this.mode === "draw-polygon") && this.draftPoints.length >= (this.mode === "draw-line" ? 2 : 3) ? c`
1992
+ <sl-button size="small" variant="primary" style="margin-bottom:.4rem;width:100%"
1993
+ @click=${() => {
1994
+ let e = G(this.mode), t = e ? this.activeLayerIds[e] : null;
1995
+ t && this.finishDraft(t);
1996
+ }}>
1997
+ Finish
1998
+ </sl-button>
1999
+ ` : ""}
2000
+
2001
+ ${this.drawLayers.length > 0 ? c`
2002
+ <div class="layers-section">
2003
+ <div class="section-label">Editing</div>
2004
+ ${this.drawLayers.map((e) => c`
2005
+ <div class="layer-row" title="Click to change layer"
2006
+ @click=${() => this.openLayerDialog(e.type === "Point" ? "draw-point" : e.type === "LineString" ? "draw-line" : "draw-polygon")}>
2007
+ <span class="color-dot" style="background:${e.color}"></span>
2008
+ <span class="layer-name">${e.name}</span>
2009
+ <span class="layer-type">${e.type === "LineString" ? "Line" : e.type}</span>
2010
+ <small style="color:var(--sl-color-neutral-400);font-size:.7rem">${this.features.filter((t) => t.layerId === e.id).length}</small>
2011
+ ${this.drawLayers.length > 1 ? c`
2012
+ <sl-tooltip content="Stop editing">
2013
+ <sl-icon-button name="x" class="remove-layer-btn"
2014
+ @click=${(t) => {
2015
+ t.stopPropagation(), this.removeFromEditing(e);
2016
+ }}>
2017
+ </sl-icon-button>
2018
+ </sl-tooltip>
2019
+ ` : ""}
2020
+ </div>
2021
+ `)}
2022
+ </div>
2023
+ ` : ""}
2024
+
2025
+ ${e && t ? c`
2026
+ <div class="section-label" style="margin-top:.5rem">Selected: ${t.name}</div>
2027
+ ${t.properties.map((n) => c`
2028
+ <div class="prop-row">
2029
+ <span class="prop-label">${n.name}</span>
2030
+ ${n.name === "id" || [
2031
+ "longitude",
2032
+ "latitude",
2033
+ "area",
2034
+ "perimeter",
2035
+ "length",
2036
+ "create-time",
2037
+ "update-time"
2038
+ ].includes(n.type) ? c`<span class="prop-value" style="color:var(--sl-color-neutral-400);font-style:italic;padding:0 0.3rem">${["create-time", "update-time"].includes(n.type) ? e.properties[n.name] ? new Date(e.properties[n.name]).toLocaleString() : "—" : e.properties[n.name] ?? "—"}</span>` : n.type === "imageURL" ? c`<div class="prop-url-wrap">
2039
+ <sl-input size="small"
2040
+ .value=${String(e.properties[n.name] ?? "")}
2041
+ placeholder="image URL"
2042
+ @sl-change=${(r) => {
2043
+ e.properties[n.name] = r.target.value, t && this.computeSpecialProperties(e, t), this.features = [...this.features], this.refreshDrawLayerSource(e.layerId);
2044
+ }}></sl-input>
2045
+ ${e.properties[n.name] ? c`<img class="prop-img" src=${String(e.properties[n.name])} @error=${(e) => {
2046
+ let t = e.target, n = document.createElement("span");
2047
+ n.className = "prop-img-error", n.textContent = "⚠ invalid image", t.replaceWith(n);
2048
+ }}>` : ""}
2049
+ </div>` : n.type === "linkURL" ? c`<div class="prop-url-wrap">
2050
+ <sl-input size="small"
2051
+ .value=${String(e.properties[n.name] ?? "")}
2052
+ placeholder="link URL"
2053
+ @sl-change=${(r) => {
2054
+ e.properties[n.name] = r.target.value, t && this.computeSpecialProperties(e, t), this.features = [...this.features], this.refreshDrawLayerSource(e.layerId);
2055
+ }}></sl-input>
2056
+ ${e.properties[n.name] ? c`<a class="prop-link" href=${String(e.properties[n.name])} target="_blank" rel="noopener noreferrer">${e.properties[n.name]}</a>` : ""}
2057
+ </div>` : c`<sl-input size="small" class="prop-value"
2058
+ .value=${String(e.properties[n.name] ?? "")}
2059
+ @sl-change=${(r) => {
2060
+ e.properties[n.name] = r.target.value, t && this.computeSpecialProperties(e, t), this.features = [...this.features], this.refreshDrawLayerSource(e.layerId);
2061
+ }}></sl-input>`}
2062
+ </div>
2063
+ `)}
2064
+ ` : ""}
2065
+ </div>
2066
+
2067
+ <webmapx-draw-layer-dialog
2068
+ @webmapx-draw-layer-confirm=${this.handleLayerConfirm}
2069
+ @webmapx-draw-layer-cancel=${this.handleLayerCancel}>
2070
+ </webmapx-draw-layer-dialog>
2071
+
2072
+ <sl-dialog id="export-dialog" label="Export GeoJSON">
2073
+ <div class="prop-row">
2074
+ <span class="prop-label">Filename</span>
2075
+ <sl-input class="prop-value" size="small"
2076
+ .value=${this.exportFilename}
2077
+ @sl-input=${(e) => {
2078
+ this.exportFilename = e.target.value;
2079
+ }}>
2080
+ <span slot="suffix">${this.exportMode === "separate" ? ".zip" : ".geojson"}</span>
2081
+ </sl-input>
2082
+ </div>
2083
+ ${this.drawLayers.length > 1 ? c`
2084
+ <div style="margin-top:.6rem">
2085
+ <sl-radio-group label="Export as" .value=${this.exportMode}
2086
+ @sl-change=${(e) => {
2087
+ this.exportMode = e.target.value;
2088
+ }}>
2089
+ <sl-radio value="combined">Single GeoJSON file (all layers combined)</sl-radio>
2090
+ <sl-radio value="separate">Separate files per layer (ZIP archive)</sl-radio>
2091
+ </sl-radio-group>
2092
+ </div>
2093
+ ` : ""}
2094
+ <sl-button slot="footer" variant="primary" autofocus @click=${() => this.doExport()}>Download</sl-button>
2095
+ <sl-button slot="footer" variant="default" @click=${() => this.exportDialog.hide()}>Cancel</sl-button>
2096
+ </sl-dialog>
2097
+ `;
2098
+ }
2099
+ };
2100
+ e([f()], W.prototype, "mode", void 0), e([f()], W.prototype, "drawLayers", void 0), e([f()], W.prototype, "features", void 0), e([f()], W.prototype, "selectedFeatureId", void 0), e([f()], W.prototype, "helpText", void 0), e([f()], W.prototype, "pendingMode", void 0), e([f()], W.prototype, "uiVersion", void 0), e([f()], W.prototype, "isTouchDevice", void 0), e([f()], W.prototype, "snapEnabled", void 0), e([f()], W.prototype, "altActive", void 0), e([f()], W.prototype, "editState", void 0), e([d("webmapx-draw-layer-dialog")], W.prototype, "layerDialog", void 0), e([d("#export-dialog")], W.prototype, "exportDialog", void 0), e([f()], W.prototype, "exportFilename", void 0), e([f()], W.prototype, "exportMode", void 0), W = e([l("webmapx-draw-tool")], W);
2101
+ function G(e) {
2102
+ return e === "draw-point" ? "Point" : e === "draw-line" ? "LineString" : e === "draw-polygon" ? "Polygon" : null;
2103
+ }
2104
+ function K(e) {
2105
+ return e === "Point" || e === "MultiPoint" || e === "LineString" || e === "MultiLineString" || e === "Polygon" || e === "MultiPolygon";
2106
+ }
2107
+ function q(e) {
2108
+ return e === "Point" || e === "MultiPoint";
2109
+ }
2110
+ function J(e) {
2111
+ return e === "LineString" || e === "MultiLineString";
2112
+ }
2113
+ function Y(e) {
2114
+ return e === "Polygon" || e === "MultiPolygon";
2115
+ }
2116
+ //#endregion
2117
+ export { W as WebmapxDrawTool };