@edugis-org/webmapx 0.1.9 → 0.1.11

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 (45) hide show
  1. package/dist-lib/alert-C_G5X6J8.js +311 -0
  2. package/dist-lib/button-BlZd3WPH.js +744 -0
  3. package/dist-lib/checkbox-DB4oPyyw.js +287 -0
  4. package/dist-lib/chunk.36O46B5H-km1dakSW.js +77 -0
  5. package/dist-lib/chunk.3RPBFEDE-Cxq6VVR6.js +138 -0
  6. package/dist-lib/chunk.3Y6SB6QS-DUJ8Ickw.js +18 -0
  7. package/dist-lib/chunk.5JY5FUCG-CYkzbQXS.js +1071 -0
  8. package/dist-lib/chunk.6CTB5ZDJ-DjZrBd6Y.js +99 -0
  9. package/dist-lib/chunk.AJ3ENQ5C-DXVGX5Dn.js +177 -0
  10. package/dist-lib/chunk.LD4M4QGE-CiCfhE8r.js +8 -0
  11. package/dist-lib/chunk.NYIIDP5N-B4UCI-rQ.js +34 -0
  12. package/dist-lib/chunk.RWUUFNUL-DFztA4uV.js +43 -0
  13. package/dist-lib/chunk.SI4ACBFK-CLb9VfMG.js +61 -0
  14. package/dist-lib/chunk.YHLNUJ7P-ByKy0MKm.js +251 -0
  15. package/dist-lib/{decorate-DnZTfaod.js → decorate-CKr8yG1g.js} +1 -1
  16. package/dist-lib/divider-BOFbR_Ny.js +41 -0
  17. package/dist-lib/icon-CBNpO1lq.js +9 -0
  18. package/dist-lib/icon-button-B5rXu3Gg.js +410 -0
  19. package/dist-lib/input-B212BH6L.js +575 -0
  20. package/dist-lib/option-BQN9Zz_8.js +1108 -0
  21. package/dist-lib/spinner-DdcBaa3z.js +6 -0
  22. package/dist-lib/{toast-BsoXOdeA.js → toast-Bq2PmEpm.js} +1 -1
  23. package/dist-lib/tooltip-CBGOiZwJ.js +197 -0
  24. package/dist-lib/{webmapx-3d-tool-CLppA7mK.js → webmapx-3d-tool-CKoLGMpk.js} +2 -2
  25. package/dist-lib/{webmapx-base-tool-DfRa7TlD.js → webmapx-base-tool-Bc9Wz0yA.js} +1 -1
  26. package/dist-lib/{webmapx-config-edit-tool-BztWspia.js → webmapx-config-edit-tool-7265_DNz.js} +7 -7
  27. package/dist-lib/{webmapx-coordinates-tool-DnTSkrRG.js → webmapx-coordinates-tool-C964bdEG.js} +3 -3
  28. package/dist-lib/{webmapx-core-bundle-CH-5vYXU.js → webmapx-core-bundle-Q6mWOib1.js} +1579 -671
  29. package/dist-lib/webmapx-draw-tool-6QRr5Gip.js +4315 -0
  30. package/dist-lib/{webmapx-geolocation-tool-DIMh2kIr.js → webmapx-geolocation-tool-qxN5aaVB.js} +6 -7
  31. package/dist-lib/{webmapx-import-layer-tool-DMiC1TpU.js → webmapx-import-layer-tool-tJ374qHR.js} +2 -2
  32. package/dist-lib/{webmapx-info-tool-D-XbAU9J.js → webmapx-info-tool-C3txgv3v.js} +4 -4
  33. package/dist-lib/{webmapx-language-osmvector-BAw9TR-M.js → webmapx-language-osmvector-jQzTOPU1.js} +3 -4
  34. package/dist-lib/{webmapx-measure-tool-CXlg11s8.js → webmapx-measure-tool-Cxxrzgdf.js} +4 -4
  35. package/dist-lib/{webmapx-modal-tool-Cs7LRrgW.js → webmapx-modal-tool-DQVHNXqX.js} +2 -2
  36. package/dist-lib/{webmapx-plugin-tool-qe2yTrWB.js → webmapx-plugin-tool-BNZ61x6s.js} +1 -1
  37. package/dist-lib/{webmapx-print-tool-DrK9sLC7.js → webmapx-print-tool-BDOTbS6u.js} +7 -8
  38. package/dist-lib/{webmapx-search-tool-H7NisgWH.js → webmapx-search-tool-CHlsE2Wx.js} +2 -2
  39. package/dist-lib/webmapx-settings-B0AT9Vjn.js +482 -0
  40. package/dist-lib/{webmapx-truearea-tool-CSU9mE1D.js → webmapx-truearea-tool-dxSgcbOj.js} +2 -2
  41. package/dist-lib/{webmapx-view-mode-tool-D1QyQfq8.js → webmapx-view-mode-tool-DVSMakdh.js} +2 -2
  42. package/dist-lib/webmapx.js +312 -311
  43. package/package.json +1 -1
  44. package/dist-lib/webmapx-draw-tool-DxQgUF1Q.js +0 -2117
  45. package/dist-lib/webmapx-settings-EHSm-AGU.js +0 -167
@@ -1,2117 +0,0 @@
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 };