@photo-sphere-viewer/map-plugin
Version: 
Photo Sphere Viewer plugin to add a minimap with the panorama location.
1,298 lines (1,271 loc) • 46 kB
JavaScript
/*!
 * Photo Sphere Viewer / Map Plugin 5.14.0
 * @copyright 2015-2025 Damien "Mistic" Sorel
 * @licence MIT (https://opensource.org/licenses/MIT)
 */
var __defProp = Object.defineProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
// src/index.ts
import { DEFAULTS } from "@photo-sphere-viewer/core";
// src/events.ts
var events_exports = {};
__export(events_exports, {
  SelectHotspot: () => SelectHotspot,
  ViewChanged: () => ViewChanged
});
import { TypedEvent } from "@photo-sphere-viewer/core";
var _SelectHotspot = class _SelectHotspot extends TypedEvent {
  /** @internal */
  constructor(hotspotId) {
    super(_SelectHotspot.type);
    this.hotspotId = hotspotId;
  }
};
_SelectHotspot.type = "select-hotspot";
var SelectHotspot = _SelectHotspot;
var _ViewChanged = class _ViewChanged extends TypedEvent {
  /** @internal */
  constructor(view) {
    super(_ViewChanged.type);
    this.view = view;
  }
};
_ViewChanged.type = "view-changed";
var ViewChanged = _ViewChanged;
// src/MapPlugin.ts
import { AbstractConfigurablePlugin, events as events2, utils as utils3 } from "@photo-sphere-viewer/core";
import { Color } from "three";
// src/components/MapComponent.ts
import { AbstractComponent as AbstractComponent3, CONSTANTS as CONSTANTS2, events, SYSTEM as SYSTEM2, utils as utils2 } from "@photo-sphere-viewer/core";
import { MathUtils } from "three";
// src/constants.ts
var MARKER_DATA_KEY = "map";
var HOTSPOT_GENERATED_ID = "__generated__";
var HOTSPOT_MARKER_ID = "__marker__";
var PIN_SHADOW_OFFSET = 2;
var PIN_SHADOW_BLUR = 4;
var MAP_SHADOW_BLUR = 10;
// src/utils.ts
import { SYSTEM } from "@photo-sphere-viewer/core";
function loadImage(src) {
  const image = document.createElement("img");
  if (!src.includes("<svg")) {
    image.src = src;
  } else {
    if (!/<svg[^>]*width="/.test(src) && src.includes("viewBox")) {
      const [, , , width, height] = /viewBox="([0-9-]+) ([0-9-]+) ([0-9]+) ([0-9]+)"/.exec(src);
      src = src.replace("<svg", `<svg width="${width}px" height="${height}px"`);
    }
    const src64 = `data:image/svg+xml;base64,${window.btoa(src)}`;
    image.src = src64;
  }
  return image;
}
function getImageHtml(src) {
  if (!src) {
    return "";
  } else if (!src.includes("<svg")) {
    return `<img src="${src}">`;
  } else {
    return src;
  }
}
function getStyle(defaultStyle, style, isHover) {
  return {
    image: isHover ? style.hoverImage ?? style.image ?? defaultStyle.hoverImage ?? defaultStyle.image : style.image ?? defaultStyle.image,
    size: isHover ? style.hoverSize ?? style.size ?? defaultStyle.hoverSize ?? defaultStyle.size : style.size ?? defaultStyle.size,
    color: isHover ? style.hoverColor ?? style.color ?? defaultStyle.hoverColor ?? defaultStyle.color : style.color ?? defaultStyle.color,
    borderColor: isHover ? style.hoverBorderColor ?? style.borderColor ?? defaultStyle.hoverBorderColor ?? defaultStyle.borderColor : style.borderColor ?? defaultStyle.borderColor,
    borderSize: isHover ? style.hoverBorderSize ?? style.borderSize ?? defaultStyle.hoverBorderSize ?? defaultStyle.borderSize : style.borderSize ?? defaultStyle.borderSize
  };
}
function unprojectPoint(pt, yaw, zoom) {
  return {
    x: (Math.cos(yaw) * pt.x - Math.sin(yaw) * pt.y) / zoom,
    y: (Math.sin(yaw) * pt.x + Math.cos(yaw) * pt.y) / zoom
  };
}
function projectPoint(pt, yaw, zoom) {
  return {
    x: (Math.cos(-yaw) * pt.x - Math.sin(-yaw) * pt.y) * zoom,
    y: (Math.sin(-yaw) * pt.x + Math.cos(-yaw) * pt.y) * zoom
  };
}
function canvasShadow(context, offsetX, offsetY, blur, color = "black") {
  context.shadowOffsetX = offsetX * SYSTEM.pixelRatio;
  context.shadowOffsetY = offsetY * SYSTEM.pixelRatio;
  context.shadowBlur = blur * SYSTEM.pixelRatio;
  context.shadowColor = color;
}
function drawImageCentered(context, image, size) {
  const w = image.width;
  const h = image.height;
  drawImageHighDpi(
    context,
    image,
    -size / 2,
    -(h / w * size) / 2,
    size,
    h / w * size
  );
}
function drawImageHighDpi(context, image, x, y, w, h) {
  context.drawImage(
    image,
    0,
    0,
    image.width,
    image.height,
    x * SYSTEM.pixelRatio,
    y * SYSTEM.pixelRatio,
    w * SYSTEM.pixelRatio,
    h * SYSTEM.pixelRatio
  );
}
function rgbToRgba(rgb, alpha) {
  return `rgba(${rgb.slice(4, -1)},${alpha})`;
}
// src/components/MapCloseButton.ts
import { CONSTANTS } from "@photo-sphere-viewer/core";
// src/icons/map.svg
var map_default = '<svg viewBox="114 45 472 472" xmlns="http://www.w3.org/2000/svg"><g fill="currentColor"><path d="M383.6 196a67.3 67.3 0 1 0-134.5.1 67.3 67.3 0 0 0 134.5-.1zm-100.8 0a33.6 33.6 0 1 1 67.3 0 33.6 33.6 0 0 1-67.3 0z"/><path d="M584 340.8a16.8 16.8 0 0 0-15.6-10.4H403.8c25.2-40.2 47-88 47-133.4A135 135 0 0 0 316.4 61.6 135 135 0 0 0 182 197c0 55.8 33 115.3 64.7 159.8L120.4 469a16.8 16.8 0 0 0 11.2 29.4H434c4.5 0 8.7-1.8 11.9-5l134.4-134.3c4.8-4.8 6.2-12 3.6-18.3zM215.5 197c0-56.1 45.2-101.8 100.8-101.8 55.6 0 100.8 45.6 100.8 101.8 0 65-57.1 144.2-100.8 192.8C273 341.7 215.6 262.3 215.6 197zM427 464.8H175.8l91.3-81.1a575.6 575.6 0 0 0 37.4 42.6 16.8 16.8 0 0 0 23.8 0c2.2-2.2 26.3-26.7 52.6-62.3h147z"/></g><!-- Created by Ayub Irawan from Noun Project --></svg>';
// src/components/AbstractMapButton.ts
import { AbstractComponent } from "@photo-sphere-viewer/core";
var INVERT_POSITIONS = {
  top: "bottom",
  bottom: "top",
  left: "right",
  right: "left"
};
function getButtonPosition(mapPosition, direction) {
  switch (direction) {
    case 1 /* DIAGONAL */:
      return [INVERT_POSITIONS[mapPosition[0]], INVERT_POSITIONS[mapPosition[1]]];
    case 2 /* HORIZONTAL */:
      return [mapPosition[0], INVERT_POSITIONS[mapPosition[1]]];
    case 3 /* VERTICAL */:
      return [INVERT_POSITIONS[mapPosition[0]], mapPosition[1]];
    default:
      return mapPosition;
  }
}
var AbstractMapButton = class extends AbstractComponent {
  constructor(map, position) {
    super(map, {});
    this.map = map;
    this.position = position;
  }
  applyConfig() {
    this.container.className = `psv-map__button psv-map__button--${getButtonPosition(this.map.config.position, this.position).join("-")}`;
    this.update();
  }
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  update() {
  }
};
// src/components/MapCloseButton.ts
var MapCloseButton = class extends AbstractMapButton {
  constructor(map) {
    super(map, 0 /* DEFAULT */);
    this.container.addEventListener("click", (e) => {
      map.toggleCollapse();
      e.stopPropagation();
    });
  }
  applyConfig() {
    super.applyConfig();
    this.container.classList.add("psv-map__button-close");
  }
  update() {
    this.container.innerHTML = this.map.collapsed ? map_default : CONSTANTS.ICONS.close;
    this.container.title = this.map.collapsed ? this.viewer.config.lang["map"] : this.viewer.config.lang.close;
  }
};
// src/icons/compass.svg
var compass_default = '<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path d="M50,0L70,50L50,100L30,50Z M50,86L64,50L36,50Z" fill="currentColor"/></svg>';
// src/components/MapCompassButton.ts
var MapCompassButton = class extends AbstractMapButton {
  constructor(map) {
    super(map, 3 /* VERTICAL */);
    this.container.innerHTML = compass_default;
    this.container.querySelector("svg").style.width = "80%";
    this.container.addEventListener("click", (e) => {
      this.viewer.dynamics.position.goto({ yaw: -map.config.rotation }, 2);
      e.stopPropagation();
    });
  }
  rotate(angle) {
    this.container.querySelector("svg").style.transform = `rotate3d(0, 0, 1, ${-angle}rad)`;
  }
  update() {
    this.container.title = this.viewer.config.lang["mapNorth"];
  }
};
// src/icons/maximize.svg
var maximize_default = '<svg viewBox="95 25 510 510" xmlns="http://www.w3.org/2000/svg"><path d="M604.2 39.8v481c0 7.8-6.1 14-14 14H358.4c-7.8 0-14-6.2-14-14s6.2-14 14-14h217.8v-453H123.8v216.7c0 7.8-6.2 14-14 14-7.9 0-14-6.2-14-14V39.8c0-7.9 6.1-14 14-14h481c7.3 0 13.4 6.1 13.4 14zm-304 304v176.4c0 7.9-6.2 14-14 14H109.8c-7.9 0-14-6.1-14-14V343.8c0-7.8 6.1-14 14-14h176.4c7.2 0 14 6.8 14 14zm-28 14H123.8v148.4h148.4zm215.6-195.4v79.5c0 7.9 6.1 14 14 14 7.8 0 14-6.1 14-14V128.2c0-7.8-6.2-14-14-14H388.6c-7.8 0-14 6.2-14 14 0 7.9 6.2 14 14 14h79L326.5 283.4a13.5 13.5 0 0 0 0 19.6c2.8 2.8 6.1 3.9 10 3.9 4 0 7.3-1.1 10.1-4z" fill="currentColor"/><!-- Created by Gregor Cresnar from Noun Project --></svg>';
// src/icons/minimize.svg
var minimize_default = '<svg viewBox="95 25 510 510" xmlns="http://www.w3.org/2000/svg"><path d="M109.8 25.8h481c7.8 0 14 6.1 14 14v481c0 7.8-6.2 14-14 14H358.4c-7.8 0-14-6.2-14-14s6.2-14 14-14h217.8v-453H123.8v216.7c0 7.8-6.2 14-14 14-7.9 0-14-6.2-14-14V39.8c0-7.9 6.1-14 14-14zm176.4 508.4H109.8c-7.9 0-14-6.1-14-14V343.8c0-7.8 6.1-14 14-14h176.4c7.8 0 14 6.2 14 14v176.4c0 7.9-6.8 14-14 14zm-14-176.4H123.8v148.4h148.4zm64.4-191.5c-7.9 0-14 6.2-14 14v113.1c0 7.9 6.1 14 14 14h113c8 0 14-6.1 14-14s-6-14-14-14h-79.4l141-141a13.5 13.5 0 0 0 0-19.7 13.5 13.5 0 0 0-19.5 0L350.6 259.8v-79.5c0-7.8-6.2-14-14-14z" fill="currentColor"/><!-- Created by Gregor Cresnar from Noun Project --></svg>';
// src/components/MapMaximizeButton.ts
var ROTATION = {
  "bottom-left": 0,
  "bottom-right": -90,
  "top-right": 180,
  "top-left": 90
};
var MapMaximizeButton = class extends AbstractMapButton {
  constructor(map) {
    super(map, 1 /* DIAGONAL */);
    this.container.addEventListener("click", (e) => {
      map.toggleMaximized();
      e.stopPropagation();
    });
  }
  update() {
    this.container.innerHTML = this.map.maximized ? minimize_default : maximize_default;
    this.container.querySelector("svg").style.transform = `rotate3d(0, 0, 1, ${ROTATION[this.map.config.position.join("-")]}deg)`;
    this.container.title = this.map.maximized ? this.viewer.config.lang["mapMinimize"] : this.viewer.config.lang["mapMaximize"];
  }
};
// src/icons/reset.svg
var reset_default = '<svg viewBox="170 100 360 360" xmlns="http://www.w3.org/2000/svg"><g fill="currentColor"><path d="M518.6 269h-18.5a150.8 150.8 0 0 0-138-137.9v-20.9c0-5.8-4.7-10.6-10.5-10.6h-3.2c-5.8 0-10.6 4.8-10.6 10.6v21A150.8 150.8 0 0 0 200 269h-18.5c-5.9 0-10.6 4.7-10.6 10.6v3.2c0 5.8 4.7 10.5 10.6 10.5h18.5c6 73.4 64.6 132 138 138v18.5c0 5.8 4.7 10.6 10.5 10.6h3.2c5.8 0 10.6-4.8 10.6-10.6v-18.6c73.3-5.9 132-64.5 137.9-137.9h18.5c5.9 0 10.6-4.7 10.6-10.5v-3.2c0-5.9-4.7-10.6-10.6-10.6zM362.2 414.4v-9.8c0-5.9-4.8-10.6-10.6-10.6h-3.2c-5.8 0-10.6 4.7-10.6 10.6v9.8a134 134 0 0 1-121-121h9.8c5.9 0 10.6-4.8 10.6-10.6v-3.2c0-5.9-4.7-10.6-10.6-10.6h-9.8a134 134 0 0 1 121-121v7.5c0 5.8 4.8 10.5 10.6 10.5h3.2c5.8 0 10.6-4.7 10.6-10.5V148a134 134 0 0 1 121 121h-9.8c-5.9 0-10.6 4.7-10.6 10.6v3.2c0 5.8 4.7 10.5 10.6 10.5h9.8a134 134 0 0 1-121 121z"/><path d="M355.4 222a6 6 0 0 0-10.7 0L291 320a8.3 8.3 0 0 0 9.7 12l39.2-11.7c6.6-2 13.6-2 20.2 0l39.2 11.7a8.3 8.3 0 0 0 9.7-12z"/></g><!-- Created by muhammad benani from Noun Project --></svg>';
// src/components/MapResetButton.ts
var MapResetButton = class extends AbstractMapButton {
  constructor(map) {
    super(map, 2 /* HORIZONTAL */);
    this.container.innerHTML = reset_default;
    this.container.querySelector("svg").style.width = "80%";
    this.container.addEventListener("click", (e) => {
      map.reset();
      e.stopPropagation();
    });
  }
  update() {
    this.container.title = this.viewer.config.lang["mapReset"];
  }
};
// src/components/MapZoomToolbar.ts
import { AbstractComponent as AbstractComponent2, utils } from "@photo-sphere-viewer/core";
// src/icons/minus.svg
var minus_default = '<svg viewBox="128 58 444 444" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M350 58.2a221.8 221.8 0 1 0 0 443.6 221.8 221.8 0 0 0 0-443.6zm130.3 252.7H219.7a31 31 0 1 1 0-61.8h260.6a31 31 0 1 1 0 61.8z"/><!-- Created by Iconika from Noun Project --></svg>';
// src/icons/plus.svg
var plus_default = '<svg viewBox="143.8 73.8 412.5 412.5" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M350 73.8a206.2 206.2 0 1 0 0 412.4 206.2 206.2 0 0 0 0-412.4zm117.3 234H378v89.5a27.9 27.9 0 1 1-55.8 0V308h-89.4a27.9 27.9 0 1 1 0-55.8H322v-89.5a27.9 27.9 0 1 1 55.8 0v89.5h89.5a27.9 27.9 0 1 1 0 55.8z"/><!-- Created by Iconika from Noun Project --></svg>';
// src/components/MapZoomToolbar.ts
var MapZoomToolbar = class extends AbstractComponent2 {
  constructor(map) {
    super(map, {
      className: "psv-map__toolbar"
    });
    this.map = map;
    this.handler = new utils.PressHandler(100);
    this.container.innerHTML = `${minus_default}<span class="psv-map__toolbar-text">100%</span>${plus_default}`;
    this.zoomIndicator = this.container.querySelector(".psv-map__toolbar-text");
    const zoomButtons = this.container.querySelectorAll("svg");
    zoomButtons[0].dataset["delta"] = "-1";
    zoomButtons[1].dataset["delta"] = "1";
    this.container.addEventListener("mousedown", this);
    window.addEventListener("mouseup", this);
    this.container.addEventListener("touchstart", this);
    window.addEventListener("touchend", this);
  }
  destroy() {
    window.removeEventListener("mouseup", this);
    window.removeEventListener("touchend", this);
    super.destroy();
  }
  handleEvent(e) {
    switch (e.type) {
      case "mousedown":
      case "touchstart": {
        const button = utils.getMatchingTarget(e, "svg[data-delta]");
        const delta = button?.dataset["delta"];
        if (delta) {
          cancelAnimationFrame(this.animation);
          this.handler.down();
          this.time = performance.now();
          this.animateZoom(parseInt(delta, 10));
          e.preventDefault();
          e.stopPropagation();
        }
        break;
      }
      case "mouseup":
      case "touchend":
        if (this.animation) {
          this.handler.up(() => {
            cancelAnimationFrame(this.animation);
            this.animation = null;
          });
          e.preventDefault();
          e.stopPropagation();
        }
        break;
      default:
        break;
    }
  }
  setText(zoom) {
    this.zoomIndicator.innerText = `${Math.round(Math.exp(zoom) * 100)}%`;
  }
  animateZoom(delta) {
    this.animation = requestAnimationFrame((t) => {
      this.map.zoom(delta * (t - this.time) / 1e3);
      this.time = t;
      this.animateZoom(delta);
    });
  }
};
// src/overlay-round.svg
var overlay_round_default = '<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">\n    <defs>\n        <radialGradient id="gradient">\n            <stop offset="80%" stop-color="rgba(255, 255, 255, 0)"/>\n            <stop offset="90%" stop-color="rgba(255, 255, 255, .5)"/>\n        </radialGradient>\n    </defs>\n    <circle cx="250" cy="250" r="250" fill="url(#gradient)"/>\n    <circle cx="250" cy="250" r="245" fill="none" stroke="rgba(255, 255, 255, 0.8)" stroke-width="10"/>\n    <g fill="#222">\n        <rect x="1" y="248" width="18" height="4"/>\n        <rect x="481" y="248" width="18" height="4"/>\n        <rect x="248" y="1" width="4" height="18"/>\n        <rect x="248" y="481" width="4" height="18"/>\n    </g>\n</svg>';
// src/overlay-square.svg
var overlay_square_default = '<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">\n    <defs>\n        <linearGradient id="gradient">\n            <stop offset="5%" stop-color="rgba(255, 255, 255, .5)"/>\n            <stop offset="10%" stop-color="rgba(255, 255, 255, 0)"/>\n            <stop offset="90%" stop-color="rgba(255, 255, 255, 0)"/>\n            <stop offset="95%" stop-color="rgba(255, 255, 255, .5)"/>\n        </linearGradient>\n        <linearGradient id="gradient2" x1="0" x2="0" y1="0" y2="1">\n            <stop offset="5%" stop-color="rgba(255, 255, 255, .5)"/>\n            <stop offset="10%" stop-color="rgba(255, 255, 255, 0)"/>\n            <stop offset="90%" stop-color="rgba(255, 255, 255, 0)"/>\n            <stop offset="95%" stop-color="rgba(255, 255, 255, .5)"/>\n        </linearGradient>\n    </defs>=\n    <rect x="0" y="0" width="500" height="500" fill="url(#gradient)"/>\n    <rect x="0" y="0" width="500" height="500" fill="url(#gradient2)"/>\n    <rect x="5" y="5" width="490" height="490" fill="none" stroke="rgba(255, 255, 255, 0.8)" stroke-width="10"/>\n    <g fill="#222">\n        <rect x="1" y="248" width="18" height="4"/>\n        <rect x="481" y="248" width="18" height="4"/>\n        <rect x="248" y="1" width="4" height="18"/>\n        <rect x="248" y="481" width="4" height="18"/>\n    </g>\n</svg>';
// src/components/MapComponent.ts
var MapComponent = class extends AbstractComponent3 {
  constructor(viewer, plugin) {
    super(viewer, {
      className: `psv-map ${CONSTANTS2.CAPTURE_EVENTS_CLASS}`
    });
    this.plugin = plugin;
    this.state = {
      visible: false,
      maximized: false,
      collapsed: false,
      galleryWasVisible: false,
      imgScale: 1,
      zoom: this.config.defaultZoom,
      offset: { x: 0, y: 0 },
      mouseX: null,
      mouseY: null,
      mousedown: false,
      pinchDist: 0,
      pinchAngle: 0,
      hotspotPos: {},
      hotspotId: null,
      hotspotTooltip: null,
      markers: [],
      forceRender: false,
      needsUpdate: false,
      renderLoop: null,
      images: {}
    };
    const canvasContainer = document.createElement("div");
    canvasContainer.className = "psv-map__container";
    canvasContainer.addEventListener("mousedown", this);
    window.addEventListener("mousemove", this);
    window.addEventListener("mouseup", this);
    canvasContainer.addEventListener("touchstart", this);
    window.addEventListener("touchmove", this);
    window.addEventListener("touchend", this);
    canvasContainer.addEventListener("wheel", this);
    viewer.addEventListener(events.KeypressEvent.type, this);
    viewer.addEventListener(events.ConfigChangedEvent.type, this);
    this.canvas = document.createElement("canvas");
    this.__setCursor("move");
    canvasContainer.appendChild(this.canvas);
    this.overlay = document.createElement("div");
    this.overlay.className = "psv-map__overlay";
    canvasContainer.appendChild(this.overlay);
    this.container.appendChild(canvasContainer);
    this.container.addEventListener("transitionstart", this);
    this.container.addEventListener("transitionend", this);
    if (this.config.buttons.reset) {
      this.resetButton = new MapResetButton(this);
    }
    if (this.config.buttons.maximize) {
      this.maximizeButton = new MapMaximizeButton(this);
    }
    if (this.config.buttons.close) {
      this.closeButton = new MapCloseButton(this);
    }
    if (this.config.buttons.north) {
      this.compassButton = new MapCompassButton(this);
    }
    this.zoomToolbar = new MapZoomToolbar(this);
    const renderLoop = () => {
      if (this.isVisible() && (this.state.needsUpdate || this.state.forceRender)) {
        this.render();
        this.state.needsUpdate = false;
      }
      this.state.renderLoop = requestAnimationFrame(renderLoop);
    };
    renderLoop();
    this.applyConfig();
    this.hide();
    if (!this.config.visibleOnLoad) {
      this.toggleCollapse();
    }
  }
  get config() {
    return this.plugin.config;
  }
  get maximized() {
    return this.state.maximized;
  }
  get collapsed() {
    return this.state.collapsed;
  }
  init() {
    this.gallery = this.viewer.getPlugin("gallery");
    this.gallery?.addEventListener("show-gallery", this);
    this.gallery?.addEventListener("hide-gallery", this);
  }
  destroy() {
    this.canvas.width = 0;
    this.canvas.height = 0;
    window.removeEventListener("touchmove", this);
    window.removeEventListener("mousemove", this);
    window.removeEventListener("touchend", this);
    window.removeEventListener("mouseup", this);
    this.viewer.removeEventListener(events.KeypressEvent.type, this);
    this.gallery?.removeEventListener("show-gallery", this);
    this.gallery?.removeEventListener("hide-gallery", this);
    cancelAnimationFrame(this.state.renderLoop);
    super.destroy();
  }
  handleEvent(e) {
    if (utils2.getMatchingTarget(e, `.${CONSTANTS2.CAPTURE_EVENTS_CLASS}:not(.psv-map)`)) {
      return;
    }
    switch (e.type) {
      case events.KeypressEvent.type:
        if (this.state.maximized) {
          this.__onKeyPress(e);
          e.preventDefault();
        }
        break;
      case events.ConfigChangedEvent.type:
        if (e.containsOptions("lang")) {
          this.resetButton?.update();
          this.closeButton?.update();
          this.compassButton?.update();
          this.maximizeButton?.update();
        }
        break;
      case "mousedown": {
        const event = e;
        this.state.mouseX = event.clientX;
        this.state.mouseY = event.clientY;
        this.state.mousedown = true;
        e.stopPropagation();
        break;
      }
      case "touchstart": {
        const event = e;
        if (event.touches.length === 1) {
          this.state.mouseX = event.touches[0].clientX;
          this.state.mouseY = event.touches[0].clientY;
          this.state.mousedown = true;
        } else if (event.touches.length === 2) {
          ({
            distance: this.state.pinchDist,
            angle: this.state.pinchAngle,
            center: { x: this.state.mouseX, y: this.state.mouseY }
          } = utils2.getTouchData(event));
        }
        e.stopPropagation();
        e.preventDefault();
        break;
      }
      case "mousemove": {
        const event = e;
        if (this.state.mousedown) {
          this.__move(event.clientX, event.clientY);
          e.stopPropagation();
        } else if (e.composedPath().includes(this.canvas)) {
          this.__handleHotspots(event.clientX, event.clientY);
        }
        break;
      }
      case "touchmove": {
        const event = e;
        if (this.state.mousedown && event.touches.length === 1) {
          this.__move(event.touches[0].clientX, event.touches[0].clientY);
          e.stopPropagation();
        } else if (this.state.mousedown && event.touches.length === 2) {
          const touchData = utils2.getTouchData(event);
          const delta = (touchData.distance - this.state.pinchDist) / SYSTEM2.pixelRatio;
          this.zoom(delta / 100);
          this.__move(touchData.center.x, touchData.center.y);
          if (this.state.maximized && !this.config.static) {
            this.viewer.dynamics.position.step({ yaw: this.state.pinchAngle - touchData.angle }, 0);
          }
          ({ distance: this.state.pinchDist, angle: this.state.pinchAngle } = touchData);
          e.stopPropagation();
        }
        break;
      }
      case "mouseup":
      case "touchend": {
        const mouse = e.changedTouches?.[0] || e;
        if (this.state.mousedown) {
          this.state.mousedown = false;
          e.stopPropagation();
        }
        if (e.composedPath().includes(this.canvas)) {
          this.__clickHotspot(mouse.clientX, mouse.clientY);
        }
        break;
      }
      case "wheel": {
        const event = e;
        const delta = event.deltaY / Math.abs(event.deltaY);
        if (event.ctrlKey) {
          this.viewer.dynamics.position.step({ yaw: delta / 10 });
        } else {
          this.zoom(-delta / 10);
        }
        e.stopPropagation();
        e.preventDefault();
        break;
      }
      case "transitionstart":
        this.state.forceRender = true;
        break;
      case "transitionend":
        if (!this.state.maximized) {
          this.overlay.style.display = "";
          this.recenter();
        }
        this.state.forceRender = false;
        this.update();
        break;
      case "hide-gallery":
        this.__onToggleGallery(false);
        break;
      case "show-gallery":
        if (!e.fullscreen) {
          this.__onToggleGallery(true);
        }
        break;
    }
  }
  applyConfig() {
    this.container.classList.remove(
      "psv-map--top-right",
      "psv-map--top-left",
      "psv-map--bottom-right",
      "psv-map--bottom-left",
      "psv-map--round",
      "psv-map--square"
    );
    this.container.classList.add(`psv-map--${this.config.position.join("-")}`);
    this.container.classList.add(`psv-map--${this.config.shape}`);
    this.container.style.width = this.config.size;
    this.container.style.height = this.config.size;
    this.overlay.innerHTML = this.config.overlayImage === null ? "" : getImageHtml(this.config.overlayImage ?? (this.config.shape === "square" ? overlay_square_default : overlay_round_default));
    this.resetButton?.applyConfig();
    this.closeButton?.applyConfig();
    this.compassButton?.applyConfig();
    this.maximizeButton?.applyConfig();
    if (this.config.static) {
      this.compassButton?.rotate(0);
      this.overlay.style.transform = "";
    }
    if (this.config.shape === "square") {
      this.overlay.style.transform = "";
    }
    this.update();
  }
  isVisible() {
    return this.state.visible && !this.state.collapsed;
  }
  show() {
    super.show();
    this.update();
    if (!this.state.maximized) {
      this.overlay.style.display = "";
    }
  }
  hide() {
    super.hide();
    this.state.forceRender = false;
  }
  /**
   * Flag for render
   */
  update(clear = true) {
    this.state.needsUpdate = true;
    if (clear) {
      this.state.hotspotPos = {};
      this.__resetHotspot();
    }
  }
  /**
   * Load a new map image
   */
  reload(url) {
    delete this.state.images[this.config.imageUrl];
    this.config.imageUrl = url;
    this.state.imgScale = 1;
    this.__loadImage(this.config.imageUrl, true);
    this.recenter();
  }
  /**
   * Clears the offset and zoom level
   */
  reset() {
    this.state.zoom = this.config.defaultZoom;
    this.recenter();
  }
  /**
   * Clears the offset
   */
  recenter() {
    this.state.offset.x = 0;
    this.state.offset.y = 0;
    this.update();
  }
  /**
   * Switch collapsed mode
   */
  toggleCollapse() {
    if (this.state.maximized) {
      this.toggleMaximized(false);
    }
    this.state.collapsed = !this.state.collapsed;
    utils2.toggleClass(this.container, "psv-map--collapsed", this.state.collapsed);
    if (!this.state.collapsed) {
      this.reset();
      this.plugin.dispatchEvent(new ViewChanged("normal"));
    } else {
      this.plugin.dispatchEvent(new ViewChanged("closed"));
    }
    this.closeButton?.update();
  }
  /**
   * Switch maximized mode
   */
  toggleMaximized(dispatchMinimizeEvent = true) {
    if (this.state.collapsed) {
      return;
    }
    this.state.maximized = !this.state.maximized;
    utils2.toggleClass(this.container, "psv-map--maximized", this.state.maximized);
    if (this.state.maximized) {
      this.state.galleryWasVisible = this.gallery?.isVisible();
      this.gallery?.hide();
      this.overlay.style.display = "none";
      this.plugin.dispatchEvent(new ViewChanged("maximized"));
    } else {
      if (this.state.galleryWasVisible) {
        this.gallery.show();
      }
      if (dispatchMinimizeEvent) {
        this.plugin.dispatchEvent(new ViewChanged("normal"));
      }
    }
    this.maximizeButton?.update();
  }
  /**
   * Changes the zoom level
   */
  zoom(d) {
    this.setZoom(this.state.zoom + d);
  }
  /**
   * Changes the zoom level
   */
  setZoom(value) {
    this.state.zoom = MathUtils.clamp(value, this.config.minZoom, this.config.maxZoom);
    this.update();
  }
  /**
   * Updates the markers
   */
  setMarkers(markers) {
    this.state.markers = markers;
    this.update();
  }
  /**
   * Changes the highlighted hotspot
   */
  setActiveHotspot(hotspotId) {
    this.state.hotspotId = hotspotId;
    this.update(false);
  }
  render() {
    if (!this.config.center) {
      return;
    }
    const mapImage = this.__loadImage(this.config.imageUrl);
    if (!mapImage) {
      return;
    }
    const yaw = this.viewer.getPosition().yaw;
    const zoom = Math.exp(this.state.zoom) / this.state.imgScale;
    const center = {
      x: this.config.center.x * this.state.imgScale,
      y: this.config.center.y * this.state.imgScale
    };
    const offset = {
      x: this.state.offset.x * this.state.imgScale,
      y: this.state.offset.y * this.state.imgScale
    };
    const rotation = this.config.rotation;
    const yawAndRotation = this.config.static ? 0 : yaw + rotation;
    if (!this.config.static) {
      if (this.config.shape === "round") {
        this.overlay.style.transform = `rotate(${-yawAndRotation}rad)`;
      }
      this.compassButton?.rotate(yawAndRotation);
    }
    this.zoomToolbar.setText(this.state.zoom);
    this.canvas.width = this.container.clientWidth * SYSTEM2.pixelRatio;
    this.canvas.height = this.container.clientHeight * SYSTEM2.pixelRatio;
    const canvasPos = utils2.getPosition(this.canvas);
    const canvasW = this.canvas.width;
    const canvasH = this.canvas.height;
    const canvasVirtualCenterX = canvasW / 2 / SYSTEM2.pixelRatio;
    const canvasVirtualCenterY = canvasH / 2 / SYSTEM2.pixelRatio;
    const context = this.canvas.getContext("2d");
    context.clearRect(0, 0, canvasW, canvasH);
    const mapW = mapImage.width;
    const mapH = mapImage.height;
    context.save();
    context.translate(canvasW / 2, canvasH / 2);
    context.rotate(-yawAndRotation);
    context.scale(zoom, zoom);
    canvasShadow(context, 0, 0, MAP_SHADOW_BLUR);
    drawImageHighDpi(
      context,
      mapImage,
      -center.x - offset.x,
      -center.y - offset.y,
      mapW,
      mapH
    );
    context.restore();
    [...this.config.hotspots, ...this.state.markers].sort((a, b) => {
      if (this.state.hotspotId === a.id) {
        return 1;
      }
      if (this.state.hotspotId === b.id) {
        return -1;
      }
      return (a.zIndex ?? 0) - (b.zIndex ?? 0);
    }).forEach((hotspot) => {
      const isHover = this.state.hotspotId === hotspot.id;
      const style = getStyle(this.config.spotStyle, hotspot, isHover);
      let image;
      if (style.image) {
        image = this.__loadImage(style.image);
        if (!image) {
          return;
        }
        if (!isHover && (hotspot.hoverImage || this.config.spotStyle.hoverImage)) {
          this.__loadImage(hotspot.hoverImage || this.config.spotStyle.hoverImage, false, false);
        }
      }
      const hotspotPos = { ...offset };
      if ("yaw" in hotspot && "distance" in hotspot) {
        const angle = utils2.parseAngle(hotspot.yaw) + rotation;
        hotspotPos.x += Math.sin(-angle) * hotspot.distance * this.state.imgScale;
        hotspotPos.y += Math.cos(-angle) * hotspot.distance * this.state.imgScale;
      } else if ("x" in hotspot && "y" in hotspot) {
        hotspotPos.x += center.x - hotspot.x * this.state.imgScale;
        hotspotPos.y += center.y - hotspot.y * this.state.imgScale;
      } else {
        utils2.logWarn(`Hotspot ${hotspot["id"]} is missing position (yaw+distance or x+y)`);
        return;
      }
      const spotPos = projectPoint(hotspotPos, yawAndRotation, zoom);
      const x = canvasVirtualCenterX - spotPos.x;
      const y = canvasVirtualCenterY - spotPos.y;
      this.state.hotspotPos[hotspot.id] = {
        x: x + canvasPos.x,
        y: y + canvasPos.y,
        s: style.size
      };
      context.save();
      context.translate(x * SYSTEM2.pixelRatio, y * SYSTEM2.pixelRatio);
      canvasShadow(context, PIN_SHADOW_OFFSET, PIN_SHADOW_OFFSET, PIN_SHADOW_BLUR);
      if (image) {
        drawImageCentered(context, image, style.size);
      } else {
        context.fillStyle = style.color;
        context.beginPath();
        context.arc(0, 0, style.size * SYSTEM2.pixelRatio / 2, 0, 2 * Math.PI);
        context.fill();
        if (style.borderColor && style.borderSize) {
          context.shadowColor = "transparent";
          context.strokeStyle = style.borderColor;
          context.lineWidth = style.borderSize;
          context.beginPath();
          context.arc(0, 0, (style.size + style.borderSize) * SYSTEM2.pixelRatio / 2, 0, 2 * Math.PI);
          context.stroke();
        }
      }
      context.restore();
    });
    const pinImage = this.__loadImage(this.config.pinImage);
    if (pinImage || this.config.coneColor && this.config.coneSize) {
      const pinPos = projectPoint(offset, yawAndRotation, zoom);
      const x = canvasVirtualCenterX - pinPos.x;
      const y = canvasVirtualCenterY - pinPos.y;
      const size = this.config.pinSize;
      const angle = this.config.static ? yaw + rotation : 0;
      context.save();
      context.translate(x * SYSTEM2.pixelRatio, y * SYSTEM2.pixelRatio);
      context.rotate(angle);
      if (this.config.coneColor && this.config.coneSize) {
        const fov = MathUtils.degToRad(this.viewer.state.hFov);
        const a1 = -Math.PI / 2 - fov / 2;
        const a2 = a1 + fov;
        const c = this.config.coneSize * SYSTEM2.pixelRatio;
        const grad = context.createRadialGradient(0, 0, c / 4, 0, 0, c);
        grad.addColorStop(0, this.config.coneColor);
        grad.addColorStop(1, rgbToRgba(this.config.coneColor, 0));
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(Math.cos(a1) * c, Math.sin(a1) * c);
        context.arc(0, 0, c, a1, a2, false);
        context.lineTo(0, 0);
        context.fillStyle = grad;
        context.fill();
      }
      if (pinImage) {
        canvasShadow(context, PIN_SHADOW_OFFSET, PIN_SHADOW_OFFSET, PIN_SHADOW_BLUR);
        drawImageCentered(context, pinImage, size);
      }
      context.restore();
    }
  }
  /**
   * Applies mouse movement to the map
   */
  __move(clientX, clientY) {
    const yaw = this.viewer.getPosition().yaw;
    const zoom = Math.exp(this.state.zoom);
    const move = unprojectPoint(
      {
        x: this.state.mouseX - clientX,
        y: this.state.mouseY - clientY
      },
      this.config.static ? 0 : yaw + this.config.rotation,
      zoom
    );
    this.state.offset.x += move.x;
    this.state.offset.y += move.y;
    this.update();
    this.state.mouseX = clientX;
    this.state.mouseY = clientY;
  }
  /**
   * Finds the hotspot under the mouse
   */
  __findHotspot(clientX, clientY) {
    const k = this.config.spotStyle.size / 2;
    let hotspotId = null;
    for (const [id, { x, y }] of Object.entries(this.state.hotspotPos)) {
      if (clientX > x - k && clientX < x + k && clientY > y - k && clientY < y + k) {
        hotspotId = id;
        break;
      }
    }
    return hotspotId;
  }
  /**
   * Updates current hotspot on mouse move and displays tooltip
   */
  __handleHotspots(clientX, clientY) {
    const hotspotId = this.__findHotspot(clientX, clientY);
    if (this.state.hotspotId !== hotspotId) {
      this.__resetHotspot();
      if (hotspotId) {
        let tooltip;
        if (hotspotId.startsWith(HOTSPOT_MARKER_ID)) {
          tooltip = this.state.markers.find(({ id }) => id === hotspotId)?.tooltip;
        } else {
          tooltip = this.config.hotspots.find(({ id }) => id === hotspotId)?.tooltip;
        }
        if (tooltip) {
          if (typeof tooltip === "string") {
            tooltip = { content: tooltip };
          }
          const hotspotPos = this.state.hotspotPos[hotspotId];
          const viewerPos = utils2.getPosition(this.viewer.container);
          this.state.hotspotTooltip = this.viewer.createTooltip({
            content: tooltip.content,
            className: tooltip.className,
            left: hotspotPos.x - viewerPos.x,
            top: hotspotPos.y - viewerPos.y,
            box: {
              width: hotspotPos.s,
              height: hotspotPos.s
            }
          });
        }
      }
      this.setActiveHotspot(hotspotId);
      this.__setCursor(hotspotId ? "pointer" : "move");
    }
  }
  /**
   * Dispatch event when a hotspot is clicked
   */
  __clickHotspot(clientX, clientY) {
    const hotspotId = this.__findHotspot(clientX, clientY);
    if (hotspotId) {
      this.plugin.dispatchEvent(new SelectHotspot(hotspotId));
      if (hotspotId.startsWith(HOTSPOT_MARKER_ID)) {
        const markerId = hotspotId.substring(HOTSPOT_MARKER_ID.length);
        this.viewer.getPlugin("markers").gotoMarker(markerId);
      }
      if (this.maximized && this.config.minimizeOnHotspotClick) {
        this.toggleMaximized();
      }
    }
    this.__resetHotspot();
  }
  __resetHotspot() {
    this.state.hotspotTooltip?.hide();
    this.state.hotspotTooltip = null;
    this.state.hotspotId = null;
  }
  /**
   * Loads an image and returns the result **synchronously**.
   * If the image is not already loaded it returns `null` and schedules a new render when the image is ready.
   */
  __loadImage(url, isInit = false, autoRefresh = true) {
    if (!url) {
      return null;
    }
    if (!this.state.images[url]) {
      const image = loadImage(url);
      this.state.images[url] = {
        loading: true,
        value: image
      };
      image.onload = () => {
        if (isInit && Math.max(image.width, image.height) > SYSTEM2.maxCanvasWidth) {
          this.state.imgScale = SYSTEM2.maxCanvasWidth / Math.max(image.width, image.height);
          const buffer = document.createElement("canvas");
          buffer.width = image.width * this.state.imgScale;
          buffer.height = image.height * this.state.imgScale;
          const ctx = buffer.getContext("2d");
          ctx.drawImage(image, 0, 0, buffer.width, buffer.height);
          this.state.images[url].value = buffer;
        }
        this.state.images[url].loading = false;
        if (autoRefresh) {
          this.update(false);
        }
        if (isInit) {
          this.show();
        }
      };
      return null;
    }
    if (this.state.images[url].loading) {
      return null;
    }
    return this.state.images[url].value;
  }
  __onKeyPress(e) {
    if (e.matches(CONSTANTS2.KEY_CODES.Escape)) {
      this.toggleMaximized();
      return;
    }
    if (!this.viewer.state.keyboardEnabled) {
      return;
    }
    let x = 0;
    let y = 0;
    let z = 0;
    if (e.matches(CONSTANTS2.KEY_CODES.ArrowUp)) y = 1;
    if (e.matches(CONSTANTS2.KEY_CODES.ArrowDown)) y = -1;
    if (e.matches(CONSTANTS2.KEY_CODES.ArrowLeft)) x = 1;
    if (e.matches(CONSTANTS2.KEY_CODES.ArrowRight)) x = -1;
    if (e.matches(CONSTANTS2.KEY_CODES.Plus)) z = 1;
    if (e.matches(CONSTANTS2.KEY_CODES.Minus)) z = -1;
    if (e.matches(CONSTANTS2.KEY_CODES.PageUp)) z = 1;
    if (e.matches(CONSTANTS2.KEY_CODES.PageDown)) z = -1;
    if (x || y) {
      this.state.mouseX = 0;
      this.state.mouseY = 0;
      this.__move(x * 10, y * 10);
    }
    if (z) {
      this.zoom(z / 10);
    }
  }
  __setCursor(cursor) {
    this.canvas.style.cursor = cursor;
  }
  __onToggleGallery(visible) {
    if (!visible) {
      this.container.style.marginBottom = "";
    } else {
      this.container.style.marginBottom = this.viewer.container.querySelector(".psv-gallery").offsetHeight + 10 + "px";
    }
  }
};
// src/icons/pin.svg
var pin_default = '<svg viewBox="-20 -20 740 740" xmlns="http://www.w3.org/2000/svg">\n    <circle cx="350" cy="350" r="190" fill="white"/>\n    <circle cx="350" cy="350" r="150" fill="#1E78E6"/>\n</svg>\n';
// src/MapPlugin.ts
var getConfig = utils3.getConfigParser(
  {
    imageUrl: null,
    center: null,
    rotation: 0,
    shape: "round",
    size: "200px",
    position: ["bottom", "left"],
    visibleOnLoad: true,
    overlayImage: void 0,
    pinImage: pin_default,
    pinSize: 35,
    coneColor: "#1E78E6",
    coneSize: 40,
    spotStyle: {
      size: 15,
      image: null,
      color: "white",
      borderSize: 0,
      borderColor: null,
      hoverSize: null,
      hoverImage: null,
      hoverColor: null,
      hoverBorderSize: 4,
      hoverBorderColor: "rgba(255, 255, 255, 0.6)"
    },
    static: false,
    defaultZoom: 100,
    minZoom: 20,
    maxZoom: 200,
    hotspots: [],
    minimizeOnHotspotClick: true,
    buttons: {
      maximize: true,
      close: true,
      reset: true,
      north: true
    }
  },
  {
    spotStyle: (spotStyle, { defValue }) => ({ ...defValue, ...spotStyle }),
    position: (position, { defValue }) => {
      return utils3.cleanCssPosition(position, { allowCenter: false, cssOrder: true }) || defValue;
    },
    rotation: (rotation) => utils3.parseAngle(rotation),
    coneColor: (coneColor) => coneColor ? new Color(coneColor).getStyle() : null,
    // must be in rgb format
    defaultZoom: (defaultZoom) => Math.log(defaultZoom / 100),
    maxZoom: (maxZoom) => Math.log(maxZoom / 100),
    minZoom: (minZoom) => Math.log(minZoom / 100),
    buttons: (buttons, { defValue }) => ({ ...defValue, ...buttons })
  }
);
var _MapPlugin = class _MapPlugin extends AbstractConfigurablePlugin {
  static withConfig(config) {
    return [_MapPlugin, config];
  }
  constructor(viewer, config) {
    super(viewer, config);
    this.component = new MapComponent(this.viewer, this);
  }
  /**
   * @internal
   */
  init() {
    super.init();
    utils3.checkStylesheet(this.viewer.container, "map-plugin");
    this.component.init();
    this.markers = this.viewer.getPlugin("markers");
    this.viewer.addEventListener(events2.PositionUpdatedEvent.type, this);
    this.viewer.addEventListener(events2.ZoomUpdatedEvent.type, this);
    this.viewer.addEventListener(events2.SizeUpdatedEvent.type, this);
    this.viewer.addEventListener(events2.ReadyEvent.type, this, { once: true });
    this.markers?.addEventListener("set-markers", this);
    this.setHotspots(this.config.hotspots, false);
  }
  /**
   * @internal
   */
  destroy() {
    this.viewer.removeEventListener(events2.PositionUpdatedEvent.type, this);
    this.viewer.removeEventListener(events2.ZoomUpdatedEvent.type, this);
    this.viewer.removeEventListener(events2.SizeUpdatedEvent.type, this);
    this.viewer.removeEventListener(events2.ReadyEvent.type, this);
    this.markers?.removeEventListener("set-markers", this);
    this.component.destroy();
    delete this.markers;
    super.destroy();
  }
  /**
   * @internal
   */
  handleEvent(e) {
    switch (e.type) {
      case events2.ReadyEvent.type:
        this.component.reload(this.config.imageUrl);
        break;
      case events2.PositionUpdatedEvent.type:
      case events2.ZoomUpdatedEvent.type:
        this.component.update();
        break;
      case events2.SizeUpdatedEvent.type:
        if (this.component.maximized) {
          this.component.update();
        }
        break;
      case "set-markers":
        this.component.setMarkers(this.__markersToHotspots(e.markers));
        break;
      default:
        break;
    }
  }
  setOptions(options) {
    super.setOptions(options);
    if (options.center) {
      this.component.recenter();
    }
    if (options.hotspots !== void 0) {
      this.setHotspots(options.hotspots);
    }
    this.component.applyConfig();
  }
  /**
   * Hides the map
   */
  hide() {
    this.component.hide();
  }
  /**
   * Shows the map
   */
  show() {
    this.component.show();
  }
  /**
   * Changes the current zoom level
   */
  setZoom(level) {
    this.component.setZoom(Math.log(level / 100));
  }
  /**
   * Closes the map
   */
  close() {
    if (!this.component.collapsed) {
      this.component.toggleCollapse();
    }
  }
  /**
   * Open the map
   */
  open() {
    if (this.component.collapsed) {
      this.component.toggleCollapse();
    }
  }
  /**
   * Minimizes the map
   */
  minimize() {
    if (this.component.maximized) {
      this.component.toggleMaximized();
    }
  }
  /**
   * Maximizes the map
   */
  maximize() {
    if (!this.component.maximized) {
      this.component.toggleMaximized();
    }
  }
  /**
   * Changes the image of the map
   * @param rotation Also change the image rotation
   * @param center Also change the position on the map
   */
  setImage(url, center, rotation) {
    if (!utils3.isNil(rotation)) {
      this.config.rotation = utils3.parseAngle(rotation);
    }
    if (!utils3.isNil(center)) {
      this.config.center = center;
    }
    this.component.reload(url);
  }
  /**
   * Changes the position on the map
   */
  setCenter(center) {
    this.config.center = center;
    this.component.recenter();
  }
  /**
   * Changes the hotspots on the map
   */
  setHotspots(hotspots, render = true) {
    const ids = [];
    let i = 1;
    hotspots?.forEach((hotspot) => {
      if (!hotspot.id) {
        hotspot.id = HOTSPOT_GENERATED_ID + i++;
      } else if (ids.includes(hotspot.id)) {
        utils3.logWarn(`Duplicated hotspot id "${hotspot.id}`);
      } else {
        ids.push(hotspot.id);
      }
    });
    this.config.hotspots = hotspots || [];
    if (render) {
      this.component.update();
    }
  }
  /**
   * Removes all hotspots
   */
  clearHotspots() {
    this.setHotspots(null);
  }
  /**
   * Changes the highlighted hotspot
   */
  setActiveHotspot(hotspotId) {
    this.component.setActiveHotspot(hotspotId);
  }
  __markersToHotspots(markers) {
    return markers.filter((marker) => marker.data?.[MARKER_DATA_KEY]).map((marker) => {
      const hotspot = {
        ...marker.data[MARKER_DATA_KEY],
        id: HOTSPOT_MARKER_ID + marker.id,
        tooltip: marker.config.tooltip
      };
      if ("distance" in hotspot) {
        hotspot.yaw = marker.state.position.yaw;
      } else if (!("x" in hotspot) || !("y" in hotspot)) {
        utils3.logWarn(`Marker #${marker.id} "map" data is missing position (distance or x+y)`);
        return null;
      }
      return hotspot;
    }).filter((h) => h);
  }
};
_MapPlugin.id = "map";
_MapPlugin.VERSION = "5.14.0";
_MapPlugin.configParser = getConfig;
_MapPlugin.readonlyOptions = [
  "imageUrl",
  "visibleOnLoad",
  "defaultZoom",
  "buttons"
];
var MapPlugin = _MapPlugin;
// src/index.ts
DEFAULTS.lang["map"] = "Map";
DEFAULTS.lang["mapMaximize"] = "Maximize";
DEFAULTS.lang["mapMinimize"] = "Minimize";
DEFAULTS.lang["mapNorth"] = "Go to north";
DEFAULTS.lang["mapReset"] = "Reset";
export {
  MapPlugin,
  events_exports as events
};
//# sourceMappingURL=index.module.js.map