maplibre-gl-geoagent 0.2.0 → 0.3.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 (31) hide show
  1. package/dist/{GeoAgentControl-C-3MIQTc.js → GeoAgentControl-DOAYVbPg.js} +5 -3575
  2. package/dist/GeoAgentControl-DOAYVbPg.js.map +1 -0
  3. package/dist/{GeoAgentControl-CKY24iWC.cjs → GeoAgentControl-DzBY1NXW.cjs} +5 -3575
  4. package/dist/GeoAgentControl-DzBY1NXW.cjs.map +1 -0
  5. package/dist/{anthropic-M2EJ1iaX.cjs → anthropic-C31X7JK8.cjs} +2 -2
  6. package/dist/{anthropic-M2EJ1iaX.cjs.map → anthropic-C31X7JK8.cjs.map} +1 -1
  7. package/dist/{anthropic-38gbSx-8.js → anthropic-DhbSHleV.js} +2 -2
  8. package/dist/{anthropic-38gbSx-8.js.map → anthropic-DhbSHleV.js.map} +1 -1
  9. package/dist/{index-BUTngzVS.cjs → index-1fng-HVi.cjs} +2 -2
  10. package/dist/{index-BUTngzVS.cjs.map → index-1fng-HVi.cjs.map} +1 -1
  11. package/dist/{index-DW6rmZdc.js → index-BU4LZEvR.js} +2 -2
  12. package/dist/{index-DW6rmZdc.js.map → index-BU4LZEvR.js.map} +1 -1
  13. package/dist/{index-DI9BALDH.js → index-Bbu2vTKz.js} +2 -2
  14. package/dist/{index-DI9BALDH.js.map → index-Bbu2vTKz.js.map} +1 -1
  15. package/dist/{index-Bc5JtMfe.cjs → index-C0CcPtSz.cjs} +2 -2
  16. package/dist/{index-Bc5JtMfe.cjs.map → index-C0CcPtSz.cjs.map} +1 -1
  17. package/dist/{index-DtsWabeI.js → index-DXB0TYg3.js} +2 -2
  18. package/dist/{index-DtsWabeI.js.map → index-DXB0TYg3.js.map} +1 -1
  19. package/dist/{index-3mfmhCHC.cjs → index-DrL1mot0.cjs} +2 -2
  20. package/dist/{index-3mfmhCHC.cjs.map → index-DrL1mot0.cjs.map} +1 -1
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.mjs +1 -1
  23. package/dist/maplibre-gl-geoagent.css +0 -1195
  24. package/dist/react.cjs +1 -1
  25. package/dist/react.mjs +1 -1
  26. package/dist/types/lib/core/GeoAgentControl.d.ts.map +1 -1
  27. package/dist/types/lib/core/maplibre-tools.d.ts +0 -5
  28. package/dist/types/lib/core/maplibre-tools.d.ts.map +1 -1
  29. package/package.json +2 -2
  30. package/dist/GeoAgentControl-C-3MIQTc.js.map +0 -1
  31. package/dist/GeoAgentControl-CKY24iWC.cjs.map +0 -1
@@ -3518,7 +3518,7 @@ class HttpProtocol extends SerdeContext {
3518
3518
  });
3519
3519
  }
3520
3520
  async loadEventStreamCapability() {
3521
- const { EventStreamSerde } = await import("./index-DI9BALDH.js");
3521
+ const { EventStreamSerde } = await import("./index-Bbu2vTKz.js");
3522
3522
  return new EventStreamSerde({
3523
3523
  marshaller: this.getEventStreamMarshaller(),
3524
3524
  serializer: this.serializer,
@@ -42205,3530 +42205,6 @@ g.walkTokens;
42205
42205
  g.parseInline;
42206
42206
  b.parse;
42207
42207
  x.lex;
42208
- var __defProp2 = Object.defineProperty;
42209
- var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
42210
- var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
42211
- class CustomLayerRegistry {
42212
- constructor() {
42213
- __publicField2(this, "adapters", /* @__PURE__ */ new Map());
42214
- __publicField2(this, "changeListeners", []);
42215
- __publicField2(this, "unsubscribers", /* @__PURE__ */ new Map());
42216
- }
42217
- /**
42218
- * Register a custom layer adapter.
42219
- * @param adapter The adapter to register
42220
- */
42221
- register(adapter) {
42222
- this.adapters.set(adapter.type, adapter);
42223
- if (adapter.onLayerChange) {
42224
- const unsubscribe = adapter.onLayerChange((event, layerId) => {
42225
- this.notifyChange(event, layerId);
42226
- });
42227
- this.unsubscribers.set(adapter.type, unsubscribe);
42228
- }
42229
- }
42230
- /**
42231
- * Unregister an adapter by type.
42232
- * @param type The adapter type to unregister
42233
- */
42234
- unregister(type2) {
42235
- const unsubscribe = this.unsubscribers.get(type2);
42236
- if (unsubscribe) {
42237
- unsubscribe();
42238
- this.unsubscribers.delete(type2);
42239
- }
42240
- this.adapters.delete(type2);
42241
- }
42242
- /**
42243
- * Get all custom layer IDs across all adapters.
42244
- * @returns Array of layer IDs
42245
- */
42246
- getAllLayerIds() {
42247
- const ids = [];
42248
- this.adapters.forEach((adapter) => {
42249
- ids.push(...adapter.getLayerIds());
42250
- });
42251
- return ids;
42252
- }
42253
- /**
42254
- * Check if a layer ID is managed by any adapter.
42255
- * @param layerId The layer ID to check
42256
- * @returns true if the layer is managed by an adapter
42257
- */
42258
- hasLayer(layerId) {
42259
- for (const adapter of this.adapters.values()) {
42260
- if (adapter.getLayerIds().includes(layerId)) {
42261
- return true;
42262
- }
42263
- }
42264
- return false;
42265
- }
42266
- /**
42267
- * Get the adapter responsible for a specific layer.
42268
- * @param layerId The layer ID
42269
- * @returns The adapter or null if not found
42270
- */
42271
- getAdapterForLayer(layerId) {
42272
- for (const adapter of this.adapters.values()) {
42273
- if (adapter.getLayerIds().includes(layerId)) {
42274
- return adapter;
42275
- }
42276
- }
42277
- return null;
42278
- }
42279
- /**
42280
- * Get the state of a custom layer.
42281
- * @param layerId The layer ID
42282
- * @returns The layer state or null if not found
42283
- */
42284
- getLayerState(layerId) {
42285
- const adapter = this.getAdapterForLayer(layerId);
42286
- return adapter ? adapter.getLayerState(layerId) : null;
42287
- }
42288
- /**
42289
- * Set visibility of a custom layer.
42290
- * @param layerId The layer ID
42291
- * @param visible Whether the layer should be visible
42292
- * @returns true if the operation was handled by an adapter
42293
- */
42294
- setVisibility(layerId, visible) {
42295
- const adapter = this.getAdapterForLayer(layerId);
42296
- if (adapter) {
42297
- adapter.setVisibility(layerId, visible);
42298
- return true;
42299
- }
42300
- return false;
42301
- }
42302
- /**
42303
- * Set opacity of a custom layer.
42304
- * @param layerId The layer ID
42305
- * @param opacity The opacity value (0-1)
42306
- * @returns true if the operation was handled by an adapter
42307
- */
42308
- setOpacity(layerId, opacity) {
42309
- const adapter = this.getAdapterForLayer(layerId);
42310
- if (adapter) {
42311
- adapter.setOpacity(layerId, opacity);
42312
- return true;
42313
- }
42314
- return false;
42315
- }
42316
- /**
42317
- * Get the symbol type for a custom layer (for UI display).
42318
- * @param layerId The layer ID
42319
- * @returns The symbol type or null if not available
42320
- */
42321
- getSymbolType(layerId) {
42322
- const adapter = this.getAdapterForLayer(layerId);
42323
- if (adapter && adapter.getSymbolType) {
42324
- return adapter.getSymbolType(layerId);
42325
- }
42326
- return null;
42327
- }
42328
- /**
42329
- * Get the bounds of a custom layer (for zoom-to-layer).
42330
- * @param layerId The layer ID
42331
- * @returns The bounds [west, south, east, north] or null if not available
42332
- */
42333
- getBounds(layerId) {
42334
- const adapter = this.getAdapterForLayer(layerId);
42335
- if (adapter && adapter.getBounds) {
42336
- return adapter.getBounds(layerId);
42337
- }
42338
- return null;
42339
- }
42340
- /**
42341
- * Get native MapLibre layer IDs for a custom layer.
42342
- * @param layerId The custom layer ID
42343
- * @returns Array of native layer IDs, or null if not available
42344
- */
42345
- getNativeLayerIds(layerId) {
42346
- const adapter = this.getAdapterForLayer(layerId);
42347
- if (adapter && adapter.getNativeLayerIds) {
42348
- return adapter.getNativeLayerIds(layerId);
42349
- }
42350
- return null;
42351
- }
42352
- /**
42353
- * Remove a custom layer through its adapter.
42354
- * @param layerId The layer ID to remove
42355
- * @returns true if the operation was handled by an adapter
42356
- */
42357
- removeLayer(layerId) {
42358
- const adapter = this.getAdapterForLayer(layerId);
42359
- if (adapter && adapter.removeLayer) {
42360
- adapter.removeLayer(layerId);
42361
- return true;
42362
- }
42363
- return false;
42364
- }
42365
- /**
42366
- * Subscribe to layer changes across all adapters.
42367
- * @param callback Function called when layers are added or removed
42368
- * @returns Unsubscribe function
42369
- */
42370
- onChange(callback) {
42371
- this.changeListeners.push(callback);
42372
- return () => {
42373
- const idx = this.changeListeners.indexOf(callback);
42374
- if (idx >= 0) {
42375
- this.changeListeners.splice(idx, 1);
42376
- }
42377
- };
42378
- }
42379
- notifyChange(event, layerId) {
42380
- this.changeListeners.forEach((cb) => cb(event, layerId));
42381
- }
42382
- /**
42383
- * Clean up all subscriptions and adapters.
42384
- */
42385
- destroy() {
42386
- this.unsubscribers.forEach((unsub) => unsub());
42387
- this.unsubscribers.clear();
42388
- this.adapters.clear();
42389
- this.changeListeners = [];
42390
- }
42391
- }
42392
- function getOpacityProperty(layerType) {
42393
- switch (layerType) {
42394
- case "fill":
42395
- return "fill-opacity";
42396
- case "line":
42397
- return "line-opacity";
42398
- case "circle":
42399
- return "circle-opacity";
42400
- case "symbol":
42401
- return ["icon-opacity", "text-opacity"];
42402
- case "raster":
42403
- return "raster-opacity";
42404
- case "background":
42405
- return "background-opacity";
42406
- case "heatmap":
42407
- return "heatmap-opacity";
42408
- case "fill-extrusion":
42409
- return "fill-extrusion-opacity";
42410
- case "hillshade":
42411
- return "hillshade-exaggeration";
42412
- case "color-relief":
42413
- return null;
42414
- default:
42415
- return `${layerType}-opacity`;
42416
- }
42417
- }
42418
- function getLayerOpacity(map, layerId, layerType) {
42419
- const opacityProp = getOpacityProperty(layerType);
42420
- if (opacityProp === null) {
42421
- return 1;
42422
- }
42423
- if (Array.isArray(opacityProp)) {
42424
- const opacity2 = map.getPaintProperty(layerId, opacityProp[0]);
42425
- return opacity2 !== void 0 && opacity2 !== null ? opacity2 : 1;
42426
- }
42427
- const opacity = map.getPaintProperty(layerId, opacityProp);
42428
- return opacity !== void 0 && opacity !== null ? opacity : 1;
42429
- }
42430
- function setLayerOpacity(map, layerId, layerType, opacity) {
42431
- const opacityProp = getOpacityProperty(layerType);
42432
- if (opacityProp === null) {
42433
- return;
42434
- }
42435
- if (Array.isArray(opacityProp)) {
42436
- opacityProp.forEach((prop) => {
42437
- map.setPaintProperty(layerId, prop, opacity);
42438
- });
42439
- } else {
42440
- map.setPaintProperty(layerId, opacityProp, opacity);
42441
- }
42442
- }
42443
- function getLayerType(map, layerId) {
42444
- try {
42445
- const layer = map.getLayer(layerId);
42446
- return layer ? layer.type : null;
42447
- } catch (error) {
42448
- console.warn(`Failed to get layer type for ${layerId}:`, error);
42449
- return null;
42450
- }
42451
- }
42452
- function clonePaintValue(value) {
42453
- if (Array.isArray(value)) {
42454
- return value.map((item) => clonePaintValue(item));
42455
- }
42456
- if (value && typeof value === "object") {
42457
- try {
42458
- return JSON.parse(JSON.stringify(value));
42459
- } catch (error) {
42460
- return value;
42461
- }
42462
- }
42463
- return value;
42464
- }
42465
- function cacheOriginalLayerStyle(map, layerId, originalStyles) {
42466
- var _a3;
42467
- if (originalStyles.has(layerId)) {
42468
- return;
42469
- }
42470
- try {
42471
- const layer = map.getLayer(layerId);
42472
- if (!layer) {
42473
- return;
42474
- }
42475
- const paint = {};
42476
- const style = map.getStyle();
42477
- const layerDef = (_a3 = style.layers) == null ? void 0 : _a3.find((l3) => l3.id === layerId);
42478
- if (layer.type === "raster") {
42479
- const rasterDefaults = {
42480
- "raster-opacity": 1,
42481
- "raster-brightness-min": 0,
42482
- "raster-brightness-max": 1,
42483
- "raster-saturation": 0,
42484
- "raster-contrast": 0,
42485
- "raster-hue-rotate": 0
42486
- };
42487
- Object.assign(paint, rasterDefaults);
42488
- if (layerDef && "paint" in layerDef && layerDef.paint) {
42489
- Object.entries(layerDef.paint).forEach(([prop, value]) => {
42490
- if (prop.startsWith("raster-")) {
42491
- paint[prop] = clonePaintValue(value);
42492
- }
42493
- });
42494
- }
42495
- } else {
42496
- if (layerDef && "paint" in layerDef && layerDef.paint) {
42497
- Object.entries(layerDef.paint).forEach(([prop, value]) => {
42498
- paint[prop] = clonePaintValue(value);
42499
- });
42500
- }
42501
- }
42502
- originalStyles.set(layerId, { paint });
42503
- } catch (error) {
42504
- console.warn(`Failed to cache original style for ${layerId}:`, error);
42505
- }
42506
- }
42507
- function restoreOriginalStyle(map, layerId, originalStyles) {
42508
- const original = originalStyles.get(layerId);
42509
- if (!original) {
42510
- return {};
42511
- }
42512
- const applied = {};
42513
- Object.entries(original.paint).forEach(([property, value]) => {
42514
- try {
42515
- const restoredValue = clonePaintValue(value);
42516
- map.setPaintProperty(layerId, property, restoredValue);
42517
- applied[property] = restoredValue;
42518
- } catch (error) {
42519
- console.warn(`Failed to restore ${property} for ${layerId}:`, error);
42520
- }
42521
- });
42522
- return applied;
42523
- }
42524
- function rgbToHex(r2, g2, b2) {
42525
- const clamp2 = (v2) => Math.max(0, Math.min(255, Math.round(v2)));
42526
- const toHex2 = (v2) => {
42527
- const hex = clamp2(v2).toString(16);
42528
- return hex.length === 1 ? `0${hex}` : hex;
42529
- };
42530
- return `#${toHex2(r2)}${toHex2(g2)}${toHex2(b2)}`;
42531
- }
42532
- function normalizeColor(value) {
42533
- if (typeof value === "string") {
42534
- if (value.startsWith("#")) {
42535
- if (value.length === 4) {
42536
- const r2 = value[1];
42537
- const g2 = value[2];
42538
- const b2 = value[3];
42539
- return `#${r2}${r2}${g2}${g2}${b2}${b2}`;
42540
- }
42541
- return value;
42542
- }
42543
- if (value.startsWith("rgb")) {
42544
- const match = value.match(/\d+/g);
42545
- if (match && match.length >= 3) {
42546
- const [r2, g2, b2] = match.map((num) => parseInt(num, 10));
42547
- return rgbToHex(r2, g2, b2);
42548
- }
42549
- }
42550
- } else if (Array.isArray(value) && value.length >= 3) {
42551
- return rgbToHex(value[0], value[1], value[2]);
42552
- }
42553
- return "#3388ff";
42554
- }
42555
- function formatNumericValue(value, step) {
42556
- let decimals = 0;
42557
- if (step && Number(step) !== 1) {
42558
- const stepNumber = Number(step);
42559
- if (stepNumber > 0 && stepNumber < 1) {
42560
- decimals = Math.min(4, Math.ceil(Math.abs(Math.log10(stepNumber))));
42561
- }
42562
- }
42563
- return value.toFixed(decimals);
42564
- }
42565
- const COLOR_PROPERTY_MAP = {
42566
- fill: ["fill-color", "fill-outline-color"],
42567
- line: ["line-color"],
42568
- circle: ["circle-color", "circle-stroke-color"],
42569
- symbol: ["icon-color", "text-color"],
42570
- background: ["background-color"],
42571
- heatmap: ["heatmap-color"],
42572
- "fill-extrusion": ["fill-extrusion-color"]
42573
- };
42574
- function extractColorFromExpression(expression) {
42575
- if (!Array.isArray(expression) || expression.length === 0) return null;
42576
- for (const item of expression) {
42577
- if (typeof item === "string") {
42578
- if (item.startsWith("#") || item.startsWith("rgb") || item.startsWith("hsl")) {
42579
- return normalizeColor(item);
42580
- }
42581
- } else if (Array.isArray(item)) {
42582
- const result = extractColorFromExpression(item);
42583
- if (result) return result;
42584
- }
42585
- }
42586
- return null;
42587
- }
42588
- function getLayerColor(map, layerId, layerType) {
42589
- var _a3;
42590
- const propertyNames2 = COLOR_PROPERTY_MAP[layerType];
42591
- if (!propertyNames2) return null;
42592
- for (const propertyName of propertyNames2) {
42593
- try {
42594
- const runtimeColor = map.getPaintProperty(layerId, propertyName);
42595
- if (runtimeColor) {
42596
- if (typeof runtimeColor === "string") {
42597
- return normalizeColor(runtimeColor);
42598
- }
42599
- if (Array.isArray(runtimeColor)) {
42600
- const extracted = extractColorFromExpression(runtimeColor);
42601
- if (extracted) return extracted;
42602
- }
42603
- }
42604
- } catch {
42605
- }
42606
- const style = map.getStyle();
42607
- const layer = (_a3 = style == null ? void 0 : style.layers) == null ? void 0 : _a3.find(
42608
- (l3) => l3.id === layerId
42609
- );
42610
- if (layer && "paint" in layer && layer.paint) {
42611
- const paintColor = layer.paint[propertyName];
42612
- if (paintColor) {
42613
- if (typeof paintColor === "string") {
42614
- return normalizeColor(paintColor);
42615
- }
42616
- if (Array.isArray(paintColor)) {
42617
- const extracted = extractColorFromExpression(paintColor);
42618
- if (extracted) return extracted;
42619
- }
42620
- }
42621
- }
42622
- }
42623
- return null;
42624
- }
42625
- function getLayerColorFromSpec(layer) {
42626
- const propertyNames2 = COLOR_PROPERTY_MAP[layer.type];
42627
- if (!propertyNames2) return null;
42628
- for (const propertyName of propertyNames2) {
42629
- if ("paint" in layer && layer.paint) {
42630
- const paintColor = layer.paint[propertyName];
42631
- if (paintColor) {
42632
- if (typeof paintColor === "string") {
42633
- return normalizeColor(paintColor);
42634
- }
42635
- if (Array.isArray(paintColor)) {
42636
- const extracted = extractColorFromExpression(paintColor);
42637
- if (extracted) return extracted;
42638
- }
42639
- }
42640
- }
42641
- }
42642
- return null;
42643
- }
42644
- function darkenColor(hexColor, amount) {
42645
- let hex = hexColor.replace("#", "");
42646
- if (hex.length === 3) {
42647
- hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
42648
- }
42649
- const r2 = Math.max(
42650
- 0,
42651
- parseInt(hex.slice(0, 2), 16) - Math.round(255 * amount)
42652
- );
42653
- const g2 = Math.max(
42654
- 0,
42655
- parseInt(hex.slice(2, 4), 16) - Math.round(255 * amount)
42656
- );
42657
- const b2 = Math.max(
42658
- 0,
42659
- parseInt(hex.slice(4, 6), 16) - Math.round(255 * amount)
42660
- );
42661
- return rgbToHex(r2, g2, b2);
42662
- }
42663
- function createFillSymbol(size, color) {
42664
- const padding = 2;
42665
- const borderColor = darkenColor(color, 0.3);
42666
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42667
- <rect x="${padding}" y="${padding}" width="${size - padding * 2}" height="${size - padding * 2}"
42668
- fill="${color}" stroke="${borderColor}" stroke-width="1" rx="1"/>
42669
- </svg>`;
42670
- }
42671
- function createLineSymbol(size, color, strokeWidth = 2) {
42672
- const y2 = size / 2;
42673
- const padding = 2;
42674
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42675
- <line x1="${padding}" y1="${y2}" x2="${size - padding}" y2="${y2}"
42676
- stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="round"/>
42677
- </svg>`;
42678
- }
42679
- function createCircleSymbol(size, color) {
42680
- const cx = size / 2;
42681
- const cy = size / 2;
42682
- const r2 = size / 2 - 3;
42683
- const borderColor = darkenColor(color, 0.3);
42684
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42685
- <circle cx="${cx}" cy="${cy}" r="${r2}" fill="${color}"
42686
- stroke="${borderColor}" stroke-width="1"/>
42687
- </svg>`;
42688
- }
42689
- function createMarkerSymbol(size, color) {
42690
- const borderColor = darkenColor(color, 0.3);
42691
- const cx = size / 2;
42692
- const pinWidth = size * 0.5;
42693
- const pinHeight = size * 0.7;
42694
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42695
- <path d="M${cx} ${size - 2}
42696
- L${cx - pinWidth / 2} ${size - pinHeight}
42697
- A${pinWidth / 2} ${pinWidth / 2} 0 1 1 ${cx + pinWidth / 2} ${size - pinHeight}
42698
- Z"
42699
- fill="${color}" stroke="${borderColor}" stroke-width="1"/>
42700
- <circle cx="${cx}" cy="${size - pinHeight - pinWidth / 4}" r="${pinWidth / 5}" fill="white"/>
42701
- </svg>`;
42702
- }
42703
- function createRasterSymbol(size) {
42704
- const padding = 2;
42705
- const id2 = `rasterGrad_${Math.random().toString(36).slice(2, 9)}`;
42706
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42707
- <defs>
42708
- <linearGradient id="${id2}" x1="0%" y1="0%" x2="100%" y2="100%">
42709
- <stop offset="0%" stop-color="#e0e0e0"/>
42710
- <stop offset="50%" stop-color="#808080"/>
42711
- <stop offset="100%" stop-color="#404040"/>
42712
- </linearGradient>
42713
- </defs>
42714
- <rect x="${padding}" y="${padding}" width="${size - padding * 2}" height="${size - padding * 2}"
42715
- fill="url(#${id2})" rx="1"/>
42716
- </svg>`;
42717
- }
42718
- function createBackgroundSymbol(size, color) {
42719
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42720
- <rect x="1" y="1" width="${size - 2}" height="${size - 2}" fill="${color}" rx="2"/>
42721
- <rect x="3" y="3" width="${size - 6}" height="${size - 6}" fill="none"
42722
- stroke="white" stroke-width="1" stroke-opacity="0.5" rx="1"/>
42723
- </svg>`;
42724
- }
42725
- function createHeatmapSymbol(size) {
42726
- const padding = 2;
42727
- const id2 = `heatmapGrad_${Math.random().toString(36).slice(2, 9)}`;
42728
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42729
- <defs>
42730
- <radialGradient id="${id2}" cx="50%" cy="50%" r="50%">
42731
- <stop offset="0%" stop-color="#ffff00"/>
42732
- <stop offset="50%" stop-color="#ff8800"/>
42733
- <stop offset="100%" stop-color="#ff0000"/>
42734
- </radialGradient>
42735
- </defs>
42736
- <rect x="${padding}" y="${padding}" width="${size - padding * 2}" height="${size - padding * 2}"
42737
- fill="url(#${id2})" rx="1"/>
42738
- </svg>`;
42739
- }
42740
- function createHillshadeSymbol(size) {
42741
- const padding = 2;
42742
- const id2 = `hillshadeGrad_${Math.random().toString(36).slice(2, 9)}`;
42743
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42744
- <defs>
42745
- <linearGradient id="${id2}" x1="0%" y1="0%" x2="100%" y2="100%">
42746
- <stop offset="0%" stop-color="#ffffff"/>
42747
- <stop offset="100%" stop-color="#666666"/>
42748
- </linearGradient>
42749
- </defs>
42750
- <rect x="${padding}" y="${padding}" width="${size - padding * 2}" height="${size - padding * 2}"
42751
- fill="url(#${id2})" rx="1"/>
42752
- </svg>`;
42753
- }
42754
- function createFillExtrusionSymbol(size, color) {
42755
- const borderColor = darkenColor(color, 0.3);
42756
- const topColor = color;
42757
- const sideColor = darkenColor(color, 0.2);
42758
- const depth = 3;
42759
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42760
- <polygon points="${2 + depth},2 ${size - 2},2 ${size - 2},${size - 2 - depth} ${size - 2 - depth},${size - 2} 2,${size - 2} 2,${2 + depth}"
42761
- fill="${topColor}" stroke="${borderColor}" stroke-width="1"/>
42762
- <polygon points="2,${2 + depth} ${2 + depth},2 ${2 + depth},${size - 2 - depth} 2,${size - 2}"
42763
- fill="${sideColor}" stroke="${borderColor}" stroke-width="0.5"/>
42764
- <polygon points="${2 + depth},${size - 2 - depth} ${size - 2},${size - 2 - depth} ${size - 2 - depth},${size - 2} 2,${size - 2}"
42765
- fill="${sideColor}" stroke="${borderColor}" stroke-width="0.5"/>
42766
- </svg>`;
42767
- }
42768
- function createDefaultSymbol(size, color) {
42769
- const padding = 2;
42770
- const borderColor = darkenColor(color, 0.3);
42771
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42772
- <rect x="${padding}" y="${padding}" width="${size - padding * 2}" height="${size - padding * 2}"
42773
- fill="${color}" stroke="${borderColor}" stroke-width="1"/>
42774
- </svg>`;
42775
- }
42776
- function createCOGSymbol(size, color) {
42777
- const padding = 2;
42778
- const borderColor = darkenColor(color, 0.3);
42779
- const cellSize = (size - padding * 2) / 2;
42780
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42781
- <rect x="${padding}" y="${padding}" width="${cellSize}" height="${cellSize}" fill="${color}" stroke="${borderColor}" stroke-width="0.5"/>
42782
- <rect x="${padding + cellSize}" y="${padding}" width="${cellSize}" height="${cellSize}" fill="${color}" stroke="${borderColor}" stroke-width="0.5" opacity="0.8"/>
42783
- <rect x="${padding}" y="${padding + cellSize}" width="${cellSize}" height="${cellSize}" fill="${color}" stroke="${borderColor}" stroke-width="0.5" opacity="0.6"/>
42784
- <rect x="${padding + cellSize}" y="${padding + cellSize}" width="${cellSize}" height="${cellSize}" fill="${color}" stroke="${borderColor}" stroke-width="0.5" opacity="0.4"/>
42785
- </svg>`;
42786
- }
42787
- function createZarrSymbol(size, color) {
42788
- const padding = 2;
42789
- const borderColor = darkenColor(color, 0.3);
42790
- const innerSize = size - padding * 2;
42791
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42792
- <rect x="${padding + 2}" y="${padding}" width="${innerSize - 2}" height="${innerSize - 2}" fill="${darkenColor(color, 0.2)}" stroke="${borderColor}" stroke-width="0.5" rx="1"/>
42793
- <rect x="${padding + 1}" y="${padding + 1}" width="${innerSize - 2}" height="${innerSize - 2}" fill="${darkenColor(color, 0.1)}" stroke="${borderColor}" stroke-width="0.5" rx="1"/>
42794
- <rect x="${padding}" y="${padding + 2}" width="${innerSize - 2}" height="${innerSize - 2}" fill="${color}" stroke="${borderColor}" stroke-width="0.5" rx="1"/>
42795
- <line x1="${padding + 3}" y1="${padding + 5}" x2="${padding + innerSize - 5}" y2="${padding + 5}" stroke="${borderColor}" stroke-width="0.5" opacity="0.5"/>
42796
- <line x1="${padding + 3}" y1="${padding + 8}" x2="${padding + innerSize - 5}" y2="${padding + 8}" stroke="${borderColor}" stroke-width="0.5" opacity="0.5"/>
42797
- </svg>`;
42798
- }
42799
- function createCustomRasterSymbol(size, color) {
42800
- const padding = 2;
42801
- const borderColor = darkenColor(color, 0.3);
42802
- const id2 = `customRasterGrad_${Math.random().toString(36).slice(2, 9)}`;
42803
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42804
- <defs>
42805
- <linearGradient id="${id2}" x1="0%" y1="0%" x2="100%" y2="100%">
42806
- <stop offset="0%" stop-color="${color}"/>
42807
- <stop offset="100%" stop-color="${darkenColor(color, 0.4)}"/>
42808
- </linearGradient>
42809
- </defs>
42810
- <rect x="${padding}" y="${padding}" width="${size - padding * 2}" height="${size - padding * 2}"
42811
- fill="url(#${id2})" stroke="${borderColor}" stroke-width="1" rx="1"/>
42812
- <line x1="${size / 2}" y1="${padding}" x2="${size / 2}" y2="${size - padding}" stroke="${borderColor}" stroke-width="0.5" opacity="0.3"/>
42813
- <line x1="${padding}" y1="${size / 2}" x2="${size - padding}" y2="${size / 2}" stroke="${borderColor}" stroke-width="0.5" opacity="0.3"/>
42814
- </svg>`;
42815
- }
42816
- function createStackedLayersSymbol(size) {
42817
- const colors = ["#a8d4a8", "#8ec4e8", "#d4c4a8"];
42818
- const borderColor = "#666666";
42819
- return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
42820
- <rect x="4" y="1" width="${size - 6}" height="${size - 6}" fill="${colors[2]}" stroke="${borderColor}" stroke-width="0.75" rx="1"/>
42821
- <rect x="2" y="3" width="${size - 6}" height="${size - 6}" fill="${colors[1]}" stroke="${borderColor}" stroke-width="0.75" rx="1"/>
42822
- <rect x="0" y="5" width="${size - 6}" height="${size - 6}" fill="${colors[0]}" stroke="${borderColor}" stroke-width="0.75" rx="1"/>
42823
- </svg>`;
42824
- }
42825
- function createLayerSymbolSVG(layerType, color, options = {}) {
42826
- const size = options.size || 16;
42827
- const strokeWidth = options.strokeWidth || 2;
42828
- const fillColor = color || "#888888";
42829
- switch (layerType) {
42830
- case "fill":
42831
- return createFillSymbol(size, fillColor);
42832
- case "line":
42833
- return createLineSymbol(size, fillColor, strokeWidth);
42834
- case "circle":
42835
- return createCircleSymbol(size, fillColor);
42836
- case "symbol":
42837
- return createMarkerSymbol(size, fillColor);
42838
- case "raster":
42839
- return createRasterSymbol(size);
42840
- case "background":
42841
- return createBackgroundSymbol(size, fillColor);
42842
- case "heatmap":
42843
- return createHeatmapSymbol(size);
42844
- case "hillshade":
42845
- return createHillshadeSymbol(size);
42846
- case "fill-extrusion":
42847
- return createFillExtrusionSymbol(size, fillColor);
42848
- case "background-group":
42849
- return createStackedLayersSymbol(size);
42850
- case "cog":
42851
- return createCOGSymbol(size, fillColor);
42852
- case "zarr":
42853
- return createZarrSymbol(size, fillColor);
42854
- case "custom-raster":
42855
- return createCustomRasterSymbol(size, fillColor);
42856
- default:
42857
- return createDefaultSymbol(size, fillColor);
42858
- }
42859
- }
42860
- function createBackgroundGroupSymbolSVG(size = 16) {
42861
- return createStackedLayersSymbol(size);
42862
- }
42863
- class LayerControl {
42864
- constructor(options = {}) {
42865
- __publicField2(this, "map");
42866
- __publicField2(this, "mapContainer");
42867
- __publicField2(this, "container");
42868
- __publicField2(this, "button");
42869
- __publicField2(this, "panel");
42870
- __publicField2(this, "resizeHandler", null);
42871
- __publicField2(this, "mapResizeHandler", null);
42872
- __publicField2(this, "state");
42873
- __publicField2(this, "targetLayers");
42874
- __publicField2(this, "styleEditors");
42875
- __publicField2(this, "initialSourceIds", null);
42876
- __publicField2(this, "initialLayerIds", null);
42877
- __publicField2(this, "initialLayerStates");
42878
- __publicField2(this, "minPanelWidth");
42879
- __publicField2(this, "maxPanelWidth");
42880
- __publicField2(this, "maxPanelHeight");
42881
- __publicField2(this, "showStyleEditor");
42882
- __publicField2(this, "showOpacitySlider");
42883
- __publicField2(this, "showLayerSymbol");
42884
- __publicField2(this, "excludeDrawnLayers");
42885
- __publicField2(this, "excludeLayerPatterns");
42886
- __publicField2(this, "customLayerRegistry", null);
42887
- __publicField2(this, "customLayerUnsubscribe", null);
42888
- __publicField2(this, "removedCustomLayerIds", /* @__PURE__ */ new Set());
42889
- __publicField2(this, "nativeLayerGroups", /* @__PURE__ */ new Map());
42890
- __publicField2(this, "basemapStyleUrl", null);
42891
- __publicField2(this, "basemapLayerIds", null);
42892
- __publicField2(this, "widthSliderEl", null);
42893
- __publicField2(this, "widthThumbEl", null);
42894
- __publicField2(this, "widthValueEl", null);
42895
- __publicField2(this, "isWidthSliderActive", false);
42896
- __publicField2(this, "widthDragRectWidth", null);
42897
- __publicField2(this, "widthDragStartX", null);
42898
- __publicField2(this, "widthDragStartWidth", null);
42899
- __publicField2(this, "widthFrame", null);
42900
- __publicField2(this, "contextMenuEl", null);
42901
- __publicField2(this, "enableContextMenu");
42902
- __publicField2(this, "enableDragAndDrop");
42903
- __publicField2(this, "onLayerRename");
42904
- __publicField2(this, "onLayerReorder");
42905
- __publicField2(this, "onLayerRemove");
42906
- this.minPanelWidth = options.panelMinWidth || 240;
42907
- this.maxPanelWidth = options.panelMaxWidth || 420;
42908
- this.maxPanelHeight = options.panelMaxHeight || 600;
42909
- this.showStyleEditor = options.showStyleEditor !== false;
42910
- this.showOpacitySlider = options.showOpacitySlider !== false;
42911
- this.showLayerSymbol = options.showLayerSymbol !== false;
42912
- this.excludeDrawnLayers = options.excludeDrawnLayers !== false;
42913
- this.excludeLayerPatterns = this.wildcardPatternsToRegex(options.excludeLayers || []);
42914
- this.enableContextMenu = options.enableContextMenu !== false;
42915
- this.enableDragAndDrop = options.enableDragAndDrop !== false;
42916
- this.onLayerRename = options.onLayerRename;
42917
- this.onLayerReorder = options.onLayerReorder;
42918
- this.onLayerRemove = options.onLayerRemove;
42919
- this.initialLayerStates = options.layerStates || {};
42920
- this.state = {
42921
- collapsed: options.collapsed !== false,
42922
- panelWidth: options.panelWidth || 350,
42923
- activeStyleEditor: null,
42924
- layerStates: {},
42925
- originalStyles: /* @__PURE__ */ new Map(),
42926
- userInteractingWithSlider: false,
42927
- backgroundLegendOpen: false,
42928
- backgroundLayerVisibility: /* @__PURE__ */ new Map(),
42929
- onlyRenderedFilter: false,
42930
- contextMenu: {
42931
- visible: false,
42932
- targetLayerId: null,
42933
- x: 0,
42934
- y: 0
42935
- },
42936
- renamingLayerId: null,
42937
- customLayerNames: /* @__PURE__ */ new Map(),
42938
- drag: {
42939
- active: false,
42940
- layerId: null,
42941
- startY: 0,
42942
- currentY: 0,
42943
- placeholder: null,
42944
- draggedElement: null
42945
- },
42946
- isStyleOperationInProgress: false
42947
- };
42948
- this.targetLayers = options.layers || Object.keys(this.initialLayerStates);
42949
- this.styleEditors = /* @__PURE__ */ new Map();
42950
- if (options.customLayerAdapters && options.customLayerAdapters.length > 0) {
42951
- this.customLayerRegistry = new CustomLayerRegistry();
42952
- options.customLayerAdapters.forEach((adapter) => {
42953
- this.customLayerRegistry.register(adapter);
42954
- });
42955
- }
42956
- this.basemapStyleUrl = options.basemapStyleUrl || null;
42957
- }
42958
- /**
42959
- * Called when the control is added to the map
42960
- */
42961
- onAdd(map) {
42962
- this.map = map;
42963
- this.mapContainer = map.getContainer();
42964
- const style = this.map.getStyle();
42965
- if (style && style.sources) {
42966
- this.initialSourceIds = new Set(Object.keys(style.sources));
42967
- } else {
42968
- this.initialSourceIds = /* @__PURE__ */ new Set();
42969
- }
42970
- if (style && style.layers) {
42971
- this.initialLayerIds = new Set(style.layers.map((layer) => layer.id));
42972
- } else {
42973
- this.initialLayerIds = /* @__PURE__ */ new Set();
42974
- }
42975
- this.container = this.createContainer();
42976
- this.button = this.createToggleButton();
42977
- this.panel = this.createPanel();
42978
- this.container.appendChild(this.button);
42979
- this.mapContainer.appendChild(this.panel);
42980
- if (this.enableContextMenu) {
42981
- this.contextMenuEl = this.createContextMenu();
42982
- this.mapContainer.appendChild(this.contextMenuEl);
42983
- }
42984
- this.updateWidthDisplay();
42985
- this.setupEventListeners();
42986
- if (this.basemapStyleUrl && !this.basemapLayerIds) {
42987
- this.fetchBasemapStyle().then(() => {
42988
- if (Object.keys(this.state.layerStates).length === 0) {
42989
- this.autoDetectLayers();
42990
- }
42991
- this.buildLayerItems();
42992
- }).catch((error) => {
42993
- console.warn("Failed to fetch basemap style, falling back to heuristic detection:", error);
42994
- if (Object.keys(this.state.layerStates).length === 0) {
42995
- this.autoDetectLayers();
42996
- }
42997
- this.buildLayerItems();
42998
- });
42999
- } else {
43000
- if (Object.keys(this.state.layerStates).length === 0) {
43001
- this.autoDetectLayers();
43002
- }
43003
- this.buildLayerItems();
43004
- }
43005
- if (!this.state.collapsed) {
43006
- requestAnimationFrame(() => {
43007
- this.updatePanelPosition();
43008
- });
43009
- }
43010
- return this.container;
43011
- }
43012
- /**
43013
- * Fetch the basemap style JSON and extract layer IDs.
43014
- * This provides reliable distinction between basemap and user-added layers.
43015
- */
43016
- async fetchBasemapStyle() {
43017
- if (!this.basemapStyleUrl) return;
43018
- try {
43019
- const response = await fetch(this.basemapStyleUrl);
43020
- if (!response.ok) {
43021
- throw new Error(`HTTP error! status: ${response.status}`);
43022
- }
43023
- const styleJson = await response.json();
43024
- if (styleJson && Array.isArray(styleJson.layers)) {
43025
- this.basemapLayerIds = new Set(
43026
- styleJson.layers.map((layer) => layer.id)
43027
- );
43028
- } else {
43029
- this.basemapLayerIds = /* @__PURE__ */ new Set();
43030
- }
43031
- } catch (error) {
43032
- console.warn("Failed to fetch basemap style from URL:", this.basemapStyleUrl, error);
43033
- throw error;
43034
- }
43035
- }
43036
- /**
43037
- * Called when the control is removed from the map
43038
- */
43039
- onRemove() {
43040
- var _a3, _b3, _c3;
43041
- if (this.customLayerUnsubscribe) {
43042
- this.customLayerUnsubscribe();
43043
- this.customLayerUnsubscribe = null;
43044
- }
43045
- if (this.customLayerRegistry) {
43046
- this.customLayerRegistry.destroy();
43047
- this.customLayerRegistry = null;
43048
- }
43049
- if (this.resizeHandler) {
43050
- window.removeEventListener("resize", this.resizeHandler);
43051
- this.resizeHandler = null;
43052
- }
43053
- if (this.mapResizeHandler) {
43054
- this.map.off("resize", this.mapResizeHandler);
43055
- this.mapResizeHandler = null;
43056
- }
43057
- if (this.contextMenuEl) {
43058
- (_a3 = this.contextMenuEl.parentNode) == null ? void 0 : _a3.removeChild(this.contextMenuEl);
43059
- this.contextMenuEl = null;
43060
- }
43061
- this.cleanupDragState();
43062
- (_b3 = this.panel.parentNode) == null ? void 0 : _b3.removeChild(this.panel);
43063
- (_c3 = this.container.parentNode) == null ? void 0 : _c3.removeChild(this.container);
43064
- }
43065
- /**
43066
- * Auto-detect layers from the map and populate layerStates
43067
- */
43068
- autoDetectLayers() {
43069
- const style = this.map.getStyle();
43070
- if (!style || !style.layers) {
43071
- return;
43072
- }
43073
- const allLayerIds = style.layers.map((layer) => layer.id);
43074
- if (this.targetLayers.length === 0) {
43075
- const userAddedLayers = [];
43076
- const backgroundLayerIds = [];
43077
- const useBasemapStyleDetection = this.basemapLayerIds !== null && this.basemapLayerIds.size > 0;
43078
- const userAddedSourceIds = useBasemapStyleDetection ? /* @__PURE__ */ new Set() : this.detectUserAddedSources();
43079
- allLayerIds.forEach((layerId) => {
43080
- const layer = this.map.getLayer(layerId);
43081
- if (!layer) return;
43082
- if (this.excludeDrawnLayers && this.isDrawnLayer(layerId)) {
43083
- backgroundLayerIds.push(layerId);
43084
- return;
43085
- }
43086
- if (this.isExcludedByPattern(layerId)) {
43087
- backgroundLayerIds.push(layerId);
43088
- return;
43089
- }
43090
- if (useBasemapStyleDetection) {
43091
- if (this.basemapLayerIds.has(layerId)) {
43092
- backgroundLayerIds.push(layerId);
43093
- } else {
43094
- userAddedLayers.push(layerId);
43095
- }
43096
- } else {
43097
- const sourceId = layer.source;
43098
- if (sourceId && userAddedSourceIds.has(sourceId)) {
43099
- userAddedLayers.push(layerId);
43100
- } else {
43101
- backgroundLayerIds.push(layerId);
43102
- }
43103
- }
43104
- });
43105
- if (backgroundLayerIds.length > 0) {
43106
- this.state.layerStates["Background"] = {
43107
- visible: true,
43108
- opacity: 1,
43109
- name: "Background"
43110
- };
43111
- }
43112
- userAddedLayers.forEach((layerId) => {
43113
- const layer = this.map.getLayer(layerId);
43114
- if (!layer) return;
43115
- const visibility = this.map.getLayoutProperty(layerId, "visibility");
43116
- const isVisible = visibility !== "none";
43117
- const layerType = layer.type;
43118
- const opacity = getLayerOpacity(this.map, layerId, layerType);
43119
- const friendlyName = this.generateFriendlyName(layerId);
43120
- this.state.layerStates[layerId] = this.mergeWithUserState(layerId, {
43121
- visible: isVisible,
43122
- opacity,
43123
- name: friendlyName
43124
- });
43125
- });
43126
- } else {
43127
- const userLayers = [];
43128
- const basemapLayers = [];
43129
- allLayerIds.forEach((layerId) => {
43130
- if (this.isExcludedByPattern(layerId)) {
43131
- basemapLayers.push(layerId);
43132
- return;
43133
- }
43134
- if (this.targetLayers.includes(layerId)) {
43135
- userLayers.push(layerId);
43136
- } else {
43137
- basemapLayers.push(layerId);
43138
- }
43139
- });
43140
- if (basemapLayers.length > 0) {
43141
- this.state.layerStates["Background"] = {
43142
- visible: true,
43143
- opacity: 1,
43144
- name: "Background"
43145
- };
43146
- }
43147
- userLayers.forEach((layerId) => {
43148
- const layer = this.map.getLayer(layerId);
43149
- if (!layer) return;
43150
- const visibility = this.map.getLayoutProperty(layerId, "visibility");
43151
- const isVisible = visibility !== "none";
43152
- const layerType = layer.type;
43153
- const opacity = getLayerOpacity(this.map, layerId, layerType);
43154
- const friendlyName = this.generateFriendlyName(layerId);
43155
- this.state.layerStates[layerId] = this.mergeWithUserState(layerId, {
43156
- visible: isVisible,
43157
- opacity,
43158
- name: friendlyName
43159
- });
43160
- });
43161
- }
43162
- if (this.customLayerRegistry) {
43163
- const customLayerIds = this.customLayerRegistry.getAllLayerIds();
43164
- customLayerIds.forEach((layerId) => {
43165
- if (this.state.layerStates[layerId]) return;
43166
- const customState = this.customLayerRegistry.getLayerState(layerId);
43167
- if (customState) {
43168
- this.state.layerStates[layerId] = {
43169
- visible: customState.visible,
43170
- opacity: customState.opacity,
43171
- name: customState.name,
43172
- isCustomLayer: true,
43173
- customLayerType: this.customLayerRegistry.getSymbolType(layerId) || void 0
43174
- };
43175
- }
43176
- });
43177
- }
43178
- this.targetLayers = Object.keys(this.state.layerStates);
43179
- }
43180
- /**
43181
- * Detect which sources are user-added (not from the basemap style)
43182
- * User-added sources are identified by:
43183
- * - Sources that were NOT present when the control was first added
43184
- * - Additionally for sources added later:
43185
- * - GeoJSON sources with inline data objects (not URL strings)
43186
- * - Image, video, canvas sources
43187
- * - Raster, raster-dem, vector sources from non-basemap tile providers
43188
- */
43189
- detectUserAddedSources() {
43190
- const userAddedSources = /* @__PURE__ */ new Set();
43191
- const style = this.map.getStyle();
43192
- if (!style || !style.sources) return userAddedSources;
43193
- const basemapDomains = /* @__PURE__ */ new Set();
43194
- const knownBasemapProviders = [
43195
- "demotiles.maplibre.org",
43196
- "api.maptiler.com",
43197
- "tiles.stadiamaps.com",
43198
- "api.mapbox.com",
43199
- "basemaps.cartocdn.com",
43200
- "tiles.mapbox.com",
43201
- "a.basemaps.cartocdn.com",
43202
- "b.basemaps.cartocdn.com",
43203
- "c.basemaps.cartocdn.com",
43204
- "d.basemaps.cartocdn.com",
43205
- "tiles.arcgis.com",
43206
- "server.arcgisonline.com",
43207
- "services.arcgisonline.com"
43208
- ];
43209
- const spriteUrl = style.sprite;
43210
- if (spriteUrl && typeof spriteUrl === "string") {
43211
- try {
43212
- const url = new URL(spriteUrl);
43213
- basemapDomains.add(url.hostname);
43214
- } catch {
43215
- }
43216
- }
43217
- if (style.glyphs) {
43218
- try {
43219
- const url = new URL(style.glyphs.replace("{fontstack}", "x").replace("{range}", "x"));
43220
- basemapDomains.add(url.hostname);
43221
- } catch {
43222
- }
43223
- }
43224
- for (const [sourceId, source] of Object.entries(style.sources)) {
43225
- if (this.initialSourceIds && this.initialSourceIds.has(sourceId)) {
43226
- continue;
43227
- }
43228
- const src = source;
43229
- const sourceType = src.type;
43230
- if (sourceType === "image" || sourceType === "video" || sourceType === "canvas") {
43231
- userAddedSources.add(sourceId);
43232
- continue;
43233
- }
43234
- if (sourceType === "geojson") {
43235
- if (src.data && typeof src.data === "object") {
43236
- userAddedSources.add(sourceId);
43237
- } else if (src.data && typeof src.data === "string") {
43238
- try {
43239
- const url = new URL(src.data);
43240
- const isBasemap = knownBasemapProviders.some((p) => url.hostname.includes(p)) || basemapDomains.has(url.hostname);
43241
- if (!isBasemap) {
43242
- userAddedSources.add(sourceId);
43243
- }
43244
- } catch {
43245
- userAddedSources.add(sourceId);
43246
- }
43247
- }
43248
- continue;
43249
- }
43250
- if (sourceType === "raster" || sourceType === "raster-dem" || sourceType === "vector") {
43251
- const tileUrl = src.url || src.tiles && src.tiles[0] || "";
43252
- if (tileUrl) {
43253
- try {
43254
- const url = new URL(tileUrl.replace(/{[^}]+}/g, "0"));
43255
- const hostname = url.hostname;
43256
- const isKnownBasemap = knownBasemapProviders.some(
43257
- (provider) => hostname === provider || hostname.endsWith("." + provider)
43258
- );
43259
- const matchesBasemapDomain = basemapDomains.has(hostname);
43260
- if (!isKnownBasemap && !matchesBasemapDomain) {
43261
- userAddedSources.add(sourceId);
43262
- }
43263
- } catch {
43264
- userAddedSources.add(sourceId);
43265
- }
43266
- }
43267
- }
43268
- }
43269
- return userAddedSources;
43270
- }
43271
- /**
43272
- * Generate a friendly display name from a layer ID
43273
- */
43274
- generateFriendlyName(layerId) {
43275
- let name = layerId.replace(/^(layer[-_]?|gl[-_]?)/, "");
43276
- name = name.replace(/[-_]/g, " ");
43277
- name = name.replace(/\b\w/g, (char) => char.toUpperCase());
43278
- return name || layerId;
43279
- }
43280
- /**
43281
- * Merge auto-detected layer state with user-provided initial state.
43282
- * User-provided values take precedence over detected values.
43283
- */
43284
- mergeWithUserState(layerId, detected) {
43285
- const userState = this.initialLayerStates[layerId];
43286
- if (!userState) return detected;
43287
- return {
43288
- visible: userState.visible ?? detected.visible,
43289
- opacity: userState.opacity ?? detected.opacity,
43290
- name: userState.name ?? detected.name,
43291
- ...userState.isCustomLayer !== void 0 && { isCustomLayer: userState.isCustomLayer },
43292
- ...userState.customLayerType !== void 0 && { customLayerType: userState.customLayerType }
43293
- };
43294
- }
43295
- /**
43296
- * Check if a layer ID belongs to a drawing library (Geoman, Mapbox GL Draw, etc.)
43297
- * @param layerId The layer ID to check
43298
- * @returns true if the layer is from a drawing library
43299
- */
43300
- isDrawnLayer(layerId) {
43301
- const drawnLayerPatterns = [
43302
- // Drawing libraries
43303
- /^gm[-_\s]/i,
43304
- // Geoman (gm-main-*, gm_*, Gm Temporary...)
43305
- /^gl-draw[-_]/i,
43306
- // Mapbox GL Draw
43307
- /^mapbox-gl-draw[-_]/i,
43308
- // Mapbox GL Draw alternative
43309
- /^terra-draw[-_]/i,
43310
- // Terra Draw
43311
- /^maplibre-gl-draw[-_]/i,
43312
- // MapLibre GL Draw
43313
- /^draw[-_]layer/i,
43314
- // Generic draw layers
43315
- // maplibre-gl-components internal layers
43316
- /^measure-/i,
43317
- // MeasureControl (measure-{id}-fill, measure-{id}-line)
43318
- /^pmtiles-source-/i,
43319
- // PMTilesLayerControl (managed via adapter)
43320
- /^stac-search-footprints/i
43321
- // StacSearchControl footprint layers
43322
- ];
43323
- return drawnLayerPatterns.some((pattern2) => pattern2.test(layerId));
43324
- }
43325
- /**
43326
- * Convert wildcard patterns (e.g., '*-temp-*', 'debug-*') to RegExp objects
43327
- */
43328
- wildcardPatternsToRegex(patterns) {
43329
- return patterns.map((pattern2) => {
43330
- const escaped = pattern2.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
43331
- const regexStr = escaped.replace(/\*/g, ".*");
43332
- return new RegExp(`^${regexStr}$`, "i");
43333
- });
43334
- }
43335
- /**
43336
- * Check if a layer matches any of the user-defined exclusion patterns
43337
- */
43338
- isExcludedByPattern(layerId) {
43339
- return this.excludeLayerPatterns.some((pattern2) => pattern2.test(layerId));
43340
- }
43341
- /**
43342
- * Create the main container element
43343
- */
43344
- createContainer() {
43345
- const container = document.createElement("div");
43346
- container.className = "maplibregl-ctrl maplibregl-ctrl-group maplibregl-ctrl-layer-control";
43347
- return container;
43348
- }
43349
- /**
43350
- * Create the toggle button
43351
- */
43352
- createToggleButton() {
43353
- const button = document.createElement("button");
43354
- button.type = "button";
43355
- button.title = "Layer Control";
43356
- button.setAttribute("aria-label", "Layer Control");
43357
- const icon = document.createElement("span");
43358
- icon.className = "layer-control-icon";
43359
- icon.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 3 3 8.25 12 13.5 21 8.25 12 3"></polygon><polyline points="3 12.75 12 18 21 12.75"></polyline><polyline points="3 17.25 12 22 21 17.25"></polyline></svg>';
43360
- button.appendChild(icon);
43361
- return button;
43362
- }
43363
- /**
43364
- * Create the panel element
43365
- */
43366
- createPanel() {
43367
- const panel = document.createElement("div");
43368
- panel.className = "layer-control-panel";
43369
- panel.style.width = `${this.state.panelWidth}px`;
43370
- panel.style.maxHeight = `${this.maxPanelHeight}px`;
43371
- if (!this.state.collapsed) {
43372
- panel.classList.add("expanded");
43373
- }
43374
- const header = this.createPanelHeader();
43375
- panel.appendChild(header);
43376
- const actionButtons = this.createActionButtons();
43377
- panel.appendChild(actionButtons);
43378
- return panel;
43379
- }
43380
- /**
43381
- * Detect which corner the control is positioned in
43382
- * @returns The position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
43383
- */
43384
- getControlPosition() {
43385
- const parent = this.container.parentElement;
43386
- if (!parent) return "top-right";
43387
- if (parent.classList.contains("maplibregl-ctrl-top-left")) return "top-left";
43388
- if (parent.classList.contains("maplibregl-ctrl-top-right")) return "top-right";
43389
- if (parent.classList.contains("maplibregl-ctrl-bottom-left")) return "bottom-left";
43390
- if (parent.classList.contains("maplibregl-ctrl-bottom-right")) return "bottom-right";
43391
- return "top-right";
43392
- }
43393
- /**
43394
- * Update the panel position based on button location and control corner
43395
- * Positions the panel next to the button, expanding in the appropriate direction
43396
- */
43397
- updatePanelPosition() {
43398
- if (!this.button || !this.panel || !this.mapContainer) return;
43399
- const buttonRect = this.button.getBoundingClientRect();
43400
- const mapRect = this.mapContainer.getBoundingClientRect();
43401
- const position = this.getControlPosition();
43402
- const buttonTop = buttonRect.top - mapRect.top;
43403
- const buttonBottom = mapRect.bottom - buttonRect.bottom;
43404
- const buttonLeft = buttonRect.left - mapRect.left;
43405
- const buttonRight = mapRect.right - buttonRect.right;
43406
- const panelGap = 5;
43407
- this.panel.style.top = "";
43408
- this.panel.style.bottom = "";
43409
- this.panel.style.left = "";
43410
- this.panel.style.right = "";
43411
- switch (position) {
43412
- case "top-left":
43413
- this.panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;
43414
- this.panel.style.left = `${buttonLeft}px`;
43415
- break;
43416
- case "top-right":
43417
- this.panel.style.top = `${buttonTop + buttonRect.height + panelGap}px`;
43418
- this.panel.style.right = `${buttonRight}px`;
43419
- break;
43420
- case "bottom-left":
43421
- this.panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;
43422
- this.panel.style.left = `${buttonLeft}px`;
43423
- break;
43424
- case "bottom-right":
43425
- this.panel.style.bottom = `${buttonBottom + buttonRect.height + panelGap}px`;
43426
- this.panel.style.right = `${buttonRight}px`;
43427
- break;
43428
- }
43429
- }
43430
- /**
43431
- * Create action buttons for Show All / Hide All
43432
- */
43433
- createActionButtons() {
43434
- const container = document.createElement("div");
43435
- container.className = "layer-control-actions";
43436
- const showAllBtn = document.createElement("button");
43437
- showAllBtn.type = "button";
43438
- showAllBtn.className = "layer-control-action-btn";
43439
- showAllBtn.textContent = "Show All";
43440
- showAllBtn.title = "Show all layers";
43441
- showAllBtn.addEventListener("click", () => this.setAllLayersVisibility(true));
43442
- const hideAllBtn = document.createElement("button");
43443
- hideAllBtn.type = "button";
43444
- hideAllBtn.className = "layer-control-action-btn";
43445
- hideAllBtn.textContent = "Hide All";
43446
- hideAllBtn.title = "Hide all layers";
43447
- hideAllBtn.addEventListener("click", () => this.setAllLayersVisibility(false));
43448
- container.appendChild(showAllBtn);
43449
- container.appendChild(hideAllBtn);
43450
- return container;
43451
- }
43452
- /**
43453
- * Set visibility of all layers
43454
- */
43455
- setAllLayersVisibility(visible) {
43456
- Object.keys(this.state.layerStates).forEach((layerId) => {
43457
- this.toggleLayerVisibility(layerId, visible);
43458
- const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
43459
- if (itemEl) {
43460
- const checkbox = itemEl.querySelector(".layer-control-checkbox");
43461
- if (checkbox) {
43462
- checkbox.checked = visible;
43463
- checkbox.indeterminate = false;
43464
- }
43465
- }
43466
- });
43467
- }
43468
- /**
43469
- * Create the panel header with title and width control
43470
- */
43471
- createPanelHeader() {
43472
- const header = document.createElement("div");
43473
- header.className = "layer-control-panel-header";
43474
- const title2 = document.createElement("span");
43475
- title2.className = "layer-control-panel-title";
43476
- title2.textContent = "Layers";
43477
- header.appendChild(title2);
43478
- const widthControl = this.createWidthControl();
43479
- header.appendChild(widthControl);
43480
- return header;
43481
- }
43482
- /**
43483
- * Create the width control slider
43484
- */
43485
- createWidthControl() {
43486
- const widthControl = document.createElement("label");
43487
- widthControl.className = "layer-control-width-control";
43488
- widthControl.title = "Adjust layer panel width";
43489
- const widthLabel = document.createElement("span");
43490
- widthLabel.textContent = "Width";
43491
- widthControl.appendChild(widthLabel);
43492
- const widthSlider = document.createElement("div");
43493
- widthSlider.className = "layer-control-width-slider";
43494
- widthSlider.setAttribute("role", "slider");
43495
- widthSlider.setAttribute("aria-valuemin", String(this.minPanelWidth));
43496
- widthSlider.setAttribute("aria-valuemax", String(this.maxPanelWidth));
43497
- widthSlider.setAttribute("aria-valuenow", String(this.state.panelWidth));
43498
- widthSlider.setAttribute("aria-valuestep", "10");
43499
- widthSlider.setAttribute("aria-label", "Layer panel width");
43500
- widthSlider.tabIndex = 0;
43501
- const widthTrack = document.createElement("div");
43502
- widthTrack.className = "layer-control-width-track";
43503
- const widthThumb = document.createElement("div");
43504
- widthThumb.className = "layer-control-width-thumb";
43505
- widthSlider.appendChild(widthTrack);
43506
- widthSlider.appendChild(widthThumb);
43507
- this.widthSliderEl = widthSlider;
43508
- this.widthThumbEl = widthThumb;
43509
- const widthValue = document.createElement("span");
43510
- widthValue.className = "layer-control-width-value";
43511
- this.widthValueEl = widthValue;
43512
- widthControl.appendChild(widthSlider);
43513
- widthControl.appendChild(widthValue);
43514
- this.updateWidthDisplay();
43515
- this.setupWidthSliderEvents(widthSlider);
43516
- return widthControl;
43517
- }
43518
- /**
43519
- * Setup event listeners for width slider
43520
- */
43521
- setupWidthSliderEvents(widthSlider) {
43522
- widthSlider.addEventListener("pointerdown", (event) => {
43523
- event.preventDefault();
43524
- const rect = widthSlider.getBoundingClientRect();
43525
- this.widthDragRectWidth = rect.width || 1;
43526
- this.widthDragStartX = event.clientX;
43527
- this.widthDragStartWidth = this.state.panelWidth;
43528
- this.isWidthSliderActive = true;
43529
- widthSlider.setPointerCapture(event.pointerId);
43530
- this.updateWidthFromPointer(event, true);
43531
- });
43532
- widthSlider.addEventListener("pointermove", (event) => {
43533
- if (!this.isWidthSliderActive) return;
43534
- this.updateWidthFromPointer(event);
43535
- });
43536
- const endPointerDrag = (event) => {
43537
- if (!this.isWidthSliderActive) return;
43538
- if (event.pointerId !== void 0) {
43539
- try {
43540
- widthSlider.releasePointerCapture(event.pointerId);
43541
- } catch (error) {
43542
- }
43543
- }
43544
- this.isWidthSliderActive = false;
43545
- this.widthDragRectWidth = null;
43546
- this.widthDragStartX = null;
43547
- this.widthDragStartWidth = null;
43548
- this.updateWidthDisplay();
43549
- };
43550
- widthSlider.addEventListener("pointerup", endPointerDrag);
43551
- widthSlider.addEventListener("pointercancel", endPointerDrag);
43552
- widthSlider.addEventListener("lostpointercapture", endPointerDrag);
43553
- widthSlider.addEventListener("keydown", (event) => {
43554
- let handled = true;
43555
- const step = event.shiftKey ? 20 : 10;
43556
- switch (event.key) {
43557
- case "ArrowLeft":
43558
- case "ArrowDown":
43559
- this.applyPanelWidth(this.state.panelWidth - step, true);
43560
- break;
43561
- case "ArrowRight":
43562
- case "ArrowUp":
43563
- this.applyPanelWidth(this.state.panelWidth + step, true);
43564
- break;
43565
- case "Home":
43566
- this.applyPanelWidth(this.minPanelWidth, true);
43567
- break;
43568
- case "End":
43569
- this.applyPanelWidth(this.maxPanelWidth, true);
43570
- break;
43571
- case "PageUp":
43572
- this.applyPanelWidth(this.state.panelWidth + 50, true);
43573
- break;
43574
- case "PageDown":
43575
- this.applyPanelWidth(this.state.panelWidth - 50, true);
43576
- break;
43577
- default:
43578
- handled = false;
43579
- }
43580
- if (handled) {
43581
- event.preventDefault();
43582
- this.updateWidthDisplay();
43583
- }
43584
- });
43585
- }
43586
- /**
43587
- * Update panel width from pointer event
43588
- */
43589
- updateWidthFromPointer(event, resetBaseline = false) {
43590
- if (!this.widthSliderEl) return;
43591
- const sliderWidth = this.widthDragRectWidth || this.widthSliderEl.getBoundingClientRect().width || 1;
43592
- const widthRange = this.maxPanelWidth - this.minPanelWidth;
43593
- let width;
43594
- if (resetBaseline) {
43595
- const rect = this.widthSliderEl.getBoundingClientRect();
43596
- const relative = rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0;
43597
- const clampedRatio = Math.min(1, Math.max(0, relative));
43598
- width = this.minPanelWidth + clampedRatio * widthRange;
43599
- this.widthDragStartWidth = width;
43600
- this.widthDragStartX = event.clientX;
43601
- } else {
43602
- const delta = event.clientX - (this.widthDragStartX || event.clientX);
43603
- width = (this.widthDragStartWidth || this.state.panelWidth) + delta / sliderWidth * widthRange;
43604
- }
43605
- this.applyPanelWidth(width, this.isWidthSliderActive);
43606
- }
43607
- /**
43608
- * Apply panel width (clamped to min/max)
43609
- */
43610
- applyPanelWidth(width, immediate = false) {
43611
- const clamped = Math.round(Math.min(this.maxPanelWidth, Math.max(this.minPanelWidth, width)));
43612
- const applyWidth = () => {
43613
- this.state.panelWidth = clamped;
43614
- const px = `${clamped}px`;
43615
- this.panel.style.width = px;
43616
- this.updateWidthDisplay();
43617
- };
43618
- if (immediate) {
43619
- applyWidth();
43620
- return;
43621
- }
43622
- if (this.widthFrame) {
43623
- cancelAnimationFrame(this.widthFrame);
43624
- }
43625
- this.widthFrame = requestAnimationFrame(() => {
43626
- applyWidth();
43627
- this.widthFrame = null;
43628
- });
43629
- }
43630
- /**
43631
- * Update width display (value label and thumb position)
43632
- */
43633
- updateWidthDisplay() {
43634
- if (this.widthValueEl) {
43635
- this.widthValueEl.textContent = `${this.state.panelWidth}px`;
43636
- }
43637
- if (this.widthSliderEl) {
43638
- this.widthSliderEl.setAttribute("aria-valuenow", String(this.state.panelWidth));
43639
- const ratio = (this.state.panelWidth - this.minPanelWidth) / (this.maxPanelWidth - this.minPanelWidth || 1);
43640
- if (this.widthThumbEl) {
43641
- const sliderWidth = this.widthSliderEl.clientWidth;
43642
- if (sliderWidth === 0) {
43643
- requestAnimationFrame(() => this.updateWidthDisplay());
43644
- return;
43645
- }
43646
- const thumbWidth = this.widthThumbEl.offsetWidth || 14;
43647
- const padding = 16;
43648
- const available = Math.max(0, sliderWidth - padding - thumbWidth);
43649
- const clampedRatio = Math.min(1, Math.max(0, ratio));
43650
- const leftPx = 8 + available * clampedRatio;
43651
- this.widthThumbEl.style.left = `${leftPx}px`;
43652
- }
43653
- }
43654
- }
43655
- /**
43656
- * Setup main event listeners
43657
- */
43658
- setupEventListeners() {
43659
- this.button.addEventListener("click", () => this.toggle());
43660
- document.addEventListener("click", (e2) => {
43661
- const target = e2.target;
43662
- if (this.contextMenuEl && this.state.contextMenu.visible) {
43663
- if (!this.contextMenuEl.contains(target)) {
43664
- this.hideContextMenu();
43665
- }
43666
- }
43667
- if (!this.container.contains(target) && !this.panel.contains(target)) {
43668
- this.collapse();
43669
- }
43670
- });
43671
- this.resizeHandler = () => {
43672
- if (!this.state.collapsed) {
43673
- this.updatePanelPosition();
43674
- }
43675
- };
43676
- window.addEventListener("resize", this.resizeHandler);
43677
- this.mapResizeHandler = () => {
43678
- if (!this.state.collapsed) {
43679
- this.updatePanelPosition();
43680
- }
43681
- };
43682
- this.map.on("resize", this.mapResizeHandler);
43683
- this.setupLayerChangeListeners();
43684
- }
43685
- /**
43686
- * Setup listeners for map layer changes
43687
- */
43688
- setupLayerChangeListeners() {
43689
- this.map.on("styledata", () => {
43690
- setTimeout(() => {
43691
- this.updateLayerStatesFromMap();
43692
- this.checkForNewLayers();
43693
- }, 100);
43694
- });
43695
- this.map.on("data", (e2) => {
43696
- if (e2.sourceDataType === "content") {
43697
- setTimeout(() => {
43698
- this.updateLayerStatesFromMap();
43699
- this.checkForNewLayers();
43700
- }, 100);
43701
- }
43702
- });
43703
- this.map.on("sourcedata", (e2) => {
43704
- if (e2.sourceDataType === "metadata") {
43705
- setTimeout(() => {
43706
- this.checkForNewLayers();
43707
- }, 150);
43708
- }
43709
- });
43710
- if (this.customLayerRegistry) {
43711
- this.customLayerUnsubscribe = this.customLayerRegistry.onChange((event, layerId) => {
43712
- if (event === "add" && layerId) {
43713
- this.removedCustomLayerIds.delete(layerId);
43714
- }
43715
- setTimeout(() => this.checkForNewLayers(), 100);
43716
- });
43717
- }
43718
- }
43719
- /**
43720
- * Toggle panel expanded/collapsed state
43721
- */
43722
- toggle() {
43723
- if (this.state.collapsed) {
43724
- this.expand();
43725
- } else {
43726
- this.collapse();
43727
- }
43728
- }
43729
- /**
43730
- * Expand the panel
43731
- */
43732
- expand() {
43733
- this.state.collapsed = false;
43734
- this.panel.classList.add("expanded");
43735
- this.updatePanelPosition();
43736
- }
43737
- /**
43738
- * Collapse the panel
43739
- */
43740
- collapse() {
43741
- this.state.collapsed = true;
43742
- this.panel.classList.remove("expanded");
43743
- }
43744
- /**
43745
- * Build layer items (called initially and when layers change)
43746
- */
43747
- buildLayerItems() {
43748
- const existingItems = this.panel.querySelectorAll(".layer-control-item");
43749
- existingItems.forEach((item) => item.remove());
43750
- this.styleEditors.clear();
43751
- Object.entries(this.state.layerStates).forEach(([layerId, state]) => {
43752
- if (this.targetLayers.length === 0 || this.targetLayers.includes(layerId)) {
43753
- this.addLayerItem(layerId, state);
43754
- }
43755
- });
43756
- }
43757
- /**
43758
- * Add a single layer item to the panel
43759
- */
43760
- addLayerItem(layerId, state) {
43761
- const item = document.createElement("div");
43762
- item.className = "layer-control-item";
43763
- item.setAttribute("data-layer-id", layerId);
43764
- const row = document.createElement("div");
43765
- row.className = "layer-control-row";
43766
- if (this.enableDragAndDrop) {
43767
- if (layerId === "Background") {
43768
- const disabledHandle = this.createDisabledDragHandle();
43769
- row.appendChild(disabledHandle);
43770
- } else {
43771
- const dragHandle = this.createDragHandle(layerId);
43772
- row.appendChild(dragHandle);
43773
- }
43774
- }
43775
- const checkbox = document.createElement("input");
43776
- checkbox.type = "checkbox";
43777
- checkbox.className = "layer-control-checkbox";
43778
- checkbox.checked = state.visible;
43779
- checkbox.addEventListener("change", () => {
43780
- this.toggleLayerVisibility(layerId, checkbox.checked);
43781
- });
43782
- const displayName = this.state.customLayerNames.get(layerId) || state.name || layerId;
43783
- const name = document.createElement("span");
43784
- name.className = "layer-control-name";
43785
- name.textContent = displayName;
43786
- name.title = displayName;
43787
- row.appendChild(checkbox);
43788
- if (this.showLayerSymbol) {
43789
- if (layerId === "Background") {
43790
- const symbol = this.createBackgroundGroupSymbol();
43791
- row.appendChild(symbol);
43792
- } else {
43793
- const symbol = this.createLayerSymbol(layerId);
43794
- if (symbol) {
43795
- row.appendChild(symbol);
43796
- }
43797
- }
43798
- }
43799
- row.appendChild(name);
43800
- if (this.showOpacitySlider) {
43801
- const opacity = document.createElement("input");
43802
- opacity.type = "range";
43803
- opacity.className = "layer-control-opacity";
43804
- opacity.min = "0";
43805
- opacity.max = "1";
43806
- opacity.step = "0.01";
43807
- opacity.value = String(state.opacity);
43808
- opacity.title = `Opacity: ${Math.round(state.opacity * 100)}%`;
43809
- opacity.addEventListener("mousedown", () => {
43810
- this.state.userInteractingWithSlider = true;
43811
- });
43812
- opacity.addEventListener("mouseup", () => {
43813
- this.state.userInteractingWithSlider = false;
43814
- });
43815
- opacity.addEventListener("input", () => {
43816
- this.changeLayerOpacity(layerId, parseFloat(opacity.value));
43817
- opacity.title = `Opacity: ${Math.round(parseFloat(opacity.value) * 100)}%`;
43818
- });
43819
- row.appendChild(opacity);
43820
- }
43821
- if (this.showStyleEditor) {
43822
- if (layerId === "Background") {
43823
- const legendButton = this.createBackgroundLegendButton();
43824
- row.appendChild(legendButton);
43825
- } else {
43826
- const styleButton = this.createStyleButton(layerId);
43827
- if (styleButton) {
43828
- row.appendChild(styleButton);
43829
- }
43830
- }
43831
- }
43832
- item.appendChild(row);
43833
- if (this.enableContextMenu && layerId !== "Background") {
43834
- row.addEventListener("contextmenu", (e2) => {
43835
- e2.preventDefault();
43836
- e2.stopPropagation();
43837
- this.showContextMenu(layerId, e2.clientX, e2.clientY);
43838
- });
43839
- }
43840
- this.panel.appendChild(item);
43841
- }
43842
- /**
43843
- * Create a symbol element for a layer
43844
- * @param layerId The layer ID
43845
- * @returns The symbol HTML element, or null if layer not found
43846
- */
43847
- createLayerSymbol(layerId) {
43848
- const layerState = this.state.layerStates[layerId];
43849
- if (layerState == null ? void 0 : layerState.isCustomLayer) {
43850
- const symbolType = layerState.customLayerType || "custom-raster";
43851
- const color2 = "#4a90d9";
43852
- const svgMarkup2 = createLayerSymbolSVG(symbolType, color2);
43853
- const symbolContainer2 = document.createElement("span");
43854
- symbolContainer2.className = "layer-control-symbol";
43855
- symbolContainer2.innerHTML = svgMarkup2;
43856
- symbolContainer2.title = `Layer type: ${symbolType}`;
43857
- return symbolContainer2;
43858
- }
43859
- const layer = this.map.getLayer(layerId);
43860
- if (!layer) return null;
43861
- const layerType = layer.type;
43862
- const color = getLayerColor(this.map, layerId, layerType);
43863
- const svgMarkup = createLayerSymbolSVG(layerType, color);
43864
- const symbolContainer = document.createElement("span");
43865
- symbolContainer.className = "layer-control-symbol";
43866
- symbolContainer.innerHTML = svgMarkup;
43867
- symbolContainer.title = `Layer type: ${layerType}`;
43868
- return symbolContainer;
43869
- }
43870
- /**
43871
- * Create a symbol element for a background layer
43872
- * @param layer The layer specification
43873
- * @returns The symbol HTML element
43874
- */
43875
- createBackgroundLayerSymbol(layer) {
43876
- const color = getLayerColorFromSpec(layer);
43877
- const svgMarkup = createLayerSymbolSVG(layer.type, color, { size: 14 });
43878
- const symbolContainer = document.createElement("span");
43879
- symbolContainer.className = "background-legend-layer-symbol";
43880
- symbolContainer.innerHTML = svgMarkup;
43881
- symbolContainer.title = `Layer type: ${layer.type}`;
43882
- return symbolContainer;
43883
- }
43884
- /**
43885
- * Create a symbol element for the Background layer group
43886
- * Shows a stacked layers icon to represent multiple background layers
43887
- * @returns The symbol HTML element
43888
- */
43889
- createBackgroundGroupSymbol() {
43890
- const svgMarkup = createBackgroundGroupSymbolSVG(16);
43891
- const symbolContainer = document.createElement("span");
43892
- symbolContainer.className = "layer-control-symbol";
43893
- symbolContainer.innerHTML = svgMarkup;
43894
- symbolContainer.title = "Background layers";
43895
- return symbolContainer;
43896
- }
43897
- /**
43898
- * Toggle layer visibility
43899
- */
43900
- toggleLayerVisibility(layerId, visible) {
43901
- var _a3;
43902
- if (layerId === "Background") {
43903
- this.toggleBackgroundVisibility(visible);
43904
- return;
43905
- }
43906
- this.state.isStyleOperationInProgress = true;
43907
- if (this.state.layerStates[layerId]) {
43908
- this.state.layerStates[layerId].visible = visible;
43909
- }
43910
- if ((_a3 = this.customLayerRegistry) == null ? void 0 : _a3.setVisibility(layerId, visible)) {
43911
- setTimeout(() => {
43912
- this.state.isStyleOperationInProgress = false;
43913
- }, 200);
43914
- return;
43915
- }
43916
- this.map.setLayoutProperty(layerId, "visibility", visible ? "visible" : "none");
43917
- setTimeout(() => {
43918
- this.state.isStyleOperationInProgress = false;
43919
- }, 200);
43920
- }
43921
- /**
43922
- * Change layer opacity
43923
- */
43924
- changeLayerOpacity(layerId, opacity) {
43925
- var _a3;
43926
- if (layerId === "Background") {
43927
- this.changeBackgroundOpacity(opacity);
43928
- return;
43929
- }
43930
- this.state.isStyleOperationInProgress = true;
43931
- if (this.state.layerStates[layerId]) {
43932
- this.state.layerStates[layerId].opacity = opacity;
43933
- }
43934
- if ((_a3 = this.customLayerRegistry) == null ? void 0 : _a3.setOpacity(layerId, opacity)) {
43935
- setTimeout(() => {
43936
- this.state.isStyleOperationInProgress = false;
43937
- }, 200);
43938
- return;
43939
- }
43940
- const layerType = getLayerType(this.map, layerId);
43941
- if (layerType) {
43942
- setLayerOpacity(this.map, layerId, layerType, opacity);
43943
- }
43944
- setTimeout(() => {
43945
- this.state.isStyleOperationInProgress = false;
43946
- }, 200);
43947
- }
43948
- /**
43949
- * Check if a layer is a user-added layer (vs basemap layer)
43950
- * Used primarily for the background legend to determine which layers are background
43951
- */
43952
- isUserAddedLayer(layerId) {
43953
- if (this.state.layerStates[layerId] !== void 0 && layerId !== "Background") {
43954
- return true;
43955
- }
43956
- if (this.basemapLayerIds !== null && this.basemapLayerIds.size > 0) {
43957
- return !this.basemapLayerIds.has(layerId);
43958
- }
43959
- if (this.initialLayerIds !== null && !this.initialLayerIds.has(layerId)) {
43960
- return true;
43961
- }
43962
- const layer = this.map.getLayer(layerId);
43963
- if (layer) {
43964
- const sourceId = layer.source;
43965
- if (sourceId) {
43966
- const userAddedSourceIds = this.detectUserAddedSources();
43967
- return userAddedSourceIds.has(sourceId);
43968
- }
43969
- }
43970
- return false;
43971
- }
43972
- /**
43973
- * Toggle visibility for all background layers (basemap layers)
43974
- */
43975
- toggleBackgroundVisibility(visible) {
43976
- if (this.state.layerStates["Background"]) {
43977
- this.state.layerStates["Background"].visible = visible;
43978
- }
43979
- const styleLayers = this.map.getStyle().layers || [];
43980
- styleLayers.forEach((layer) => {
43981
- if (!this.isUserAddedLayer(layer.id)) {
43982
- this.state.backgroundLayerVisibility.set(layer.id, visible);
43983
- this.map.setLayoutProperty(layer.id, "visibility", visible ? "visible" : "none");
43984
- }
43985
- });
43986
- if (this.state.backgroundLegendOpen) {
43987
- const legendPanel = this.panel.querySelector(".layer-control-background-legend");
43988
- if (legendPanel) {
43989
- const checkboxes = legendPanel.querySelectorAll(".background-legend-checkbox");
43990
- checkboxes.forEach((checkbox) => {
43991
- checkbox.checked = visible;
43992
- });
43993
- }
43994
- }
43995
- }
43996
- /**
43997
- * Change opacity for all background layers (basemap layers)
43998
- */
43999
- changeBackgroundOpacity(opacity) {
44000
- if (this.state.layerStates["Background"]) {
44001
- this.state.layerStates["Background"].opacity = opacity;
44002
- }
44003
- const styleLayers = this.map.getStyle().layers || [];
44004
- styleLayers.forEach((styleLayer) => {
44005
- if (!this.isUserAddedLayer(styleLayer.id)) {
44006
- const layerType = getLayerType(this.map, styleLayer.id);
44007
- if (layerType) {
44008
- setLayerOpacity(this.map, styleLayer.id, layerType, opacity);
44009
- }
44010
- }
44011
- });
44012
- }
44013
- // ===== Background Legend Methods =====
44014
- /**
44015
- * Create legend button for Background layer
44016
- */
44017
- createBackgroundLegendButton() {
44018
- const button = document.createElement("button");
44019
- button.className = "layer-control-style-button layer-control-background-legend-button";
44020
- button.innerHTML = "&#9881;";
44021
- button.title = "Show background layer details";
44022
- button.setAttribute("aria-label", "Show background layer visibility controls");
44023
- button.setAttribute("aria-expanded", String(this.state.backgroundLegendOpen));
44024
- button.addEventListener("click", (e2) => {
44025
- e2.stopPropagation();
44026
- this.toggleBackgroundLegend();
44027
- });
44028
- return button;
44029
- }
44030
- /**
44031
- * Toggle background legend panel visibility
44032
- */
44033
- toggleBackgroundLegend() {
44034
- if (this.state.backgroundLegendOpen) {
44035
- this.closeBackgroundLegend();
44036
- } else {
44037
- this.openBackgroundLegend();
44038
- }
44039
- }
44040
- /**
44041
- * Open background legend panel
44042
- */
44043
- openBackgroundLegend() {
44044
- if (this.state.activeStyleEditor) {
44045
- this.closeStyleEditor(this.state.activeStyleEditor);
44046
- }
44047
- const itemEl = this.panel.querySelector('[data-layer-id="Background"]');
44048
- if (!itemEl) return;
44049
- let legendPanel = itemEl.querySelector(".layer-control-background-legend");
44050
- if (legendPanel) {
44051
- const layerList = legendPanel.querySelector(".background-legend-layer-list");
44052
- if (layerList) {
44053
- this.populateBackgroundLayerList(layerList);
44054
- }
44055
- } else {
44056
- legendPanel = this.createBackgroundLegendPanel();
44057
- itemEl.appendChild(legendPanel);
44058
- }
44059
- this.state.backgroundLegendOpen = true;
44060
- const button = itemEl.querySelector(".layer-control-background-legend-button");
44061
- if (button) {
44062
- button.setAttribute("aria-expanded", "true");
44063
- button.classList.add("active");
44064
- }
44065
- setTimeout(() => {
44066
- legendPanel == null ? void 0 : legendPanel.scrollIntoView({ behavior: "smooth", block: "nearest" });
44067
- }, 50);
44068
- }
44069
- /**
44070
- * Close background legend panel
44071
- */
44072
- closeBackgroundLegend() {
44073
- const itemEl = this.panel.querySelector('[data-layer-id="Background"]');
44074
- if (!itemEl) return;
44075
- const legendPanel = itemEl.querySelector(".layer-control-background-legend");
44076
- if (legendPanel) {
44077
- legendPanel.remove();
44078
- }
44079
- this.state.backgroundLegendOpen = false;
44080
- const button = itemEl.querySelector(".layer-control-background-legend-button");
44081
- if (button) {
44082
- button.setAttribute("aria-expanded", "false");
44083
- button.classList.remove("active");
44084
- }
44085
- }
44086
- /**
44087
- * Create the background legend panel with individual layer controls
44088
- */
44089
- createBackgroundLegendPanel() {
44090
- const panel = document.createElement("div");
44091
- panel.className = "layer-control-background-legend";
44092
- const header = document.createElement("div");
44093
- header.className = "background-legend-header";
44094
- const title2 = document.createElement("span");
44095
- title2.className = "background-legend-title";
44096
- title2.textContent = "Background Layers";
44097
- const closeBtn = document.createElement("button");
44098
- closeBtn.className = "background-legend-close";
44099
- closeBtn.innerHTML = "&times;";
44100
- closeBtn.title = "Close";
44101
- closeBtn.addEventListener("click", (e2) => {
44102
- e2.stopPropagation();
44103
- this.closeBackgroundLegend();
44104
- });
44105
- header.appendChild(title2);
44106
- header.appendChild(closeBtn);
44107
- const actionsRow = document.createElement("div");
44108
- actionsRow.className = "background-legend-actions";
44109
- const showAllBtn = document.createElement("button");
44110
- showAllBtn.className = "background-legend-action-btn";
44111
- showAllBtn.textContent = "Show All";
44112
- showAllBtn.addEventListener("click", () => this.setAllBackgroundLayersVisibility(true));
44113
- const hideAllBtn = document.createElement("button");
44114
- hideAllBtn.className = "background-legend-action-btn";
44115
- hideAllBtn.textContent = "Hide All";
44116
- hideAllBtn.addEventListener("click", () => this.setAllBackgroundLayersVisibility(false));
44117
- actionsRow.appendChild(showAllBtn);
44118
- actionsRow.appendChild(hideAllBtn);
44119
- const filterRow = document.createElement("div");
44120
- filterRow.className = "background-legend-filter";
44121
- const filterCheckbox = document.createElement("input");
44122
- filterCheckbox.type = "checkbox";
44123
- filterCheckbox.className = "background-legend-filter-checkbox";
44124
- filterCheckbox.id = "background-legend-only-rendered";
44125
- filterCheckbox.checked = this.state.onlyRenderedFilter;
44126
- filterCheckbox.addEventListener("change", () => {
44127
- this.state.onlyRenderedFilter = filterCheckbox.checked;
44128
- const layerList2 = panel.querySelector(".background-legend-layer-list");
44129
- if (layerList2) {
44130
- this.populateBackgroundLayerList(layerList2);
44131
- }
44132
- });
44133
- const filterLabel = document.createElement("label");
44134
- filterLabel.className = "background-legend-filter-label";
44135
- filterLabel.htmlFor = "background-legend-only-rendered";
44136
- filterLabel.textContent = "Only rendered";
44137
- filterRow.appendChild(filterCheckbox);
44138
- filterRow.appendChild(filterLabel);
44139
- const layerList = document.createElement("div");
44140
- layerList.className = "background-legend-layer-list";
44141
- this.populateBackgroundLayerList(layerList);
44142
- panel.appendChild(header);
44143
- panel.appendChild(actionsRow);
44144
- panel.appendChild(filterRow);
44145
- panel.appendChild(layerList);
44146
- return panel;
44147
- }
44148
- /**
44149
- * Check if a layer is currently rendered in the map viewport
44150
- */
44151
- isLayerRendered(layerId) {
44152
- try {
44153
- const layer = this.map.getLayer(layerId);
44154
- if (!layer) return false;
44155
- const visibility = this.map.getLayoutProperty(layerId, "visibility");
44156
- if (visibility === "none") return false;
44157
- if (layer.type === "raster" || layer.type === "hillshade") {
44158
- return true;
44159
- }
44160
- if (layer.type === "background") {
44161
- return true;
44162
- }
44163
- const features = this.map.queryRenderedFeatures({ layers: [layerId] });
44164
- return features.length > 0;
44165
- } catch (error) {
44166
- return true;
44167
- }
44168
- }
44169
- /**
44170
- * Populate the background layer list with individual layers
44171
- */
44172
- populateBackgroundLayerList(container) {
44173
- container.innerHTML = "";
44174
- const styleLayers = this.map.getStyle().layers || [];
44175
- styleLayers.forEach((layer) => {
44176
- if (!this.isUserAddedLayer(layer.id)) {
44177
- if (this.excludeDrawnLayers && this.isDrawnLayer(layer.id)) {
44178
- return;
44179
- }
44180
- if (this.isExcludedByPattern(layer.id)) {
44181
- return;
44182
- }
44183
- if (this.state.onlyRenderedFilter && !this.isLayerRendered(layer.id)) {
44184
- return;
44185
- }
44186
- const layerRow = document.createElement("div");
44187
- layerRow.className = "background-legend-layer-row";
44188
- layerRow.setAttribute("data-background-layer-id", layer.id);
44189
- const checkbox = document.createElement("input");
44190
- checkbox.type = "checkbox";
44191
- checkbox.className = "background-legend-checkbox";
44192
- const visibility = this.map.getLayoutProperty(layer.id, "visibility");
44193
- const isVisible = visibility !== "none";
44194
- checkbox.checked = isVisible;
44195
- this.state.backgroundLayerVisibility.set(layer.id, isVisible);
44196
- checkbox.addEventListener("change", () => {
44197
- this.toggleIndividualBackgroundLayer(layer.id, checkbox.checked);
44198
- });
44199
- const name = document.createElement("span");
44200
- name.className = "background-legend-layer-name";
44201
- name.textContent = this.generateFriendlyName(layer.id);
44202
- name.title = layer.id;
44203
- const typeIndicator = document.createElement("span");
44204
- typeIndicator.className = "background-legend-layer-type";
44205
- typeIndicator.textContent = layer.type;
44206
- layerRow.appendChild(checkbox);
44207
- if (this.showLayerSymbol) {
44208
- const symbol = this.createBackgroundLayerSymbol(layer);
44209
- if (symbol) {
44210
- layerRow.appendChild(symbol);
44211
- }
44212
- }
44213
- layerRow.appendChild(name);
44214
- layerRow.appendChild(typeIndicator);
44215
- container.appendChild(layerRow);
44216
- }
44217
- });
44218
- if (container.children.length === 0) {
44219
- const emptyMsg = document.createElement("p");
44220
- emptyMsg.className = "background-legend-empty";
44221
- emptyMsg.textContent = this.state.onlyRenderedFilter ? "No rendered layers in current view." : "No background layers found.";
44222
- container.appendChild(emptyMsg);
44223
- }
44224
- }
44225
- /**
44226
- * Toggle visibility of an individual background layer
44227
- */
44228
- toggleIndividualBackgroundLayer(layerId, visible) {
44229
- this.state.backgroundLayerVisibility.set(layerId, visible);
44230
- this.map.setLayoutProperty(layerId, "visibility", visible ? "visible" : "none");
44231
- this.updateBackgroundCheckboxState();
44232
- }
44233
- /**
44234
- * Set visibility for all background layers
44235
- */
44236
- setAllBackgroundLayersVisibility(visible) {
44237
- const styleLayers = this.map.getStyle().layers || [];
44238
- styleLayers.forEach((layer) => {
44239
- if (!this.isUserAddedLayer(layer.id)) {
44240
- this.state.backgroundLayerVisibility.set(layer.id, visible);
44241
- this.map.setLayoutProperty(layer.id, "visibility", visible ? "visible" : "none");
44242
- }
44243
- });
44244
- const legendPanel = this.panel.querySelector(".layer-control-background-legend");
44245
- if (legendPanel) {
44246
- const checkboxes = legendPanel.querySelectorAll(".background-legend-checkbox");
44247
- checkboxes.forEach((checkbox) => {
44248
- checkbox.checked = visible;
44249
- });
44250
- }
44251
- this.updateBackgroundCheckboxState();
44252
- }
44253
- /**
44254
- * Update the main Background checkbox based on individual layer states
44255
- */
44256
- updateBackgroundCheckboxState() {
44257
- const styleLayers = this.map.getStyle().layers || [];
44258
- let anyVisible = false;
44259
- let allVisible = true;
44260
- styleLayers.forEach((layer) => {
44261
- if (!this.isUserAddedLayer(layer.id)) {
44262
- const visible = this.state.backgroundLayerVisibility.get(layer.id);
44263
- if (visible === true) anyVisible = true;
44264
- if (visible === false) allVisible = false;
44265
- }
44266
- });
44267
- const backgroundItem = this.panel.querySelector('[data-layer-id="Background"]');
44268
- if (backgroundItem) {
44269
- const checkbox = backgroundItem.querySelector(".layer-control-checkbox");
44270
- if (checkbox) {
44271
- checkbox.checked = anyVisible;
44272
- checkbox.indeterminate = anyVisible && !allVisible;
44273
- }
44274
- }
44275
- if (this.state.layerStates["Background"]) {
44276
- this.state.layerStates["Background"].visible = anyVisible;
44277
- }
44278
- }
44279
- /**
44280
- * Create style button for a layer
44281
- */
44282
- createStyleButton(layerId) {
44283
- if (layerId === "Background") {
44284
- return null;
44285
- }
44286
- const layerState = this.state.layerStates[layerId];
44287
- const isCustomLayer = (layerState == null ? void 0 : layerState.isCustomLayer) === true;
44288
- const button = document.createElement("button");
44289
- button.className = "layer-control-style-button";
44290
- button.innerHTML = "&#9881;";
44291
- if (isCustomLayer) {
44292
- button.title = "Layer info (style editing not available)";
44293
- button.setAttribute("aria-label", `Layer info for ${layerId}`);
44294
- } else {
44295
- button.title = "Edit layer style";
44296
- button.setAttribute("aria-label", `Edit style for ${layerId}`);
44297
- }
44298
- button.addEventListener("click", (e2) => {
44299
- e2.stopPropagation();
44300
- this.toggleStyleEditor(layerId);
44301
- });
44302
- return button;
44303
- }
44304
- /**
44305
- * Toggle style editor for a layer
44306
- */
44307
- toggleStyleEditor(layerId) {
44308
- if (this.state.activeStyleEditor === layerId) {
44309
- this.closeStyleEditor(layerId);
44310
- return;
44311
- }
44312
- if (this.state.activeStyleEditor) {
44313
- this.closeStyleEditor(this.state.activeStyleEditor);
44314
- }
44315
- this.openStyleEditor(layerId);
44316
- }
44317
- /**
44318
- * Open style editor for a layer
44319
- */
44320
- openStyleEditor(layerId) {
44321
- var _a3;
44322
- const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
44323
- if (!itemEl) return;
44324
- const layerState = this.state.layerStates[layerId];
44325
- if (layerState == null ? void 0 : layerState.isCustomLayer) {
44326
- const nativeLayerIds = (_a3 = this.customLayerRegistry) == null ? void 0 : _a3.getNativeLayerIds(layerId);
44327
- if (nativeLayerIds && nativeLayerIds.length > 0) {
44328
- for (const nativeId of nativeLayerIds) {
44329
- if (!this.state.originalStyles.has(nativeId)) {
44330
- const nativeLayer = this.map.getLayer(nativeId);
44331
- if (nativeLayer) {
44332
- cacheOriginalLayerStyle(this.map, nativeId, this.state.originalStyles);
44333
- }
44334
- }
44335
- }
44336
- const editor3 = this.createNativeSubLayerStyleEditor(layerId, nativeLayerIds);
44337
- if (editor3) {
44338
- itemEl.appendChild(editor3);
44339
- this.styleEditors.set(layerId, editor3);
44340
- this.state.activeStyleEditor = layerId;
44341
- setTimeout(() => {
44342
- editor3.scrollIntoView({ behavior: "smooth", block: "nearest" });
44343
- }, 50);
44344
- return;
44345
- }
44346
- }
44347
- const editor2 = this.createCustomLayerInfoPanel(layerId);
44348
- itemEl.appendChild(editor2);
44349
- this.styleEditors.set(layerId, editor2);
44350
- this.state.activeStyleEditor = layerId;
44351
- setTimeout(() => {
44352
- editor2.scrollIntoView({ behavior: "smooth", block: "nearest" });
44353
- }, 50);
44354
- return;
44355
- }
44356
- if (!this.state.originalStyles.has(layerId)) {
44357
- const layer = this.map.getLayer(layerId);
44358
- if (layer) {
44359
- cacheOriginalLayerStyle(this.map, layerId, this.state.originalStyles);
44360
- }
44361
- }
44362
- const editor = this.createStyleEditor(layerId);
44363
- if (!editor) return;
44364
- itemEl.appendChild(editor);
44365
- this.styleEditors.set(layerId, editor);
44366
- this.state.activeStyleEditor = layerId;
44367
- setTimeout(() => {
44368
- editor.scrollIntoView({ behavior: "smooth", block: "nearest" });
44369
- }, 50);
44370
- }
44371
- /**
44372
- * Create info panel for custom layers (style editing not supported)
44373
- */
44374
- createCustomLayerInfoPanel(layerId) {
44375
- const editor = document.createElement("div");
44376
- editor.className = "layer-control-style-editor layer-control-custom-info";
44377
- const header = document.createElement("div");
44378
- header.className = "style-editor-header";
44379
- const title2 = document.createElement("span");
44380
- title2.className = "style-editor-title";
44381
- title2.textContent = "Layer Info";
44382
- const closeBtn = document.createElement("button");
44383
- closeBtn.className = "style-editor-close";
44384
- closeBtn.innerHTML = "&times;";
44385
- closeBtn.title = "Close";
44386
- closeBtn.addEventListener("click", (e2) => {
44387
- e2.stopPropagation();
44388
- this.closeStyleEditor(layerId);
44389
- });
44390
- header.appendChild(title2);
44391
- header.appendChild(closeBtn);
44392
- const content = document.createElement("div");
44393
- content.className = "style-editor-controls";
44394
- const infoText = document.createElement("p");
44395
- infoText.className = "layer-control-custom-info-text";
44396
- infoText.textContent = `This is a custom layer. Style editing is not available for this layer type. Use the visibility toggle and opacity slider to control the layer.`;
44397
- content.appendChild(infoText);
44398
- const actions = document.createElement("div");
44399
- actions.className = "style-editor-actions";
44400
- const removeBtn = document.createElement("button");
44401
- removeBtn.className = "style-editor-button style-editor-button-remove";
44402
- removeBtn.textContent = "Remove";
44403
- removeBtn.title = "Remove layer from map";
44404
- removeBtn.addEventListener("click", (e2) => {
44405
- e2.stopPropagation();
44406
- this.showRemoveConfirmation(editor, () => {
44407
- this.closeStyleEditor(layerId);
44408
- this.removeLayer(layerId);
44409
- });
44410
- });
44411
- const closeActionBtn = document.createElement("button");
44412
- closeActionBtn.className = "style-editor-button style-editor-button-close";
44413
- closeActionBtn.textContent = "Close";
44414
- closeActionBtn.addEventListener("click", (e2) => {
44415
- e2.stopPropagation();
44416
- this.closeStyleEditor(layerId);
44417
- });
44418
- actions.appendChild(removeBtn);
44419
- actions.appendChild(closeActionBtn);
44420
- editor.appendChild(header);
44421
- editor.appendChild(content);
44422
- editor.appendChild(actions);
44423
- return editor;
44424
- }
44425
- /**
44426
- * Create a combined style editor for custom layers with native MapLibre sublayers.
44427
- * Groups controls by sublayer type (fill, line, circle, etc.).
44428
- */
44429
- createNativeSubLayerStyleEditor(layerId, nativeLayerIds) {
44430
- const layersByType = /* @__PURE__ */ new Map();
44431
- for (const nativeId of nativeLayerIds) {
44432
- const layer = this.map.getLayer(nativeId);
44433
- if (layer) {
44434
- const type2 = layer.type;
44435
- if (!layersByType.has(type2)) {
44436
- layersByType.set(type2, []);
44437
- }
44438
- layersByType.get(type2).push(nativeId);
44439
- }
44440
- }
44441
- if (layersByType.size === 0) return null;
44442
- const editor = document.createElement("div");
44443
- editor.className = "layer-control-style-editor";
44444
- const header = document.createElement("div");
44445
- header.className = "style-editor-header";
44446
- const title2 = document.createElement("span");
44447
- title2.className = "style-editor-title";
44448
- title2.textContent = "Edit Style";
44449
- const closeBtn = document.createElement("button");
44450
- closeBtn.className = "style-editor-close";
44451
- closeBtn.innerHTML = "&times;";
44452
- closeBtn.title = "Close";
44453
- closeBtn.addEventListener("click", (e2) => {
44454
- e2.stopPropagation();
44455
- this.closeStyleEditor(layerId);
44456
- });
44457
- header.appendChild(title2);
44458
- header.appendChild(closeBtn);
44459
- editor.appendChild(header);
44460
- const controls = document.createElement("div");
44461
- controls.className = "style-editor-controls";
44462
- const typeLabels = {
44463
- fill: "Fill",
44464
- line: "Line",
44465
- circle: "Circle",
44466
- symbol: "Symbol",
44467
- raster: "Raster"
44468
- };
44469
- for (const [type2, ids] of layersByType) {
44470
- if (layersByType.size > 1) {
44471
- const sectionHeader = document.createElement("div");
44472
- sectionHeader.className = "style-editor-section-header";
44473
- sectionHeader.textContent = typeLabels[type2] || type2;
44474
- controls.appendChild(sectionHeader);
44475
- }
44476
- const primaryId = ids[0];
44477
- this.addStyleControlsForNativeGroup(controls, ids, primaryId, type2);
44478
- }
44479
- const actions = document.createElement("div");
44480
- actions.className = "style-editor-actions";
44481
- const resetBtn = document.createElement("button");
44482
- resetBtn.className = "style-editor-button style-editor-button-reset";
44483
- resetBtn.textContent = "Reset";
44484
- resetBtn.addEventListener("click", (e2) => {
44485
- e2.stopPropagation();
44486
- for (const nativeId of nativeLayerIds) {
44487
- restoreOriginalStyle(this.map, nativeId, this.state.originalStyles);
44488
- }
44489
- this.closeStyleEditor(layerId);
44490
- this.openStyleEditor(layerId);
44491
- });
44492
- const removeBtn = document.createElement("button");
44493
- removeBtn.className = "style-editor-button style-editor-button-remove";
44494
- removeBtn.textContent = "Remove";
44495
- removeBtn.title = "Remove layer from map";
44496
- removeBtn.addEventListener("click", (e2) => {
44497
- e2.stopPropagation();
44498
- this.showRemoveConfirmation(editor, () => {
44499
- this.closeStyleEditor(layerId);
44500
- this.removeLayer(layerId);
44501
- });
44502
- });
44503
- const closeActionBtn = document.createElement("button");
44504
- closeActionBtn.className = "style-editor-button style-editor-button-close";
44505
- closeActionBtn.textContent = "Close";
44506
- closeActionBtn.addEventListener("click", (e2) => {
44507
- e2.stopPropagation();
44508
- this.closeStyleEditor(layerId);
44509
- });
44510
- actions.appendChild(resetBtn);
44511
- actions.appendChild(removeBtn);
44512
- actions.appendChild(closeActionBtn);
44513
- editor.appendChild(controls);
44514
- editor.appendChild(actions);
44515
- return editor;
44516
- }
44517
- /**
44518
- * Add style controls for a group of native layers of the same type.
44519
- * Changes to any control are applied to all layers in the group.
44520
- */
44521
- addStyleControlsForNativeGroup(container, layerIds, primaryLayerId, layerType) {
44522
- this.nativeLayerGroups.set(primaryLayerId, layerIds);
44523
- this.addStyleControlsForLayerType(container, primaryLayerId, layerType);
44524
- }
44525
- /**
44526
- * Close style editor for a layer
44527
- */
44528
- closeStyleEditor(layerId) {
44529
- const editor = this.styleEditors.get(layerId);
44530
- if (editor) {
44531
- editor.remove();
44532
- this.styleEditors.delete(layerId);
44533
- }
44534
- this.nativeLayerGroups.clear();
44535
- if (this.state.activeStyleEditor === layerId) {
44536
- this.state.activeStyleEditor = null;
44537
- }
44538
- }
44539
- /**
44540
- * Create style editor UI
44541
- */
44542
- createStyleEditor(layerId) {
44543
- const layer = this.map.getLayer(layerId);
44544
- if (!layer) return null;
44545
- const editor = document.createElement("div");
44546
- editor.className = "layer-control-style-editor";
44547
- const header = document.createElement("div");
44548
- header.className = "style-editor-header";
44549
- const title2 = document.createElement("span");
44550
- title2.className = "style-editor-title";
44551
- title2.textContent = "Edit Style";
44552
- const closeBtn = document.createElement("button");
44553
- closeBtn.className = "style-editor-close";
44554
- closeBtn.innerHTML = "&times;";
44555
- closeBtn.title = "Close";
44556
- closeBtn.addEventListener("click", (e2) => {
44557
- e2.stopPropagation();
44558
- this.closeStyleEditor(layerId);
44559
- });
44560
- header.appendChild(title2);
44561
- header.appendChild(closeBtn);
44562
- const controls = document.createElement("div");
44563
- controls.className = "style-editor-controls";
44564
- const layerType = layer.type;
44565
- this.addStyleControlsForLayerType(controls, layerId, layerType);
44566
- const actions = document.createElement("div");
44567
- actions.className = "style-editor-actions";
44568
- const resetBtn = document.createElement("button");
44569
- resetBtn.className = "style-editor-button style-editor-button-reset";
44570
- resetBtn.textContent = "Reset";
44571
- resetBtn.addEventListener("click", (e2) => {
44572
- e2.stopPropagation();
44573
- this.resetLayerStyle(layerId);
44574
- });
44575
- const removeBtn = document.createElement("button");
44576
- removeBtn.className = "style-editor-button style-editor-button-remove";
44577
- removeBtn.textContent = "Remove";
44578
- removeBtn.title = "Remove layer from map";
44579
- removeBtn.addEventListener("click", (e2) => {
44580
- e2.stopPropagation();
44581
- this.showRemoveConfirmation(editor, () => {
44582
- this.closeStyleEditor(layerId);
44583
- this.removeLayer(layerId);
44584
- });
44585
- });
44586
- const closeActionBtn = document.createElement("button");
44587
- closeActionBtn.className = "style-editor-button style-editor-button-close";
44588
- closeActionBtn.textContent = "Close";
44589
- closeActionBtn.addEventListener("click", (e2) => {
44590
- e2.stopPropagation();
44591
- this.closeStyleEditor(layerId);
44592
- });
44593
- actions.appendChild(resetBtn);
44594
- actions.appendChild(removeBtn);
44595
- actions.appendChild(closeActionBtn);
44596
- editor.appendChild(header);
44597
- editor.appendChild(controls);
44598
- editor.appendChild(actions);
44599
- return editor;
44600
- }
44601
- /**
44602
- * Add style controls based on layer type
44603
- */
44604
- addStyleControlsForLayerType(container, layerId, layerType) {
44605
- switch (layerType) {
44606
- case "fill":
44607
- this.addFillControls(container, layerId);
44608
- break;
44609
- case "line":
44610
- this.addLineControls(container, layerId);
44611
- break;
44612
- case "circle":
44613
- this.addCircleControls(container, layerId);
44614
- break;
44615
- case "raster":
44616
- this.addRasterControls(container, layerId);
44617
- break;
44618
- case "symbol":
44619
- this.addSymbolControls(container, layerId);
44620
- break;
44621
- default:
44622
- container.textContent = `Style controls for ${layerType} layers not yet implemented.`;
44623
- }
44624
- }
44625
- /**
44626
- * Add controls for fill layers
44627
- */
44628
- addFillControls(container, layerId) {
44629
- var _a3;
44630
- const style = this.map.getStyle();
44631
- const layer = (_a3 = style.layers) == null ? void 0 : _a3.find((l3) => l3.id === layerId);
44632
- let fillColor = void 0;
44633
- if (layer && "paint" in layer && layer.paint && "fill-color" in layer.paint) {
44634
- fillColor = layer.paint["fill-color"];
44635
- }
44636
- if (!fillColor) {
44637
- fillColor = this.map.getPaintProperty(layerId, "fill-color");
44638
- }
44639
- this.createColorControl(container, layerId, "fill-color", "Fill Color", normalizeColor(fillColor || "#088"));
44640
- const fillOpacity = this.map.getPaintProperty(layerId, "fill-opacity");
44641
- if (fillOpacity !== void 0 && typeof fillOpacity === "number") {
44642
- this.createSliderControl(container, layerId, "fill-opacity", "Fill Opacity", fillOpacity, 0, 1, 0.05);
44643
- }
44644
- const outlineColor = this.map.getPaintProperty(layerId, "fill-outline-color");
44645
- if (outlineColor !== void 0) {
44646
- this.createColorControl(container, layerId, "fill-outline-color", "Outline Color", normalizeColor(outlineColor));
44647
- }
44648
- }
44649
- /**
44650
- * Add controls for line layers
44651
- */
44652
- addLineControls(container, layerId) {
44653
- var _a3;
44654
- const style = this.map.getStyle();
44655
- const layer = (_a3 = style.layers) == null ? void 0 : _a3.find((l3) => l3.id === layerId);
44656
- let lineColor = void 0;
44657
- if (layer && "paint" in layer && layer.paint && "line-color" in layer.paint) {
44658
- lineColor = layer.paint["line-color"];
44659
- }
44660
- if (!lineColor) {
44661
- lineColor = this.map.getPaintProperty(layerId, "line-color");
44662
- }
44663
- this.createColorControl(container, layerId, "line-color", "Line Color", normalizeColor(lineColor || "#000"));
44664
- const lineWidth = this.map.getPaintProperty(layerId, "line-width");
44665
- this.createSliderControl(container, layerId, "line-width", "Line Width", typeof lineWidth === "number" ? lineWidth : 1, 0, 20, 0.5);
44666
- const lineOpacity = this.map.getPaintProperty(layerId, "line-opacity");
44667
- if (lineOpacity !== void 0 && typeof lineOpacity === "number") {
44668
- this.createSliderControl(container, layerId, "line-opacity", "Line Opacity", lineOpacity, 0, 1, 0.05);
44669
- }
44670
- const lineBlur = this.map.getPaintProperty(layerId, "line-blur");
44671
- if (lineBlur !== void 0 && typeof lineBlur === "number") {
44672
- this.createSliderControl(container, layerId, "line-blur", "Line Blur", lineBlur, 0, 5, 0.1);
44673
- }
44674
- }
44675
- /**
44676
- * Add controls for circle layers
44677
- */
44678
- addCircleControls(container, layerId) {
44679
- var _a3;
44680
- const style = this.map.getStyle();
44681
- const layer = (_a3 = style.layers) == null ? void 0 : _a3.find((l3) => l3.id === layerId);
44682
- let circleColor = void 0;
44683
- if (layer && "paint" in layer && layer.paint && "circle-color" in layer.paint) {
44684
- circleColor = layer.paint["circle-color"];
44685
- }
44686
- if (!circleColor) {
44687
- circleColor = this.map.getPaintProperty(layerId, "circle-color");
44688
- }
44689
- this.createColorControl(container, layerId, "circle-color", "Circle Color", normalizeColor(circleColor || "#000"));
44690
- const circleRadius = this.map.getPaintProperty(layerId, "circle-radius");
44691
- this.createSliderControl(container, layerId, "circle-radius", "Radius", typeof circleRadius === "number" ? circleRadius : 5, 0, 40, 0.5);
44692
- const circleOpacity = this.map.getPaintProperty(layerId, "circle-opacity");
44693
- if (circleOpacity !== void 0 && typeof circleOpacity === "number") {
44694
- this.createSliderControl(container, layerId, "circle-opacity", "Opacity", circleOpacity, 0, 1, 0.05);
44695
- }
44696
- const strokeColor = this.map.getPaintProperty(layerId, "circle-stroke-color");
44697
- if (strokeColor !== void 0) {
44698
- this.createColorControl(container, layerId, "circle-stroke-color", "Stroke Color", normalizeColor(strokeColor));
44699
- }
44700
- const strokeWidth = this.map.getPaintProperty(layerId, "circle-stroke-width");
44701
- if (strokeWidth !== void 0 && typeof strokeWidth === "number") {
44702
- this.createSliderControl(container, layerId, "circle-stroke-width", "Stroke Width", strokeWidth, 0, 10, 0.1);
44703
- }
44704
- }
44705
- /**
44706
- * Add controls for raster layers
44707
- */
44708
- addRasterControls(container, layerId) {
44709
- const rasterOpacity = this.map.getPaintProperty(layerId, "raster-opacity");
44710
- this.createSliderControl(container, layerId, "raster-opacity", "Opacity", typeof rasterOpacity === "number" ? rasterOpacity : 1, 0, 1, 0.05);
44711
- const brightnessMin = this.map.getPaintProperty(layerId, "raster-brightness-min");
44712
- this.createSliderControl(container, layerId, "raster-brightness-min", "Brightness Min", typeof brightnessMin === "number" ? brightnessMin : 0, -1, 1, 0.05);
44713
- const brightnessMax = this.map.getPaintProperty(layerId, "raster-brightness-max");
44714
- this.createSliderControl(container, layerId, "raster-brightness-max", "Brightness Max", typeof brightnessMax === "number" ? brightnessMax : 1, -1, 1, 0.05);
44715
- const saturation = this.map.getPaintProperty(layerId, "raster-saturation");
44716
- this.createSliderControl(container, layerId, "raster-saturation", "Saturation", typeof saturation === "number" ? saturation : 0, -1, 1, 0.05);
44717
- const contrast = this.map.getPaintProperty(layerId, "raster-contrast");
44718
- this.createSliderControl(container, layerId, "raster-contrast", "Contrast", typeof contrast === "number" ? contrast : 0, -1, 1, 0.05);
44719
- const hueRotate = this.map.getPaintProperty(layerId, "raster-hue-rotate");
44720
- this.createSliderControl(container, layerId, "raster-hue-rotate", "Hue Rotate", typeof hueRotate === "number" ? hueRotate : 0, 0, 350, 5);
44721
- }
44722
- /**
44723
- * Add controls for symbol layers
44724
- */
44725
- addSymbolControls(container, layerId) {
44726
- const textColor = this.map.getPaintProperty(layerId, "text-color");
44727
- if (textColor !== void 0) {
44728
- this.createColorControl(container, layerId, "text-color", "Text Color", normalizeColor(textColor));
44729
- }
44730
- const textOpacity = this.map.getPaintProperty(layerId, "text-opacity");
44731
- if (textOpacity !== void 0 && typeof textOpacity === "number") {
44732
- this.createSliderControl(container, layerId, "text-opacity", "Text Opacity", textOpacity, 0, 1, 0.05);
44733
- }
44734
- const iconOpacity = this.map.getPaintProperty(layerId, "icon-opacity");
44735
- if (iconOpacity !== void 0 && typeof iconOpacity === "number") {
44736
- this.createSliderControl(container, layerId, "icon-opacity", "Icon Opacity", iconOpacity, 0, 1, 0.05);
44737
- }
44738
- }
44739
- /**
44740
- * Create a color control
44741
- */
44742
- createColorControl(container, layerId, property, label, initialValue) {
44743
- const controlGroup = document.createElement("div");
44744
- controlGroup.className = "style-control-group";
44745
- const labelEl = document.createElement("label");
44746
- labelEl.className = "style-control-label";
44747
- labelEl.textContent = label;
44748
- const inputWrapper = document.createElement("div");
44749
- inputWrapper.className = "style-control-color-group";
44750
- const colorInput = document.createElement("input");
44751
- colorInput.type = "color";
44752
- colorInput.className = "style-control-color-picker";
44753
- colorInput.value = initialValue;
44754
- colorInput.dataset.property = property;
44755
- const hexDisplay = document.createElement("input");
44756
- hexDisplay.type = "text";
44757
- hexDisplay.className = "style-control-color-value";
44758
- hexDisplay.value = initialValue;
44759
- hexDisplay.readOnly = true;
44760
- colorInput.addEventListener("input", () => {
44761
- const color = colorInput.value;
44762
- hexDisplay.value = color;
44763
- const targetIds = this.nativeLayerGroups.get(layerId) || [layerId];
44764
- for (const id2 of targetIds) {
44765
- this.map.setPaintProperty(id2, property, color);
44766
- }
44767
- });
44768
- inputWrapper.appendChild(colorInput);
44769
- inputWrapper.appendChild(hexDisplay);
44770
- controlGroup.appendChild(labelEl);
44771
- controlGroup.appendChild(inputWrapper);
44772
- container.appendChild(controlGroup);
44773
- }
44774
- /**
44775
- * Create a slider control
44776
- */
44777
- createSliderControl(container, layerId, property, label, initialValue, min, max, step) {
44778
- const controlGroup = document.createElement("div");
44779
- controlGroup.className = "style-control-group";
44780
- const labelEl = document.createElement("label");
44781
- labelEl.className = "style-control-label";
44782
- labelEl.textContent = label;
44783
- const inputWrapper = document.createElement("div");
44784
- inputWrapper.className = "style-control-input-wrapper";
44785
- const slider = document.createElement("input");
44786
- slider.type = "range";
44787
- slider.className = "style-control-slider";
44788
- slider.min = String(min);
44789
- slider.max = String(max);
44790
- slider.step = String(step);
44791
- slider.value = String(initialValue);
44792
- slider.dataset.property = property;
44793
- const valueDisplay = document.createElement("span");
44794
- valueDisplay.className = "style-control-value";
44795
- valueDisplay.textContent = formatNumericValue(initialValue, step);
44796
- slider.addEventListener("input", () => {
44797
- const value = parseFloat(slider.value);
44798
- valueDisplay.textContent = formatNumericValue(value, step);
44799
- const targetIds = this.nativeLayerGroups.get(layerId) || [layerId];
44800
- for (const id2 of targetIds) {
44801
- this.map.setPaintProperty(id2, property, value);
44802
- }
44803
- });
44804
- inputWrapper.appendChild(slider);
44805
- inputWrapper.appendChild(valueDisplay);
44806
- controlGroup.appendChild(labelEl);
44807
- controlGroup.appendChild(inputWrapper);
44808
- container.appendChild(controlGroup);
44809
- }
44810
- /**
44811
- * Reset layer style to original
44812
- */
44813
- resetLayerStyle(layerId) {
44814
- const originalStyle = this.state.originalStyles.get(layerId);
44815
- if (!originalStyle) return;
44816
- restoreOriginalStyle(this.map, layerId, this.state.originalStyles);
44817
- const editor = this.styleEditors.get(layerId);
44818
- if (editor) {
44819
- const sliders = editor.querySelectorAll(".style-control-slider");
44820
- sliders.forEach((slider) => {
44821
- var _a3;
44822
- const property = slider.dataset.property;
44823
- if (property) {
44824
- const value = this.map.getPaintProperty(layerId, property);
44825
- if (value !== void 0 && typeof value === "number") {
44826
- slider.value = String(value);
44827
- const valueDisplay = (_a3 = slider.parentElement) == null ? void 0 : _a3.querySelector(".style-control-value");
44828
- if (valueDisplay) {
44829
- const step = parseFloat(slider.step);
44830
- valueDisplay.textContent = formatNumericValue(value, step);
44831
- }
44832
- }
44833
- }
44834
- });
44835
- const colorPickers = editor.querySelectorAll(".style-control-color-picker");
44836
- colorPickers.forEach((picker) => {
44837
- var _a3;
44838
- const property = picker.dataset.property;
44839
- if (property) {
44840
- const value = this.map.getPaintProperty(layerId, property);
44841
- if (value !== void 0) {
44842
- const hexColor = normalizeColor(value);
44843
- picker.value = hexColor;
44844
- const hexDisplay = (_a3 = picker.parentElement) == null ? void 0 : _a3.querySelector(".style-control-color-value");
44845
- if (hexDisplay) {
44846
- hexDisplay.textContent = hexColor;
44847
- }
44848
- }
44849
- }
44850
- });
44851
- }
44852
- }
44853
- /**
44854
- * Update layer states from map (sync UI with map)
44855
- */
44856
- updateLayerStatesFromMap() {
44857
- if (this.state.userInteractingWithSlider) {
44858
- return;
44859
- }
44860
- Object.keys(this.state.layerStates).forEach((layerId) => {
44861
- var _a3;
44862
- try {
44863
- if ((_a3 = this.state.layerStates[layerId]) == null ? void 0 : _a3.isCustomLayer) {
44864
- return;
44865
- }
44866
- if (layerId === "Background") {
44867
- return;
44868
- }
44869
- const layer = this.map.getLayer(layerId);
44870
- if (!layer) return;
44871
- const visibility = this.map.getLayoutProperty(layerId, "visibility");
44872
- const isVisible = visibility !== "none";
44873
- const layerType = layer.type;
44874
- const opacity = getLayerOpacity(this.map, layerId, layerType);
44875
- if (this.state.layerStates[layerId]) {
44876
- this.state.layerStates[layerId].visible = isVisible;
44877
- this.state.layerStates[layerId].opacity = opacity;
44878
- }
44879
- this.updateUIForLayer(layerId, isVisible, opacity);
44880
- } catch (error) {
44881
- console.warn(`Failed to update state for layer ${layerId}:`, error);
44882
- }
44883
- });
44884
- }
44885
- /**
44886
- * Update UI elements for a specific layer
44887
- */
44888
- updateUIForLayer(layerId, visible, opacity) {
44889
- const layerItems = this.panel.querySelectorAll(".layer-control-item");
44890
- layerItems.forEach((item) => {
44891
- if (item.dataset.layerId === layerId) {
44892
- const checkbox = item.querySelector(".layer-control-checkbox");
44893
- const opacitySlider = item.querySelector(".layer-control-opacity");
44894
- if (checkbox) {
44895
- checkbox.checked = visible;
44896
- }
44897
- if (opacitySlider) {
44898
- opacitySlider.value = String(opacity);
44899
- opacitySlider.title = `Opacity: ${Math.round(opacity * 100)}%`;
44900
- }
44901
- }
44902
- });
44903
- }
44904
- /**
44905
- * Check for new layers and add them to the control, remove deleted layers
44906
- */
44907
- checkForNewLayers() {
44908
- if (this.state.isStyleOperationInProgress) {
44909
- return;
44910
- }
44911
- try {
44912
- const style = this.map.getStyle();
44913
- if (!style || !style.layers) {
44914
- return;
44915
- }
44916
- const currentMapLayerIds = new Set(style.layers.map((layer) => layer.id));
44917
- const isAutoDetectMode = this.targetLayers.length === 0 || this.targetLayers.length === 1 && this.targetLayers[0] === "Background" || this.targetLayers.every((id2) => id2 === "Background" || this.state.layerStates[id2]);
44918
- const newLayers = [];
44919
- const useBasemapStyleDetection = this.basemapLayerIds !== null && this.basemapLayerIds.size > 0;
44920
- const useInitialLayerDetection = !useBasemapStyleDetection && this.initialLayerIds !== null && this.initialLayerIds.size > 0;
44921
- currentMapLayerIds.forEach((layerId) => {
44922
- if (layerId !== "Background" && !this.state.layerStates[layerId]) {
44923
- const layer = this.map.getLayer(layerId);
44924
- if (layer) {
44925
- if (this.initialLayerIds !== null && this.initialLayerIds.has(layerId)) {
44926
- return;
44927
- }
44928
- if (isAutoDetectMode) {
44929
- if (this.excludeDrawnLayers && this.isDrawnLayer(layerId)) {
44930
- return;
44931
- }
44932
- if (this.isExcludedByPattern(layerId)) {
44933
- return;
44934
- }
44935
- if (!this.isUserAddedLayer(layerId)) {
44936
- return;
44937
- }
44938
- if (useBasemapStyleDetection) {
44939
- if (this.basemapLayerIds.has(layerId)) {
44940
- return;
44941
- }
44942
- } else if (useInitialLayerDetection) {
44943
- if (this.initialLayerIds.has(layerId)) {
44944
- const userAddedSourceIds = this.detectUserAddedSources();
44945
- const sourceId = layer.source;
44946
- if (!sourceId || !userAddedSourceIds.has(sourceId)) {
44947
- return;
44948
- }
44949
- }
44950
- } else {
44951
- const userAddedSourceIds = this.detectUserAddedSources();
44952
- const sourceId = layer.source;
44953
- if (!sourceId || !userAddedSourceIds.has(sourceId)) {
44954
- return;
44955
- }
44956
- }
44957
- }
44958
- newLayers.push(layerId);
44959
- }
44960
- }
44961
- });
44962
- const removedLayers = [];
44963
- Object.keys(this.state.layerStates).forEach((layerId) => {
44964
- const state = this.state.layerStates[layerId];
44965
- if (layerId !== "Background" && !state.isCustomLayer && !currentMapLayerIds.has(layerId)) {
44966
- removedLayers.push(layerId);
44967
- }
44968
- });
44969
- if (removedLayers.length > 0) {
44970
- removedLayers.forEach((layerId) => {
44971
- delete this.state.layerStates[layerId];
44972
- const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
44973
- if (itemEl) {
44974
- itemEl.remove();
44975
- }
44976
- if (this.state.activeStyleEditor === layerId) {
44977
- this.state.activeStyleEditor = null;
44978
- }
44979
- this.styleEditors.delete(layerId);
44980
- });
44981
- }
44982
- if (newLayers.length > 0) {
44983
- newLayers.forEach((layerId) => {
44984
- const layer = this.map.getLayer(layerId);
44985
- if (!layer) return;
44986
- const layerType = layer.type;
44987
- const opacity = getLayerOpacity(this.map, layerId, layerType);
44988
- const visibility = this.map.getLayoutProperty(layerId, "visibility");
44989
- const isVisible = visibility !== "none";
44990
- this.state.layerStates[layerId] = {
44991
- visible: isVisible,
44992
- opacity,
44993
- name: this.generateFriendlyName(layerId)
44994
- };
44995
- this.addLayerItem(layerId, this.state.layerStates[layerId]);
44996
- });
44997
- }
44998
- if (this.customLayerRegistry) {
44999
- const customLayerIds = this.customLayerRegistry.getAllLayerIds();
45000
- customLayerIds.forEach((layerId) => {
45001
- if (!this.state.layerStates[layerId] && !this.removedCustomLayerIds.has(layerId)) {
45002
- const customState = this.customLayerRegistry.getLayerState(layerId);
45003
- if (customState) {
45004
- this.state.layerStates[layerId] = {
45005
- visible: customState.visible,
45006
- opacity: customState.opacity,
45007
- name: customState.name,
45008
- isCustomLayer: true,
45009
- customLayerType: this.customLayerRegistry.getSymbolType(layerId) || void 0
45010
- };
45011
- this.addLayerItem(layerId, this.state.layerStates[layerId]);
45012
- }
45013
- }
45014
- });
45015
- Object.keys(this.state.layerStates).forEach((layerId) => {
45016
- const state = this.state.layerStates[layerId];
45017
- if (state.isCustomLayer && !customLayerIds.includes(layerId)) {
45018
- delete this.state.layerStates[layerId];
45019
- const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
45020
- if (itemEl) {
45021
- itemEl.remove();
45022
- }
45023
- if (this.state.activeStyleEditor === layerId) {
45024
- this.state.activeStyleEditor = null;
45025
- }
45026
- this.styleEditors.delete(layerId);
45027
- }
45028
- });
45029
- }
45030
- } catch (error) {
45031
- console.warn("Failed to check for new layers:", error);
45032
- }
45033
- }
45034
- /**
45035
- * Register a custom layer adapter dynamically.
45036
- * This allows adding adapters after the LayerControl has been initialized.
45037
- * @param adapter The custom layer adapter to register
45038
- */
45039
- registerCustomAdapter(adapter) {
45040
- if (!this.customLayerRegistry) {
45041
- this.customLayerRegistry = new CustomLayerRegistry();
45042
- this.customLayerUnsubscribe = this.customLayerRegistry.onChange((event, layerId) => {
45043
- if (event === "add" && layerId) {
45044
- this.removedCustomLayerIds.delete(layerId);
45045
- }
45046
- this.checkForNewLayers();
45047
- });
45048
- }
45049
- this.customLayerRegistry.register(adapter);
45050
- if (this.panel) {
45051
- this.checkForNewLayers();
45052
- }
45053
- }
45054
- // ===== Context Menu Methods =====
45055
- /**
45056
- * Create context menu element
45057
- */
45058
- createContextMenu() {
45059
- const menu = document.createElement("div");
45060
- menu.className = "layer-control-context-menu";
45061
- menu.style.display = "none";
45062
- const renameItem = this.createContextMenuItem("Rename", "✏️", () => {
45063
- if (this.state.contextMenu.targetLayerId) {
45064
- this.startRenaming(this.state.contextMenu.targetLayerId);
45065
- }
45066
- this.hideContextMenu();
45067
- });
45068
- const zoomItem = this.createContextMenuItem("Zoom to Layer", "🔍", () => {
45069
- if (this.state.contextMenu.targetLayerId) {
45070
- this.zoomToLayer(this.state.contextMenu.targetLayerId);
45071
- }
45072
- this.hideContextMenu();
45073
- });
45074
- const sep1 = document.createElement("div");
45075
- sep1.className = "context-menu-separator";
45076
- const moveUpItem = this.createContextMenuItem("Move Up", "↑", () => {
45077
- if (this.state.contextMenu.targetLayerId) {
45078
- this.moveLayerUp(this.state.contextMenu.targetLayerId);
45079
- }
45080
- this.hideContextMenu();
45081
- });
45082
- const moveTopItem = this.createContextMenuItem("Move to Top", "⤒", () => {
45083
- if (this.state.contextMenu.targetLayerId) {
45084
- this.moveLayerToTop(this.state.contextMenu.targetLayerId);
45085
- }
45086
- this.hideContextMenu();
45087
- });
45088
- const moveDownItem = this.createContextMenuItem("Move Down", "↓", () => {
45089
- if (this.state.contextMenu.targetLayerId) {
45090
- this.moveLayerDown(this.state.contextMenu.targetLayerId);
45091
- }
45092
- this.hideContextMenu();
45093
- });
45094
- const moveBottomItem = this.createContextMenuItem("Move to Bottom", "⤓", () => {
45095
- if (this.state.contextMenu.targetLayerId) {
45096
- this.moveLayerToBottom(this.state.contextMenu.targetLayerId);
45097
- }
45098
- this.hideContextMenu();
45099
- });
45100
- const sep2 = document.createElement("div");
45101
- sep2.className = "context-menu-separator";
45102
- const removeItem = this.createContextMenuItem("Remove Layer", "🗑️", () => {
45103
- if (this.state.contextMenu.targetLayerId) {
45104
- this.removeLayer(this.state.contextMenu.targetLayerId);
45105
- }
45106
- this.hideContextMenu();
45107
- }, true);
45108
- menu.appendChild(renameItem);
45109
- menu.appendChild(zoomItem);
45110
- menu.appendChild(sep1);
45111
- menu.appendChild(moveUpItem);
45112
- menu.appendChild(moveTopItem);
45113
- menu.appendChild(moveDownItem);
45114
- menu.appendChild(moveBottomItem);
45115
- menu.appendChild(sep2);
45116
- menu.appendChild(removeItem);
45117
- return menu;
45118
- }
45119
- /**
45120
- * Create a context menu item
45121
- */
45122
- createContextMenuItem(label, icon, onClick, isDanger = false) {
45123
- const item = document.createElement("div");
45124
- item.className = "context-menu-item" + (isDanger ? " context-menu-item-danger" : "");
45125
- const iconEl = document.createElement("span");
45126
- iconEl.className = "context-menu-item-icon";
45127
- iconEl.textContent = icon;
45128
- const labelEl = document.createElement("span");
45129
- labelEl.className = "context-menu-item-label";
45130
- labelEl.textContent = label;
45131
- item.appendChild(iconEl);
45132
- item.appendChild(labelEl);
45133
- item.addEventListener("click", (e2) => {
45134
- e2.stopPropagation();
45135
- onClick();
45136
- });
45137
- return item;
45138
- }
45139
- /**
45140
- * Show context menu at position
45141
- */
45142
- showContextMenu(layerId, x2, y2) {
45143
- if (!this.contextMenuEl) return;
45144
- this.state.contextMenu = {
45145
- visible: true,
45146
- targetLayerId: layerId,
45147
- x: x2,
45148
- y: y2
45149
- };
45150
- const mapRect = this.mapContainer.getBoundingClientRect();
45151
- let menuX = x2 - mapRect.left;
45152
- let menuY = y2 - mapRect.top;
45153
- this.contextMenuEl.style.display = "block";
45154
- const menuRect = this.contextMenuEl.getBoundingClientRect();
45155
- if (menuX + menuRect.width > mapRect.width) {
45156
- menuX = mapRect.width - menuRect.width - 5;
45157
- }
45158
- if (menuY + menuRect.height > mapRect.height) {
45159
- menuY = mapRect.height - menuRect.height - 5;
45160
- }
45161
- this.contextMenuEl.style.left = `${menuX}px`;
45162
- this.contextMenuEl.style.top = `${menuY}px`;
45163
- }
45164
- /**
45165
- * Hide context menu
45166
- */
45167
- hideContextMenu() {
45168
- if (!this.contextMenuEl) return;
45169
- this.state.contextMenu = {
45170
- visible: false,
45171
- targetLayerId: null,
45172
- x: 0,
45173
- y: 0
45174
- };
45175
- this.contextMenuEl.style.display = "none";
45176
- }
45177
- /**
45178
- * Start renaming a layer
45179
- */
45180
- startRenaming(layerId) {
45181
- var _a3;
45182
- const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
45183
- if (!itemEl) return;
45184
- const nameEl = itemEl.querySelector(".layer-control-name");
45185
- if (!nameEl) return;
45186
- this.state.renamingLayerId = layerId;
45187
- const currentName = this.state.customLayerNames.get(layerId) || ((_a3 = this.state.layerStates[layerId]) == null ? void 0 : _a3.name) || layerId;
45188
- const input = document.createElement("input");
45189
- input.type = "text";
45190
- input.className = "layer-control-name-input";
45191
- input.value = currentName;
45192
- const finishRename = () => {
45193
- var _a22;
45194
- const newName = input.value.trim() || currentName;
45195
- const oldName = currentName;
45196
- if (newName !== oldName) {
45197
- this.state.customLayerNames.set(layerId, newName);
45198
- if (this.state.layerStates[layerId]) {
45199
- this.state.layerStates[layerId].name = newName;
45200
- }
45201
- (_a22 = this.onLayerRename) == null ? void 0 : _a22.call(this, layerId, oldName, newName);
45202
- }
45203
- const newNameEl = document.createElement("span");
45204
- newNameEl.className = "layer-control-name";
45205
- newNameEl.textContent = newName;
45206
- newNameEl.title = newName;
45207
- input.replaceWith(newNameEl);
45208
- this.state.renamingLayerId = null;
45209
- };
45210
- input.addEventListener("blur", finishRename);
45211
- input.addEventListener("keydown", (e2) => {
45212
- if (e2.key === "Enter") {
45213
- e2.preventDefault();
45214
- finishRename();
45215
- } else if (e2.key === "Escape") {
45216
- e2.preventDefault();
45217
- const newNameEl = document.createElement("span");
45218
- newNameEl.className = "layer-control-name";
45219
- newNameEl.textContent = currentName;
45220
- newNameEl.title = currentName;
45221
- input.replaceWith(newNameEl);
45222
- this.state.renamingLayerId = null;
45223
- }
45224
- });
45225
- nameEl.replaceWith(input);
45226
- input.focus();
45227
- input.select();
45228
- }
45229
- /**
45230
- * Zoom to a layer's bounds
45231
- */
45232
- zoomToLayer(layerId) {
45233
- const layerState = this.state.layerStates[layerId];
45234
- if ((layerState == null ? void 0 : layerState.isCustomLayer) && this.customLayerRegistry) {
45235
- const bounds = this.customLayerRegistry.getBounds(layerId);
45236
- if (bounds) {
45237
- this.map.fitBounds(bounds, { padding: 50 });
45238
- return;
45239
- }
45240
- }
45241
- const layer = this.map.getLayer(layerId);
45242
- if (!layer) return;
45243
- try {
45244
- const sourceId = layer.source;
45245
- let features = sourceId ? this.map.querySourceFeatures(sourceId) : [];
45246
- if (features.length === 0) {
45247
- features = this.map.queryRenderedFeatures({ layers: [layerId] });
45248
- }
45249
- if (features.length === 0) return;
45250
- let minLng = Infinity, minLat = Infinity, maxLng = -Infinity, maxLat = -Infinity;
45251
- features.forEach((feature) => {
45252
- if (!feature.geometry) return;
45253
- const processCoords = (coords) => {
45254
- if (typeof coords[0] === "number") {
45255
- const [lng, lat] = coords;
45256
- minLng = Math.min(minLng, lng);
45257
- minLat = Math.min(minLat, lat);
45258
- maxLng = Math.max(maxLng, lng);
45259
- maxLat = Math.max(maxLat, lat);
45260
- } else {
45261
- coords.forEach(processCoords);
45262
- }
45263
- };
45264
- if (feature.geometry.type === "Point") {
45265
- processCoords(feature.geometry.coordinates);
45266
- } else if (feature.geometry.type === "LineString" || feature.geometry.type === "MultiPoint") {
45267
- feature.geometry.coordinates.forEach(processCoords);
45268
- } else if (feature.geometry.type === "Polygon" || feature.geometry.type === "MultiLineString") {
45269
- feature.geometry.coordinates.forEach((ring) => ring.forEach(processCoords));
45270
- } else if (feature.geometry.type === "MultiPolygon") {
45271
- feature.geometry.coordinates.forEach(
45272
- (polygon) => polygon.forEach((ring) => ring.forEach(processCoords))
45273
- );
45274
- }
45275
- });
45276
- if (minLng !== Infinity && minLat !== Infinity && maxLng !== -Infinity && maxLat !== -Infinity) {
45277
- this.map.fitBounds([[minLng, minLat], [maxLng, maxLat]], { padding: 50 });
45278
- }
45279
- } catch (error) {
45280
- console.warn(`Failed to zoom to layer ${layerId}:`, error);
45281
- }
45282
- }
45283
- /**
45284
- * Get user layer IDs in current map order (top to bottom in UI = high z-index to low)
45285
- * Includes both MapLibre layers and custom layers managed by adapters.
45286
- */
45287
- getUserLayerIdsInMapOrder() {
45288
- const style = this.map.getStyle();
45289
- if (!(style == null ? void 0 : style.layers)) return [];
45290
- const mapLayerIds = style.layers.map((l3) => l3.id);
45291
- const userLayerIds = Object.keys(this.state.layerStates).filter((id2) => id2 !== "Background");
45292
- const mapLibreLayers = userLayerIds.filter((id2) => mapLayerIds.includes(id2));
45293
- const customLayers = userLayerIds.filter(
45294
- (id2) => {
45295
- var _a3;
45296
- return ((_a3 = this.state.layerStates[id2]) == null ? void 0 : _a3.isCustomLayer) && !mapLayerIds.includes(id2);
45297
- }
45298
- );
45299
- const sortedMapLibreLayers = mapLibreLayers.sort((a2, b2) => mapLayerIds.indexOf(b2) - mapLayerIds.indexOf(a2));
45300
- return [...customLayers, ...sortedMapLibreLayers];
45301
- }
45302
- /**
45303
- * Check if a layer is a MapLibre layer (not a custom layer)
45304
- */
45305
- isMapLibreLayer(layerId) {
45306
- return this.map.getLayer(layerId) !== void 0;
45307
- }
45308
- /**
45309
- * Find the next MapLibre layer ID in a direction from a given index
45310
- * @param layerIds Array of layer IDs
45311
- * @param startIndex Index to start searching from
45312
- * @param direction 1 for forward (toward bottom), -1 for backward (toward top)
45313
- * @returns MapLibre layer ID or undefined if not found
45314
- */
45315
- findNextMapLibreLayer(layerIds, startIndex, direction) {
45316
- for (let i2 = startIndex; direction === 1 ? i2 < layerIds.length : i2 >= 0; i2 += direction) {
45317
- if (this.isMapLibreLayer(layerIds[i2])) {
45318
- return layerIds[i2];
45319
- }
45320
- }
45321
- return void 0;
45322
- }
45323
- /**
45324
- * Move a layer up in UI (higher rendering order = move to higher z-index)
45325
- */
45326
- moveLayerUp(layerId) {
45327
- var _a3, _b3;
45328
- const layerIds = this.getUserLayerIdsInMapOrder();
45329
- const index = layerIds.indexOf(layerId);
45330
- if (index <= 0) return;
45331
- const isCustom = (_a3 = this.state.layerStates[layerId]) == null ? void 0 : _a3.isCustomLayer;
45332
- if (!isCustom && this.isMapLibreLayer(layerId)) {
45333
- try {
45334
- const targetBeforeId = index >= 2 ? this.findNextMapLibreLayer(layerIds, index - 2, -1) : void 0;
45335
- this.map.moveLayer(layerId, targetBeforeId);
45336
- } catch (e2) {
45337
- }
45338
- }
45339
- const newLayerStates = {};
45340
- const orderedIds = [...layerIds];
45341
- [orderedIds[index], orderedIds[index - 1]] = [orderedIds[index - 1], orderedIds[index]];
45342
- if (this.state.layerStates["Background"]) {
45343
- newLayerStates["Background"] = this.state.layerStates["Background"];
45344
- }
45345
- orderedIds.forEach((id2) => {
45346
- if (this.state.layerStates[id2]) {
45347
- newLayerStates[id2] = this.state.layerStates[id2];
45348
- }
45349
- });
45350
- this.state.layerStates = newLayerStates;
45351
- this.buildLayerItems();
45352
- (_b3 = this.onLayerReorder) == null ? void 0 : _b3.call(this, this.getUserLayerIdsInMapOrder());
45353
- }
45354
- /**
45355
- * Move a layer to the top (highest rendering order)
45356
- */
45357
- moveLayerToTop(layerId) {
45358
- var _a3, _b3;
45359
- const layerIds = this.getUserLayerIdsInMapOrder();
45360
- const index = layerIds.indexOf(layerId);
45361
- if (index <= 0) return;
45362
- const isCustom = (_a3 = this.state.layerStates[layerId]) == null ? void 0 : _a3.isCustomLayer;
45363
- if (!isCustom && this.isMapLibreLayer(layerId)) {
45364
- try {
45365
- this.map.moveLayer(layerId);
45366
- } catch (e2) {
45367
- }
45368
- }
45369
- const newLayerStates = {};
45370
- const orderedIds = [...layerIds];
45371
- orderedIds.splice(index, 1);
45372
- orderedIds.unshift(layerId);
45373
- if (this.state.layerStates["Background"]) {
45374
- newLayerStates["Background"] = this.state.layerStates["Background"];
45375
- }
45376
- orderedIds.forEach((id2) => {
45377
- if (this.state.layerStates[id2]) {
45378
- newLayerStates[id2] = this.state.layerStates[id2];
45379
- }
45380
- });
45381
- this.state.layerStates = newLayerStates;
45382
- this.buildLayerItems();
45383
- (_b3 = this.onLayerReorder) == null ? void 0 : _b3.call(this, this.getUserLayerIdsInMapOrder());
45384
- }
45385
- /**
45386
- * Move a layer down in UI (lower rendering order = move to lower z-index)
45387
- */
45388
- moveLayerDown(layerId) {
45389
- var _a3, _b3;
45390
- const layerIds = this.getUserLayerIdsInMapOrder();
45391
- const index = layerIds.indexOf(layerId);
45392
- if (index < 0 || index >= layerIds.length - 1) return;
45393
- const isCustom = (_a3 = this.state.layerStates[layerId]) == null ? void 0 : _a3.isCustomLayer;
45394
- if (!isCustom && this.isMapLibreLayer(layerId)) {
45395
- const belowLayerId = this.findNextMapLibreLayer(layerIds, index + 1, 1);
45396
- if (belowLayerId) {
45397
- try {
45398
- this.map.moveLayer(layerId, belowLayerId);
45399
- } catch (e2) {
45400
- }
45401
- }
45402
- }
45403
- const newLayerStates = {};
45404
- const orderedIds = [...layerIds];
45405
- [orderedIds[index], orderedIds[index + 1]] = [orderedIds[index + 1], orderedIds[index]];
45406
- if (this.state.layerStates["Background"]) {
45407
- newLayerStates["Background"] = this.state.layerStates["Background"];
45408
- }
45409
- orderedIds.forEach((id2) => {
45410
- if (this.state.layerStates[id2]) {
45411
- newLayerStates[id2] = this.state.layerStates[id2];
45412
- }
45413
- });
45414
- this.state.layerStates = newLayerStates;
45415
- this.buildLayerItems();
45416
- (_b3 = this.onLayerReorder) == null ? void 0 : _b3.call(this, this.getUserLayerIdsInMapOrder());
45417
- }
45418
- /**
45419
- * Move a layer to the bottom (lowest rendering order among user layers)
45420
- */
45421
- moveLayerToBottom(layerId) {
45422
- var _a3, _b3;
45423
- const layerIds = this.getUserLayerIdsInMapOrder();
45424
- if (layerIds.length <= 1) return;
45425
- const index = layerIds.indexOf(layerId);
45426
- if (index < 0 || index === layerIds.length - 1) return;
45427
- const isCustom = (_a3 = this.state.layerStates[layerId]) == null ? void 0 : _a3.isCustomLayer;
45428
- if (!isCustom && this.isMapLibreLayer(layerId)) {
45429
- const bottomLayerId = this.findNextMapLibreLayer(layerIds, layerIds.length - 1, -1);
45430
- if (bottomLayerId && bottomLayerId !== layerId) {
45431
- try {
45432
- this.map.moveLayer(layerId, bottomLayerId);
45433
- } catch (e2) {
45434
- }
45435
- }
45436
- }
45437
- const newLayerStates = {};
45438
- const orderedIds = [...layerIds];
45439
- orderedIds.splice(index, 1);
45440
- orderedIds.push(layerId);
45441
- if (this.state.layerStates["Background"]) {
45442
- newLayerStates["Background"] = this.state.layerStates["Background"];
45443
- }
45444
- orderedIds.forEach((id2) => {
45445
- if (this.state.layerStates[id2]) {
45446
- newLayerStates[id2] = this.state.layerStates[id2];
45447
- }
45448
- });
45449
- this.state.layerStates = newLayerStates;
45450
- this.buildLayerItems();
45451
- (_b3 = this.onLayerReorder) == null ? void 0 : _b3.call(this, this.getUserLayerIdsInMapOrder());
45452
- }
45453
- /**
45454
- * Show inline remove confirmation instead of browser confirm() dialog.
45455
- * Works in Jupyter notebooks where native dialogs may not appear.
45456
- */
45457
- showRemoveConfirmation(container, onConfirm) {
45458
- const existing = container.querySelector(".layer-control-remove-confirm");
45459
- if (existing) {
45460
- existing.remove();
45461
- }
45462
- const confirmEl = document.createElement("div");
45463
- confirmEl.className = "layer-control-remove-confirm";
45464
- const message = document.createElement("div");
45465
- message.className = "layer-control-remove-confirm-message";
45466
- message.innerHTML = `
45467
- <svg class="layer-control-remove-confirm-icon" viewBox="0 0 20 20" fill="currentColor">
45468
- <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"/>
45469
- </svg>
45470
- <span>Remove this layer?</span>
45471
- `;
45472
- const buttons = document.createElement("div");
45473
- buttons.className = "layer-control-remove-confirm-buttons";
45474
- const cancelBtn = document.createElement("button");
45475
- cancelBtn.className = "layer-control-remove-confirm-btn layer-control-remove-confirm-btn-cancel";
45476
- cancelBtn.textContent = "Cancel";
45477
- cancelBtn.addEventListener("click", (e2) => {
45478
- e2.stopPropagation();
45479
- confirmEl.remove();
45480
- });
45481
- const confirmBtn = document.createElement("button");
45482
- confirmBtn.className = "layer-control-remove-confirm-btn layer-control-remove-confirm-btn-confirm";
45483
- confirmBtn.textContent = "Remove";
45484
- confirmBtn.addEventListener("click", (e2) => {
45485
- e2.stopPropagation();
45486
- confirmEl.remove();
45487
- onConfirm();
45488
- });
45489
- buttons.appendChild(cancelBtn);
45490
- buttons.appendChild(confirmBtn);
45491
- confirmEl.appendChild(message);
45492
- confirmEl.appendChild(buttons);
45493
- container.appendChild(confirmEl);
45494
- requestAnimationFrame(() => {
45495
- confirmEl.scrollIntoView({ behavior: "smooth", block: "nearest" });
45496
- });
45497
- }
45498
- /**
45499
- * Remove a layer from the map
45500
- */
45501
- removeLayer(layerId) {
45502
- var _a3, _b3;
45503
- this.state.isStyleOperationInProgress = true;
45504
- const layerState = this.state.layerStates[layerId];
45505
- if ((layerState == null ? void 0 : layerState.isCustomLayer) && this.customLayerRegistry) {
45506
- this.removedCustomLayerIds.add(layerId);
45507
- this.customLayerRegistry.setVisibility(layerId, false);
45508
- this.customLayerRegistry.removeLayer(layerId);
45509
- }
45510
- try {
45511
- const layer = this.map.getLayer(layerId);
45512
- if (layer) {
45513
- const sourceId = layer.source;
45514
- this.map.removeLayer(layerId);
45515
- if (sourceId) {
45516
- const style = this.map.getStyle();
45517
- const sourceStillUsed = (_a3 = style == null ? void 0 : style.layers) == null ? void 0 : _a3.some((l3) => l3.source === sourceId);
45518
- if (!sourceStillUsed) {
45519
- try {
45520
- this.map.removeSource(sourceId);
45521
- } catch (e2) {
45522
- }
45523
- }
45524
- }
45525
- }
45526
- } catch (error) {
45527
- console.warn(`Failed to remove layer ${layerId}:`, error);
45528
- }
45529
- delete this.state.layerStates[layerId];
45530
- this.state.customLayerNames.delete(layerId);
45531
- const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
45532
- if (itemEl) {
45533
- itemEl.remove();
45534
- }
45535
- (_b3 = this.onLayerRemove) == null ? void 0 : _b3.call(this, layerId);
45536
- setTimeout(() => {
45537
- this.state.isStyleOperationInProgress = false;
45538
- }, 200);
45539
- }
45540
- // ===== Drag and Drop Methods =====
45541
- /**
45542
- * Create drag handle element
45543
- */
45544
- createDragHandle(layerId) {
45545
- const handle = document.createElement("div");
45546
- handle.className = "layer-control-drag-handle";
45547
- handle.innerHTML = `<svg viewBox="0 0 16 16" fill="currentColor">
45548
- <circle cx="5" cy="3" r="1.5"/>
45549
- <circle cx="11" cy="3" r="1.5"/>
45550
- <circle cx="5" cy="8" r="1.5"/>
45551
- <circle cx="11" cy="8" r="1.5"/>
45552
- <circle cx="5" cy="13" r="1.5"/>
45553
- <circle cx="11" cy="13" r="1.5"/>
45554
- </svg>`;
45555
- handle.title = "Drag to reorder";
45556
- handle.addEventListener("pointerdown", (e2) => {
45557
- e2.preventDefault();
45558
- e2.stopPropagation();
45559
- this.startDrag(layerId, e2);
45560
- });
45561
- return handle;
45562
- }
45563
- /**
45564
- * Create a disabled drag handle for alignment (used for Background layer)
45565
- */
45566
- createDisabledDragHandle() {
45567
- const handle = document.createElement("div");
45568
- handle.className = "layer-control-drag-handle layer-control-drag-handle-disabled";
45569
- handle.innerHTML = `<svg viewBox="0 0 16 16" fill="currentColor">
45570
- <circle cx="5" cy="3" r="1.5"/>
45571
- <circle cx="11" cy="3" r="1.5"/>
45572
- <circle cx="5" cy="8" r="1.5"/>
45573
- <circle cx="11" cy="8" r="1.5"/>
45574
- <circle cx="5" cy="13" r="1.5"/>
45575
- <circle cx="11" cy="13" r="1.5"/>
45576
- </svg>`;
45577
- return handle;
45578
- }
45579
- /**
45580
- * Start dragging a layer
45581
- */
45582
- startDrag(layerId, e2) {
45583
- var _a3;
45584
- const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
45585
- if (!itemEl) return;
45586
- const rect = itemEl.getBoundingClientRect();
45587
- this.state.drag = {
45588
- active: true,
45589
- layerId,
45590
- startY: e2.clientY,
45591
- currentY: e2.clientY,
45592
- placeholder: null,
45593
- draggedElement: null
45594
- };
45595
- this.panel.classList.add("dragging-active");
45596
- itemEl.classList.add("dragging");
45597
- const placeholder = document.createElement("div");
45598
- placeholder.className = "layer-control-drop-placeholder";
45599
- placeholder.style.height = `${rect.height}px`;
45600
- (_a3 = itemEl.parentNode) == null ? void 0 : _a3.insertBefore(placeholder, itemEl);
45601
- this.state.drag.placeholder = placeholder;
45602
- const clone2 = itemEl.cloneNode(true);
45603
- clone2.classList.remove("dragging");
45604
- clone2.className = "layer-control-item layer-control-item-dragging";
45605
- clone2.style.width = `${rect.width}px`;
45606
- clone2.style.left = `${rect.left}px`;
45607
- clone2.style.top = `${rect.top}px`;
45608
- document.body.appendChild(clone2);
45609
- this.state.drag.draggedElement = clone2;
45610
- const onMove = (moveE) => this.onDragMove(moveE);
45611
- const onEnd = (endE) => {
45612
- document.removeEventListener("pointermove", onMove);
45613
- document.removeEventListener("pointerup", onEnd);
45614
- document.removeEventListener("pointercancel", onEnd);
45615
- this.endDrag(endE);
45616
- };
45617
- document.addEventListener("pointermove", onMove);
45618
- document.addEventListener("pointerup", onEnd);
45619
- document.addEventListener("pointercancel", onEnd);
45620
- }
45621
- /**
45622
- * Handle drag move
45623
- */
45624
- onDragMove(e2) {
45625
- var _a3, _b3;
45626
- if (!this.state.drag.active || !this.state.drag.draggedElement) return;
45627
- const placeholder = this.state.drag.placeholder;
45628
- if (!placeholder) return;
45629
- const deltaY = e2.clientY - this.state.drag.startY;
45630
- const clone2 = this.state.drag.draggedElement;
45631
- const currentTop = parseFloat(clone2.style.top) || 0;
45632
- clone2.style.top = `${currentTop + deltaY}px`;
45633
- this.state.drag.startY = e2.clientY;
45634
- this.state.drag.currentY = e2.clientY;
45635
- const items2 = Array.from(this.panel.querySelectorAll(".layer-control-item:not(.dragging)")).filter((item) => item.dataset.layerId !== "Background");
45636
- for (const item of items2) {
45637
- const itemRect = item.getBoundingClientRect();
45638
- if (e2.clientY >= itemRect.top && e2.clientY <= itemRect.bottom) {
45639
- const itemMiddle = itemRect.top + itemRect.height / 2;
45640
- if (e2.clientY < itemMiddle) {
45641
- if (placeholder.nextSibling !== item) {
45642
- (_a3 = item.parentNode) == null ? void 0 : _a3.insertBefore(placeholder, item);
45643
- }
45644
- } else {
45645
- if (placeholder.previousSibling !== item) {
45646
- (_b3 = item.parentNode) == null ? void 0 : _b3.insertBefore(placeholder, item.nextSibling);
45647
- }
45648
- }
45649
- break;
45650
- }
45651
- }
45652
- }
45653
- /**
45654
- * End dragging
45655
- */
45656
- endDrag(_e2) {
45657
- var _a3;
45658
- if (!this.state.drag.active) return;
45659
- const layerId = this.state.drag.layerId;
45660
- const placeholder = this.state.drag.placeholder;
45661
- const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
45662
- if (itemEl && placeholder) {
45663
- (_a3 = placeholder.parentNode) == null ? void 0 : _a3.insertBefore(itemEl, placeholder);
45664
- itemEl.classList.remove("dragging");
45665
- }
45666
- this.cleanupDragState();
45667
- this.applyUIOrderToMap();
45668
- }
45669
- /**
45670
- * Clean up drag state
45671
- */
45672
- cleanupDragState() {
45673
- if (this.state.drag.draggedElement) {
45674
- this.state.drag.draggedElement.remove();
45675
- }
45676
- if (this.state.drag.placeholder) {
45677
- this.state.drag.placeholder.remove();
45678
- }
45679
- this.panel.classList.remove("dragging-active");
45680
- this.panel.querySelectorAll(".layer-control-item.dragging").forEach((el) => {
45681
- el.classList.remove("dragging");
45682
- });
45683
- this.state.drag = {
45684
- active: false,
45685
- layerId: null,
45686
- startY: 0,
45687
- currentY: 0,
45688
- placeholder: null,
45689
- draggedElement: null
45690
- };
45691
- }
45692
- /**
45693
- * Apply UI order to map layers
45694
- */
45695
- applyUIOrderToMap() {
45696
- var _a3, _b3;
45697
- this.state.isStyleOperationInProgress = true;
45698
- const items2 = this.panel.querySelectorAll(".layer-control-item");
45699
- const uiLayerIds = [];
45700
- items2.forEach((item) => {
45701
- const layerId = item.dataset.layerId;
45702
- if (layerId && layerId !== "Background") {
45703
- uiLayerIds.push(layerId);
45704
- }
45705
- });
45706
- const reversedIds = [...uiLayerIds].reverse();
45707
- const style = this.map.getStyle();
45708
- const mapLibreLayerIds = new Set(((_a3 = style == null ? void 0 : style.layers) == null ? void 0 : _a3.map((l3) => l3.id)) || []);
45709
- for (let i2 = 0; i2 < reversedIds.length; i2++) {
45710
- const layerId = reversedIds[i2];
45711
- if (!mapLibreLayerIds.has(layerId)) {
45712
- continue;
45713
- }
45714
- let beforeId = void 0;
45715
- for (let j2 = i2 - 1; j2 >= 0; j2--) {
45716
- if (mapLibreLayerIds.has(reversedIds[j2])) {
45717
- beforeId = reversedIds[j2];
45718
- break;
45719
- }
45720
- }
45721
- try {
45722
- this.map.moveLayer(layerId, beforeId);
45723
- } catch (e2) {
45724
- }
45725
- }
45726
- (_b3 = this.onLayerReorder) == null ? void 0 : _b3.call(this, uiLayerIds);
45727
- setTimeout(() => {
45728
- this.state.isStyleOperationInProgress = false;
45729
- }, 200);
45730
- }
45731
- }
45732
42208
  const DEFAULT_BASEMAPS = {
45733
42209
  liberty: "https://tiles.openfreemap.org/styles/liberty",
45734
42210
  positron: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
@@ -45948,9 +42424,6 @@ function toJsonValue(value) {
45948
42424
  return String(value);
45949
42425
  }
45950
42426
  }
45951
- function basemapStyleUrl(style) {
45952
- return typeof style === "string" && /^https?:\/\//.test(style) ? style : void 0;
45953
- }
45954
42427
  class MapLibreAgentTools {
45955
42428
  constructor(map, options) {
45956
42429
  __publicField(this, "map");
@@ -45958,34 +42431,13 @@ class MapLibreAgentTools {
45958
42431
  __publicField(this, "allowCodeExecution");
45959
42432
  __publicField(this, "allowDestructiveTools");
45960
42433
  __publicField(this, "overlays", new globalThis.Map());
45961
- __publicField(this, "layerControl", null);
45962
- __publicField(this, "pendingLayerControlInstallHandler", null);
45963
42434
  this.map = map;
45964
42435
  this.basemaps = { ...DEFAULT_BASEMAPS, ...options.basemaps };
45965
42436
  this.allowCodeExecution = options.allowCodeExecution;
45966
42437
  this.allowDestructiveTools = options.allowDestructiveTools;
45967
42438
  }
45968
42439
  destroy() {
45969
- if (this.pendingLayerControlInstallHandler) {
45970
- this.map.off("load", this.pendingLayerControlInstallHandler);
45971
- this.pendingLayerControlInstallHandler = null;
45972
- }
45973
42440
  this.clearOverlays();
45974
- this.removeLayerControl();
45975
- }
45976
- installDefaultLayerControl() {
45977
- if (this.layerControl || this.pendingLayerControlInstallHandler) {
45978
- return;
45979
- }
45980
- if (this.map.loaded()) {
45981
- this.installLayerControl();
45982
- return;
45983
- }
45984
- this.pendingLayerControlInstallHandler = () => {
45985
- this.pendingLayerControlInstallHandler = null;
45986
- this.installLayerControl();
45987
- };
45988
- this.map.once("load", this.pendingLayerControlInstallHandler);
45989
42441
  }
45990
42442
  createTools() {
45991
42443
  const optionalStyleSchema = record(string(), any()).optional().describe("Optional MapLibre-compatible paint/style values.");
@@ -46256,11 +42708,9 @@ class MapLibreAgentTools {
46256
42708
  if (typeof style === "string" && this.basemaps[style]) {
46257
42709
  style = this.basemaps[style];
46258
42710
  }
46259
- this.removeLayerControl();
46260
42711
  this.map.setStyle(style);
46261
42712
  await new Promise((resolve2) => this.map.once("style.load", () => resolve2()));
46262
42713
  await this.restoreOverlaysAfterStyleChange();
46263
- this.installLayerControl(style);
46264
42714
  return `Basemap changed to ${rawStyle}.`;
46265
42715
  }
46266
42716
  if (command === "add_marker") {
@@ -46630,25 +43080,6 @@ ${code2}
46630
43080
  }
46631
43081
  }
46632
43082
  }
46633
- installLayerControl(style) {
46634
- this.removeLayerControl();
46635
- const styleUrl = style ? basemapStyleUrl(style) : void 0;
46636
- this.layerControl = new LayerControl({
46637
- collapsed: true,
46638
- ...styleUrl ? { basemapStyleUrl: styleUrl } : {},
46639
- panelWidth: 320,
46640
- panelMinWidth: 240,
46641
- panelMaxWidth: 420
46642
- });
46643
- this.map.addControl(this.layerControl, "top-right");
46644
- }
46645
- removeLayerControl() {
46646
- if (!this.layerControl) {
46647
- return;
46648
- }
46649
- this.map.removeControl(this.layerControl);
46650
- this.layerControl = null;
46651
- }
46652
43083
  requireDestructiveApproval(action) {
46653
43084
  if (!this.allowDestructiveTools()) {
46654
43085
  throw new Error(`${action} is disabled. Enable layer removal first.`);
@@ -46799,7 +43230,6 @@ class GeoAgentControl {
46799
43230
  allowCodeExecution: () => this.state.allowCodeExecution,
46800
43231
  allowDestructiveTools: () => this.state.allowDestructiveTools
46801
43232
  });
46802
- this.tools.installDefaultLayerControl();
46803
43233
  this.setupEventListeners();
46804
43234
  this.loadProviderSettings();
46805
43235
  this.setStatus("Ready", "connected");
@@ -47458,7 +43888,7 @@ ${markdown.trim()}`;
47458
43888
  }
47459
43889
  async createProviderModel(providerId, modelId, apiKey, bedrockRegion) {
47460
43890
  if (providerId === "openai-responses" || providerId === "openai-chat") {
47461
- const { OpenAIModel } = await import("./index-DW6rmZdc.js");
43891
+ const { OpenAIModel } = await import("./index-BU4LZEvR.js");
47462
43892
  return new OpenAIModel({
47463
43893
  api: providerId === "openai-responses" ? "responses" : "chat",
47464
43894
  modelId,
@@ -47469,7 +43899,7 @@ ${markdown.trim()}`;
47469
43899
  });
47470
43900
  }
47471
43901
  if (providerId === "anthropic") {
47472
- const { AnthropicModel } = await import("./anthropic-38gbSx-8.js");
43902
+ const { AnthropicModel } = await import("./anthropic-DhbSHleV.js");
47473
43903
  return new AnthropicModel({
47474
43904
  modelId,
47475
43905
  apiKey,
@@ -47479,7 +43909,7 @@ ${markdown.trim()}`;
47479
43909
  });
47480
43910
  }
47481
43911
  if (providerId === "google") {
47482
- const { GoogleModel } = await import("./index-DtsWabeI.js");
43912
+ const { GoogleModel } = await import("./index-DXB0TYg3.js");
47483
43913
  return new GoogleModel({
47484
43914
  modelId,
47485
43915
  apiKey
@@ -47689,4 +44119,4 @@ export {
47689
44119
  toJsonValue as t,
47690
44120
  warnOnce as w
47691
44121
  };
47692
- //# sourceMappingURL=GeoAgentControl-C-3MIQTc.js.map
44122
+ //# sourceMappingURL=GeoAgentControl-DOAYVbPg.js.map