function evaluateExpression(expression, properties) {
  if (!Array.isArray(expression)) {
    return expression;
  }

  const operator = expression[0];

  switch (operator) {
    case "get":
      return properties[expression[1]];
    case "concat":
      return expression
        .slice(1)
        .map((item) => evaluateExpression(item, properties))
        .join("");
    case "to-string":
      return String(evaluateExpression(expression[1], properties));
    case "to-number":
      return Number(evaluateExpression(expression[1], properties));
    case "number-format":
      const value = evaluateExpression(expression[1], properties);
      const options = expression[2] || {};

      // Handle locale option
      const locale = options.locale || "en-US";

      // Build Intl.NumberFormat options
      const formatOptions = {};

      // Style options
      if (options.style) formatOptions.style = options.style; // 'decimal', 'currency', 'percent', 'unit'
      if (options.currency) formatOptions.currency = options.currency;
      if (options.unit) formatOptions.unit = options.unit;

      // Digit options
      if (options.hasOwnProperty("min-fraction-digits")) {
        formatOptions.minimumFractionDigits = options["min-fraction-digits"];
      }
      if (options.hasOwnProperty("max-fraction-digits")) {
        formatOptions.maximumFractionDigits = options["max-fraction-digits"];
      }
      if (options.hasOwnProperty("min-integer-digits")) {
        formatOptions.minimumIntegerDigits = options["min-integer-digits"];
      }

      // Notation options
      if (options.notation) formatOptions.notation = options.notation; // 'standard', 'scientific', 'engineering', 'compact'
      if (options.compactDisplay)
        formatOptions.compactDisplay = options.compactDisplay; // 'short', 'long'

      // Grouping
      if (options.hasOwnProperty("useGrouping")) {
        formatOptions.useGrouping = options.useGrouping;
      }

      return new Intl.NumberFormat(locale, formatOptions).format(value);
    default:
      // For literals and other simple values
      return expression;
  }
}

// Escape a value for safe insertion as tooltip/popup text.
function escapeTemplateValue(value) {
  return String(value)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

// Render a {brace} template against a properties object. Shared, package-wide
// tooltip/popup syntax. NOTE: duplicated verbatim in mapboxgl.js,
// maplibregl.js, mapboxgl_compare.js, and flowmap.js — keep in sync.
function renderTemplate(template, properties) {
  if (typeof template !== "string") {
    return template;
  }
  return template.replace(/\{([^}]+)\}/g, function (match, path) {
    const value = path
      .trim()
      .split(".")
      .reduce(function (acc, key) {
        return acc == null ? undefined : acc[key];
      }, properties);
    return value == null ? "" : escapeTemplateValue(value);
  });
}

// Resolve a tooltip/popup content spec: array -> expression, "{..}" -> brace
// template, string -> column name lookup.
function resolveTooltipContent(spec, properties) {
  if (Array.isArray(spec)) {
    return evaluateExpression(spec, properties);
  }
  if (typeof spec === "string" && spec.indexOf("{") !== -1) {
    return renderTemplate(spec, properties);
  }
  return properties[spec];
}

// Convert "#rgb"/"#rrggbb" + alpha to rgba(); pass other color strings through.
function tooltipHexToRgba(color, alpha) {
  if (typeof color !== "string" || color.charAt(0) !== "#") {
    return color;
  }
  let h = color.slice(1);
  if (h.length === 3) {
    h = h
      .split("")
      .map(function (c) {
        return c + c;
      })
      .join("");
  }
  const r = parseInt(h.slice(0, 2), 16);
  const g = parseInt(h.slice(2, 4), 16);
  const b = parseInt(h.slice(4, 6), 16);
  return "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")";
}

// Build (and cache) a scoped CSS class from a tooltip/popup style spec. Returns
// null for an empty/absent spec. NOTE: duplicated across the bindings and
// flowmap.js — keep in sync.
function tooltipStyleToClass(spec) {
  if (!spec || typeof spec !== "object") {
    return null;
  }
  if (!window._mapglTooltipStyleClasses) {
    window._mapglTooltipStyleClasses = {};
  }
  const cache = window._mapglTooltipStyleClasses;
  const key = JSON.stringify(spec);
  if (cache[key]) {
    return cache[key];
  }
  const cls = "mapgl-tooltip-style-" + (Object.keys(cache).length + 1);

  let bg = spec.background_color;
  if (bg != null && spec.background_opacity != null) {
    bg = tooltipHexToRgba(bg, spec.background_opacity);
  }

  const content = [];
  if (bg != null) content.push("background:" + bg + ";");
  if (spec.text_color != null) content.push("color:" + spec.text_color + ";");
  if (spec.border_color != null || spec.border_width != null) {
    const bw = spec.border_width == null ? 1 : spec.border_width;
    const bc = spec.border_color == null ? "transparent" : spec.border_color;
    content.push("border:" + bw + "px solid " + bc + ";");
  }
  if (spec.border_radius != null) {
    content.push("border-radius:" + spec.border_radius + "px;");
  }
  if (spec.font_family != null) {
    content.push("font-family:" + spec.font_family + ";");
  }
  if (spec.font_size != null) {
    content.push("font-size:" + spec.font_size + "px;");
  }
  if (spec.font_weight != null) {
    content.push("font-weight:" + spec.font_weight + ";");
  }
  if (spec.padding != null) content.push("padding:" + spec.padding + "px;");
  if (spec.max_width != null) {
    const mw =
      typeof spec.max_width === "number"
        ? spec.max_width + "px"
        : spec.max_width;
    content.push("max-width:" + mw + ";");
  }
  if (spec.shadow) {
    const ss = spec.shadow_size == null ? 8 : spec.shadow_size;
    const sc = spec.shadow_color == null ? "rgba(0, 0, 0, 0.2)" : spec.shadow_color;
    content.push("box-shadow:0 2px " + ss + "px " + sc + ";");
  }

  let css =
    "." +
    cls +
    " .mapboxgl-popup-content, ." +
    cls +
    " .maplibregl-popup-content {" +
    content.join("") +
    "}";
  if (bg != null) {
    css +=
      "." +
      cls +
      " .mapboxgl-popup-tip, ." +
      cls +
      " .maplibregl-popup-tip {border-top-color:" +
      bg +
      ";border-bottom-color:" +
      bg +
      ";}";
  }

  const styleEl = document.createElement("style");
  styleEl.textContent = css;
  document.head.appendChild(styleEl);
  cache[key] = cls;
  return cls;
}

// Apply a style spec's generated class to a popup. (Re)applies on the "open"
// event so a lazily-built tooltip container gets themed in both Mapbox and
// MapLibre, swapping out any previously applied generated class.
function applyPopupClass(popup, spec) {
  if (!popup) {
    return;
  }
  const cls = tooltipStyleToClass(spec);
  const prev = popup._mapglStyleClass;
  if (popup._container && prev && prev !== cls) {
    popup._container.classList.remove(prev);
  }
  popup._mapglStyleClass = cls || null;
  if (popup._container && cls) {
    popup._container.classList.add(cls);
  }
  if (!popup._mapglClassHook && typeof popup.on === "function") {
    popup._mapglClassHook = true;
    popup.on("open", function () {
      if (popup._container && popup._mapglStyleClass) {
        popup._container.classList.add(popup._mapglStyleClass);
      }
    });
  }
}

function formatDmsCoordinate(value, axis, precision) {
  const direction =
    axis === "lng" ? (value < 0 ? "W" : "E") : value < 0 ? "S" : "N";
  const absolute = Math.abs(value);
  let degrees = Math.floor(absolute);
  const minutesFloat = (absolute - degrees) * 60;
  let minutes = Math.floor(minutesFloat);
  let seconds = (minutesFloat - minutes) * 60;

  const factor = Math.pow(10, precision);
  seconds = Math.round(seconds * factor) / factor;
  if (seconds >= 60) {
    seconds = 0;
    minutes += 1;
  }
  if (minutes >= 60) {
    minutes = 0;
    degrees += 1;
  }

  const secondsText = seconds
    .toFixed(precision)
    .padStart(precision > 0 ? precision + 3 : 2, "0");
  return `${degrees}\u00b0${String(minutes).padStart(2, "0")}'${secondsText}"${direction}`;
}

function formatCoordinates(lngLat, format, precision) {
  if (format === "dms") {
    return `${formatDmsCoordinate(lngLat.lng, "lng", precision)}, ${formatDmsCoordinate(lngLat.lat, "lat", precision)}`;
  }
  return `${lngLat.lng.toFixed(precision)}, ${lngLat.lat.toFixed(precision)}`;
}

function createCoordinatesControl(options) {
  const controlOptions = options || {};
  const format = controlOptions.format || "decimal";
  const precisionValue = Number(controlOptions.precision);
  const precision = Number.isFinite(precisionValue)
    ? Math.min(20, Math.max(0, Math.floor(precisionValue)))
    : format === "dms" ? 1 : 5;
  const emptyText = controlOptions.empty_text || "Move cursor over map";
  const labelText = controlOptions.label || "";
  const wrapLongitude = controlOptions.wrap !== false;

  const container = document.createElement("div");
  container.className = "mapgl-coordinates-control maplibregl-ctrl";
  container.style.cssText = `
    background: #ffffff;
    padding: 8px 10px;
    border-radius: 4px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
    color: #222;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 12px;
    line-height: 1.35;
    min-width: 142px;
    max-width: 240px;
    border: 1px solid rgba(0,0,0,0.1);
    pointer-events: none;
  `;

  if (labelText) {
    const label = document.createElement("div");
    label.className = "mapgl-coordinates-label";
    label.textContent = labelText;
    label.style.cssText = `
      margin-bottom: 2px;
      color: #666;
      font-size: 11px;
      font-weight: 600;
      letter-spacing: 0.02em;
      text-transform: uppercase;
    `;
    container.appendChild(label);
  }

  const value = document.createElement("div");
  value.className = "mapgl-coordinates-value";
  value.textContent = emptyText;
  value.style.cssText = `
    color: #111;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  `;
  container.appendChild(value);

  let mapRef = null;
  const update = (event) => {
    if (!event || !event.lngLat) return;
    const lngLat =
      wrapLongitude && typeof event.lngLat.wrap === "function"
        ? event.lngLat.wrap()
        : event.lngLat;
    value.textContent = formatCoordinates(lngLat, format, precision);
  };
  const clear = () => {
    value.textContent = emptyText;
  };

  return {
    onAdd: function (map) {
      mapRef = map;
      mapRef.on("mousemove", update);
      mapRef.getCanvas().addEventListener("mouseleave", clear);
      return container;
    },
    onRemove: function () {
      if (mapRef) {
        mapRef.off("mousemove", update);
        mapRef.getCanvas().removeEventListener("mouseleave", clear);
      }
      if (container.parentNode) {
        container.parentNode.removeChild(container);
      }
      mapRef = null;
    },
  };
}

function createMapglLaserPointer(options) {
  const pointerOptions = options || {};
  const color = pointerOptions.color || "#ff2d55";
  const sizeValue = Number(pointerOptions.size);
  const size = Number.isFinite(sizeValue) && sizeValue > 0 ? sizeValue : 14;

  const pointer = document.createElement("div");
  pointer.className = "mapgl-sync-laser-pointer";
  pointer.style.cssText = `
    position: absolute;
    display: none;
    width: ${size}px;
    height: ${size}px;
    margin: 0;
    padding: 0;
    border: 2px solid ${color};
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.2);
    box-shadow: 0 0 0 2px rgba(255,255,255,0.9), 0 0 14px ${color};
    box-sizing: border-box;
    pointer-events: none;
    transform: translate(-50%, -50%);
    z-index: 10;
  `;

  const dot = document.createElement("div");
  dot.style.cssText = `
    position: absolute;
    left: 50%;
    top: 50%;
    width: 4px;
    height: 4px;
    border-radius: 999px;
    background: ${color};
    transform: translate(-50%, -50%);
  `;
  pointer.appendChild(dot);
  return pointer;
}

function setupSyncLaserPointer(maps, options) {
  const pointers = maps.map(() => createMapglLaserPointer(options));

  maps.forEach((map, index) => {
    map.getContainer().appendChild(pointers[index]);
  });

  const hide = (pointer) => {
    pointer.style.display = "none";
  };

  const update = (targetMap, pointer, event) => {
    if (!event || !event.lngLat || !targetMap || !pointer) return;

    const point = targetMap.project(event.lngLat);
    const container = targetMap.getContainer();
    const width = container.clientWidth;
    const height = container.clientHeight;

    if (
      point.x < 0 ||
      point.y < 0 ||
      point.x > width ||
      point.y > height
    ) {
      hide(pointer);
      return;
    }

    pointer.style.display = "block";
    pointer.style.left = `${point.x}px`;
    pointer.style.top = `${point.y}px`;
  };

  // Moving the cursor on one map shows the laser on every other map
  const moveHandlers = maps.map((sourceMap, index) => (event) => {
    maps.forEach((targetMap, targetIndex) => {
      if (targetIndex !== index) {
        update(targetMap, pointers[targetIndex], event);
      }
    });
  });
  const leaveHandlers = maps.map((sourceMap, index) => () => {
    pointers.forEach((pointer, targetIndex) => {
      if (targetIndex !== index) {
        hide(pointer);
      }
    });
  });

  maps.forEach((map, index) => {
    map.on("mousemove", moveHandlers[index]);
    map.getCanvas().addEventListener("mouseleave", leaveHandlers[index]);
  });

  return function cleanupSyncLaserPointer() {
    maps.forEach((map, index) => {
      map.off("mousemove", moveHandlers[index]);
      map.getCanvas().removeEventListener("mouseleave", leaveHandlers[index]);
      pointers[index].remove();
    });
  };
}

function onMouseMoveTooltip(e, map, tooltipPopup, tooltipProperty, layerId) {
  if (e.features.length > 0) {
    // Query all features at this point to determine z-order
    // Features are returned in top-to-bottom rendering order
    const allFeatures = map.queryRenderedFeatures(e.point);

    // Find the topmost layer that has a tooltip
    let topmostLayerWithTooltip = null;
    for (let i = 0; i < allFeatures.length; i++) {
      const feature = allFeatures[i];
      const featureLayerId = feature.layer.id;

      // Check if this layer has a tooltip handler registered
      if (window._mapboxHandlers && window._mapboxHandlers[featureLayerId]) {
        topmostLayerWithTooltip = featureLayerId;
        break;
      }
    }

    // Only show tooltip if this is the topmost layer with a tooltip
    if (topmostLayerWithTooltip === layerId) {
      map.getCanvas().style.cursor = "pointer";

      // Clear any existing active tooltip first to prevent stacking
      if (window._activeTooltip && window._activeTooltip !== tooltipPopup) {
        window._activeTooltip.remove();
      }

      // tooltipProperty may be a column name, a {brace} template, or a
      // concat()/number_format() expression.
      const description = resolveTooltipContent(
        tooltipProperty,
        e.features[0].properties,
      );

      tooltipPopup.setLngLat(e.lngLat).setHTML(description).addTo(map);

      // Store reference to currently active tooltip
      window._activeTooltip = tooltipPopup;
    } else {
      // This layer is not topmost, hide tooltip if it was showing
      tooltipPopup.remove();
      if (window._activeTooltip === tooltipPopup) {
        delete window._activeTooltip;
      }
    }
  } else {
    tooltipPopup.remove();
    // If this was the active tooltip, clear the reference
    if (window._activeTooltip === tooltipPopup) {
      delete window._activeTooltip;
    }
  }
}

function onMouseLeaveTooltip(map, tooltipPopup) {
  map.getCanvas().style.cursor = "";
  tooltipPopup.remove();
  if (window._activeTooltip === tooltipPopup) {
    delete window._activeTooltip;
  }
}

function onClickPopup(e, map, popupProperty, layerId, popupStyle) {
  if (e.features.length > 0) {
    // Query all features at this point to determine z-order
    const allFeatures = map.queryRenderedFeatures(e.point);

    // Find the topmost layer that has a popup
    let topmostLayerWithPopup = null;
    for (let i = 0; i < allFeatures.length; i++) {
      const feature = allFeatures[i];
      const featureLayerId = feature.layer.id;

      // Check if this layer has a popup handler registered
      if (
        window._mapboxClickHandlers &&
        window._mapboxClickHandlers[featureLayerId]
      ) {
        topmostLayerWithPopup = featureLayerId;
        break;
      }
    }

    // Only show popup if this is the topmost layer with a popup
    if (topmostLayerWithPopup === layerId) {
      // popupProperty may be a column name, a {brace} template, or a
      // concat()/number_format() expression.
      const description = resolveTooltipContent(
        popupProperty,
        e.features[0].properties,
      );

      // Remove any existing popup for this layer
      if (window._maplibrePopups && window._maplibrePopups[layerId]) {
        window._maplibrePopups[layerId].remove();
      }

      // Create and show the popup
      const popup = new maplibregl.Popup({ maxWidth: "400px" })
        .setLngLat(e.lngLat)
        .setHTML(description)
        .addTo(map);
      applyPopupClass(popup, popupStyle);

      // Store reference to this popup
      if (!window._maplibrePopups) {
        window._maplibrePopups = {};
      }
      window._maplibrePopups[layerId] = popup;

      // Remove reference when popup is closed
      popup.on("close", function () {
        if (window._maplibrePopups[layerId] === popup) {
          delete window._maplibrePopups[layerId];
        }
      });
    }
  }
}

HTMLWidgets.widget({
  name: "maplibregl_compare",

  type: "output",

  factory: function (el, width, height) {
    // Store maps and compare object to allow access during Shiny updates
    let beforeMap, afterMap, compareControl, draw;
    let compareMaps = [];
    let compareMapsData = [];

    return {
      renderValue: function (x) {
        if (typeof maplibregl === "undefined") {
          console.error("Maplibre GL JS is not loaded.");
          return;
        }
        if (typeof maplibregl.Compare === "undefined") {
          console.error("Maplibre GL Compare plugin is not loaded.");
          return;
        }

        // Add PMTiles support
        if (typeof pmtiles !== "undefined") {
          let protocol = new pmtiles.Protocol({ metadata: true });
          maplibregl.addProtocol("pmtiles", protocol.tile);
        }

        // Set position relative on container to properly contain absolutely positioned maps
        el.style.position = "relative";

        // Normalize map configs: x.maps is present for sync grids with more
        // than two maps; classic compare widgets supply x.map1/x.map2
        compareMapsData = Array.isArray(x.maps) && x.maps.length > 0
          ? x.maps
          : [x.map1, x.map2];
        const nMaps = compareMapsData.length;

        // Side names drive container ids, Shiny input prefixes, and proxy
        // targeting; 2-map widgets keep the historical "before"/"after" names
        const sideNameFor = (index) =>
          nMaps === 2 ? (index === 0 ? "before" : "after") : `map${index + 1}`;
        const containerIds = compareMapsData.map(
          (mapData, index) => `${el.id}-${sideNameFor(index)}`,
        );

        // Different HTML structure based on mode
        if (x.mode === "sync") {
          // Grid of synced maps; legacy 2-map sync keeps its side-by-side
          // (vertical) or stacked (horizontal) layout via the column count
          const cols =
            x.sync_cols || (x.orientation === "horizontal" ? 1 : 2);

          const containerStyle = `display: grid; grid-template-columns: repeat(${cols}, 1fr); grid-auto-rows: 1fr; width: 100%; height: 100%;`;
          const mapStyle = `position: relative; width: 100%; height: 100%; min-width: 0; min-height: 0;`;

          el.innerHTML = `
                      <div style="${containerStyle}">
                        ${containerIds
                          .map(
                            (id) =>
                              `<div id="${id}" class="map" style="${mapStyle}"></div>`,
                          )
                          .join("\n")}
                      </div>
                    `;
        } else {
          // Default swipe mode
          el.innerHTML = `
                      <div id="${containerIds[0]}" class="map" style="width: 100%; height: 100%; position: absolute;"></div>
                      <div id="${containerIds[1]}" class="map" style="width: 100%; height: 100%; position: absolute;"></div>
                    `;
        }

        compareMaps = compareMapsData.map(function (mapData, index) {
          const syncedMap = new maplibregl.Map({
            container: containerIds[index],
            style: mapData.style,
            center: mapData.center,
            zoom: mapData.zoom,
            bearing: mapData.bearing,
            pitch: mapData.pitch,
            accessToken: mapData.access_token,
            ...mapData.additional_params,
          });

          // Initialize controls array
          syncedMap.controls = [];

          // Set projection on style load (MapLibre doesn't support projection in constructor)
          syncedMap.on("style.load", function () {
            if (!syncedMap._basemapLayerIds) {
              syncedMap._basemapLayerIds = new Set(
                syncedMap.getStyle().layers.map((layer) => layer.id),
              );
            }

            if (mapData.projection) {
              syncedMap.setProjection({ type: mapData.projection });
            }
          });

          return syncedMap;
        });

        beforeMap = compareMaps[0];
        afterMap = compareMaps[1];

        // Resolve a side name ("before", "after", or "mapN") to its map.
        // An out-of-range "mapN" returns undefined so callers no-op rather
        // than mutating the wrong map; other unknown values fall back to the
        // after map, matching the historical behavior where anything other
        // than "before" targeted the after map.
        const resolveCompareMap = (side) => {
          if (side === "before") return compareMaps[0];
          if (side === "after") return compareMaps[1];
          const match = /^map(\d+)$/.exec(side || "");
          if (match) {
            return compareMaps[parseInt(match[1], 10) - 1];
          }
          return afterMap;
        };

        if (x.mode === "swipe") {
          // Only create the swiper in swipe mode
          compareControl = new maplibregl.Compare(
            beforeMap,
            afterMap,
            `#${el.id}`,
            {
              mousemove: x.mousemove,
              orientation: x.orientation,
            },
          );

          // Apply custom swiper color if provided
          if (x.swiper_color) {
            const swiperSelector =
              x.orientation === "vertical"
                ? ".maplibregl-compare .compare-swiper-vertical"
                : ".maplibregl-compare .compare-swiper-horizontal";

            const styleEl = document.createElement("style");
            styleEl.innerHTML = `${swiperSelector} { background-color: ${x.swiper_color}; }`;
            document.head.appendChild(styleEl);
          }
        } else {
          // For sync mode, we directly leverage the sync-move module's approach

          // Function to synchronize maps as seen in the mapbox-gl-sync-move module
          const syncMaps = () => {
            // Array of maps to sync
            const maps = compareMaps;
            // Array of move event handlers
            const moveHandlers = [];

            // Setup the sync between maps
            maps.forEach((map, index) => {
              // Create a handler for each map that syncs all other maps
              moveHandlers[index] = (e) => {
                // Disable all move events temporarily
                maps.forEach((m, i) => {
                  m.off("move", moveHandlers[i]);
                });

                // Get the state from the map that triggered the event
                const center = map.getCenter();
                const zoom = map.getZoom();
                const bearing = map.getBearing();
                const pitch = map.getPitch();

                // Apply this state to all other maps
                maps
                  .filter((m, i) => i !== index)
                  .forEach((m) => {
                    m.jumpTo({
                      center: center,
                      zoom: zoom,
                      bearing: bearing,
                      pitch: pitch,
                    });
                  });

                // Re-enable move events
                maps.forEach((m, i) => {
                  m.on("move", moveHandlers[i]);
                });
              };

              // Add the move handler to each map
              map.on("move", moveHandlers[index]);
            });
          };

          // Initialize the sync
          syncMaps();

          if (x.laser && x.laser.enabled) {
            setupSyncLaserPointer(compareMaps, x.laser);
          }
        }

        // Add compare-level legends once every map has loaded (load order is
        // not guaranteed, and legend interactivity depends on layers added
        // during each map's modifications)
        let loadedCount = 0;
        const addCompareLegends = function () {
          if (!x.compare_legends || !Array.isArray(x.compare_legends)) {
            return;
          }
          x.compare_legends.forEach(function (legendInfo) {
            // Add CSS
            const legendCss = document.createElement("style");
            legendCss.innerHTML = legendInfo.css;
            legendCss.setAttribute("data-mapgl-legend-css", el.id);
            document.head.appendChild(legendCss);

            // Create legend element
            const legend = document.createElement("div");
            legend.innerHTML = legendInfo.html;
            legend.classList.add("mapboxgl-legend");

            // Append to the appropriate container based on target
            if (legendInfo.target === "compare") {
              // Append to the main compare container
              el.appendChild(legend);
            } else {
              const targetMap = resolveCompareMap(legendInfo.target);
              if (targetMap) {
                targetMap.getContainer().appendChild(legend);
                if (
                  legendInfo.interactivity &&
                  typeof initializeLegendInteractivity === "function"
                ) {
                  initializeLegendInteractivity(
                    targetMap,
                    targetMap.getContainer().id,
                    legendInfo.interactivity,
                  );
                }
              }
            }
          });
        };

        // Ensure all maps resize correctly
        compareMaps.forEach(function (syncedMap, index) {
          syncedMap.on("load", function () {
            syncedMap.resize();
            applyMapModifications(syncedMap, compareMapsData[index]);

            // Setup Shiny event handlers for this map
            if (HTMLWidgets.shinyMode) {
              setupShinyEvents(syncedMap, el.id, sideNameFor(index));
            }

            loadedCount++;
            if (loadedCount === compareMaps.length) {
              addCompareLegends();
            }
          });
        });

        // Define updateDrawnFeatures function for the draw tool
        window.updateDrawnFeatures = function () {
          if (draw) {
            const features = draw.getAll();
            Shiny.setInputValue(
              el.id + "_drawn_features",
              JSON.stringify(features),
            );
          }
        };

        // Handle Shiny messages
        if (HTMLWidgets.shinyMode) {
          Shiny.addCustomMessageHandler(
            "maplibre-compare-proxy",
            async function (data) {
              if (data.id !== el.id) return;

              // Get the message and determine which map to target
              var message = data.message;
              var map = resolveCompareMap(message.map);

              if (!map) return;

              // Initialize layer state tracking if not already present
              if (!window._mapglLayerState) {
                window._mapglLayerState = {};
              }
              const mapId = map.getContainer().id;
              if (!window._mapglLayerState[mapId]) {
                window._mapglLayerState[mapId] = {
                  filters: {}, // layerId -> filter expression
                  paintProperties: {}, // layerId -> {propertyName -> value}
                  layoutProperties: {}, // layerId -> {propertyName -> value}
                  tooltips: {}, // layerId -> tooltip property
                  popups: {}, // layerId -> popup property
                  legends: {}, // legendId -> {html: string, css: string}
                };
              }
              const layerState = window._mapglLayerState[mapId];

              // Process the message based on type
              if (message.type === "set_filter") {
                map.setFilter(message.layer, message.filter);
                // Track filter state for layer restoration
                layerState.filters[message.layer] = message.filter;
              } else if (message.type === "add_source") {
                if (message.source.type === "vector") {
                  const sourceConfig = {
                    type: "vector",
                  };
                  // Add url or tiles
                  if (message.source.url) {
                    sourceConfig.url = message.source.url;
                    // Auto-detect MLT encoding for PMTiles sources
                    if (
                      message.source.url.startsWith("pmtiles://") &&
                      typeof pmtiles !== "undefined" &&
                      !message.source.encoding
                    ) {
                      try {
                        const rawUrl = message.source.url.replace("pmtiles://", "");
                        const p = new pmtiles.PMTiles(rawUrl);
                        const header = await p.getHeader();
                        if (header.tileType === pmtiles.TileType.Mlt) {
                          sourceConfig.encoding = "mlt";
                        }
                      } catch (e) {
                        console.warn("Failed to detect PMTiles tile type:", e);
                      }
                    }
                  }
                  if (message.source.tiles) {
                    sourceConfig.tiles = message.source.tiles;
                  }
                  // Add promoteId if provided
                  if (message.source.promoteId) {
                    sourceConfig.promoteId = message.source.promoteId;
                  }
                  // Add any other properties from the source object
                  Object.keys(message.source).forEach(function (key) {
                    if (
                      key !== "id" &&
                      key !== "type" &&
                      key !== "url" &&
                      key !== "tiles" &&
                      key !== "promoteId"
                    ) {
                      sourceConfig[key] = message.source[key];
                    }
                  });
                  map.addSource(message.source.id, sourceConfig);
                } else if (message.source.type === "geojson") {
                  const sourceConfig = {
                    type: "geojson",
                    data: message.source.data,
                    generateId: message.source.generateId,
                  };
                  // Add any other properties from the source object
                  Object.keys(message.source).forEach(function (key) {
                    if (
                      key !== "id" &&
                      key !== "type" &&
                      key !== "data" &&
                      key !== "generateId"
                    ) {
                      sourceConfig[key] = message.source[key];
                    }
                  });
                  map.addSource(message.source.id, sourceConfig);
                } else if (message.source.type === "raster") {
                  const sourceConfig = {
                    type: "raster",
                    tileSize: message.source.tileSize,
                  };
                  if (message.source.url) {
                    sourceConfig.url = message.source.url;
                  } else if (message.source.tiles) {
                    sourceConfig.tiles = message.source.tiles;
                  }
                  if (message.source.maxzoom) {
                    sourceConfig.maxzoom = message.source.maxzoom;
                  }
                  // Add any other properties from the source object
                  Object.keys(message.source).forEach(function (key) {
                    if (
                      key !== "id" &&
                      key !== "type" &&
                      key !== "url" &&
                      key !== "tiles" &&
                      key !== "tileSize" &&
                      key !== "maxzoom"
                    ) {
                      sourceConfig[key] = message.source[key];
                    }
                  });
                  map.addSource(message.source.id, sourceConfig);
                } else if (message.source.type === "raster-dem") {
                  const sourceConfig = {
                    type: "raster-dem",
                    url: message.source.url,
                    tileSize: message.source.tileSize,
                  };
                  if (message.source.maxzoom) {
                    sourceConfig.maxzoom = message.source.maxzoom;
                  }
                  // Add any other properties from the source object
                  Object.keys(message.source).forEach(function (key) {
                    if (
                      key !== "id" &&
                      key !== "type" &&
                      key !== "url" &&
                      key !== "tileSize" &&
                      key !== "maxzoom"
                    ) {
                      sourceConfig[key] = message.source[key];
                    }
                  });
                  map.addSource(message.source.id, sourceConfig);
                } else if (message.source.type === "image") {
                  const sourceConfig = {
                    type: "image",
                    url: message.source.url,
                    coordinates: message.source.coordinates,
                  };
                  // Add any other properties from the source object
                  Object.keys(message.source).forEach(function (key) {
                    if (
                      key !== "id" &&
                      key !== "type" &&
                      key !== "url" &&
                      key !== "coordinates"
                    ) {
                      sourceConfig[key] = message.source[key];
                    }
                  });
                  map.addSource(message.source.id, sourceConfig);
                } else if (message.source.type === "video") {
                  const sourceConfig = {
                    type: "video",
                    urls: message.source.urls,
                    coordinates: message.source.coordinates,
                  };
                  // Add any other properties from the source object
                  Object.keys(message.source).forEach(function (key) {
                    if (
                      key !== "id" &&
                      key !== "type" &&
                      key !== "urls" &&
                      key !== "coordinates"
                    ) {
                      sourceConfig[key] = message.source[key];
                    }
                  });
                  map.addSource(message.source.id, sourceConfig);
                } else {
                  // Handle custom source types
                  const sourceConfig = { type: message.source.type };

                  // Copy all properties except id
                  Object.keys(message.source).forEach(function (key) {
                    if (key !== "id") {
                      sourceConfig[key] = message.source[key];
                    }
                  });

                  map.addSource(message.source.id, sourceConfig);
                }
              } else if (message.type === "add_layer") {
                try {
                  // Ensure paint and layout are objects, not null
                  if (message.layer.paint === null) {
                    message.layer.paint = {};
                  }
                  if (message.layer.layout === null) {
                    message.layer.layout = {};
                  }
                  if (message.layer.before_id) {
                    map.addLayer(message.layer, message.layer.before_id);
                  } else {
                    map.addLayer(message.layer);
                  }

                  // Add popups or tooltips if provided
                  if (message.layer.popup) {
                    // Create click handler for this layer
                    const clickHandler = function (e) {
                      onClickPopup(
                        e,
                        map,
                        message.layer.popup,
                        message.layer.id,
                        message.layer.popup_style,
                      );
                    };

                    // Store these handler references so we can remove them later if needed
                    if (!window._mapboxClickHandlers) {
                      window._mapboxClickHandlers = {};
                    }
                    window._mapboxClickHandlers[message.layer.id] =
                      clickHandler;

                    // Add the click handler
                    map.on("click", message.layer.id, clickHandler);

                    // Change cursor to pointer when hovering over the layer
                    map.on("mouseenter", message.layer.id, function () {
                      map.getCanvas().style.cursor = "pointer";
                    });

                    // Change cursor back to default when leaving the layer
                    map.on("mouseleave", message.layer.id, function () {
                      map.getCanvas().style.cursor = "";
                    });
                  }

                  if (message.layer.tooltip) {
                    const tooltip = new maplibregl.Popup({
                      closeButton: false,
                      closeOnClick: false,
                      maxWidth: "400px",
                    });
                    applyPopupClass(tooltip, message.layer.tooltip_style);

                    // Define named handler functions:
                    const mouseMoveHandler = function (e) {
                      onMouseMoveTooltip(
                        e,
                        map,
                        tooltip,
                        message.layer.tooltip,
                        message.layer.id,
                      );
                    };

                    const mouseLeaveHandler = function () {
                      onMouseLeaveTooltip(map, tooltip);
                    };

                    // Attach handlers by reference:
                    map.on("mousemove", message.layer.id, mouseMoveHandler);
                    map.on("mouseleave", message.layer.id, mouseLeaveHandler);

                    // Store these handler references for later removal:
                    if (!window._mapboxHandlers) {
                      window._mapboxHandlers = {};
                    }
                    window._mapboxHandlers[message.layer.id] = {
                      mousemove: mouseMoveHandler,
                      mouseleave: mouseLeaveHandler,
                    };
                  }

                  // Add hover effect if provided
                  if (message.layer.hover_options) {
                    const jsHoverOptions = {};
                    for (const [key, value] of Object.entries(
                      message.layer.hover_options,
                    )) {
                      const jsKey = key.replace(/_/g, "-");
                      jsHoverOptions[jsKey] = value;
                    }

                    let hoveredFeatureId = null;

                    map.on("mousemove", message.layer.id, function (e) {
                      if (e.features.length > 0) {
                        if (hoveredFeatureId !== null) {
                          const featureState = {
                            source:
                              typeof message.layer.source === "string"
                                ? message.layer.source
                                : message.layer.id,
                            id: hoveredFeatureId,
                          };
                          if (message.layer.source_layer) {
                            featureState.sourceLayer =
                              message.layer.source_layer;
                          }
                          map.setFeatureState(featureState, {
                            hover: false,
                          });
                        }
                        hoveredFeatureId = e.features[0].id;
                        const featureState = {
                          source:
                            typeof message.layer.source === "string"
                              ? message.layer.source
                              : message.layer.id,
                          id: hoveredFeatureId,
                        };
                        if (message.layer.source_layer) {
                          featureState.sourceLayer = message.layer.source_layer;
                        }
                        map.setFeatureState(featureState, {
                          hover: true,
                        });
                      }
                    });

                    map.on("mouseleave", message.layer.id, function () {
                      if (hoveredFeatureId !== null) {
                        const featureState = {
                          source:
                            typeof message.layer.source === "string"
                              ? message.layer.source
                              : message.layer.id,
                          id: hoveredFeatureId,
                        };
                        if (message.layer.source_layer) {
                          featureState.sourceLayer = message.layer.source_layer;
                        }
                        map.setFeatureState(featureState, {
                          hover: false,
                        });
                      }
                      hoveredFeatureId = null;
                    });

                    Object.keys(jsHoverOptions).forEach(function (key) {
                      const originalPaint =
                        map.getPaintProperty(message.layer.id, key) ||
                        message.layer.paint[key];
                      map.setPaintProperty(message.layer.id, key, [
                        "case",
                        ["boolean", ["feature-state", "hover"], false],
                        jsHoverOptions[key],
                        originalPaint,
                      ]);
                    });
                  }
                } catch (e) {
                  console.error(
                    "Failed to add layer via proxy: ",
                    message.layer,
                    e,
                  );
                }
              } else if (message.type === "remove_layer") {
                // If there's an active tooltip, remove it first
                if (window._activeTooltip) {
                  window._activeTooltip.remove();
                  delete window._activeTooltip;
                }

                // If there's an active popup for this layer, remove it
                // Check both message.layer_id and message.layer.id as keys due to different message formats
                if (window._mapboxPopups) {
                  // First check if we have a popup stored with message.layer_id key
                  if (window._mapboxPopups[message.layer_id]) {
                    window._mapboxPopups[message.layer_id].remove();
                    delete window._mapboxPopups[message.layer_id];
                  }

                  // Also check if we have a popup stored with message.layer.id key, which happens when added via add_layer
                  if (
                    message.layer &&
                    message.layer.id &&
                    window._mapboxPopups[message.layer.id]
                  ) {
                    window._mapboxPopups[message.layer.id].remove();
                    delete window._mapboxPopups[message.layer.id];
                  }
                }

                if (map.getLayer(message.layer_id)) {
                  // Remove tooltip handlers
                  if (
                    window._mapboxHandlers &&
                    window._mapboxHandlers[message.layer_id]
                  ) {
                    const handlers = window._mapboxHandlers[message.layer_id];
                    if (handlers.mousemove) {
                      map.off(
                        "mousemove",
                        message.layer_id,
                        handlers.mousemove,
                      );
                    }
                    if (handlers.mouseleave) {
                      map.off(
                        "mouseleave",
                        message.layer_id,
                        handlers.mouseleave,
                      );
                    }
                    // Clean up the reference
                    delete window._mapboxHandlers[message.layer_id];
                  }

                  // Remove click handlers for popups
                  if (window._mapboxClickHandlers) {
                    // First check for handlers stored with message.layer_id key
                    if (window._mapboxClickHandlers[message.layer_id]) {
                      map.off(
                        "click",
                        message.layer_id,
                        window._mapboxClickHandlers[message.layer_id],
                      );
                      delete window._mapboxClickHandlers[message.layer_id];
                    }

                    // Also check for handlers stored with message.layer.id key from add_layer
                    if (
                      message.layer &&
                      message.layer.id &&
                      window._mapboxClickHandlers[message.layer.id]
                    ) {
                      map.off(
                        "click",
                        message.layer_id,
                        window._mapboxClickHandlers[message.layer.id],
                      );
                      delete window._mapboxClickHandlers[message.layer.id];
                    }
                  }

                  // Remove the layer
                  map.removeLayer(message.layer_id);
                }
                if (map.getSource(message.layer_id)) {
                  map.removeSource(message.layer_id);
                }

                // Clean up tracked layer state
                const mapId = map.getContainer().id;
                if (window._mapglLayerState && window._mapglLayerState[mapId]) {
                  const layerState = window._mapglLayerState[mapId];
                  delete layerState.filters[message.layer_id];
                  delete layerState.paintProperties[message.layer_id];
                  delete layerState.layoutProperties[message.layer_id];
                  delete layerState.tooltips[message.layer_id];
                  delete layerState.popups[message.layer_id];
                  // Note: legends are not tied to specific layers, so we don't clear them here
                }
              } else if (message.type === "fit_bounds") {
                map.fitBounds(message.bounds, message.options);
              } else if (message.type === "fly_to") {
                map.flyTo(message.options);
              } else if (message.type === "ease_to") {
                map.easeTo(message.options);
              } else if (message.type === "set_center") {
                map.setCenter(message.center);
              } else if (message.type === "set_zoom") {
                map.setZoom(message.zoom);
              } else if (message.type === "jump_to") {
                map.jumpTo(message.options);
              } else if (message.type === "set_layout_property") {
                map.setLayoutProperty(
                  message.layer,
                  message.name,
                  message.value,
                );
                // Track layout property state for layer restoration
                if (!layerState.layoutProperties[message.layer]) {
                  layerState.layoutProperties[message.layer] = {};
                }
                layerState.layoutProperties[message.layer][message.name] =
                  message.value;
              } else if (message.type === "set_flowmap_filter") {
                if (window.MapGLFlowmapPlugin) {
                  window.MapGLFlowmapPlugin.setFilter(
                    map,
                    message.id,
                    message.filter,
                  );
                }
              } else if (message.type === "set_flowmap_settings") {
                if (window.MapGLFlowmapPlugin) {
                  window.MapGLFlowmapPlugin.setSettings(
                    map,
                    message.id,
                    message.settings,
                  );
                }
              } else if (message.type === "set_paint_property") {
                const layerId = message.layer;
                const propertyName = message.name;
                const newValue = message.value;

                // Check if the layer has hover options
                const layerStyle = map
                  .getStyle()
                  .layers.find((layer) => layer.id === layerId);
                const currentPaintProperty = map.getPaintProperty(
                  layerId,
                  propertyName,
                );

                if (
                  currentPaintProperty &&
                  Array.isArray(currentPaintProperty) &&
                  currentPaintProperty[0] === "case"
                ) {
                  // This property has hover options, so we need to preserve them
                  const hoverValue = currentPaintProperty[2];
                  const newPaintProperty = [
                    "case",
                    ["boolean", ["feature-state", "hover"], false],
                    hoverValue,
                    newValue,
                  ];
                  map.setPaintProperty(layerId, propertyName, newPaintProperty);
                } else {
                  // No hover options, just set the new value directly
                  map.setPaintProperty(layerId, propertyName, newValue);
                }
                // Track paint property state for layer restoration
                if (!layerState.paintProperties[layerId]) {
                  layerState.paintProperties[layerId] = {};
                }
                layerState.paintProperties[layerId][propertyName] = newValue;
              } else if (message.type === "add_legend") {
                if (!message.add) {
                  const existingLegends = document.querySelectorAll(
                    `#${data.id} .maplibregl-legend`,
                  );
                  existingLegends.forEach((legend) => legend.remove());

                  // Clean up any existing legend styles that might have been added
                  const legendStyles = document.querySelectorAll(
                    `style[data-mapgl-legend-css="${data.id}"]`,
                  );
                  legendStyles.forEach((style) => style.remove());
                }

                const legendCss = document.createElement("style");
                legendCss.innerHTML = message.legend_css;
                legendCss.setAttribute("data-mapgl-legend-css", data.id); // Mark this style for later cleanup
                document.head.appendChild(legendCss);

                const legend = document.createElement("div");
                legend.innerHTML = message.html;
                legend.classList.add("maplibregl-legend");

                // Append legend to the correct map container
                const targetContainer = map.getContainer();
                targetContainer.appendChild(legend);
                if (
                  message.interactivity &&
                  typeof initializeLegendInteractivity === "function"
                ) {
                  initializeLegendInteractivity(
                    map,
                    targetContainer.id,
                    message.interactivity,
                  );
                }
              } else if (message.type === "set_config_property") {
                map.setConfigProperty(
                  message.importId,
                  message.configName,
                  message.value,
                );
              } else if (message.type === "set_style") {
                // Save the current view state
                const center = map.getCenter();
                const zoom = map.getZoom();
                const bearing = map.getBearing();
                const pitch = map.getPitch();

                // Default preserve_layers to true if not specified
                const preserveLayers = message.preserve_layers !== false;

                // If we should preserve layers and sources
                if (preserveLayers) {
                  // Store the current style before changing it
                  const currentStyle = map.getStyle();
                  const userSourceIds = [];
                  const userLayers = [];

                  console.log(
                    "[MapGL Debug] Current style sources:",
                    Object.keys(currentStyle.sources),
                  );
                  console.log(
                    "[MapGL Debug] Current style layers:",
                    currentStyle.layers.map((l) => l.id),
                  );

                  // Store layer IDs we know were added by the user via R code
                  // This is the most reliable way to identify user-added layers
                  const knownUserLayerIds = [];

                  // For each layer in the current style, determine if it's a user-added layer
                  currentStyle.layers.forEach(function (layer) {
                    const layerId = layer.id;

                    // Critical: Check for nc_counties specifically since we know that's used in the test app
                    if (layerId === "nc_counties") {
                      console.log(
                        "[MapGL Debug] Found explicit test layer:",
                        layerId,
                      );
                      knownUserLayerIds.push(layerId);
                      if (
                        layer.source &&
                        !userSourceIds.includes(layer.source)
                      ) {
                        console.log(
                          "[MapGL Debug] Found source from test layer:",
                          layer.source,
                        );
                        userSourceIds.push(layer.source);
                      }
                      return; // Skip other checks for this layer
                    }

                    // These are common patterns for user-added layers from R code
                    if (
                      // Specific layer IDs from the R package
                      layerId.endsWith("_counties") ||
                      layerId.endsWith("_label") ||
                      layerId.endsWith("_layer") ||
                      // Look for hover handlers - only user-added layers have these
                      (window._mapboxHandlers &&
                        window._mapboxHandlers[layerId]) ||
                      // If the layer ID contains these strings, it's likely user-added
                      layerId.includes("user") ||
                      layerId.includes("custom") ||
                      // If the paint property has a hover case, it's user-added
                      (layer.paint &&
                        Object.values(layer.paint).some(
                          (value) =>
                            Array.isArray(value) &&
                            value[0] === "case" &&
                            Array.isArray(value[1]) &&
                            value[1][1] &&
                            Array.isArray(value[1][1]) &&
                            value[1][1][0] === "feature-state" &&
                            value[1][1][1] === "hover",
                        ))
                    ) {
                      console.log("[MapGL Debug] Found user layer:", layerId);
                      knownUserLayerIds.push(layerId);
                      // Only include its source if it's not a base map source
                      if (
                        layer.source &&
                        !userSourceIds.includes(layer.source)
                      ) {
                        const layerSource = currentStyle.sources[layer.source];
                        const isBaseMapSource =
                          layerSource &&
                          layerSource.type === "vector" &&
                          (layer.source === "composite" ||
                            layer.source === "mapbox" ||
                            layer.source.startsWith("mapbox-") ||
                            layer.source === "openmaptiles" ||
                            layer.source.startsWith("carto") ||
                            layer.source.startsWith("maptiler"));

                        if (!isBaseMapSource) {
                          console.log(
                            "[MapGL Debug] Found user source from layer:",
                            layer.source,
                          );
                          userSourceIds.push(layer.source);
                        } else {
                          console.log(
                            "[MapGL Debug] Not adding base map source from layer:",
                            layer.source,
                          );
                        }
                      }
                    }
                  });

                  // For each source, determine if it's a user-added source
                  for (const sourceId in currentStyle.sources) {
                    const source = currentStyle.sources[sourceId];

                    // Strategy 1: All GeoJSON sources are likely user-added
                    if (source.type === "geojson") {
                      console.log(
                        "[MapGL Debug] Found user GeoJSON source:",
                        sourceId,
                      );
                      if (!userSourceIds.includes(sourceId)) {
                        userSourceIds.push(sourceId);
                      }
                    }
                    // Strategy 2: Check for source data URL patterns typical of R-generated data
                    else if (
                      source.url &&
                      typeof source.url === "string" &&
                      (source.url.includes("data:application/json") ||
                        source.url.includes("blob:"))
                    ) {
                      console.log(
                        "[MapGL Debug] Found user source with data URL:",
                        sourceId,
                      );
                      if (!userSourceIds.includes(sourceId)) {
                        userSourceIds.push(sourceId);
                      }
                    }
                    // Strategy 3: Standard filtering - exclude common base map sources
                    else if (
                      sourceId !== "composite" &&
                      sourceId !== "mapbox" &&
                      !sourceId.startsWith("mapbox-") &&
                      sourceId !== "openmaptiles" && // Common in MapLibre styles
                      !(
                        sourceId.startsWith("carto") &&
                        sourceId !== "carto-source"
                      ) && // Filter CARTO base sources but keep user ones
                      !(
                        sourceId.startsWith("maptiler") &&
                        !sourceId.includes("user")
                      ) && // Filter MapTiler sources but keep user ones
                      !sourceId.includes("terrain") && // Common terrain sources
                      !sourceId.includes("hillshade") && // Common hillshade sources
                      !(
                        sourceId.includes("basemap") &&
                        !sourceId.includes("user")
                      ) // Filter basemap sources but keep user ones
                    ) {
                      console.log(
                        "[MapGL Debug] Found user source via filtering:",
                        sourceId,
                      );
                      if (!userSourceIds.includes(sourceId)) {
                        userSourceIds.push(sourceId);
                      }
                    }

                    // Store layer-specific handler references
                    if (window._mapboxHandlers) {
                      const handlers = window._mapboxHandlers;
                      for (const layerId in handlers) {
                        // Find layers associated with this source
                        const layer = currentStyle.layers.find(
                          (l) => l.id === layerId,
                        );
                        if (layer && layer.source === sourceId) {
                          layer._handlers = handlers[layerId];
                        }
                      }
                    }
                  }

                  // Identify layers using user-added sources or known user layer IDs
                  // ONLY include layers that use genuinely user-added sources (not base map sources)
                  currentStyle.layers.forEach(function (layer) {
                    // Check if this layer uses a genuine user source (not filtered out base map sources)
                    const usesUserSource = userSourceIds.includes(layer.source);
                    const isKnownUserLayer = knownUserLayerIds.includes(
                      layer.id,
                    );

                    // Additional check: exclude layers that use base map sources even if they were temporarily added to userSourceIds
                    const layerSource = currentStyle.sources[layer.source];
                    const isBaseMapSource =
                      layerSource &&
                      layerSource.type === "vector" &&
                      (layer.source === "composite" ||
                        layer.source === "mapbox" ||
                        layer.source.startsWith("mapbox-") ||
                        layer.source === "openmaptiles" ||
                        layer.source.startsWith("carto") ||
                        layer.source.startsWith("maptiler"));

                    if (
                      (usesUserSource || isKnownUserLayer) &&
                      !isBaseMapSource
                    ) {
                      // Capture current visibility state (may differ from layer definition)
                      const currentVisibility = map.getLayoutProperty(layer.id, 'visibility');
                      if (currentVisibility !== undefined) {
                        if (!layer.layout) {
                          layer.layout = {};
                        }
                        layer.layout.visibility = currentVisibility;
                      }
                      userLayers.push(layer);
                      console.log(
                        "[MapGL Debug] Including user layer:",
                        layer.id,
                        "source:",
                        layer.source,
                      );
                    } else if (isBaseMapSource) {
                      console.log(
                        "[MapGL Debug] Excluding base map layer:",
                        layer.id,
                        "source:",
                        layer.source,
                      );
                    }
                  });

                  // Set up event listener to re-add sources and layers after style loads
                  const onStyleLoad = function () {
                    // Re-add user sources
                    userSourceIds.forEach(function (sourceId) {
                      if (!map.getSource(sourceId)) {
                        const source = currentStyle.sources[sourceId];
                        map.addSource(sourceId, source);
                      }
                    });

                    // Re-add user layers
                    userLayers.forEach(function (layer) {
                      if (!map.getLayer(layer.id)) {
                        map.addLayer(layer);

                        // Explicitly set visibility if it was set to 'none'
                        if (layer.layout && layer.layout.visibility === 'none') {
                          map.setLayoutProperty(layer.id, 'visibility', 'none');
                        }

                        // Re-add event handlers for tooltips and hover effects
                        if (layer._handlers) {
                          const handlers = layer._handlers;

                          if (handlers.mousemove) {
                            console.log(
                              "[MapGL Debug] Re-adding mousemove handler for:",
                              layer.id,
                            );
                            map.on("mousemove", layer.id, handlers.mousemove);
                          }

                          if (handlers.mouseleave) {
                            console.log(
                              "[MapGL Debug] Re-adding mouseleave handler for:",
                              layer.id,
                            );
                            map.on("mouseleave", layer.id, handlers.mouseleave);
                          }
                        }

                        // Check if we need to restore tooltip handlers
                        const layerId = layer.id;
                        if (layerId === "nc_counties" || layer.tooltip) {
                          console.log(
                            "[MapGL Debug] Restoring tooltip for:",
                            layerId,
                          );

                          // Create a new tooltip popup
                          const tooltip = new maplibregl.Popup({
                            closeButton: false,
                            closeOnClick: false,
                            maxWidth: "400px",
                          });

                          // Re-add tooltip handlers
                          const tooltipProperty = layer.tooltip || "NAME";

                          const mouseMoveHandler = function (e) {
                            map.getCanvas().style.cursor = "pointer";
                            if (e.features.length > 0) {
                              const description =
                                e.features[0].properties[tooltipProperty];
                              tooltip
                                .setLngLat(e.lngLat)
                                .setHTML(description)
                                .addTo(map);
                            }
                          };

                          const mouseLeaveHandler = function () {
                            map.getCanvas().style.cursor = "";
                            tooltip.remove();
                          };

                          map.on("mousemove", layerId, mouseMoveHandler);
                          map.on("mouseleave", layerId, mouseLeaveHandler);

                          // Store these handlers
                          if (!window._mapboxHandlers) {
                            window._mapboxHandlers = {};
                          }
                          window._mapboxHandlers[layerId] = {
                            mousemove: mouseMoveHandler,
                            mouseleave: mouseLeaveHandler,
                          };
                        }

                        // Recreate hover states if needed
                        if (layer.paint) {
                          for (const key in layer.paint) {
                            const value = layer.paint[key];
                            if (
                              Array.isArray(value) &&
                              value[0] === "case" &&
                              Array.isArray(value[1]) &&
                              value[1][0] === "boolean" &&
                              value[1][1][0] === "feature-state" &&
                              value[1][1][1] === "hover"
                            ) {
                              // This is a hover-enabled paint property
                              map.setPaintProperty(layer.id, key, value);
                            }
                          }
                        }
                      }
                    });

                    // Clear any active tooltips before restoration to prevent stacking
                    if (window._activeTooltip) {
                      window._activeTooltip.remove();
                      delete window._activeTooltip;
                    }

                    // Restore tracked layer modifications
                    const mapId = map.getContainer().id;
                    const savedLayerState =
                      window._mapglLayerState && window._mapglLayerState[mapId];
                    if (savedLayerState) {
                      console.log(
                        "[MapGL Debug] Restoring tracked layer modifications",
                      );

                      // Restore filters
                      for (const layerId in savedLayerState.filters) {
                        if (map.getLayer(layerId)) {
                          console.log(
                            "[MapGL Debug] Restoring filter for layer:",
                            layerId,
                          );
                          map.setFilter(
                            layerId,
                            savedLayerState.filters[layerId],
                          );
                        }
                      }

                      // Restore paint properties
                      for (const layerId in savedLayerState.paintProperties) {
                        if (map.getLayer(layerId)) {
                          const properties =
                            savedLayerState.paintProperties[layerId];
                          for (const propertyName in properties) {
                            const savedValue = properties[propertyName];

                            console.log(
                              "[MapGL Debug] Restoring paint property:",
                              layerId,
                              propertyName,
                              savedValue,
                            );

                            // Check if layer has hover effects that need to be preserved
                            const currentValue = map.getPaintProperty(
                              layerId,
                              propertyName,
                            );
                            if (
                              currentValue &&
                              Array.isArray(currentValue) &&
                              currentValue[0] === "case"
                            ) {
                              // Preserve hover effects while updating base value
                              const hoverValue = currentValue[2];
                              const newPaintProperty = [
                                "case",
                                ["boolean", ["feature-state", "hover"], false],
                                hoverValue,
                                savedValue,
                              ];
                              map.setPaintProperty(
                                layerId,
                                propertyName,
                                newPaintProperty,
                              );
                            } else {
                              map.setPaintProperty(
                                layerId,
                                propertyName,
                                savedValue,
                              );
                            }
                          }
                        }
                      }

                      // Restore layout properties
                      for (const layerId in savedLayerState.layoutProperties) {
                        if (map.getLayer(layerId)) {
                          const properties =
                            savedLayerState.layoutProperties[layerId];
                          for (const propertyName in properties) {
                            console.log(
                              "[MapGL Debug] Restoring layout property:",
                              layerId,
                              propertyName,
                              properties[propertyName],
                            );
                            map.setLayoutProperty(
                              layerId,
                              propertyName,
                              properties[propertyName],
                            );
                          }
                        }
                      }

                      // Restore tooltips
                      for (const layerId in savedLayerState.tooltips) {
                        if (map.getLayer(layerId)) {
                          const tooltipProperty =
                            savedLayerState.tooltips[layerId];
                          console.log(
                            "[MapGL Debug] Restoring tooltip:",
                            layerId,
                            tooltipProperty,
                          );

                          // Remove existing tooltip handlers first
                          map.off("mousemove", layerId);
                          map.off("mouseleave", layerId);

                          const tooltip = new maplibregl.Popup({
                            closeButton: false,
                            closeOnClick: false,
                            maxWidth: "400px",
                          });

                          map.on("mousemove", layerId, function (e) {
                            onMouseMoveTooltip(
                              e,
                              map,
                              tooltip,
                              tooltipProperty,
                              layerId,
                            );
                          });

                          map.on("mouseleave", layerId, function () {
                            onMouseLeaveTooltip(map, tooltip);
                          });
                        }
                      }

                      // Restore popups
                      for (const layerId in savedLayerState.popups) {
                        if (map.getLayer(layerId)) {
                          const popupProperty = savedLayerState.popups[layerId];
                          console.log(
                            "[MapGL Debug] Restoring popup:",
                            layerId,
                            popupProperty,
                          );

                          // Remove existing popup handlers first
                          if (
                            window._maplibreClickHandlers &&
                            window._maplibreClickHandlers[layerId]
                          ) {
                            map.off(
                              "click",
                              layerId,
                              window._maplibreClickHandlers[layerId],
                            );
                            delete window._maplibreClickHandlers[layerId];
                          }

                          const clickHandler = function (e) {
                            onClickPopup(e, map, popupProperty, layerId);
                          };

                          map.on("click", layerId, clickHandler);

                          if (!window._maplibreClickHandlers) {
                            window._maplibreClickHandlers = {};
                          }
                          window._maplibreClickHandlers[layerId] = clickHandler;
                        }
                      }
                    }

                    // Remove this listener to avoid adding the same layers multiple times
                    map.off("style.load", onStyleLoad);
                  };

                  map.once("style.load", function() {
                    // Wait for map to be fully idle before adding layers
                    map.once("idle", onStyleLoad);
                  });

                  // Store them for potential use outside the onStyleLoad event
                  // This helps in case the event timing is different in MapLibre
                  if (!window._mapglPreservedData) {
                    window._mapglPreservedData = {};
                  }
                  window._mapglPreservedData[map.getContainer().id] = {
                    sources: userSourceIds.map((id) => ({
                      id,
                      source: currentStyle.sources[id],
                    })),
                    layers: userLayers,
                  };

                  // Add a backup mechanism specific to MapLibre
                  // Some MapLibre styles or versions may have different event timing
                  if (userLayers.length > 0) {
                    // Set a timeout to check if layers were added after a reasonable delay
                    setTimeout(function () {
                      try {
                        console.log("[MapGL Debug] Running backup layer check");
                        const mapId = map.getContainer().id;
                        const preserved =
                          window._mapglPreservedData &&
                          window._mapglPreservedData[mapId];

                        if (preserved) {
                          // Check if user layers were successfully restored
                          const firstLayerId = preserved.layers[0]?.id;
                          if (firstLayerId && !map.getLayer(firstLayerId)) {
                            console.log(
                              "[MapGL Debug] Backup restoration needed for layers",
                            );

                            // Re-add sources first
                            preserved.sources.forEach(function (src) {
                              try {
                                if (!map.getSource(src.id)) {
                                  console.log(
                                    "[MapGL Debug] Backup: adding source",
                                    src.id,
                                  );
                                  map.addSource(src.id, src.source);
                                }
                              } catch (err) {
                                console.error(
                                  "[MapGL Debug] Backup: error adding source",
                                  src.id,
                                  err,
                                );
                              }
                            });

                            // Then re-add layers
                            preserved.layers.forEach(function (layer) {
                              try {
                                if (!map.getLayer(layer.id)) {
                                  console.log(
                                    "[MapGL Debug] Backup: adding layer",
                                    layer.id,
                                  );
                                  map.addLayer(layer);

                                  // Check for nc_counties layer to restore tooltip
                                  if (layer.id === "nc_counties") {
                                    console.log(
                                      "[MapGL Debug] Backup: restoring tooltip for",
                                      layer.id,
                                    );

                                    // Create a new tooltip popup
                                    const tooltip = new maplibregl.Popup({
                                      closeButton: false,
                                      closeOnClick: false,
                                      maxWidth: "400px",
                                    });

                                    // Re-add tooltip handlers
                                    const tooltipProperty = "NAME";

                                    const mouseMoveHandler = function (e) {
                                      map.getCanvas().style.cursor = "pointer";
                                      if (e.features.length > 0) {
                                        const description =
                                          e.features[0].properties[
                                            tooltipProperty
                                          ];
                                        tooltip
                                          .setLngLat(e.lngLat)
                                          .setHTML(description)
                                          .addTo(map);
                                      }
                                    };

                                    const mouseLeaveHandler = function () {
                                      map.getCanvas().style.cursor = "";
                                      tooltip.remove();
                                    };

                                    map.on(
                                      "mousemove",
                                      layer.id,
                                      mouseMoveHandler,
                                    );
                                    map.on(
                                      "mouseleave",
                                      layer.id,
                                      mouseLeaveHandler,
                                    );

                                    // Store these handlers
                                    if (!window._mapboxHandlers) {
                                      window._mapboxHandlers = {};
                                    }
                                    window._mapboxHandlers[layer.id] = {
                                      mousemove: mouseMoveHandler,
                                      mouseleave: mouseLeaveHandler,
                                    };
                                  }

                                  // Restore hover states
                                  if (layer.paint) {
                                    for (const key in layer.paint) {
                                      const value = layer.paint[key];
                                      if (
                                        Array.isArray(value) &&
                                        value[0] === "case" &&
                                        Array.isArray(value[1]) &&
                                        value[1][0] === "boolean" &&
                                        value[1][1] &&
                                        Array.isArray(value[1][1]) &&
                                        value[1][1][0] === "feature-state" &&
                                        value[1][1][1] === "hover"
                                      ) {
                                        // This is a hover-enabled paint property
                                        console.log(
                                          "[MapGL Debug] Backup: restoring hover style for",
                                          layer.id,
                                          key,
                                        );
                                        map.setPaintProperty(
                                          layer.id,
                                          key,
                                          value,
                                        );
                                      }
                                    }
                                  }
                                }
                              } catch (err) {
                                console.error(
                                  "[MapGL Debug] Backup: error adding layer",
                                  layer.id,
                                  err,
                                );
                              }
                            });
                          } else {
                            console.log(
                              "[MapGL Debug] Backup check: layers already restored properly",
                            );
                          }
                        }
                      } catch (err) {
                        console.error(
                          "[MapGL Debug] Error in backup restoration:",
                          err,
                        );
                      }
                    }, 500); // 500ms delay - faster recovery
                  }
                }

                // Apply the new style
                map.setStyle(message.style, {
                  diff: message.diff,
                });

                if (message.config) {
                  Object.keys(message.config).forEach(function (key) {
                    map.setConfigProperty("basemap", key, message.config[key]);
                  });
                }

                // Restore the view state after the style has loaded
                map.once("style.load", function () {
                  map.jumpTo({
                    center: center,
                    zoom: zoom,
                    bearing: bearing,
                    pitch: pitch,
                  });

                  // Re-apply map modifications
                  const mapIndex = compareMaps.indexOf(map);
                  if (mapIndex >= 0) {
                    applyMapModifications(map, compareMapsData[mapIndex]);
                  }
                });
              } else if (message.type === "add_navigation_control") {
                const nav = new maplibregl.NavigationControl({
                  showCompass: message.options.show_compass,
                  showZoom: message.options.show_zoom,
                  visualizePitch: message.options.visualize_pitch,
                });
                map.addControl(nav, message.position);

                if (message.orientation === "horizontal") {
                  const navBar = map
                    .getContainer()
                    .querySelector(
                      ".maplibregl-ctrl.maplibregl-ctrl-group:not(.maplibre-gl-draw_ctrl-draw-btn)",
                    );
                  if (navBar) {
                    navBar.style.display = "flex";
                    navBar.style.flexDirection = "row";
                  }
                }
                map.controls.push({ type: "navigation", control: nav });
              } else if (message.type === "add_reset_control") {
                const resetControl = document.createElement("button");
                resetControl.className =
                  "maplibregl-ctrl-icon maplibregl-ctrl-reset";
                resetControl.type = "button";
                resetControl.setAttribute("aria-label", "Reset");
                resetControl.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>';
                resetControl.style.cssText = "display:flex;justify-content:center;align-items:center;cursor:pointer;";

                const resetContainer = document.createElement("div");
                resetContainer.className =
                  "maplibregl-ctrl maplibregl-ctrl-group";
                resetContainer.appendChild(resetControl);

                const initialView = {
                  center: map.getCenter(),
                  zoom: map.getZoom(),
                  pitch: map.getPitch(),
                  bearing: map.getBearing(),
                  animate: message.animate,
                };

                if (message.duration) {
                  initialView.duration = message.duration;
                }

                resetControl.onclick = function () {
                  map.easeTo(initialView);
                };

                const resetControlObj = {
                  onAdd: function () {
                    return resetContainer;
                  },
                  onRemove: function () {
                    resetContainer.parentNode.removeChild(resetContainer);
                  },
                };

                map.addControl(resetControlObj, message.position);

                // Add to controls array
                map.controls.push({ type: "reset", control: resetControlObj });
              } else if (message.type === "add_coordinates_control") {
                const coordinatesControlObj = createCoordinatesControl(message.options);
                map.addControl(
                  coordinatesControlObj,
                  message.options.position || "bottom-right",
                );

                if (!map.controls) {
                  map.controls = [];
                }
                map.controls.push({ type: "coordinates", control: coordinatesControlObj });
              } else if (message.type === "add_draw_control") {
                let drawOptions = message.options || {};
                if (message.freehand) {
                  drawOptions = Object.assign({}, drawOptions, {
                    modes: Object.assign({}, MapboxDraw.modes, {
                      draw_polygon: MapboxDraw.modes.draw_freehand,
                    }),
                  });
                }

                const drawControl = new MapboxDraw(drawOptions);
                map.addControl(drawControl, message.position);
                map.controls.push({ type: "draw", control: drawControl });

                // Store draw control on map for feature click detection
                map._mapgl_draw = drawControl;

                // Add lasso icon CSS for freehand mode
                if (message.freehand) {
                  if (!document.querySelector("#mapgl-freehand-lasso-styles")) {
                    const style = document.createElement("style");
                    style.id = "mapgl-freehand-lasso-styles";
                    style.textContent = `
                                      .mapbox-gl-draw_polygon {
                                        background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"%3E%3Cpath d="M 7 3 C 5 2.5, 3.5 3, 3 5 C 2.5 7, 3 8.5, 4 9.5 C 5 10.5, 5.5 11.5, 5 13 C 4.5 14.5, 5 15.5, 6.5 16 C 8 16.5, 9.5 16, 11 15 C 12.5 14, 13.5 13, 14.5 11.5 C 15.5 10, 16 8.5, 15.5 7 C 15 5.5, 14 4.5, 12.5 3.5 C 11 2.5, 9 2.5, 7 3 Z" fill="none" stroke="%23000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E') !important;
                                      }
                                    `;
                    document.head.appendChild(style);
                  }

                  // Update tooltip for freehand mode
                  setTimeout(() => {
                    const polygonButton = map
                      .getContainer()
                      .querySelector(".mapbox-gl-draw_polygon");
                    if (polygonButton) {
                      polygonButton.title = "Freehand polygon tool (p)";
                    }
                  }, 100);
                }

                // Add event listeners
                map.on("draw.create", window.updateDrawnFeatures);
                map.on("draw.delete", window.updateDrawnFeatures);
                map.on("draw.update", window.updateDrawnFeatures);

                if (message.orientation === "horizontal") {
                  const drawBar = map
                    .getContainer()
                    .querySelector(".maplibregl-ctrl-group");
                  if (drawBar) {
                    drawBar.style.display = "flex";
                    drawBar.style.flexDirection = "row";
                  }
                }
              } else if (message.type === "get_drawn_features") {
                if (draw) {
                  const features = draw ? draw.getAll() : null;
                  Shiny.setInputValue(
                    data.id + "_drawn_features",
                    JSON.stringify(features),
                  );
                } else {
                  Shiny.setInputValue(
                    data.id + "_drawn_features",
                    JSON.stringify(null),
                  );
                }
              } else if (message.type === "clear_drawn_features") {
                if (draw) {
                  draw.deleteAll();
                  // Update the drawn features
                  window.updateDrawnFeatures();
                }
              } else if (message.type === "add_markers") {
                if (!window.maplibreglMarkers) {
                  window.maplibreglMarkers = [];
                }
                message.markers.forEach(function (marker) {
                  const markerOptions = {
                    color: marker.color,
                    rotation: marker.rotation,
                    draggable: marker.options.draggable || false,
                    ...marker.options,
                  };
                  const mapMarker = new maplibregl.Marker(markerOptions)
                    .setLngLat([marker.lng, marker.lat])
                    .addTo(map);

                  if (marker.popup) {
                    mapMarker.setPopup(
                      new maplibregl.Popup({
                        offset: 25,
                        maxWidth: "400px",
                      }).setHTML(marker.popup),
                    );
                  }

                  const markerId = marker.id;
                  if (markerId) {
                    const lngLat = mapMarker.getLngLat();
                    Shiny.setInputValue(data.id + "_marker_" + markerId, {
                      id: markerId,
                      lng: lngLat.lng,
                      lat: lngLat.lat,
                    });

                    mapMarker.on("dragend", function () {
                      const lngLat = mapMarker.getLngLat();
                      Shiny.setInputValue(data.id + "_marker_" + markerId, {
                        id: markerId,
                        lng: lngLat.lng,
                        lat: lngLat.lat,
                      });
                    });
                  }

                  window.maplibreglMarkers.push(mapMarker);
                });
              } else if (message.type === "clear_markers") {
                if (window.maplibreglMarkers) {
                  window.maplibreglMarkers.forEach(function (marker) {
                    marker.remove();
                  });
                  window.maplibreglMarkers = [];
                }
              } else if (message.type === "add_fullscreen_control") {
                const position = message.position || "top-right";
                const fullscreen = new maplibregl.FullscreenControl();
                map.addControl(fullscreen, position);
                map.controls.push({ type: "fullscreen", control: fullscreen });
              } else if (message.type === "add_scale_control") {
                const scaleControl = new maplibregl.ScaleControl({
                  maxWidth: message.options.maxWidth,
                  unit: message.options.unit,
                });
                map.addControl(scaleControl, message.options.position);
                map.controls.push({ type: "scale", control: scaleControl });
              } else if (message.type === "add_geolocate_control") {
                const geolocate = new maplibregl.GeolocateControl({
                  positionOptions: message.options.positionOptions,
                  trackUserLocation: message.options.trackUserLocation,
                  showAccuracyCircle: message.options.showAccuracyCircle,
                  showUserLocation: message.options.showUserLocation,
                  showUserHeading: message.options.showUserHeading,
                  fitBoundsOptions: message.options.fitBoundsOptions,
                });
                map.addControl(geolocate, message.options.position);
                map.controls.push({ type: "geolocate", control: geolocate });

                if (HTMLWidgets.shinyMode) {
                  geolocate.on("geolocate", function (event) {
                    Shiny.setInputValue(data.id + "_geolocate", {
                      coords: event.coords,
                      time: new Date(),
                    });
                  });

                  geolocate.on("trackuserlocationstart", function () {
                    Shiny.setInputValue(data.id + "_geolocate_tracking", {
                      status: "start",
                      time: new Date(),
                    });
                  });

                  geolocate.on("trackuserlocationend", function () {
                    Shiny.setInputValue(data.id + "_geolocate_tracking", {
                      status: "end",
                      time: new Date(),
                    });
                  });

                  geolocate.on("error", function (error) {
                    if (error.error.code === 1) {
                      Shiny.setInputValue(data.id + "_geolocate_error", {
                        message: "Location permission denied",
                        time: new Date(),
                      });
                    }
                  });
                }
              } else if (message.type === "add_geocoder_control") {
                const provider = message.options.provider || "osm";
                let geocoder;

                if (provider === "maptiler") {
                  // MapTiler geocoder
                  const maptilerOptions = {
                    apiKey: message.options.api_key,
                    maplibregl: maplibregl,
                    ...message.options,
                  };

                  // Create MapTiler geocoder
                  geocoder = new maplibreglMaptilerGeocoder.GeocodingControl(
                    maptilerOptions,
                  );
                } else {
                  // OSM/Nominatim geocoder (default)
                  const geocoderApi = {
                    forwardGeocode: async (config) => {
                      const features = [];
                      try {
                        const request = `https://nominatim.openstreetmap.org/search?q=${
                          config.query
                        }&format=geojson&polygon_geojson=1&addressdetails=1`;
                        const response = await fetch(request);
                        const geojson = await response.json();
                        for (const feature of geojson.features) {
                          const center = [
                            feature.bbox[0] +
                              (feature.bbox[2] - feature.bbox[0]) / 2,
                            feature.bbox[1] +
                              (feature.bbox[3] - feature.bbox[1]) / 2,
                          ];
                          const point = {
                            type: "Feature",
                            geometry: {
                              type: "Point",
                              coordinates: center,
                            },
                            place_name: feature.properties.display_name,
                            properties: feature.properties,
                            text: feature.properties.display_name,
                            place_type: ["place"],
                            center,
                          };
                          features.push(point);
                        }
                      } catch (e) {
                        console.error(
                          `Failed to forwardGeocode with error: ${e}`,
                        );
                      }

                      return {
                        features,
                      };
                    },
                  };

                  const geocoderOptions = {
                    maplibregl: maplibregl,
                    ...message.options,
                  };

                  // Set default values if not provided
                  if (!geocoderOptions.placeholder)
                    geocoderOptions.placeholder = "Search";
                  if (typeof geocoderOptions.collapsed === "undefined")
                    geocoderOptions.collapsed = false;

                  geocoder = new MaplibreGeocoder(geocoderApi, geocoderOptions);
                }

                map.addControl(
                  geocoder,
                  message.options.position || "top-right",
                );
                map.controls.push({ type: "geocoder", control: geocoder });

                // Apply CSS fix for MapTiler geocoder to prevent cutoff
                if (provider === "maptiler") {
                  setTimeout(() => {
                    const controlContainer = document.querySelector(
                      ".maplibregl-ctrl-geocoder",
                    );
                    if (controlContainer) {
                      controlContainer.style.maxWidth = "300px";
                      controlContainer.style.width = "auto";
                    }
                  }, 100);
                }

                // Handle geocoder results in Shiny mode
                if (provider === "maptiler") {
                  // MapTiler uses different event names
                  geocoder.on("pick", function (e) {
                    Shiny.setInputValue(data.id + "_geocoder", {
                      result: e,
                      time: new Date(),
                    });
                  });
                } else {
                  // OSM geocoder
                  geocoder.on("results", function (e) {
                    Shiny.setInputValue(data.id + "_geocoder", {
                      result: e,
                      time: new Date(),
                    });
                  });
                }
              } else if (message.type === "add_layers_control") {
                const layersControl = document.createElement("div");
                layersControl.id = message.control_id;
                layersControl.className = message.collapsible
                  ? "layers-control collapsible"
                  : "layers-control";
                layersControl.style.position = "absolute";

                // Set the position correctly
                const position = message.position || "top-left";
                if (position === "top-left") {
                  layersControl.style.top = (message.margin_top || 10) + "px";
                  layersControl.style.left = (message.margin_left || 10) + "px";
                } else if (position === "top-right") {
                  layersControl.style.top = (message.margin_top || 10) + "px";
                  layersControl.style.right =
                    (message.margin_right || 10) + "px";
                } else if (position === "bottom-left") {
                  layersControl.style.bottom =
                    (message.margin_bottom || 30) + "px";
                  layersControl.style.left = (message.margin_left || 10) + "px";
                } else if (position === "bottom-right") {
                  layersControl.style.bottom =
                    (message.margin_bottom || 40) + "px";
                  layersControl.style.right =
                    (message.margin_right || 10) + "px";
                }

                // Apply custom colors if provided
                if (message.custom_colors) {
                  const colors = message.custom_colors;

                  // Create a style element for custom colors
                  const styleEl = document.createElement("style");
                  let css = "";

                  if (colors.background) {
                    css += `.layers-control { background-color: ${colors.background} !important; }`;
                  }
                  if (colors.text) {
                    css += `.layers-control a { color: ${colors.text} !important; }`;
                  }
                  if (colors.activeBackground) {
                    css += `.layers-control a.active { background-color: ${colors.activeBackground} !important; }`;
                  }
                  if (colors.activeText) {
                    css += `.layers-control a.active { color: ${colors.activeText} !important; }`;
                  }
                  if (colors.hoverBackground) {
                    css += `.layers-control a:hover { background-color: ${colors.hoverBackground} !important; }`;
                  }
                  if (colors.hoverText) {
                    css += `.layers-control a:hover { color: ${colors.hoverText} !important; }`;
                  }
                  if (colors.toggleButtonBackground) {
                    css += `.layers-control .toggle-button { background-color: ${colors.toggleButtonBackground}
                  !important; }`;
                  }
                  if (colors.toggleButtonText) {
                    css += `.layers-control .toggle-button { color: ${colors.toggleButtonText} !important; }`;
                  }

                  styleEl.innerHTML = css;
                  document.head.appendChild(styleEl);
                }

                document.getElementById(data.id).appendChild(layersControl);

                const layersList = document.createElement("div");
                layersList.className = "layers-list";
                layersControl.appendChild(layersList);

                const allMaps = compareMaps;
                let layersConfig = message.layers_config;

                if (layersConfig && Array.isArray(layersConfig)) {
                  // grouped layers format (from named list in R)
                  layersConfig.forEach((config, index) => {
                    const link = document.createElement("a");
                    const layerIds = Array.isArray(config.ids)
                      ? config.ids
                      : [config.ids];
                    link.id = layerIds.join("-");
                    link.href = "#";
                    link.textContent = config.label;
                    link.setAttribute("data-layer-ids", JSON.stringify(layerIds));

                    // check initial visibility from whichever map has the layer
                    let initVis = "visible";
                    for (const m of allMaps) {
                      try {
                        initVis = m.getLayoutProperty(layerIds[0], "visibility");
                        break;
                      } catch(err) {}
                    }
                    link.className = initVis === "none" ? "" : "active";

                    link.onclick = function (e) {
                      e.preventDefault();
                      e.stopPropagation();
                      const ids = JSON.parse(this.getAttribute("data-layer-ids"));
                      let vis = "visible";
                      for (const m of allMaps) {
                        try { vis = m.getLayoutProperty(ids[0], "visibility"); break; } catch(err) {}
                      }
                      const newVis = vis === "visible" ? "none" : "visible";
                      ids.forEach((layerId) => {
                        allMaps.forEach((m) => {
                          try { m.setLayoutProperty(layerId, "visibility", newVis); } catch(err) {}
                        });
                      });
                      this.className = newVis === "visible" ? "active" : "";
                    };

                    layersList.appendChild(link);
                  });
                } else {
                  // flat array fallback
                  let layers =
                    message.layers ||
                    map.getStyle().layers.map((layer) => layer.id);

                  layers.forEach((layerId, index) => {
                    const link = document.createElement("a");
                    link.id = layerId;
                    link.href = "#";
                    link.textContent = layerId;
                    link.className = "active";

                    link.onclick = function (e) {
                      const clickedLayer = this.textContent;
                      e.preventDefault();
                      e.stopPropagation();
                      const visibility = map.getLayoutProperty(clickedLayer, "visibility");
                      const newVis = visibility === "visible" ? "none" : "visible";
                      allMaps.forEach((m) => {
                        try { m.setLayoutProperty(clickedLayer, "visibility", newVis); } catch(err) {}
                      });
                      this.className = newVis === "visible" ? "active" : "";
                    };

                    layersList.appendChild(link);
                  });
                }

                // Handle collapsible behavior
                if (message.collapsible) {
                  const toggleButton = document.createElement("div");
                  toggleButton.className = "toggle-button";

                  if (message.use_icon) {
                    layersControl.classList.add("icon-only");
                    toggleButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></svg>`;
                  } else {
                    toggleButton.textContent = "Layers";
                  }

                  toggleButton.onclick = function () {
                    layersControl.classList.toggle("open");
                  };
                  layersControl.insertBefore(toggleButton, layersList);
                }
              } else if (message.type === "add_globe_minimap") {
                // Add the globe minimap control if supported
                if (typeof MapboxGlobeMinimap !== "undefined") {
                  const minimap = new MapboxGlobeMinimap({
                    center: map.getCenter(),
                    zoom: map.getZoom(),
                    bearing: map.getBearing(),
                    pitch: map.getPitch(),
                    globeSize: message.globe_size,
                    landColor: message.land_color,
                    waterColor: message.water_color,
                    markerColor: message.marker_color,
                    markerSize: message.marker_size,
                  });

                  map.addControl(minimap, message.position);
                } else {
                  console.warn("MapboxGlobeMinimap is not defined");
                }
              } else if (message.type === "add_globe_control") {
                // Add the globe control
                const globeControl = new maplibregl.GlobeControl();
                map.addControl(globeControl, message.position);
                map.controls.push({ type: "globe", control: globeControl });
              } else if (message.type === "add_draw_control") {
                MapboxDraw.constants.classes.CONTROL_BASE = "maplibregl-ctrl";
                MapboxDraw.constants.classes.CONTROL_PREFIX =
                  "maplibregl-ctrl-";
                MapboxDraw.constants.classes.CONTROL_GROUP =
                  "maplibregl-ctrl-group";

                let drawOptions = message.options || {};

                // Generate styles if styling parameters provided
                if (message.styling) {
                  const generatedStyles = generateDrawStyles(message.styling);
                  if (generatedStyles) {
                    drawOptions.styles = generatedStyles;
                  }
                }

                if (message.freehand) {
                  drawOptions = Object.assign({}, drawOptions, {
                    modes: Object.assign({}, MapboxDraw.modes, {
                      draw_polygon: MapboxDraw.modes.draw_freehand,
                    }),
                    // defaultMode: 'draw_polygon' # Don't set the default yet
                  });
                }

                // Fix MapLibre compatibility - ensure we always have custom styles
                if (!drawOptions.styles) {
                  drawOptions.styles = generateDrawStyles({
                    vertex_radius: 5,
                    active_color: "#fbb03b",
                    point_color: "#3bb2d0",
                    line_color: "#3bb2d0",
                    fill_color: "#3bb2d0",
                    fill_opacity: 0.1,
                    line_width: 2,
                  });
                }

                const drawControl = new MapboxDraw(drawOptions);
                map.addControl(drawControl, message.position);
                map.controls.push({ type: "draw", control: drawControl });

                // Store draw control on map for feature click detection
                map._mapgl_draw = drawControl;

                // Add lasso icon CSS for freehand mode
                if (message.freehand) {
                  if (!document.querySelector("#mapgl-freehand-lasso-styles")) {
                    const style = document.createElement("style");
                    style.id = "mapgl-freehand-lasso-styles";
                    style.textContent = `
                                      .mapbox-gl-draw_polygon {
                                        background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"%3E%3Cpath d="M 7 3 C 5 2.5, 3.5 3, 3 5 C 2.5 7, 3 8.5, 4 9.5 C 5 10.5, 5.5 11.5, 5 13 C 4.5 14.5, 5 15.5, 6.5 16 C 8 16.5, 9.5 16, 11 15 C 12.5 14, 13.5 13, 14.5 11.5 C 15.5 10, 16 8.5, 15.5 7 C 15 5.5, 14 4.5, 12.5 3.5 C 11 2.5, 9 2.5, 7 3 Z" fill="none" stroke="%23000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/%3E%3C/svg%3E') !important;
                                      }
                                    `;
                    document.head.appendChild(style);
                  }

                  // Update tooltip for freehand mode
                  setTimeout(() => {
                    const polygonButton = map
                      .getContainer()
                      .querySelector(".mapbox-gl-draw_polygon");
                    if (polygonButton) {
                      polygonButton.title = "Freehand polygon tool (p)";
                    }
                  }, 100);
                }

                // Add initial features if provided
                if (message.source) {
                  addSourceFeaturesToDraw(draw, message.source, map);
                }

                // Add event listeners
                map.on("draw.create", updateDrawnFeatures);
                map.on("draw.delete", updateDrawnFeatures);
                map.on("draw.update", updateDrawnFeatures);

                if (message.orientation === "horizontal") {
                  const drawBar = map
                    .getContainer()
                    .querySelector(".maplibregl-ctrl-group");
                  if (drawBar) {
                    drawBar.style.display = "flex";
                    drawBar.style.flexDirection = "row";
                  }
                }

                // Add download button if requested
                if (message.download_button) {
                  // Add CSS for download button if not already added
                  if (!document.querySelector("#mapgl-draw-download-styles")) {
                    const style = document.createElement("style");
                    style.id = "mapgl-draw-download-styles";
                    style.textContent = `
                                          .mapbox-gl-draw_download {
                                            background: transparent;
                                            border: none;
                                            cursor: pointer;
                                            display: block;
                                            height: 30px;
                                            width: 30px;
                                            padding: 0;
                                            outline: none;
                                          }
                                          .mapbox-gl-draw_download:hover {
                                            background-color: rgba(0, 0, 0, 0.05);
                                          }
                                          .mapbox-gl-draw_download svg {
                                            width: 20px;
                                            height: 20px;
                                            margin: 5px;
                                            fill: #333;
                                          }
                                        `;
                    document.head.appendChild(style);
                  }

                  // Small delay to ensure Draw control is fully rendered
                  setTimeout(() => {
                    // Find the Draw control button group
                    const drawButtons = map
                      .getContainer()
                      .querySelector(
                        ".maplibregl-ctrl-group:has(.mapbox-gl-draw_polygon)",
                      );

                    if (drawButtons) {
                      // Create download button
                      const downloadBtn = document.createElement("button");
                      downloadBtn.className = "mapbox-gl-draw_download";
                      downloadBtn.title = "Download drawn features as GeoJSON";

                      // Add SVG download icon
                      downloadBtn.innerHTML = `
                                              <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                                                <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
                                              </svg>
                                            `;

                      downloadBtn.addEventListener("click", () => {
                        // Get all drawn features
                        const data = draw.getAll();

                        if (data.features.length === 0) {
                          alert(
                            "No features to download. Please draw something first!",
                          );
                          return;
                        }

                        // Convert to string with nice formatting
                        const dataStr = JSON.stringify(data, null, 2);

                        // Create blob and download
                        const blob = new Blob([dataStr], {
                          type: "application/json",
                        });
                        const url = URL.createObjectURL(blob);

                        const a = document.createElement("a");
                        a.href = url;
                        a.download = `${message.download_filename || "drawn-features"}.geojson`;
                        document.body.appendChild(a);
                        a.click();
                        document.body.removeChild(a);
                        URL.revokeObjectURL(url);
                      });

                      // Append to the Draw control button group
                      drawButtons.appendChild(downloadBtn);
                    }
                  }, 100);
                }
              } else if (message.type === "get_drawn_features") {
                if (draw) {
                  const features = draw ? draw.getAll() : null;
                  Shiny.setInputValue(
                    data.id + "_drawn_features",
                    JSON.stringify(features),
                  );
                } else {
                  Shiny.setInputValue(
                    data.id + "_drawn_features",
                    JSON.stringify(null),
                  );
                }
              } else if (message.type === "clear_drawn_features") {
                if (draw) {
                  draw.deleteAll();
                  // Update the drawn features
                  updateDrawnFeatures();
                }
              } else if (message.type === "add_features_to_draw") {
                if (draw) {
                  if (message.data.clear_existing) {
                    draw.deleteAll();
                  }
                  addSourceFeaturesToDraw(draw, message.data.source, map);
                  // Update the drawn features
                  updateDrawnFeatures();
                } else {
                  console.warn("Draw control not initialized");
                }
              } else if (message.type === "set_projection") {
                // Only if maplibre supports projection
                if (typeof map.setProjection === "function") {
                  map.setProjection(message.projection);
                }
              } else if (message.type === "set_source") {
                if (map.getLayer(message.layer)) {
                  const sourceId = map.getLayer(message.layer).source;
                  const newData = typeof message.source === "string" ? JSON.parse(message.source) : message.source;
                  map.getSource(sourceId).setData(newData);
                }
              } else if (message.type === "set_tooltip") {
                // Track tooltip state
                layerState.tooltips[message.layer] = message.tooltip;

                if (map.getLayer(message.layer)) {
                  // Remove any existing tooltip handlers
                  map.off("mousemove", message.layer);
                  map.off("mouseleave", message.layer);

                  const tooltip = new maplibregl.Popup({
                    closeButton: false,
                    closeOnClick: false,
                    maxWidth: "400px",
                  });
                  applyPopupClass(tooltip, message.tooltip_style);

                  map.on("mousemove", message.layer, function (e) {
                    map.getCanvas().style.cursor = "pointer";
                    if (e.features.length > 0) {
                      const description = resolveTooltipContent(
                        message.tooltip,
                        e.features[0].properties,
                      );
                      tooltip
                        .setLngLat(e.lngLat)
                        .setHTML(description)
                        .addTo(map);
                    }
                  });

                  map.on("mouseleave", message.layer, function () {
                    map.getCanvas().style.cursor = "";
                    tooltip.remove();
                  });
                }
              } else if (message.type === "set_popup") {
                // Track popup state
                layerState.popups[message.layer] = message.popup;

                if (map.getLayer(message.layer)) {
                  // Remove any existing popup click handlers for this layer
                  if (
                    window._maplibreClickHandlers &&
                    window._maplibreClickHandlers[message.layer]
                  ) {
                    map.off(
                      "click",
                      message.layer,
                      window._maplibreClickHandlers[message.layer],
                    );
                    delete window._maplibreClickHandlers[message.layer];
                  }

                  // Remove any existing popup for this layer
                  if (
                    window._maplibrePopups &&
                    window._maplibrePopups[message.layer]
                  ) {
                    window._maplibrePopups[message.layer].remove();
                    delete window._maplibrePopups[message.layer];
                  }

                  // Create new click handler for popup
                  const clickHandler = function (e) {
                    onClickPopup(
                      e,
                      map,
                      message.popup,
                      message.layer,
                      message.popup_style,
                    );
                  };

                  // Store handler reference
                  if (!window._maplibreClickHandlers) {
                    window._maplibreClickHandlers = {};
                  }
                  window._maplibreClickHandlers[message.layer] = clickHandler;

                  // Add click handler
                  map.on("click", message.layer, clickHandler);

                  // Change cursor to pointer when hovering over the layer
                  map.on("mouseenter", message.layer, function () {
                    map.getCanvas().style.cursor = "pointer";
                  });

                  // Change cursor back to default when leaving the layer
                  map.on("mouseleave", message.layer, function () {
                    map.getCanvas().style.cursor = "";
                  });
                }
              } else if (message.type === "move_layer") {
                if (map.getLayer(message.layer)) {
                  if (message.before) {
                    map.moveLayer(message.layer, message.before);
                  } else {
                    map.moveLayer(message.layer);
                  }
                }
              } else if (message.type === "set_opacity") {
                // Set opacity for all fill layers
                const style = map.getStyle();
                if (style && style.layers) {
                  style.layers.forEach(function (layer) {
                    if (layer.type === "fill" && map.getLayer(layer.id)) {
                      map.setPaintProperty(
                        layer.id,
                        "fill-opacity",
                        message.opacity,
                      );
                    }
                  });
                }
              } else if (message.type === "add_custom_control") {
                const controlOptions = message.options;
                const customControlContainer = document.createElement("div");

                if (controlOptions.className) {
                  customControlContainer.className = controlOptions.className;
                } else {
                  customControlContainer.className =
                    "maplibregl-ctrl maplibregl-ctrl-group";
                }

                customControlContainer.innerHTML = controlOptions.html;

                const customControl = {
                  onAdd: function () {
                    return customControlContainer;
                  },
                  onRemove: function () {
                    if (customControlContainer.parentNode) {
                      customControlContainer.parentNode.removeChild(
                        customControlContainer,
                      );
                    }
                  },
                };

                map.addControl(
                  customControl,
                  controlOptions.position || "top-right",
                );
                map.controls.push({
                  type: message.control_id,
                  control: customControl,
                });
              } else if (message.type === "query_rendered_features") {
                // Query rendered features
                let queryOptions = {};
                if (message.layers) {
                  // Ensure layers is always an array
                  queryOptions.layers = Array.isArray(message.layers)
                    ? message.layers
                    : [message.layers];
                }
                if (message.filter) queryOptions.filter = message.filter;

                let features;
                if (message.geometry) {
                  features = map.queryRenderedFeatures(
                    message.geometry,
                    queryOptions,
                  );
                } else {
                  // No geometry specified - query entire viewport
                  features = map.queryRenderedFeatures(queryOptions);
                }

                // Deduplicate features by id or by properties if no id
                const uniqueFeatures = new Map();
                features.forEach(function (feature) {
                  let key;
                  if (feature.id !== undefined && feature.id !== null) {
                    key = feature.id;
                  } else {
                    // Create a key from properties if no id available
                    key = JSON.stringify(feature.properties);
                  }

                  if (!uniqueFeatures.has(key)) {
                    uniqueFeatures.set(key, feature);
                  }
                });

                // Convert to GeoJSON FeatureCollection
                const deduplicatedFeatures = Array.from(
                  uniqueFeatures.values(),
                );
                const featureCollection = {
                  type: "FeatureCollection",
                  features: deduplicatedFeatures,
                };

                Shiny.setInputValue(
                  data.id + "_queried_features",
                  JSON.stringify(featureCollection),
                );
              } else if (message.type === "clear_controls") {
                // Handle clear_controls for compare widgets
                if (!message.controls || message.controls.length === 0) {
                  // Clear all controls
                  map.controls.forEach((controlObj) => {
                    if (controlObj.control) {
                      map.removeControl(controlObj.control);
                    }
                  });
                  map.controls = [];
                } else {
                  // Clear specific controls
                  const controlsToRemove = Array.isArray(message.controls)
                    ? message.controls
                    : [message.controls];

                  map.controls = map.controls.filter((controlObj) => {
                    if (controlsToRemove.includes(controlObj.type)) {
                      if (controlObj.control) {
                        map.removeControl(controlObj.control);
                      }
                      return false; // Remove from array
                    }
                    return true; // Keep in array
                  });
                }
              }
            },
          );
        }

        function setupShinyEvents(map, parentId, mapType) {
          // Set view state on move end
          map.on("moveend", function () {
            const center = map.getCenter();
            const zoom = map.getZoom();
            const bearing = map.getBearing();
            const pitch = map.getPitch();

            if (window.Shiny) {
              Shiny.setInputValue(parentId + "_" + mapType + "_view", {
                center: [center.lng, center.lat],
                zoom: zoom,
                bearing: bearing,
                pitch: pitch,
              });
            }
          });

          // Send clicked point coordinates to Shiny
          map.on("click", function (e) {
            // Check if this map's draw control is active and in a drawing mode
            let isDrawing = false;
            if (map._mapgl_draw && map._mapgl_draw.getMode) {
              const mode = map._mapgl_draw.getMode();
              isDrawing =
                mode === "draw_point" ||
                mode === "draw_line_string" ||
                mode === "draw_polygon";
              console.log(
                `[${mapType}] Draw mode: ${mode}, isDrawing: ${isDrawing}`,
              );
            } else {
              console.log(`[${mapType}] No draw control found`);
            }

            // Only process feature clicks if not actively drawing
            if (!isDrawing) {
              const features = map.queryRenderedFeatures(e.point);
              // Filter out draw layers
              const nonDrawFeatures = features.filter(
                (feature) =>
                  !feature.layer.id.includes("gl-draw") &&
                  !feature.source.includes("gl-draw"),
              );
              console.log(
                `[${mapType}] Features found: ${features.length}, non-draw: ${nonDrawFeatures.length}`,
              );

              if (nonDrawFeatures.length > 0) {
                const feature = nonDrawFeatures[0];
                console.log(
                  `[${mapType}] Feature click detected, layer: ${feature.layer.id}`,
                );
                if (window.Shiny) {
                  Shiny.setInputValue(
                    parentId + "_" + mapType + "_feature_click",
                    {
                      id: feature.id,
                      properties: feature.properties,
                      layer: feature.layer.id,
                      lng: e.lngLat.lng,
                      lat: e.lngLat.lat,
                      time: Date.now(),
                    },
                  );
                }
              } else {
                console.log(
                  `[${mapType}] No non-draw features found at click point`,
                );
                if (window.Shiny) {
                  Shiny.setInputValue(
                    parentId + "_" + mapType + "_feature_click",
                    null,
                  );
                }
              }
            } else {
              console.log(
                `[${mapType}] Feature click suppressed - currently drawing`,
              );
            }

            // Always send regular click event
            if (window.Shiny) {
              Shiny.setInputValue(parentId + "_" + mapType + "_click", {
                lng: e.lngLat.lng,
                lat: e.lngLat.lat,
                time: Date.now(),
              });
            }
          });

          // Add hover events if enabled for this map; resolve the side name
          // to the matching map config ("before"/"after" or "mapN")
          const sideMatch = /^map(\d+)$/.exec(mapType);
          const configIndex = sideMatch
            ? parseInt(sideMatch[1], 10) - 1
            : mapType === "before"
              ? 0
              : 1;
          const mapConfig = compareMapsData[configIndex] || {};
          if (mapConfig.hover_events && mapConfig.hover_events.enabled) {
            map.on("mousemove", function (e) {
              if (window.Shiny) {
                // Feature hover events
                if (mapConfig.hover_events.features) {
                  const options = mapConfig.hover_events.layer_id
                    ? {
                        layers: Array.isArray(mapConfig.hover_events.layer_id)
                          ? mapConfig.hover_events.layer_id
                          : mapConfig.hover_events.layer_id
                              .split(",")
                              .map((id) => id.trim()),
                      }
                    : undefined;
                  const features = map.queryRenderedFeatures(e.point, options);

                  if (features.length > 0) {
                    const feature = features[0];
                    Shiny.setInputValue(
                      parentId + "_" + mapType + "_feature_hover",
                      {
                        id: feature.id,
                        properties: feature.properties,
                        layer: feature.layer.id,
                        lng: e.lngLat.lng,
                        lat: e.lngLat.lat,
                        time: new Date(),
                      },
                    );
                  } else {
                    Shiny.setInputValue(
                      parentId + "_" + mapType + "_feature_hover",
                      null,
                    );
                  }
                }

                // Coordinate hover events
                if (mapConfig.hover_events.coordinates) {
                  Shiny.setInputValue(parentId + "_" + mapType + "_hover", {
                    lng: e.lngLat.lng,
                    lat: e.lngLat.lat,
                    time: new Date(),
                  });
                }
              }
            });
          }
        }

        async function applyMapModifications(map, mapData) {
          // Initialize controls array if it doesn't exist
          if (!map.controls) {
            map.controls = [];
          }
          // Define the tooltip handler functions to match the ones in maplibregl.js
          function onMouseMoveTooltip(
            e,
            map,
            tooltipPopup,
            tooltipProperty,
            layerId,
          ) {
            if (e.features.length > 0) {
              // Query all features at this point to determine z-order
              const allFeatures = map.queryRenderedFeatures(e.point);

              // Find the topmost layer that has a tooltip
              let topmostLayerWithTooltip = null;
              for (let i = 0; i < allFeatures.length; i++) {
                const feature = allFeatures[i];
                const featureLayerId = feature.layer.id;

                // Check if this layer has a tooltip handler registered
                if (
                  window._mapboxHandlers &&
                  window._mapboxHandlers[featureLayerId]
                ) {
                  topmostLayerWithTooltip = featureLayerId;
                  break;
                }
              }

              // Only show tooltip if this is the topmost layer with a tooltip
              if (topmostLayerWithTooltip === layerId) {
                map.getCanvas().style.cursor = "pointer";

                // tooltipProperty may be a column name, a {brace} template, or
                // a concat()/number_format() expression.
                const description = resolveTooltipContent(
                  tooltipProperty,
                  e.features[0].properties,
                );

                tooltipPopup
                  .setLngLat(e.lngLat)
                  .setHTML(description)
                  .addTo(map);

                // Store reference to currently active tooltip
                window._activeTooltip = tooltipPopup;
              } else {
                // This layer is not topmost, hide tooltip if it was showing
                tooltipPopup.remove();
                if (window._activeTooltip === tooltipPopup) {
                  delete window._activeTooltip;
                }
              }
            } else {
              tooltipPopup.remove();
              // If this was the active tooltip, clear the reference
              if (window._activeTooltip === tooltipPopup) {
                delete window._activeTooltip;
              }
            }
          }

          function onMouseLeaveTooltip(map, tooltipPopup) {
            map.getCanvas().style.cursor = "";
            tooltipPopup.remove();
            if (window._activeTooltip === tooltipPopup) {
              delete window._activeTooltip;
            }
          }

          function evaluateExpression(expression, properties) {
            if (!Array.isArray(expression)) {
              return expression;
            }

            const operator = expression[0];

            switch (operator) {
              case "get":
                return properties[expression[1]];
              case "concat":
                return expression
                  .slice(1)
                  .map((item) => evaluateExpression(item, properties))
                  .join("");
              case "to-string":
                return String(evaluateExpression(expression[1], properties));
              case "to-number":
                return Number(evaluateExpression(expression[1], properties));
              case "number-format":
                const value = evaluateExpression(expression[1], properties);
                const options = expression[2] || {};

                // Handle locale option
                const locale = options.locale || "en-US";

                // Build Intl.NumberFormat options
                const formatOptions = {};

                // Style options
                if (options.style) formatOptions.style = options.style; // 'decimal', 'currency', 'percent', 'unit'
                if (options.currency) formatOptions.currency = options.currency;
                if (options.unit) formatOptions.unit = options.unit;

                // Digit options
                if (options.hasOwnProperty("min-fraction-digits")) {
                  formatOptions.minimumFractionDigits =
                    options["min-fraction-digits"];
                }
                if (options.hasOwnProperty("max-fraction-digits")) {
                  formatOptions.maximumFractionDigits =
                    options["max-fraction-digits"];
                }
                if (options.hasOwnProperty("min-integer-digits")) {
                  formatOptions.minimumIntegerDigits =
                    options["min-integer-digits"];
                }

                // Notation options
                if (options.notation) formatOptions.notation = options.notation; // 'standard', 'scientific', 'engineering', 'compact'
                if (options.compactDisplay)
                  formatOptions.compactDisplay = options.compactDisplay; // 'short', 'long'

                // Grouping
                if (options.hasOwnProperty("useGrouping")) {
                  formatOptions.useGrouping = options.useGrouping;
                }

                return new Intl.NumberFormat(locale, formatOptions).format(
                  value,
                );
              default:
                // For literals and other simple values
                return expression;
            }
          }

          function onClickPopup(e, map, popupProperty, layerId, popupStyle) {
            // popupProperty may be a column name, a {brace} template, or a
            // concat()/number_format() expression.
            const description = resolveTooltipContent(
              popupProperty,
              e.features[0].properties,
            );

            // Remove any existing popup for this layer
            if (window._maplibrePopups && window._maplibrePopups[layerId]) {
              window._maplibrePopups[layerId].remove();
            }

            // Create and show the popup
            const popup = new maplibregl.Popup({ maxWidth: "400px" })
              .setLngLat(e.lngLat)
              .setHTML(description)
              .addTo(map);
            applyPopupClass(popup, popupStyle);

            // Store reference to this popup
            if (!window._maplibrePopups) {
              window._maplibrePopups = {};
            }
            window._maplibrePopups[layerId] = popup;

            // Remove reference when popup is closed
            popup.on("close", function () {
              if (window._maplibrePopups[layerId] === popup) {
                delete window._maplibrePopups[layerId];
              }
            });
          }

          // Set config properties if provided
          if (mapData.config_properties) {
            mapData.config_properties.forEach(function (config) {
              map.setConfigProperty(
                config.importId,
                config.configName,
                config.value,
              );
            });
          }

          // Process H3J sources if provided
          if (mapData.h3j_sources) {
            mapData.h3j_sources.forEach(async function (source) {
              await map.addH3JSource(source.id, {
                data: source.url,
              });
            });
          }
          // Process H3T (tiled h3j) sources if provided
          if (mapData.h3t_sources) {
            mapData.h3t_sources.forEach(function (source) {
              map.addH3TSource(source.id, {
                tiles: source.tiles,
                sourcelayer: source.sourcelayer,
                geometry_type: source.geometry_type,
                minzoom: source.minzoom,
                maxzoom: source.maxzoom,
                promoteId: source.promoteId,
                debug: source.debug,
              });
            });
          }

          if (mapData.markers) {
            if (!window.maplibreglMarkers) {
              window.maplibreglMarkers = [];
            }
            mapData.markers.forEach(function (marker) {
              const markerOptions = {
                color: marker.color,
                rotation: marker.rotation,
                draggable: marker.options.draggable || false,
                ...marker.options,
              };
              const mapMarker = new maplibregl.Marker(markerOptions)
                .setLngLat([marker.lng, marker.lat])
                .addTo(map);

              if (marker.popup) {
                mapMarker.setPopup(
                  new maplibregl.Popup({
                    offset: 25,
                    maxWidth: "400px",
                  }).setText(marker.popup),
                );
              }

              const markerId = marker.id;
              if (markerId) {
                const lngLat = mapMarker.getLngLat();
                if (HTMLWidgets.shinyMode) {
                  Shiny.setInputValue(el.id + "_marker_" + markerId, {
                    id: markerId,
                    lng: lngLat.lng,
                    lat: lngLat.lat,
                  });
                }

                mapMarker.on("dragend", function () {
                  const lngLat = mapMarker.getLngLat();
                  if (HTMLWidgets.shinyMode) {
                    Shiny.setInputValue(el.id + "_marker_" + markerId, {
                      id: markerId,
                      lng: lngLat.lng,
                      lat: lngLat.lat,
                    });
                  }
                });
              }

              window.maplibreglMarkers.push(mapMarker);
            });
          }

          // Add sources if provided
          if (mapData.sources) {
            for (const source of mapData.sources) {
              if (source.type === "vector") {
                const sourceConfig = {
                  type: "vector",
                };
                // Add url or tiles
                if (source.url) {
                  sourceConfig.url = source.url;
                  // Auto-detect MLT encoding for PMTiles sources
                  if (
                    source.url.startsWith("pmtiles://") &&
                    typeof pmtiles !== "undefined" &&
                    !source.encoding
                  ) {
                    try {
                      const rawUrl = source.url.replace("pmtiles://", "");
                      const p = new pmtiles.PMTiles(rawUrl);
                      const header = await p.getHeader();
                      if (header.tileType === pmtiles.TileType.Mlt) {
                        sourceConfig.encoding = "mlt";
                      }
                    } catch (e) {
                      console.warn("Failed to detect PMTiles tile type:", e);
                    }
                  }
                }
                if (source.tiles) {
                  sourceConfig.tiles = source.tiles;
                }
                if (source.promoteId) {
                  sourceConfig.promoteId = source.promoteId;
                }
                map.addSource(source.id, sourceConfig);
              } else if (source.type === "geojson") {
                map.addSource(source.id, {
                  type: "geojson",
                  data: source.data,
                  generateId: source.generateId !== false,
                });
              } else if (source.type === "raster") {
                if (source.url) {
                  map.addSource(source.id, {
                    type: "raster",
                    url: source.url,
                    tileSize: source.tileSize,
                    maxzoom: source.maxzoom,
                  });
                } else if (source.tiles) {
                  map.addSource(source.id, {
                    type: "raster",
                    tiles: source.tiles,
                    tileSize: source.tileSize,
                    maxzoom: source.maxzoom,
                  });
                }
              } else if (source.type === "raster-dem") {
                map.addSource(source.id, {
                  type: "raster-dem",
                  url: source.url,
                  tileSize: source.tileSize,
                  maxzoom: source.maxzoom,
                });
              } else if (source.type === "image") {
                map.addSource(source.id, {
                  type: "image",
                  url: source.url,
                  coordinates: source.coordinates,
                });
              } else if (source.type === "video") {
                map.addSource(source.id, {
                  type: "video",
                  urls: source.urls,
                  coordinates: source.coordinates,
                });
              }
            }
          }

          // Add layers if provided
          if (mapData.layers) {
            mapData.layers.forEach(function (layer) {
              try {
                const layerConfig = {
                  id: layer.id,
                  type: layer.type,
                  source: layer.source,
                  layout: layer.layout || {},
                  paint: layer.paint || {},
                };

                // Check if source is an object and set generateId if source type is 'geojson'
                if (
                  typeof layer.source === "object" &&
                  layer.source.type === "geojson"
                ) {
                  layerConfig.source.generateId = true;
                } else if (typeof layer.source === "string") {
                  // Handle string source if needed
                  layerConfig.source = layer.source;
                }

                if (layer.source_layer) {
                  layerConfig["source-layer"] = layer.source_layer;
                }

                if (layer.filter) {
                  layerConfig["filter"] = layer.filter;
                }

                if (layer.slot) {
                  layerConfig["slot"] = layer.slot;
                }

                if (layer.minzoom) {
                  layerConfig["minzoom"] = layer.minzoom;
                }

                if (layer.maxzoom) {
                  layerConfig["maxzoom"] = layer.maxzoom;
                }

                if (layer.before_id) {
                  map.addLayer(layerConfig, layer.before_id);
                } else {
                  map.addLayer(layerConfig);
                }

                // Add popups or tooltips if provided
                if (layer.popup) {
                  map.on("click", layer.id, function (e) {
                    onClickPopup(e, map, layer.popup, layer.id, layer.popup_style);
                  });

                  // Change cursor to pointer when hovering over the layer
                  map.on("mouseenter", layer.id, function () {
                    map.getCanvas().style.cursor = "pointer";
                  });

                  // Change cursor back to default when leaving the layer
                  map.on("mouseleave", layer.id, function () {
                    map.getCanvas().style.cursor = "";
                  });
                }

                if (layer.tooltip) {
                  const tooltip = new maplibregl.Popup({
                    closeButton: false,
                    closeOnClick: false,
                    maxWidth: "400px",
                  });
                  applyPopupClass(tooltip, layer.tooltip_style);

                  // Create a reference to the mousemove handler function
                  const mouseMoveHandler = function (e) {
                    onMouseMoveTooltip(
                      e,
                      map,
                      tooltip,
                      layer.tooltip,
                      layer.id,
                    );
                  };

                  // Create a reference to the mouseleave handler function
                  const mouseLeaveHandler = function () {
                    onMouseLeaveTooltip(map, tooltip);
                  };

                  // Attach the named handler references
                  map.on("mousemove", layer.id, mouseMoveHandler);
                  map.on("mouseleave", layer.id, mouseLeaveHandler);

                  // Store these handler references
                  if (!window._mapboxHandlers) {
                    window._mapboxHandlers = {};
                  }
                  window._mapboxHandlers[layer.id] = {
                    mousemove: mouseMoveHandler,
                    mouseleave: mouseLeaveHandler,
                  };
                }

                // Add hover effect if provided
                if (layer.hover_options) {
                  const jsHoverOptions = {};
                  for (const [key, value] of Object.entries(
                    layer.hover_options,
                  )) {
                    const jsKey = key.replace(/_/g, "-");
                    jsHoverOptions[jsKey] = value;
                  }

                  let hoveredFeatureId = null;

                  map.on("mousemove", layer.id, function (e) {
                    if (e.features.length > 0) {
                      if (hoveredFeatureId !== null) {
                        const featureState = {
                          source:
                            typeof layer.source === "string"
                              ? layer.source
                              : layer.id,
                          id: hoveredFeatureId,
                        };
                        if (layer.source_layer) {
                          featureState.sourceLayer = layer.source_layer;
                        }
                        map.setFeatureState(featureState, { hover: false });
                      }
                      hoveredFeatureId = e.features[0].id;
                      const featureState = {
                        source:
                          typeof layer.source === "string"
                            ? layer.source
                            : layer.id,
                        id: hoveredFeatureId,
                      };
                      if (layer.source_layer) {
                        featureState.sourceLayer = layer.source_layer;
                      }
                      map.setFeatureState(featureState, { hover: true });
                    }
                  });

                  map.on("mouseleave", layer.id, function () {
                    if (hoveredFeatureId !== null) {
                      const featureState = {
                        source:
                          typeof layer.source === "string"
                            ? layer.source
                            : layer.id,
                        id: hoveredFeatureId,
                      };
                      if (layer.source_layer) {
                        featureState.sourceLayer = layer.source_layer;
                      }
                      map.setFeatureState(featureState, { hover: false });
                    }
                    hoveredFeatureId = null;
                  });

                  Object.keys(jsHoverOptions).forEach(function (key) {
                    const originalPaint =
                      map.getPaintProperty(layer.id, key) || layer.paint[key];
                    map.setPaintProperty(layer.id, key, [
                      "case",
                      ["boolean", ["feature-state", "hover"], false],
                      jsHoverOptions[key],
                      originalPaint,
                    ]);
                  });
                }
              } catch (e) {
                console.error("Failed to add layer: ", layer, e);
              }
            });
          }

          if (mapData.flowmaps) {
            if (window.MapGLFlowmapPlugin) {
              const flowmapEl = {
                id: `${el.id}-map${compareMaps.indexOf(map) + 1}`,
              };
              window.MapGLFlowmapPlugin.init(
                map,
                mapData,
                flowmapEl,
                HTMLWidgets,
              );
            } else {
              console.error("Flowmap plugin is not loaded. Cannot add flowmap layers.");
            }
          }

          // Set terrain if provided
          if (mapData.terrain) {
            map.setTerrain({
              source: mapData.terrain.source,
              exaggeration: mapData.terrain.exaggeration,
            });
          }

          // Set fog
          if (mapData.fog) {
            map.setFog(mapData.fog);
          }

          if (mapData.fitBounds) {
            map.fitBounds(mapData.fitBounds.bounds, mapData.fitBounds.options);
          }
          if (mapData.flyTo) {
            map.flyTo(mapData.flyTo);
          }
          if (mapData.easeTo) {
            map.easeTo(mapData.easeTo);
          }
          if (mapData.setCenter) {
            map.setCenter(mapData.setCenter);
          }
          if (mapData.setZoom) {
            map.setZoom(mapData.setZoom);
          }

          // Apply moveLayer operations if provided
          if (mapData.moveLayer) {
            mapData.moveLayer.forEach(function (moveOp) {
              if (map.getLayer(moveOp.layer)) {
                if (moveOp.before) {
                  map.moveLayer(moveOp.layer, moveOp.before);
                } else {
                  map.moveLayer(moveOp.layer);
                }
              }
            });
          }
          if (mapData.jumpTo) {
            map.jumpTo(mapData.jumpTo);
          }

          // Add custom images if provided
          if (mapData.images && Array.isArray(mapData.images)) {
            mapData.images.forEach(async function (imageInfo) {
              try {
                const image = await map.loadImage(imageInfo.url);
                if (!map.hasImage(imageInfo.id)) {
                  map.addImage(imageInfo.id, image.data, imageInfo.options);
                }
              } catch (error) {
                console.error("Error loading image:", error);
              }
            });
          } else if (mapData.images) {
            console.error("mapData.images is not an array:", mapData.images);
          }

          // Remove existing legends only from this specific map container
          const mapContainer = map.getContainer();
          const existingLegends =
            mapContainer.querySelectorAll(".mapboxgl-legend");
          existingLegends.forEach((legend) => legend.remove());

          if (mapData.legend_html && mapData.legend_css) {
            const legendCss = document.createElement("style");
            legendCss.innerHTML = mapData.legend_css;
            document.head.appendChild(legendCss);

            const legend = document.createElement("div");
            legend.innerHTML = mapData.legend_html;
            legend.classList.add("mapboxgl-legend");

            // Append legend to the correct map container instead of main container
            mapContainer.appendChild(legend);
          }

          // Add fullscreen control if enabled
          if (
            mapData.fullscreen_control &&
            mapData.fullscreen_control.enabled
          ) {
            const position = mapData.fullscreen_control.position || "top-right";
            map.addControl(new maplibregl.FullscreenControl(), position);
          }

          // Helper function to generate draw styles based on parameters
          function generateDrawStyles(styling) {
            if (!styling) return null;

            return [
              // Point styles
              {
                id: "gl-draw-point-active",
                type: "circle",
                filter: [
                  "all",
                  ["==", "$type", "Point"],
                  ["==", "meta", "feature"],
                  ["==", "active", "true"],
                ],
                paint: {
                  "circle-radius": styling.vertex_radius + 2,
                  "circle-color": styling.active_color,
                },
              },
              {
                id: "gl-draw-point",
                type: "circle",
                filter: [
                  "all",
                  ["==", "$type", "Point"],
                  ["==", "meta", "feature"],
                  ["==", "active", "false"],
                ],
                paint: {
                  "circle-radius": styling.vertex_radius,
                  "circle-color": styling.point_color,
                },
              },
              // Line styles
              {
                id: "gl-draw-line",
                type: "line",
                filter: ["all", ["==", "$type", "LineString"]],
                layout: {
                  "line-cap": "round",
                  "line-join": "round",
                },
                paint: {
                  "line-color": [
                    "case",
                    ["==", ["get", "active"], "true"],
                    styling.active_color,
                    styling.line_color,
                  ],
                  "line-width": styling.line_width,
                },
              },
              // Polygon fill
              {
                id: "gl-draw-polygon-fill",
                type: "fill",
                filter: ["all", ["==", "$type", "Polygon"]],
                paint: {
                  "fill-color": [
                    "case",
                    ["==", ["get", "active"], "true"],
                    styling.active_color,
                    styling.fill_color,
                  ],
                  "fill-outline-color": [
                    "case",
                    ["==", ["get", "active"], "true"],
                    styling.active_color,
                    styling.fill_color,
                  ],
                  "fill-opacity": styling.fill_opacity,
                },
              },
              // Polygon outline
              {
                id: "gl-draw-polygon-stroke",
                type: "line",
                filter: ["all", ["==", "$type", "Polygon"]],
                layout: {
                  "line-cap": "round",
                  "line-join": "round",
                },
                paint: {
                  "line-color": [
                    "case",
                    ["==", ["get", "active"], "true"],
                    styling.active_color,
                    styling.line_color,
                  ],
                  "line-width": styling.line_width,
                },
              },
              // Midpoints
              {
                id: "gl-draw-polygon-midpoint",
                type: "circle",
                filter: [
                  "all",
                  ["==", "$type", "Point"],
                  ["==", "meta", "midpoint"],
                ],
                paint: {
                  "circle-radius": 3,
                  "circle-color": styling.active_color,
                },
              },
              // Vertex point halos
              {
                id: "gl-draw-vertex-halo-active",
                type: "circle",
                filter: [
                  "all",
                  ["==", "meta", "vertex"],
                  ["==", "$type", "Point"],
                ],
                paint: {
                  "circle-radius": [
                    "case",
                    ["==", ["get", "active"], "true"],
                    styling.vertex_radius + 4,
                    styling.vertex_radius + 2,
                  ],
                  "circle-color": "#FFF",
                },
              },
              // Vertex points
              {
                id: "gl-draw-vertex-active",
                type: "circle",
                filter: [
                  "all",
                  ["==", "meta", "vertex"],
                  ["==", "$type", "Point"],
                ],
                paint: {
                  "circle-radius": [
                    "case",
                    ["==", ["get", "active"], "true"],
                    styling.vertex_radius + 2,
                    styling.vertex_radius,
                  ],
                  "circle-color": styling.active_color,
                },
              },
            ];
          }

          // Helper function to add features from a source to draw
          function addSourceFeaturesToDraw(draw, sourceId, map) {
            const source = map.getSource(sourceId);
            if (source && source._data) {
              draw.add(source._data);
            } else {
              console.warn("Source not found or has no data:", sourceId);
            }
          }

          if (mapData.scale_control) {
            const scaleControl = new maplibregl.ScaleControl({
              maxWidth: mapData.scale_control.maxWidth,
              unit: mapData.scale_control.unit,
            });
            map.addControl(scaleControl, mapData.scale_control.position);
            map.controls.push(scaleControl);
          }

          // Add navigation control if enabled
          if (mapData.navigation_control) {
            const nav = new maplibregl.NavigationControl({
              showCompass: mapData.navigation_control.show_compass,
              showZoom: mapData.navigation_control.show_zoom,
              visualizePitch: mapData.navigation_control.visualize_pitch,
            });
            map.addControl(nav, mapData.navigation_control.position);
            map.controls.push({ type: "navigation", control: nav });
          }

          // Add geolocate control if enabled
          if (mapData.geolocate_control) {
            const geolocate = new maplibregl.GeolocateControl({
              positionOptions: mapData.geolocate_control.positionOptions,
              trackUserLocation: mapData.geolocate_control.trackUserLocation,
              showAccuracyCircle: mapData.geolocate_control.showAccuracyCircle,
              showUserLocation: mapData.geolocate_control.showUserLocation,
              showUserHeading: mapData.geolocate_control.showUserHeading,
              fitBoundsOptions: mapData.geolocate_control.fitBoundsOptions,
            });
            map.addControl(geolocate, mapData.geolocate_control.position);

            map.controls.push(geolocate);
          }

          // Add globe control if enabled
          if (mapData.globe_control) {
            const globeControl = new maplibregl.GlobeControl();
            map.addControl(globeControl, mapData.globe_control.position);
            map.controls.push(globeControl);
          }

          // Add draw control if enabled
          if (mapData.draw_control && mapData.draw_control.enabled) {
            MapboxDraw.constants.classes.CONTROL_BASE = "maplibregl-ctrl";
            MapboxDraw.constants.classes.CONTROL_PREFIX = "maplibregl-ctrl-";
            MapboxDraw.constants.classes.CONTROL_GROUP =
              "maplibregl-ctrl-group";

            let drawOptions = mapData.draw_control.options || {};

            // Generate styles if styling parameters provided
            if (mapData.draw_control.styling) {
              const generatedStyles = generateDrawStyles(
                mapData.draw_control.styling,
              );
              if (generatedStyles) {
                drawOptions.styles = generatedStyles;
              }
            }

            if (mapData.draw_control.freehand) {
              drawOptions = Object.assign({}, drawOptions, {
                modes: Object.assign({}, MapboxDraw.modes, {
                  draw_polygon: Object.assign(
                    {},
                    MapboxDraw.modes.draw_freehand,
                    {
                      // Store the simplify_freehand option on the map object
                      onSetup: function (opts) {
                        const state =
                          MapboxDraw.modes.draw_freehand.onSetup.call(
                            this,
                            opts,
                          );
                        this.map.simplify_freehand =
                          mapData.draw_control.simplify_freehand;
                        return state;
                      },
                    },
                  ),
                }),
                // defaultMode: 'draw_polygon' # Don't set the default yet
              });
            }

            // Add rectangle mode if enabled
            if (mapData.draw_control.rectangle) {
              if (!drawOptions.modes) {
                drawOptions.modes = Object.assign({}, MapboxDraw.modes);
              }
              drawOptions.modes.draw_rectangle =
                MapboxDraw.modes.draw_rectangle;
            }

            // Add radius mode if enabled
            if (mapData.draw_control.radius) {
              if (!drawOptions.modes) {
                drawOptions.modes = Object.assign({}, MapboxDraw.modes);
              }
              drawOptions.modes.draw_radius = MapboxDraw.modes.draw_radius;
            }

            // Fix MapLibre compatibility - ensure we always have custom styles
            if (!drawOptions.styles) {
              drawOptions.styles = generateDrawStyles({
                vertex_radius: 5,
                active_color: "#fbb03b",
                point_color: "#3bb2d0",
                line_color: "#3bb2d0",
                fill_color: "#3bb2d0",
                fill_opacity: 0.1,
                line_width: 2,
              });
            }

            const drawControl = new MapboxDraw(drawOptions);
            map.addControl(drawControl, mapData.draw_control.position);
            map.controls.push({ type: "draw", control: drawControl });

            // Store draw control on map for feature click detection
            map._mapgl_draw = drawControl;

            // Add custom mode buttons and styling
            setTimeout(() => {
              const drawControlGroup = map
                .getContainer()
                .querySelector(".maplibregl-ctrl-group");

              // Add rectangle styling and button
              if (mapData.draw_control.rectangle) {
                if (!document.querySelector("#mapgl-rectangle-styles")) {
                  const style = document.createElement("style");
                  style.id = "mapgl-rectangle-styles";
                  style.textContent = `
                                      .mapbox-gl-draw_rectangle {
                                        background: transparent;
                                        border: none;
                                        cursor: pointer;
                                        display: block;
                                        height: 30px;
                                        width: 30px;
                                        padding: 0;
                                        outline: none;
                                        background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"%3E%3Crect x="4" y="5" width="12" height="10" fill="none" stroke="%23000000" stroke-width="2"/%3E%3C/svg%3E') !important;
                                        background-repeat: no-repeat !important;
                                        background-position: center !important;
                                      }
                                      .mapbox-gl-draw_rectangle:hover {
                                        background-color: rgba(0, 0, 0, 0.05);
                                      }
                                      .mapbox-gl-draw_rectangle.active {
                                        background-color: rgba(0, 0, 0, 0.05);
                                      }
                                    `;
                  document.head.appendChild(style);
                }

                if (drawControlGroup) {
                  const rectangleBtn = document.createElement("button");
                  rectangleBtn.className = "mapbox-gl-draw_rectangle";
                  rectangleBtn.title = "Rectangle tool";
                  rectangleBtn.type = "button";
                  rectangleBtn.onclick = function () {
                    drawControl.changeMode("draw_rectangle");
                    drawControlGroup
                      .querySelectorAll("button")
                      .forEach((btn) => btn.classList.remove("active"));
                    rectangleBtn.classList.add("active");
                  };
                  drawControlGroup.appendChild(rectangleBtn);
                }
              }

              // Add radius styling and button
              if (mapData.draw_control.radius) {
                if (!document.querySelector("#mapgl-radius-styles")) {
                  const style = document.createElement("style");
                  style.id = "mapgl-radius-styles";
                  style.textContent = `
                                      .mapbox-gl-draw_radius {
                                        background: transparent;
                                        border: none;
                                        cursor: pointer;
                                        display: block;
                                        height: 30px;
                                        width: 30px;
                                        padding: 0;
                                        outline: none;
                                        background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"%3E%3Ccircle cx="10" cy="10" r="7" fill="none" stroke="%23000000" stroke-width="2"/%3E%3Ccircle cx="10" cy="10" r="1.5" fill="%23000000"/%3E%3C/svg%3E') !important;
                                        background-repeat: no-repeat !important;
                                        background-position: center !important;
                                      }
                                      .mapbox-gl-draw_radius:hover {
                                        background-color: rgba(0, 0, 0, 0.05);
                                      }
                                      .mapbox-gl-draw_radius.active {
                                        background-color: rgba(0, 0, 0, 0.05);
                                      }
                                    `;
                  document.head.appendChild(style);
                }

                if (drawControlGroup) {
                  const radiusBtn = document.createElement("button");
                  radiusBtn.className = "mapbox-gl-draw_radius";
                  radiusBtn.title = "Radius/Circle tool";
                  radiusBtn.type = "button";
                  radiusBtn.onclick = function () {
                    drawControl.changeMode("draw_radius");
                    drawControlGroup
                      .querySelectorAll("button")
                      .forEach((btn) => btn.classList.remove("active"));
                    radiusBtn.classList.add("active");
                  };
                  drawControlGroup.appendChild(radiusBtn);
                }
              }
            }, 100);

            // Add initial features if provided
            if (mapData.draw_control.source) {
              addSourceFeaturesToDraw(
                drawControl,
                mapData.draw_control.source,
                map,
              );
            }

            // Process any queued features
            if (mapData.draw_features_queue) {
              mapData.draw_features_queue.forEach(function (data) {
                if (data.clear_existing) {
                  drawControl.deleteAll();
                }
                addSourceFeaturesToDraw(drawControl, data.source, map);
              });
            }

            // Apply orientation styling
            if (mapData.draw_control.orientation === "horizontal") {
              const drawBar = map
                .getContainer()
                .querySelector(".maplibregl-ctrl-group");
              if (drawBar) {
                drawBar.style.display = "flex";
                drawBar.style.flexDirection = "row";
              }
            }

            // Add download button if requested
            if (mapData.draw_control.download_button) {
              // Add CSS for download button if not already added
              if (!document.querySelector("#mapgl-draw-download-styles")) {
                const style = document.createElement("style");
                style.id = "mapgl-draw-download-styles";
                style.textContent = `
                                  .mapbox-gl-draw_download {
                                    background: transparent;
                                    border: none;
                                    cursor: pointer;
                                    display: block;
                                    height: 30px;
                                    width: 30px;
                                    padding: 0;
                                    outline: none;
                                  }
                                  .mapbox-gl-draw_download:hover {
                                    background-color: rgba(0, 0, 0, 0.05);
                                  }
                                  .mapbox-gl-draw_download svg {
                                    width: 20px;
                                    height: 20px;
                                    margin: 5px;
                                    fill: #333;
                                  }
                                `;
                document.head.appendChild(style);
              }

              // Small delay to ensure Draw control is fully rendered
              setTimeout(() => {
                // Find the Draw control button group
                const drawButtons = map
                  .getContainer()
                  .querySelector(
                    ".maplibregl-ctrl-group:has(.mapbox-gl-draw_polygon)",
                  );

                if (drawButtons) {
                  // Create download button
                  const downloadBtn = document.createElement("button");
                  downloadBtn.className = "mapbox-gl-draw_download";
                  downloadBtn.title = "Download drawn features as GeoJSON";

                  // Add SVG download icon
                  downloadBtn.innerHTML = `
                                      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                                        <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
                                      </svg>
                                    `;

                  downloadBtn.addEventListener("click", () => {
                    // Get all drawn features
                    const data = draw.getAll();

                    if (data.features.length === 0) {
                      alert(
                        "No features to download. Please draw something first!",
                      );
                      return;
                    }

                    // Convert to string with nice formatting
                    const dataStr = JSON.stringify(data, null, 2);

                    // Create blob and download
                    const blob = new Blob([dataStr], {
                      type: "application/json",
                    });
                    const url = URL.createObjectURL(blob);

                    const a = document.createElement("a");
                    a.href = url;
                    a.download = `${mapData.draw_control.download_filename}.geojson`;
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                  });

                  // Append to the Draw control button group
                  drawButtons.appendChild(downloadBtn);
                }
              }, 100);
            }

            // Helper function for updating drawn features
            function updateDrawnFeatures() {
              if (HTMLWidgets.shinyMode && draw) {
                const features = draw.getAll();
                Shiny.setInputValue(
                  el.id + "_drawn_features",
                  JSON.stringify(features),
                );
              }
            }

            // Add event listeners
            map.on("draw.create", updateDrawnFeatures);
            map.on("draw.delete", updateDrawnFeatures);
            map.on("draw.update", updateDrawnFeatures);
          }

          if (mapData.geolocate_control && HTMLWidgets.shinyMode) {
            geolocate.on("geolocate", function (event) {
              Shiny.setInputValue(el.id + "_geolocate", {
                coords: event.coords,
                time: new Date(),
              });
            });

            geolocate.on("trackuserlocationstart", function () {
              Shiny.setInputValue(el.id + "_geolocate_tracking", {
                status: "start",
                time: new Date(),
              });
            });

            geolocate.on("trackuserlocationend", function () {
              Shiny.setInputValue(el.id + "_geolocate_tracking", {
                status: "end",
                time: new Date(),
              });
            });

            geolocate.on("error", function (error) {
              if (error.error.code === 1) {
                Shiny.setInputValue(el.id + "_geolocate_error", {
                  message: "Location permission denied",
                  time: new Date(),
                });
              }
            });
          }

          // Add geocoder control if enabled
          if (mapData.geocoder_control) {
            const provider = mapData.geocoder_control.provider || "osm";
            let geocoder;

            if (provider === "maptiler") {
              // MapTiler geocoder
              const maptilerOptions = {
                apiKey: mapData.geocoder_control.api_key,
                maplibregl: maplibregl,
                ...mapData.geocoder_control,
              };

              // Create MapTiler geocoder
              geocoder = new maplibreglMaptilerGeocoder.GeocodingControl(
                maptilerOptions,
              );
            } else {
              // OSM/Nominatim geocoder (default)
              const geocoderApi = {
                forwardGeocode: async (config) => {
                  const features = [];
                  try {
                    const request = `https://nominatim.openstreetmap.org/search?q=${
                      config.query
                    }&format=geojson&polygon_geojson=1&addressdetails=1`;
                    const response = await fetch(request);
                    const geojson = await response.json();
                    for (const feature of geojson.features) {
                      const center = [
                        feature.bbox[0] +
                          (feature.bbox[2] - feature.bbox[0]) / 2,
                        feature.bbox[1] +
                          (feature.bbox[3] - feature.bbox[1]) / 2,
                      ];
                      const point = {
                        type: "Feature",
                        geometry: {
                          type: "Point",
                          coordinates: center,
                        },
                        place_name: feature.properties.display_name,
                        properties: feature.properties,
                        text: feature.properties.display_name,
                        place_type: ["place"],
                        center,
                      };
                      features.push(point);
                    }
                  } catch (e) {
                    console.error(`Failed to forwardGeocode with error: ${e}`);
                  }

                  return {
                    features,
                  };
                },
              };
              const geocoderOptions = {
                maplibregl: maplibregl,
                ...mapData.geocoder_control,
              };

              // Set default values if not provided
              if (!geocoderOptions.placeholder)
                geocoderOptions.placeholder = "Search";
              if (typeof geocoderOptions.collapsed === "undefined")
                geocoderOptions.collapsed = false;

              geocoder = new MaplibreGeocoder(geocoderApi, geocoderOptions);
            }

            map.addControl(
              geocoder,
              mapData.geocoder_control.position || "top-right",
            );

            // Apply CSS fix for MapTiler geocoder to prevent cutoff
            if (provider === "maptiler") {
              setTimeout(() => {
                const controlContainer = document.querySelector(
                  ".maplibregl-ctrl-geocoder",
                );
                if (controlContainer) {
                  controlContainer.style.maxWidth = "300px";
                  controlContainer.style.width = "auto";
                }
              }, 100);
            }

            // Handle geocoder results in Shiny mode
            if (HTMLWidgets.shinyMode) {
              if (provider === "maptiler") {
                // MapTiler uses different event names
                geocoder.on("pick", function (e) {
                  Shiny.setInputValue(el.id + "_geocoder", {
                    result: e,
                    time: new Date(),
                  });
                });
              } else {
                // OSM geocoder
                geocoder.on("results", function (e) {
                  Shiny.setInputValue(el.id + "_geocoder", {
                    result: e,
                    time: new Date(),
                  });
                });
              }
            }
          }

          // Add reset control if enabled
          if (mapData.reset_control) {
            const resetControl = document.createElement("button");
            resetControl.className =
              "maplibregl-ctrl-icon maplibregl-ctrl-reset";
            resetControl.type = "button";
            resetControl.setAttribute("aria-label", "Reset");
            resetControl.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>';
            resetControl.style.cssText = "display:flex;justify-content:center;align-items:center;cursor:pointer;";

            const resetContainer = document.createElement("div");
            resetContainer.className = "maplibregl-ctrl maplibregl-ctrl-group";
            resetContainer.appendChild(resetControl);

            const initialView = {
              center: map.getCenter(),
              zoom: map.getZoom(),
              pitch: map.getPitch(),
              bearing: map.getBearing(),
              animate: mapData.reset_control.animate,
            };

            if (mapData.reset_control.duration) {
              initialView.duration = mapData.reset_control.duration;
            }

            resetControl.onclick = function () {
              map.easeTo(initialView);
            };

            map.addControl(
              {
                onAdd: function () {
                  return resetContainer;
                },
                onRemove: function () {
                  resetContainer.parentNode.removeChild(resetContainer);
                },
              },
              mapData.reset_control.position,
            );
          }

          // Add coordinates control if enabled
          if (mapData.coordinates_control) {
            const coordinatesControlObj = createCoordinatesControl(
              mapData.coordinates_control,
            );
            map.addControl(
              coordinatesControlObj,
              mapData.coordinates_control.position || "bottom-right",
            );
            map.controls.push({
              type: "coordinates",
              control: coordinatesControlObj,
            });
          }

          function updateDrawnFeatures() {
            if (map._mapgl_draw) {
              var drawnFeatures = map._mapgl_draw.getAll();
              if (HTMLWidgets.shinyMode) {
                Shiny.setInputValue(
                  el.id + "_drawn_features",
                  JSON.stringify(drawnFeatures),
                );
              }
              // Store drawn features in the widget's data
              if (el.querySelector) {
                var widget = HTMLWidgets.find("#" + el.id);
                if (widget) {
                  widget.drawFeatures = drawnFeatures;
                }
              }
            }
          }

          // Add the layers control if provided
          if (mapData.layers_control) {
            const layersControl = document.createElement("div");
            layersControl.id = mapData.layers_control.control_id;

            // Handle use_icon parameter
            let className = mapData.layers_control.collapsible
              ? "layers-control collapsible"
              : "layers-control";

            layersControl.className = className;
            layersControl.style.position = "absolute";

            // Set the position correctly - fix position bug by using correct CSS positioning
            const position = mapData.layers_control.position || "top-left";
            if (position === "top-left") {
              layersControl.style.top =
                (mapData.layers_control.margin_top || 10) + "px";
              layersControl.style.left =
                (mapData.layers_control.margin_left || 10) + "px";
            } else if (position === "top-right") {
              layersControl.style.top =
                (mapData.layers_control.margin_top || 10) + "px";
              layersControl.style.right =
                (mapData.layers_control.margin_right || 10) + "px";
            } else if (position === "bottom-left") {
              layersControl.style.bottom =
                (mapData.layers_control.margin_bottom || 30) + "px";
              layersControl.style.left =
                (mapData.layers_control.margin_left || 10) + "px";
            } else if (position === "bottom-right") {
              layersControl.style.bottom =
                (mapData.layers_control.margin_bottom || 40) + "px";
              layersControl.style.right =
                (mapData.layers_control.margin_right || 10) + "px";
            }

            el.appendChild(layersControl);

            const layersList = document.createElement("div");
            layersList.className = "layers-list";
            layersControl.appendChild(layersList);

            // Fetch layers to be included in the control
            let layers =
              mapData.layers_control.layers ||
              map.getStyle().layers.map((layer) => layer.id);
            let layersConfig = mapData.layers_control.layers_config;
            const getLayerControlVisibility = (targetMap, layerId) => {
              if (
                window.MapGLFlowmapPlugin &&
                window.MapGLFlowmapPlugin.hasLayer(targetMap, layerId)
              ) {
                return window.MapGLFlowmapPlugin.getVisibility(
                  targetMap,
                  layerId,
                );
              }
              if (targetMap.getLayer(layerId)) {
                return (
                  targetMap.getLayoutProperty(layerId, "visibility") ||
                  "visible"
                );
              }
              return "visible";
            };
            const setLayerControlVisibility = (
              targetMap,
              layerId,
              visibility,
            ) => {
              if (
                window.MapGLFlowmapPlugin &&
                window.MapGLFlowmapPlugin.setVisibility(
                  targetMap,
                  layerId,
                  visibility,
                )
              ) {
                return;
              }
              if (targetMap.getLayer(layerId)) {
                targetMap.setLayoutProperty(layerId, "visibility", visibility);
              }
            };

            // If we have a layers_config, use that; otherwise fall back to original behavior
            if (layersConfig && Array.isArray(layersConfig)) {
              layersConfig.forEach((config, index) => {
                const link = document.createElement("a");
                // Ensure config.ids is always an array
                const layerIds = Array.isArray(config.ids)
                  ? config.ids
                  : [config.ids];
                link.id = layerIds.join("-");
                link.href = "#";
                link.textContent = config.label;
                link.setAttribute("data-layer-ids", JSON.stringify(layerIds));
                link.setAttribute("data-layer-type", config.type);

                // Check if the first layer's visibility is set to "none" initially.
                // In a compare widget each side only has its own layers, but a single
                // layers_control can reference ids from both sides (e.g. "sp" and "env")
                // so the toggle can fire on both maps. If the first id isn't present on
                // this map, fall back to "visible". We pre-check with getLayer() because
                // getLayoutProperty() on a missing layer fires an error event (not a
                // throw), which try/catch cannot silence.
                const firstLayerId = layerIds[0];
                const initialVisibility = getLayerControlVisibility(
                  map,
                  firstLayerId,
                );
                link.className = initialVisibility === "none" ? "" : "active";

                // Show or hide layer(s) when the toggle is clicked
                // toggle on BOTH maps in the compare widget
                link.onclick = function (e) {
                  e.preventDefault();
                  e.stopPropagation();

                  const layerIds = JSON.parse(
                    this.getAttribute("data-layer-ids"),
                  );
                  // read visibility from whichever map actually has this layer
                  // (pre-check with getLayer() — getLayoutProperty fires an error
                  // event on missing layers and try/catch cannot silence it)
                  const firstLayerId = layerIds[0];
                  const visibility = getLayerControlVisibility(
                    map,
                    firstLayerId,
                  );

                  const newVis = visibility === "visible" ? "none" : "visible";
                  const allMaps = compareMaps;
                  layerIds.forEach((layerId) => {
                    allMaps.forEach((m) => {
                      setLayerControlVisibility(m, layerId, newVis);
                    });
                  });
                  this.className = newVis === "visible" ? "active" : "";
                };

                layersList.appendChild(link);
              });
            } else {
              // Fallback to original behavior for simple layer arrays
              layers.forEach((layerId, index) => {
                const link = document.createElement("a");
                link.id = layerId;
                link.href = "#";
                link.textContent = layerId;
                link.className = "active";

                // Show or hide layer when the toggle is clicked
                // toggle on BOTH maps in the compare widget
                link.onclick = function (e) {
                  const clickedLayer = this.textContent;
                  e.preventDefault();
                  e.stopPropagation();

                  const visibility = getLayerControlVisibility(
                    map,
                    clickedLayer,
                  );

                  // toggle on BOTH maps in the compare widget
                  const newVis = visibility === "visible" ? "none" : "visible";
                  compareMaps.forEach((m) => {
                    setLayerControlVisibility(m, clickedLayer, newVis);
                  });
                  this.className = newVis === "visible" ? "active" : "";
                };

                layersList.appendChild(link);
              });
            }

            // Handle collapsible behavior
            if (mapData.layers_control.collapsible) {
              const toggleButton = document.createElement("div");
              toggleButton.className = "toggle-button";

              if (mapData.layers_control.use_icon) {
                // Add icon-only class to the control for compact styling
                layersControl.classList.add("icon-only");

                // More GIS-like layers stack icon
                toggleButton.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                    <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
                                    <polyline points="2 17 12 22 22 17"></polyline>
                                    <polyline points="2 12 12 17 22 12"></polyline>
                                </svg>`;
                toggleButton.style.display = "flex";
                toggleButton.style.alignItems = "center";
                toggleButton.style.justifyContent = "center";
              } else {
                toggleButton.textContent = "Layers";
              }

              toggleButton.onclick = function () {
                layersControl.classList.toggle("open");
              };
              layersControl.insertBefore(toggleButton, layersList);
            }
          }

          // Set projection if provided (after all other setup is complete)
          if (mapData.setProjection && mapData.setProjection.length > 0) {
            mapData.setProjection.forEach(function (projectionConfig) {
              if (projectionConfig.projection) {
                const projection =
                  typeof projectionConfig.projection === "string"
                    ? { type: projectionConfig.projection }
                    : projectionConfig.projection;
                try {
                  map.setProjection(projection);
                } catch (e) {
                  console.error("Failed to set projection in compare view:", e);
                }
              }
            });
          }
        }
      },

      resize: function (width, height) {
        // Code to handle resizing if necessary
      },

      getBeforeMap: function () {
        return beforeMap;
      },

      getAfterMap: function () {
        return afterMap;
      },

      getMaps: function () {
        return compareMaps;
      },
    };
  },
});
