@edugis-org/webmapx 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +149 -0
  2. package/dist-lib/WMTS-DCN4zX0-.js +1169 -0
  3. package/dist-lib/alert-GeHlqlN8.js +310 -0
  4. package/dist-lib/assets/epsg-lookup.worker-J7TVnHDq.js +1 -0
  5. package/dist-lib/assets/shapefile.worker-kQfZj6G2.js +4 -0
  6. package/dist-lib/attribution-format-B4f05_u0.js +62 -0
  7. package/dist-lib/button-DFdGkRPQ.js +741 -0
  8. package/dist-lib/cesium-adapter-BHXNytkU.js +1488 -0
  9. package/dist-lib/checkbox-QoR4S8tV.js +284 -0
  10. package/dist-lib/chunk-HEgqtunE.js +20 -0
  11. package/dist-lib/chunk.36O46B5H-B6ZL7Sm1.js +77 -0
  12. package/dist-lib/chunk.3RPBFEDE-BFO1fHVm.js +138 -0
  13. package/dist-lib/chunk.5JY5FUCG-DTXsslmx.js +1090 -0
  14. package/dist-lib/chunk.6CTB5ZDJ-DjZrBd6Y.js +99 -0
  15. package/dist-lib/chunk.AJ3ENQ5C-Ci7Gm2b6.js +175 -0
  16. package/dist-lib/chunk.LD4M4QGE-CiCfhE8r.js +8 -0
  17. package/dist-lib/chunk.NYIIDP5N-BikXIStD.js +99 -0
  18. package/dist-lib/chunk.RWUUFNUL-DFztA4uV.js +43 -0
  19. package/dist-lib/chunk.SI4ACBFK-CLb9VfMG.js +61 -0
  20. package/dist-lib/chunk.YHLNUJ7P-D-kanrCf.js +503 -0
  21. package/dist-lib/core-CMAlSR68.js +22 -0
  22. package/dist-lib/decorate-CWgUV1hU.js +1139 -0
  23. package/dist-lib/decorators-B35AgiCU.js +351 -0
  24. package/dist-lib/deferred-query-service-BJDcngw6.js +386 -0
  25. package/dist-lib/dist-Dm6b7XCs.js +12318 -0
  26. package/dist-lib/dist-Ha9LQCut.js +244 -0
  27. package/dist-lib/dist-c1PlDAd1.js +2359 -0
  28. package/dist-lib/dist-kKlmcBXq.js +50 -0
  29. package/dist-lib/divider-CPm675yY.js +41 -0
  30. package/dist-lib/dropped-config-C_GyVa8o.js +17 -0
  31. package/dist-lib/dropped-layer-builder-DAaYgUPk.js +396 -0
  32. package/dist-lib/epsg-definitions-BZLZWa8Q.js +668 -0
  33. package/dist-lib/esm-CTuscnN5.js +46 -0
  34. package/dist-lib/file-sniff-Dhxj3KTF.js +251 -0
  35. package/dist-lib/geo-calculations-DbFJAUoI.js +30 -0
  36. package/dist-lib/icon-CEOgWlro.js +9 -0
  37. package/dist-lib/icon-button-Da_nBTy3.js +408 -0
  38. package/dist-lib/input-CeGntPlT.js +590 -0
  39. package/dist-lib/layer-discovery-afWzu5hY.js +2825 -0
  40. package/dist-lib/leaflet-adapter-D9djjrKv.js +1227 -0
  41. package/dist-lib/lib-CdHVicAE.js +4074 -0
  42. package/dist-lib/map-layer-registry-2cmkiRDK.js +62 -0
  43. package/dist-lib/maplibre-adapter-TFc3e0G9.js +1190 -0
  44. package/dist-lib/maplibre-expression-evaluator-DCWUcpwf.js +7233 -0
  45. package/dist-lib/marker-utils-DztWXeop.js +12 -0
  46. package/dist-lib/ol-tilegrid-9VtyxaLG.js +64 -0
  47. package/dist-lib/openlayers-adapter-DVW1KCRv.js +13307 -0
  48. package/dist-lib/option-CBxl1mZP.js +1106 -0
  49. package/dist-lib/papaparse.min-B7v3c0D7.js +501 -0
  50. package/dist-lib/rbush-C8k41T4z.js +254 -0
  51. package/dist-lib/shapefile-SawVY6xg.js +207 -0
  52. package/dist-lib/spinner-DysxdNG9.js +6 -0
  53. package/dist-lib/src-CL94RDe3.js +111 -0
  54. package/dist-lib/throttle-BeneRNYK.js +16 -0
  55. package/dist-lib/toast-Cm28o9U6.js +15 -0
  56. package/dist-lib/togeojson.es-DAgiTBvg.js +579 -0
  57. package/dist-lib/tooltip-Cucn1SiD.js +197 -0
  58. package/dist-lib/webmapx-3d-tool-D4CTD2gB.js +176 -0
  59. package/dist-lib/webmapx-base-tool-Dm9NAWLD.js +75 -0
  60. package/dist-lib/webmapx-config-edit-tool-DCTyxqTk.js +389 -0
  61. package/dist-lib/webmapx-coordinates-tool-jeWohup9.js +648 -0
  62. package/dist-lib/webmapx-core-bundle-BDImi1RE.js +8203 -0
  63. package/dist-lib/webmapx-draw-tool-DooAV8cF.js +4336 -0
  64. package/dist-lib/webmapx-geolocation-tool-Rw3-Iad1.js +788 -0
  65. package/dist-lib/webmapx-import-layer-tool-DRYviHd5.js +250 -0
  66. package/dist-lib/webmapx-info-tool-BJA157cy.js +412 -0
  67. package/dist-lib/webmapx-language-osmvector-M5y_lwOg.js +489 -0
  68. package/dist-lib/webmapx-measure-tool-BXhMJFC6.js +590 -0
  69. package/dist-lib/webmapx-modal-tool-eF6Naluv.js +84 -0
  70. package/dist-lib/webmapx-plugin-tool-D2Hghf9n.js +45 -0
  71. package/dist-lib/webmapx-print-tool-ob1bOsR5.js +348 -0
  72. package/dist-lib/webmapx-search-tool-Cv8BrYvY.js +437 -0
  73. package/dist-lib/webmapx-settings-DDEJ8aoV.js +479 -0
  74. package/dist-lib/webmapx-truearea-tool-CMB4Orm-.js +615 -0
  75. package/dist-lib/webmapx-view-mode-tool-CUpLNjOj.js +106 -0
  76. package/dist-lib/webmapx.css +2 -0
  77. package/dist-lib/webmapx.js +1321 -0
  78. package/dist-lib/wms-feature-info-C0RVMEQC.js +145 -0
  79. package/dist-lib/wms-url-builder-DIJLQ1v2.js +54 -0
  80. package/dist-lib/zip.js-DVhmtjxZ.js +3615 -0
  81. package/package.json +107 -0
  82. package/public/data/country-epsg-codes.json +297 -0
  83. package/public/data/world-countries-simplified.topojson +1 -0
  84. package/src/locales/en/core.json +20 -0
@@ -0,0 +1,250 @@
1
+ import { r as e, t } from "./decorate-CWgUV1hU.js";
2
+ import { t as n } from "./webmapx-base-tool-Dm9NAWLD.js";
3
+ import { i as r, o as i } from "./decorators-B35AgiCU.js";
4
+ import { discoverLayers as a } from "./layer-discovery-afWzu5hY.js";
5
+ import { css as o, html as s } from "lit";
6
+ //#region src/components/webmapx-import-layer-tool.ts
7
+ var c = class extends n {
8
+ constructor(...e) {
9
+ super(...e), this.active = !1, this.mapElement = null, this.url = "", this.discovering = !1, this.results = [], this.selected = /* @__PURE__ */ new Set(), this.error = null, this.filterText = "", this.catalog = [], this.fileDropActive = !1, this.fileImporting = !1, this.discoverySeq = 0;
10
+ }
11
+ static {
12
+ this.styles = o`
13
+ :host { display: block; width: 100%; pointer-events: auto; }
14
+ :host([hidden]) { display: none !important; }
15
+ .container { width: 100%; color: var(--color-text-primary); box-sizing: border-box; padding: var(--webmapx-tool-padding, 0); }
16
+ .section-title { font-size: 0.75rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-text-secondary); margin: 10px 0 6px; }
17
+ .section-title:first-child { margin-top: 0; }
18
+ .urlbox { display:flex; gap:6px; align-items:center; }
19
+ input[type="text"] { flex:1; padding:6px; min-width:0; }
20
+ .error { color: var(--sl-color-danger-600, #c0392b); font-size: 12px; margin-top: 6px; }
21
+ .filter { width:100%; box-sizing:border-box; padding:6px; margin-top:8px; }
22
+ .results { margin-top:8px; max-height:50%; overflow:auto; }
23
+ .results ul { list-style: none; margin: 0; padding: 0; }
24
+ .result-item { padding:6px; border-bottom:1px solid rgba(0,0,0,0.05); display:flex; align-items:center; gap:8px; }
25
+ .meta { font-size: 11px; color: var(--color-text-secondary); }
26
+ .actions { margin: 8px 0; display:flex; justify-content:flex-end; gap:6px; }
27
+ .file-row { display: flex; gap: 6px; align-items: center; }
28
+ .drop-zone {
29
+ border: 2px dashed var(--sl-color-neutral-400);
30
+ border-radius: var(--sl-border-radius-medium);
31
+ padding: 16px 8px;
32
+ text-align: center;
33
+ font-size: 0.8rem;
34
+ color: var(--color-text-secondary);
35
+ cursor: pointer;
36
+ transition: border-color 0.15s, background 0.15s;
37
+ margin-top: 6px;
38
+ }
39
+ .drop-zone:hover, .drop-zone.active {
40
+ border-color: var(--sl-color-primary-500);
41
+ background: var(--sl-color-primary-50);
42
+ color: var(--sl-color-primary-700);
43
+ }
44
+ input[type="file"] { display: none; }
45
+ `;
46
+ }
47
+ onMapAttached(t) {
48
+ super.onMapAttached(t), this.adapter = t, this.mapElement = e(this);
49
+ }
50
+ onMapDetached() {
51
+ this.adapter = null, this.mapElement = null, super.onMapDetached();
52
+ }
53
+ onConfigReady(e) {}
54
+ onStateChanged(e) {}
55
+ activate() {
56
+ this.active = !0, this.hidden = !1, setTimeout(() => {
57
+ (this.renderRoot?.querySelector("input"))?.focus();
58
+ }, 0);
59
+ }
60
+ deactivate() {
61
+ this.active = !1, this.hidden = !0;
62
+ }
63
+ async discoverUrl(e) {
64
+ let t = ++this.discoverySeq;
65
+ this.discovering = !0, this.error = null, this.results = [], this.catalog = [], this.selected = /* @__PURE__ */ new Set(), this.filterText = "";
66
+ try {
67
+ let n = await a(e);
68
+ if (t !== this.discoverySeq) return;
69
+ this.results = n.layers, this.catalog = n.catalog ?? [], this.selected = /* @__PURE__ */ new Set(), n.layers.length === 0 && this.catalog.length === 0 && (this.error = "No services discovered at this URL.");
70
+ } catch (e) {
71
+ if (t !== this.discoverySeq) return;
72
+ console.error("layer discovery failed", e), this.error = e instanceof Error ? e.message : "Discovery failed";
73
+ } finally {
74
+ t === this.discoverySeq && (this.discovering = !1);
75
+ }
76
+ }
77
+ handleDiscover() {
78
+ let e = (this.renderRoot?.querySelector("input")?.value ?? this.url).trim();
79
+ this.url = e, e && this.discoverUrl(e);
80
+ }
81
+ openCatalogEntry(e) {
82
+ this.discoverUrl(e.url);
83
+ }
84
+ get filteredResults() {
85
+ let e = this.filterText.trim().toLowerCase();
86
+ return e ? this.results.filter((t) => {
87
+ let n = t.source;
88
+ return [
89
+ t.title,
90
+ t.abstract,
91
+ t.layer.id,
92
+ n.data,
93
+ n.tiles,
94
+ n.url
95
+ ].flat().some((t) => typeof t == "string" && t.toLowerCase().includes(e));
96
+ }) : this.results;
97
+ }
98
+ toggleSelected(e, t) {
99
+ let n = t, r = typeof n?.detail?.checked == "boolean" ? n.detail.checked : !!t.target?.checked, i = new Set(this.selected);
100
+ r ? i.add(e) : i.delete(e), this.selected = i;
101
+ }
102
+ handleAdd() {
103
+ if (!this.adapter || !this.mapElement || this.selected.size === 0) return;
104
+ let e = Array.from(this.selected);
105
+ for (let { source: t, layer: n } of e) try {
106
+ this.mapElement.addLayerRequest({
107
+ ...n,
108
+ sources: { [t.id]: t }
109
+ });
110
+ } catch (e) {
111
+ console.error("Failed to add discovered layer", n.id, e);
112
+ }
113
+ this.dispatchEvent(new CustomEvent("webmapx-layers-found", {
114
+ detail: {
115
+ url: this.url.trim(),
116
+ layers: e
117
+ },
118
+ bubbles: !0,
119
+ composed: !0
120
+ })), this.results = [], this.selected = /* @__PURE__ */ new Set();
121
+ }
122
+ async handleFiles(e) {
123
+ if (!(!this.mapElement || e.length === 0)) {
124
+ this.fileImporting = !0;
125
+ try {
126
+ await this.mapElement.addFilesAsLayers(e);
127
+ } finally {
128
+ this.fileImporting = !1;
129
+ }
130
+ }
131
+ }
132
+ handleFileInput(e) {
133
+ let t = e.target, n = Array.from(t.files ?? []);
134
+ t.value = "", this.handleFiles(n);
135
+ }
136
+ handleDropZoneDrop(e) {
137
+ e.preventDefault(), e.stopPropagation(), this.fileDropActive = !1;
138
+ let t = Array.from(e.dataTransfer?.files ?? []);
139
+ this.handleFiles(t);
140
+ }
141
+ handleDropZoneDragOver(e) {
142
+ e.dataTransfer?.types.includes("Files") && (e.preventDefault(), e.stopPropagation(), e.dataTransfer.dropEffect = "copy", this.fileDropActive = !0);
143
+ }
144
+ render() {
145
+ return s`
146
+ <div class="container tool-content">
147
+ <div class="section-title">From URL</div>
148
+ <div class="urlbox">
149
+ <input
150
+ type="text"
151
+ placeholder="Paste a service or tile URL"
152
+ aria-label="Service or tile URL"
153
+ autocomplete="off"
154
+ autocorrect="off"
155
+ autocapitalize="off"
156
+ spellcheck="false"
157
+ data-lpignore="true"
158
+ data-1p-ignore
159
+ .value="${this.url}"
160
+ @input="${(e) => {
161
+ this.url = e.target.value;
162
+ }}"
163
+ @keyup="${(e) => {
164
+ e.key === "Enter" && this.handleDiscover();
165
+ }}"
166
+ />
167
+ <sl-button size="small" ?loading=${this.discovering} ?disabled=${this.discovering} @click="${() => this.handleDiscover()}">Discover</sl-button>
168
+ </div>
169
+
170
+ ${this.error ? s`<div class="error">${this.error}</div>` : ""}
171
+
172
+ ${this.catalog.length === 0 ? "" : s`
173
+ <div class="results">
174
+ <ul>
175
+ ${this.catalog.map((e) => s`
176
+ <li class="result-item" @click="${() => this.openCatalogEntry(e)}" style="cursor:pointer;">
177
+ <div style="flex:1; min-width:0;">
178
+ <div style="white-space:normal; word-break:break-word;" title="${e.name}">${e.kind === "folder" ? "📁" : "🗺"} ${e.name}</div>
179
+ ${e.type ? s`<div class="meta">${e.type}</div>` : ""}
180
+ </div>
181
+ </li>
182
+ `)}
183
+ </ul>
184
+ </div>
185
+ `}
186
+
187
+ ${this.results.length === 0 ? "" : s`
188
+ <input
189
+ type="text"
190
+ class="filter"
191
+ placeholder="Filter layers..."
192
+ aria-label="Filter layers"
193
+ .value="${this.filterText}"
194
+ @input="${(e) => {
195
+ this.filterText = e.target.value;
196
+ }}"
197
+ />
198
+ <div class="actions">
199
+ <sl-button size="small" variant="primary" ?disabled=${this.selected.size === 0} @click="${() => this.handleAdd()}">
200
+ Add selected
201
+ </sl-button>
202
+ </div>
203
+ <div class="results">
204
+ <ul>
205
+ ${this.filteredResults.map((e) => s`
206
+ <li class="result-item">
207
+ <sl-checkbox
208
+ .checked=${this.selected.has(e)}
209
+ @sl-change=${(t) => this.toggleSelected(e, t)}
210
+ ></sl-checkbox>
211
+ <div style="flex:1; min-width:0;">
212
+ <div style="white-space:normal; word-break:break-word;" title="${e.title}">${e.title}</div>
213
+ <div class="meta">${e.serviceType}</div>
214
+ ${e.abstract ? s`<div class="meta">${e.abstract}</div>` : ""}
215
+ </div>
216
+ </li>
217
+ `)}
218
+ </ul>
219
+ </div>
220
+ `}
221
+
222
+ <div class="section-title">From file</div>
223
+ <div class="file-row">
224
+ <sl-button size="small" ?loading=${this.fileImporting} ?disabled=${this.fileImporting}
225
+ @click=${() => (this.renderRoot?.querySelector("input[type=\"file\"]"))?.click()}>
226
+ <sl-icon slot="prefix" name="folder2-open"></sl-icon>
227
+ Open file…
228
+ </sl-button>
229
+ </div>
230
+ <input type="file" multiple accept=".geojson,.json,.zip,.topojson,.gpx,.kml,.kmz,.csv"
231
+ @change=${this.handleFileInput} />
232
+ <div
233
+ class="drop-zone ${this.fileDropActive ? "active" : ""}"
234
+ @dragover=${this.handleDropZoneDragOver}
235
+ @dragleave=${() => {
236
+ this.fileDropActive = !1;
237
+ }}
238
+ @drop=${this.handleDropZoneDrop}
239
+ @click=${() => (this.renderRoot?.querySelector("input[type=\"file\"]"))?.click()}
240
+ >
241
+ <sl-icon name="file-earmark-arrow-up"></sl-icon>
242
+ Drop files here or click to browse
243
+ </div>
244
+ </div>
245
+ `;
246
+ }
247
+ };
248
+ t([r()], c.prototype, "url", void 0), t([r()], c.prototype, "discovering", void 0), t([r()], c.prototype, "results", void 0), t([r()], c.prototype, "selected", void 0), t([r()], c.prototype, "error", void 0), t([r()], c.prototype, "filterText", void 0), t([r()], c.prototype, "catalog", void 0), t([r()], c.prototype, "fileDropActive", void 0), t([r()], c.prototype, "fileImporting", void 0), c = t([i("webmapx-import-layer-tool")], c);
249
+ //#endregion
250
+ export { c as WebmapxImportLayerTool };
@@ -0,0 +1,412 @@
1
+ import { t as e } from "./decorate-CWgUV1hU.js";
2
+ import { t } from "./webmapx-base-tool-Dm9NAWLD.js";
3
+ import { i as n, o as r } from "./decorators-B35AgiCU.js";
4
+ import "./icon-CEOgWlro.js";
5
+ import "./spinner-DysxdNG9.js";
6
+ import { t as i } from "./throttle-BeneRNYK.js";
7
+ import { t as a } from "./wms-feature-info-C0RVMEQC.js";
8
+ import { css as o, html as s, nothing as c } from "lit";
9
+ //#region src/components/webmapx-info-tool.ts
10
+ var l = "webmapx-info-pin", u = 8, d = 120, f = 4, p = 6, m = class extends t {
11
+ constructor(...e) {
12
+ super(...e), this.active = !1, this.features = [], this.loading = !1, this.mode = "hover", this.pinnedLocation = null, this.pinnedPixel = null, this.pinMarkerAdded = !1, this.unsubClick = null, this.unsubPointerMove = null, this.throttledHoverQuery = i(async (e, t) => {
13
+ if (this.mode !== "hover" || !this.active || !this.adapter) return;
14
+ let n;
15
+ try {
16
+ n = await this.adapter.queryService.queryFeatures({
17
+ pixel: e,
18
+ lngLat: t
19
+ }, {
20
+ tolerancePx: f,
21
+ includeWMS: !1
22
+ });
23
+ } catch (e) {
24
+ console.warn("[info-tool] queryFeatures failed", e), n = [];
25
+ }
26
+ this.mode === "hover" && (this.features = n);
27
+ }, d);
28
+ }
29
+ static {
30
+ this.styles = o`
31
+ :host {
32
+ display: block;
33
+ pointer-events: auto;
34
+ }
35
+
36
+ .info-container {
37
+ display: flex;
38
+ flex-direction: column;
39
+ gap: 0.5rem;
40
+ padding: var(--webmapx-tool-padding, 0);
41
+ font-size: var(--font-size-small, 0.875rem);
42
+ min-width: 200px;
43
+ }
44
+
45
+ .mode-badge {
46
+ display: inline-flex;
47
+ align-items: center;
48
+ gap: 0.25rem;
49
+ font-size: 0.7rem;
50
+ font-weight: 600;
51
+ letter-spacing: 0.05em;
52
+ text-transform: uppercase;
53
+ color: var(--color-text-secondary, #666);
54
+ }
55
+
56
+ .mode-badge.pinned {
57
+ color: var(--color-primary, #0f62fe);
58
+ }
59
+
60
+ .instructions {
61
+ color: var(--color-text-secondary, #666);
62
+ font-size: 0.75rem;
63
+ font-style: italic;
64
+ margin: 0;
65
+ }
66
+
67
+ .layer-group {
68
+ border: 1px solid var(--color-border-light, #eee);
69
+ border-radius: 4px;
70
+ overflow: hidden;
71
+ }
72
+ .feature-limit-notice {
73
+ padding: 0.2rem 0.5rem;
74
+ font-size: 0.7rem;
75
+ color: var(--color-text-secondary, #888);
76
+ font-style: italic;
77
+ border-top: 1px solid var(--color-border-light, #eee);
78
+ }
79
+
80
+ .layer-title {
81
+ background: var(--color-surface-alt, #f5f5f5);
82
+ padding: 0.25rem 0.5rem;
83
+ font-weight: 600;
84
+ font-size: 0.75rem;
85
+ color: var(--color-text-secondary, #555);
86
+ border-bottom: 1px solid var(--color-border-light, #eee);
87
+ }
88
+
89
+ .props-list {
90
+ display: grid;
91
+ grid-template-columns: minmax(0, 0.8fr) minmax(0, 1fr);
92
+ font-size: 0.8rem;
93
+ }
94
+
95
+ .props-row {
96
+ display: contents;
97
+ }
98
+
99
+ .props-row:nth-child(even) > .props-key,
100
+ .props-row:nth-child(even) > .props-val {
101
+ background: var(--color-surface-alt, #fafafa);
102
+ }
103
+
104
+ .props-key {
105
+ min-width: 0;
106
+ padding: 0.2rem 0.5rem;
107
+ font-weight: 500;
108
+ color: var(--color-text-secondary, #555);
109
+ overflow-wrap: break-word;
110
+ border-bottom: 1px solid var(--color-border-light, #f0f0f0);
111
+ }
112
+
113
+ .props-val {
114
+ min-width: 0;
115
+ overflow-wrap: break-word;
116
+ padding: 0.2rem 0.5rem;
117
+ border-bottom: 1px solid var(--color-border-light, #f0f0f0);
118
+ }
119
+
120
+ .img-error {
121
+ font-size: 0.75rem;
122
+ color: var(--sl-color-danger-600, #c0392b);
123
+ font-style: italic;
124
+ }
125
+
126
+ .props-val a[href] {
127
+ display: block;
128
+ width: 100%;
129
+ overflow: hidden;
130
+ text-overflow: ellipsis;
131
+ white-space: nowrap;
132
+ }
133
+
134
+ .props-val img {
135
+ max-width: 100%;
136
+ max-height: 120px;
137
+ border-radius: 3px;
138
+ object-fit: cover;
139
+ }
140
+
141
+ .raw-value {
142
+ padding: 0.4rem 0.5rem;
143
+ font-size: 0.75rem;
144
+ white-space: pre-wrap;
145
+ overflow: auto;
146
+ max-height: 200px;
147
+ }
148
+
149
+ .props-table img {
150
+ max-width: 100%;
151
+ max-height: 120px;
152
+ border-radius: 3px;
153
+ object-fit: cover;
154
+ }
155
+
156
+ .sub-layer-title {
157
+ display: flex;
158
+ align-items: center;
159
+ gap: 0.3rem;
160
+ padding: 0.15rem 0.5rem;
161
+ background: var(--color-surface-alt, #f5f5f5);
162
+ border-bottom: 1px solid var(--color-border-light, #eee);
163
+ }
164
+
165
+ .sub-layer-name {
166
+ font-size: 0.75rem;
167
+ font-weight: 500;
168
+ color: var(--color-text-secondary, #555);
169
+ }
170
+
171
+ .sub-layer-type {
172
+ font-size: 0.65rem;
173
+ color: var(--color-text-secondary, #888);
174
+ font-style: italic;
175
+ }
176
+
177
+ .source-badge.composite {
178
+ background: var(--sl-color-violet-100, #ede9fe);
179
+ color: var(--sl-color-violet-700, #6d28d9);
180
+ }
181
+
182
+ .source-badge {
183
+ display: inline-block;
184
+ font-size: 0.65rem;
185
+ padding: 0 0.3rem;
186
+ border-radius: 3px;
187
+ background: var(--color-border-light, #e0e0e0);
188
+ color: var(--color-text-secondary, #555);
189
+ margin-left: 0.25rem;
190
+ vertical-align: middle;
191
+ }
192
+
193
+ .source-badge.wms {
194
+ background: var(--color-primary-light, #d0e4ff);
195
+ color: var(--color-primary, #0043a8);
196
+ }
197
+
198
+ .empty-hint {
199
+ padding: 0.3rem 0.5rem;
200
+ color: var(--color-text-secondary, #999);
201
+ font-size: 0.8rem;
202
+ font-style: italic;
203
+ }
204
+
205
+ sl-spinner {
206
+ font-size: 0.9rem;
207
+ }
208
+ `;
209
+ }
210
+ onMapAttached(e) {
211
+ super.onMapAttached(e), this.unsubClick = e.events.on("click", this.handleClick.bind(this)), this.unsubPointerMove = e.events.on("pointer-move", this.handlePointerMove.bind(this));
212
+ }
213
+ onMapDetached() {
214
+ this.unsubClick?.(), this.unsubPointerMove?.(), this.unsubClick = null, this.unsubPointerMove = null, super.onMapDetached();
215
+ }
216
+ disconnectedCallback() {
217
+ this.removePinMarker(), super.disconnectedCallback();
218
+ }
219
+ activate() {
220
+ this.active = !0, this.style.display = "block", this.onActivate();
221
+ }
222
+ deactivate() {
223
+ this.active = !1, this.style.display = "none", this.onDeactivate();
224
+ }
225
+ onStateChanged(e) {}
226
+ onActivate() {
227
+ this.features = [], this.mode = "hover", this.pinnedLocation = null, this.pinnedPixel = null;
228
+ }
229
+ onDeactivate() {
230
+ this.features = [], this.mode = "hover", this.pinnedLocation = null, this.pinnedPixel = null, this.removePinMarker();
231
+ }
232
+ handlePointerMove(e) {
233
+ !this.active || this.mode === "pinned" || this.throttledHoverQuery(e.pixel, e.coords);
234
+ }
235
+ async handleClick(e) {
236
+ if (!(!this.active || !this.adapter)) {
237
+ if (this.mode === "pinned" && this.pinnedPixel) {
238
+ let t = e.pixel[0] - this.pinnedPixel[0], n = e.pixel[1] - this.pinnedPixel[1];
239
+ if (Math.sqrt(t * t + n * n) <= u) {
240
+ this.unpin();
241
+ return;
242
+ }
243
+ }
244
+ this.mode = "pinned", this.pinnedLocation = e.coords, this.pinnedPixel = e.pixel, this.loading = !0, this.features = [], this.updatePinMarker(e.coords);
245
+ try {
246
+ let t = await this.adapter.queryService.queryFeatures({
247
+ pixel: e.pixel,
248
+ lngLat: e.coords
249
+ }, {
250
+ tolerancePx: p,
251
+ includeWMS: !0
252
+ }), n = await this.queryGFILayers(e.pixel, e.coords);
253
+ this.features = [...t, ...n];
254
+ } finally {
255
+ this.loading = !1;
256
+ }
257
+ }
258
+ }
259
+ async queryGFILayers(e, t) {
260
+ if (!this.adapter) return [];
261
+ let n = this.adapter.store.getState().mapLayers ?? {}, r = 128 * (360 / (256 * 2 ** (this.adapter.getViewportState()?.zoom ?? 0))), i = {
262
+ west: t[0] - r,
263
+ south: t[1] - r,
264
+ east: t[0] + r,
265
+ north: t[1] + r
266
+ }, o = [];
267
+ return await Promise.all(Object.entries(n).map(async ([e, t]) => {
268
+ let n = t, r = typeof n?.getFeatureInfoUrl == "string" ? n.getFeatureInfoUrl : null;
269
+ if (r) try {
270
+ let t = new URL(r), s = (e) => {
271
+ for (let [n, r] of t.searchParams) if (n.toLowerCase() === e) return r;
272
+ return null;
273
+ }, c = await a({
274
+ sourceConfig: {
275
+ id: e,
276
+ type: "raster",
277
+ service: "wms",
278
+ url: r,
279
+ version: s("version") ?? "1.1.1",
280
+ layers: s("layers") ?? s("query_layers") ?? "",
281
+ format: typeof n.getFeatureInfoFormat == "string" ? n.getFeatureInfoFormat : "application/json",
282
+ crs: s("crs") ?? s("srs") ?? "EPSG:3857"
283
+ },
284
+ layerId: e,
285
+ layerTitle: typeof n.label == "string" ? n.label : e,
286
+ bounds: i,
287
+ containerWidth: 256,
288
+ containerHeight: 256,
289
+ pixelX: 128,
290
+ pixelY: 128
291
+ });
292
+ o.push(...c);
293
+ } catch {}
294
+ })), o;
295
+ }
296
+ updatePinMarker(e) {
297
+ this.adapter && (this.pinMarkerAdded ? this.adapter.moveMarker(l, e) : (this.adapter.addMarker(l, e, { color: "#0f62fe" }), this.pinMarkerAdded = !0));
298
+ }
299
+ removePinMarker() {
300
+ this.pinMarkerAdded &&= (this.adapter?.removeMarker(l), !1);
301
+ }
302
+ unpin() {
303
+ this.mode = "hover", this.pinnedLocation = null, this.pinnedPixel = null, this.features = [], this.removePinMarker();
304
+ }
305
+ renderFeatures() {
306
+ if (this.features.length === 0) return c;
307
+ let e = /* @__PURE__ */ new Map();
308
+ for (let t of this.features) {
309
+ let n = e.get(t.layerId);
310
+ n ? n.push(t) : e.set(t.layerId, [t]);
311
+ }
312
+ return s`
313
+ ${[...e.entries()].map(([e, t]) => {
314
+ let n = this.adapter?.store.getState().mapLayers?.[e], r = Array.isArray(n?.sublayers) && n.sublayers.length > 1, i = r ? "composite" : t[0].source, a = typeof n?.featureInfoLimit == "number" ? n.featureInfoLimit : null, o = a !== null && t.length > a, c = o ? t.slice(0, a) : t, l = /* @__PURE__ */ new Map();
315
+ for (let e of c) {
316
+ let t = e.subLayerId ?? "", n = l.get(t);
317
+ n ? n.push(e) : l.set(t, [e]);
318
+ }
319
+ return s`
320
+ <div class="layer-group">
321
+ <div class="layer-title">
322
+ ${t[0].layerTitle ?? e}
323
+ <span class="source-badge ${r ? "composite" : t[0].source}">${i}</span>
324
+ </div>
325
+ ${[...l.entries()].map(([e, t]) => s`
326
+ ${e ? s`<div class="sub-layer-title">
327
+ <span class="sub-layer-name">${e}</span>
328
+ ${t[0].subLayerType ? s`<span class="sub-layer-type">${t[0].subLayerType}</span>` : ""}
329
+ </div>` : ""}
330
+ ${t.map((e) => this.renderPropsTable(e))}
331
+ `)}
332
+ ${o ? s`<div class="feature-limit-notice">Showing ${a} of ${t.length} features</div>` : ""}
333
+ </div>`;
334
+ })}
335
+ `;
336
+ }
337
+ getPropertySchema(e) {
338
+ let t = this.adapter?.store.getState().mapLayers?.[e];
339
+ return Array.isArray(t?.properties) ? t.properties : null;
340
+ }
341
+ getAttributeMeta(e) {
342
+ let t = this.adapter?.store.getState().mapLayers?.[e], n = t?.attributes && typeof t.attributes == "object" ? t.attributes : {};
343
+ return {
344
+ translations: Array.isArray(n.translations) ? n.translations : [],
345
+ allowed: Array.isArray(n.allowedAttributes) ? new Set(n.allowedAttributes) : null,
346
+ denied: Array.isArray(n.deniedAttributes) ? new Set(n.deniedAttributes) : /* @__PURE__ */ new Set()
347
+ };
348
+ }
349
+ renderPropsTable(e) {
350
+ let t = this.getPropertySchema(e.layerId), { translations: n, allowed: r, denied: i } = this.getAttributeMeta(e.layerId), a = e.properties;
351
+ if (a._raw) return s`<div class="raw-value">${a._raw}</div>`;
352
+ let o = [], c = /* @__PURE__ */ new Set();
353
+ for (let e of n) {
354
+ let n = e.name;
355
+ if (i.has(n) || r && !r.has(n) || !(n in a) || a[n] === null || a[n] === void 0) continue;
356
+ c.add(n);
357
+ let s = a[n];
358
+ if (e.multiplier && !isNaN(parseFloat(String(e.multiplier))) && (s = parseFloat(String(s)) * parseFloat(String(e.multiplier))), e.decimals !== void 0 && !isNaN(parseInt(String(e.decimals)))) {
359
+ let t = 10 ** parseInt(String(e.decimals));
360
+ s = Math.round(parseFloat(String(s)) * t) / t;
361
+ }
362
+ if (e.valuemap && Array.isArray(e.valuemap)) {
363
+ let t = e.valuemap.find((e) => e.value === s);
364
+ t && (s = t.label);
365
+ }
366
+ e.date && (s &&= new Date(s).toLocaleString());
367
+ let l = e.unit && !isNaN(Number(s)) ? e.unit : "", u = e.translation || n, d = l ? `${s}${l}` : typeof s == "object" ? JSON.stringify(s) : String(s ?? "");
368
+ o.push(this.renderPropRow(n, u, d, t));
369
+ }
370
+ for (let [e, s] of Object.entries(a)) {
371
+ if (e === "_raw" || c.has(e) || i.has(e) || r && !r.has(e) || s == null || n.length > 0 && !n.find((t) => t.name === e) && r === null) continue;
372
+ let a = typeof s == "object" ? JSON.stringify(s) : String(s ?? "");
373
+ o.push(this.renderPropRow(e, e.replace(/_/g, " "), a, t));
374
+ }
375
+ return o.length === 0 ? s`<div class="empty-hint">No properties</div>` : s`<div class="props-list">${o}</div>`;
376
+ }
377
+ renderPropRow(e, t, n, r) {
378
+ let i = r?.find((t) => t.name === e)?.type ?? "string", a = /^https?:\/\/\S+$/.test(n.trim()), o = /^data:image\//i.test(n), c = i === "string" ? o ? "imageURL" : a ? "linkURL" : "string" : i;
379
+ return s`
380
+ <div class="props-row">
381
+ <span class="props-key" title=${e}>${t}</span>
382
+ <span class="props-val">${(c === "create-time" || c === "update-time") && n ? new Date(Number(n)).toLocaleString() : c === "imageURL" && n ? s`<img src=${n} @error=${(e) => {
383
+ let t = e.target, n = document.createElement("span");
384
+ n.className = "img-error", n.textContent = "⚠ invalid image", t.replaceWith(n);
385
+ }}>` : c === "linkURL" && n ? s`<a href=${n} target="_blank" rel="noopener noreferrer">${n}</a>` : n.split(",").map((e, t) => t === 0 ? s`${e}` : s`,<wbr>${e}`)}</span>
386
+ </div>
387
+ `;
388
+ }
389
+ render() {
390
+ let e = this.mode === "pinned";
391
+ return s`
392
+ <div class="info-container">
393
+ <div class="mode-badge ${e ? "pinned" : ""}">
394
+ <sl-icon name=${e ? "pin-angle-fill" : "cursor"}></sl-icon>
395
+ ${e ? "Pinned" : "Hover"}
396
+ ${this.loading ? s`<sl-spinner></sl-spinner>` : c}
397
+ </div>
398
+
399
+ ${!e && this.features.length === 0 ? s`<p class="instructions">Move cursor over features to inspect. Click to pin and query WMS.</p>` : c}
400
+
401
+ ${e && !this.loading && this.features.length === 0 ? s`<p class="empty-hint">No features at this location.</p>` : c}
402
+
403
+ ${this.renderFeatures()}
404
+
405
+ ${e ? s`<p class="instructions">Click same location to unpin.</p>` : c}
406
+ </div>
407
+ `;
408
+ }
409
+ };
410
+ e([n()], m.prototype, "features", void 0), e([n()], m.prototype, "loading", void 0), e([n()], m.prototype, "mode", void 0), e([n()], m.prototype, "pinnedLocation", void 0), m = e([r("webmapx-info-tool")], m);
411
+ //#endregion
412
+ export { m as WebmapxInfoTool };