UNPKG

maplair

Version:

JavaScript library for mobile-friendly interactive maps

844 lines (843 loc) • 32.3 kB
import { setOptions, stamp } from "../core/Util.js"; import { Point } from "../geometry/Point.js"; import Browser_default from "../core/Browser.js"; import { disablePointerDetection, enablePointerDetection } from "../dom/DomEvent.PointerEvents.js"; import { getPointerPosition, isExternalTarget, off, on } from "../dom/DomEvent.js"; import { create, get, getPosition, preventOutline, setPosition, setTransform } from "../dom/DomUtil.js"; import { Control } from "../control/Control.js"; import { AttributionControl } from "../control/AttributionControl.js"; import { ZoomControl } from "../control/ZoomControl.js"; import { Evented } from "../core/Events.js"; import { PosAnimation } from "../dom/PosAnimation.js"; import { Bounds } from "../geometry/Bounds.js"; import { LatLngBounds } from "../geo/LatLngBounds.js"; import { LatLng } from "../geo/LatLng.js"; import { EPSG3857 } from "../geo/crs/EPSG3857.js"; import { Layer } from "../layer/Layer.js"; import { DivOverlay } from "../layer/DivOverlay.js"; import { Popup } from "../layer/Popup.js"; import { Tooltip } from "../layer/Tooltip.js"; import { Renderer_getRenderer_exports } from "../layer/vector/Renderer.getRenderer.js"; import { BoxZoomHandler } from "./handler/BoxZoomHandler.js"; import { DoubleClickZoomHandler } from "./handler/DoubleClickZoomHandler.js"; import { DragHandler } from "./handler/DragHandler.js"; import { KeyboardHandler } from "./handler/KeyboardHandler.js"; import { PinchZoomHandler } from "./handler/PinchZoomHandler.js"; import { ScrollWheelZoomHandler } from "./handler/ScrollWheelZoomHandler.js"; import { TapHoldHandler } from "./handler/TapHoldHandler.js"; //#region src/map/Map.js var MaplairMap = class MaplairMap extends Evented { static { this.setDefaultOptions({ crs: EPSG3857, center: void 0, zoom: void 0, minZoom: void 0, maxZoom: void 0, layers: [], maxBounds: void 0, renderer: void 0, zoomAnimation: !Browser_default.reducedMotion, zoomAnimationThreshold: 4, fadeAnimation: !Browser_default.reducedMotion, markerZoomAnimation: true, markerCulling: false, transform3DLimit: 8388608, zoomSnap: 1, zoomDelta: 1, trackResize: true }); } constructor(id, options) { super(); options = setOptions(this, options); this._handlers = []; this._layers = {}; this._zoomBoundLayers = {}; this._sizeChanged = true; this._initContainer(id); this._initLayout(); this._initEvents(); if (options.maxBounds) this.setMaxBounds(options.maxBounds); if (options.zoom !== void 0) this._zoom = this._limitZoom(options.zoom); if (options.center && options.zoom !== void 0) this.setView(new LatLng(options.center), options.zoom, { reset: true }); this.callInitHooks(MaplairMap); this._zoomAnimated = this.options.zoomAnimation; if (this._zoomAnimated) this._createAnimProxy(); this._addLayers(this.options.layers); } setView(center, zoom, options) { zoom = zoom === void 0 ? this._zoom : this._limitZoom(zoom); center = this._limitCenter(new LatLng(center), zoom, this.options.maxBounds); options ??= {}; this._stop(); if (this._loaded && !options.reset && options !== true) { if (options.animate !== void 0) { options.zoom = { animate: options.animate, ...options.zoom }; options.pan = { animate: options.animate, duration: options.duration, ...options.pan }; } if (this._zoom !== zoom ? this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) : this._tryAnimatedPan(center, options.pan)) { clearTimeout(this._sizeTimer); return this; } } this._resetView(center, zoom, options.pan?.noMoveStart); return this; } setZoom(zoom, options) { if (!this._loaded) { this._zoom = zoom; return this; } return this.setView(this.getCenter(), zoom, { zoom: options }); } zoomIn(delta, options) { delta ??= this.options.zoomDelta; return this.setZoom(this._zoom + delta, options); } zoomOut(delta, options) { delta ??= this.options.zoomDelta; return this.setZoom(this._zoom - delta, options); } setZoomAround(latlng, zoom, options) { const scale = this.getZoomScale(zoom), viewHalf = this.getSize().divideBy(2), centerOffset = (latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng)).subtract(viewHalf).multiplyBy(1 - 1 / scale), newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); return this.setView(newCenter, zoom, { zoom: options }); } _getBoundsCenterZoom(bounds, options) { options ??= {}; bounds = bounds.getBounds ? bounds.getBounds() : new LatLngBounds(bounds); const paddingTL = new Point(options.paddingTopLeft || options.padding || [0, 0]), paddingBR = new Point(options.paddingBottomRight || options.padding || [0, 0]); let zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); zoom = typeof options.maxZoom === "number" ? Math.min(options.maxZoom, zoom) : zoom; if (zoom === Infinity) return { center: bounds.getCenter(), zoom }; const paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), swPoint = this.project(bounds.getSouthWest(), zoom), nePoint = this.project(bounds.getNorthEast(), zoom); return { center: this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom), zoom }; } fitBounds(bounds, options) { bounds = new LatLngBounds(bounds); if (!bounds.isValid()) throw new Error("Bounds are not valid."); const target = this._getBoundsCenterZoom(bounds, options); return this.setView(target.center, target.zoom, options); } fitWorld(options) { return this.fitBounds([[-90, -180], [90, 180]], options); } panTo(center, options) { return this.setView(center, this._zoom, { pan: options }); } panBy(offset, options) { offset = new Point(offset).round(); options ??= {}; if (!offset.x && !offset.y) return this.fire("moveend"); if (options.animate !== true && !this.getSize().contains(offset)) { this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom()); return this; } if (!this._panAnim) { this._panAnim = new PosAnimation(); this._panAnim.on({ "step": this._onPanTransitionStep, "end": this._onPanTransitionEnd }, this); } if (!options.noMoveStart) this.fire("movestart"); if (!Browser_default.reducedMotion && options.animate !== false) { this._mapPane.classList.add("maplair-pan-anim"); const newPos = this._getMapPanePos().subtract(offset).round(); this._panAnim.run(this._mapPane, newPos, options.duration || .25, options.easeLinearity); } else { this._rawPanBy(offset); this.fire("move").fire("moveend"); } return this; } flyTo(targetCenter, targetZoom, options) { options ??= {}; if (Browser_default.reducedMotion || options.animate === false) return this.setView(targetCenter, targetZoom, options); this._stop(); const from = this.project(this.getCenter()), to = this.project(targetCenter), size = this.getSize(), startZoom = this._zoom; targetCenter = new LatLng(targetCenter); targetZoom = targetZoom === void 0 ? startZoom : this._limitZoom(targetZoom); const w0 = Math.max(size.x, size.y), w1 = w0 * this.getZoomScale(startZoom, targetZoom), u1 = to.distanceTo(from) || 1, rho = 1.42, rho2 = rho * rho; function r(i) { const s1 = i ? -1 : 1, s2 = i ? w1 : w0, b = (w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1) / (2 * s2 * rho2 * u1), sq = Math.sqrt(b * b + 1) - b; return sq < 15e-9 ? -18 : Math.log(sq); } function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; } function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; } function tanh(n) { return sinh(n) / cosh(n); } const r0 = r(0); function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); } function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; } function easeOut(t) { return 1 - (1 - t) ** 1.5; } const start = Date.now(), S = (r(1) - r0) / rho, duration = options.duration ? 1e3 * options.duration : 1e3 * S * .8; const frame = () => { const t = (Date.now() - start) / duration, s = easeOut(t) * S; if (t <= 1) { this._flyToFrame = requestAnimationFrame(frame); this._move(this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom), this.getScaleZoom(w0 / w(s), startZoom), { flyTo: true }); } else this._move(targetCenter, targetZoom)._moveEnd(true); }; this._moveStart(true, options.noMoveStart); frame(this); return this; } flyToBounds(bounds, options) { const target = this._getBoundsCenterZoom(bounds, options); return this.flyTo(target.center, target.zoom, options); } setMaxBounds(bounds) { bounds = new LatLngBounds(bounds); if (this.listens("moveend", this._panInsideMaxBounds)) this.off("moveend", this._panInsideMaxBounds); if (!bounds.isValid()) { this.options.maxBounds = null; return this; } this.options.maxBounds = bounds; if (this._loaded) this._panInsideMaxBounds(); return this.on("moveend", this._panInsideMaxBounds); } setMinZoom(zoom) { const oldZoom = this.options.minZoom; this.options.minZoom = zoom; if (this._loaded && oldZoom !== zoom) { this.fire("zoomlevelschange"); if (this.getZoom() < this.options.minZoom) return this.setZoom(zoom); } return this; } setMaxZoom(zoom) { const oldZoom = this.options.maxZoom; this.options.maxZoom = zoom; if (this._loaded && oldZoom !== zoom) { this.fire("zoomlevelschange"); if (this.getZoom() > this.options.maxZoom) return this.setZoom(zoom); } return this; } panInsideBounds(bounds, options) { this._enforcingBounds = true; const center = this.getCenter(), newCenter = this._limitCenter(center, this._zoom, new LatLngBounds(bounds)); if (!center.equals(newCenter)) this.panTo(newCenter, options); this._enforcingBounds = false; return this; } panInside(latlng, options) { options ??= {}; const paddingTL = new Point(options.paddingTopLeft || options.padding || [0, 0]), paddingBR = new Point(options.paddingBottomRight || options.padding || [0, 0]), pixelCenter = this.project(this.getCenter()), pixelPoint = this.project(latlng), pixelBounds = this.getPixelBounds(), paddedBounds = new Bounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]), paddedSize = paddedBounds.getSize(); if (!paddedBounds.contains(pixelPoint)) { this._enforcingBounds = true; const centerOffset = pixelPoint.subtract(paddedBounds.getCenter()); const offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize); pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x; pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y; this.panTo(this.unproject(pixelCenter), options); this._enforcingBounds = false; } return this; } invalidateSize(options) { if (!this._loaded) return this; options = { animate: false, pan: true, ...options === true ? { animate: true } : options }; const oldSize = this.getSize(); this._sizeChanged = true; this._lastCenter = null; const newSize = this.getSize(), oldCenter = oldSize.divideBy(2).round(), newCenter = newSize.divideBy(2).round(), offset = oldCenter.subtract(newCenter); if (!offset.x && !offset.y) return this; if (options.animate && options.pan) this.panBy(offset); else { if (options.pan) this._rawPanBy(offset); this.fire("move"); if (options.debounceMoveend) { clearTimeout(this._sizeTimer); this._sizeTimer = setTimeout(this.fire.bind(this, "moveend"), 200); } else this.fire("moveend"); } return this.fire("resize", { oldSize, newSize }); } stop() { this.setZoom(this._limitZoom(this._zoom)); if (!this.options.zoomSnap) this.fire("viewreset"); return this._stop(); } locate(options) { options = this._locateOptions = { timeout: 1e4, watch: false, ...options }; if (!("geolocation" in navigator)) { this._handleGeolocationError({ code: 0, message: "Geolocation not supported." }); return this; } const onResponse = this._handleGeolocationResponse.bind(this), onError = this._handleGeolocationError.bind(this); if (options.watch) { if (this._locationWatchId !== void 0) navigator.geolocation.clearWatch(this._locationWatchId); this._locationWatchId = navigator.geolocation.watchPosition(onResponse, onError, options); } else navigator.geolocation.getCurrentPosition(onResponse, onError, options); return this; } stopLocate() { navigator.geolocation?.clearWatch?.(this._locationWatchId); if (this._locateOptions) this._locateOptions.setView = false; return this; } _handleGeolocationError(error) { if (!this._container._maplair_id) return; const c = error.code, message = error.message || (c === 1 ? "permission denied" : c === 2 ? "position unavailable" : "timeout"); if (this._locateOptions.setView && !this._loaded) this.fitWorld(); this.fire("locationerror", { code: c, message: `Geolocation error: ${message}.` }); } _handleGeolocationResponse(pos) { if (!this._container._maplair_id) return; const lat = pos.coords.latitude, lng = pos.coords.longitude, latlng = new LatLng(lat, lng), bounds = latlng.toBounds(pos.coords.accuracy * 2), options = this._locateOptions; if (options.setView) { const zoom = this.getBoundsZoom(bounds); this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom); } const data = { latlng, bounds, timestamp: pos.timestamp }; for (const i in pos.coords) if (typeof pos.coords[i] === "number") data[i] = pos.coords[i]; this.fire("locationfound", data); } addHandler(name, HandlerClass) { if (!HandlerClass) return this; const handler = this[name] = new HandlerClass(this); this._handlers.push(handler); if (this.options[name]) handler.enable(); return this; } remove() { this._initEvents(true); if (this.options.maxBounds) this.off("moveend", this._panInsideMaxBounds); if (this._containerId !== this._container._maplair_id) throw new Error("Map container is being reused by another instance"); delete this._container._maplair_id; delete this._containerId; if (this._locationWatchId !== void 0) this.stopLocate(); this._stop(); disablePointerDetection(this._container); this._mapPane.remove(); if (this._clearControlPos) this._clearControlPos(); if (this._resizeRequest) { cancelAnimationFrame(this._resizeRequest); this._resizeRequest = null; } this._clearHandlers(); clearTimeout(this._transitionEndTimer); clearTimeout(this._sizeTimer); if (this._loaded) this.fire("unload"); this._destroyAnimProxy(); for (const layer of Object.values(this._layers)) layer.remove(); for (const pane of Object.values(this._panes)) pane.remove(); this._layers = {}; this._panes = {}; delete this._mapPane; delete this._renderer; return this; } createPane(name, container) { const className = `maplair-pane${name ? ` maplair-${name.replace("Pane", "")}-pane` : ""}`, pane = create("div", className, container || this._mapPane); if (name) this._panes[name] = pane; return pane; } getCenter() { this._checkIfLoaded(); if (this._lastCenter && !this._moved()) return new LatLng(this._lastCenter); return this.layerPointToLatLng(this._getCenterLayerPoint()); } getZoom() { return this._zoom; } getBounds() { const bounds = this.getPixelBounds(), sw = this.unproject(bounds.getBottomLeft()), ne = this.unproject(bounds.getTopRight()); return new LatLngBounds(sw, ne); } getMinZoom() { return this.options.minZoom ?? this._layersMinZoom ?? 0; } getMaxZoom() { return this.options.maxZoom ?? this._layersMaxZoom ?? Infinity; } getBoundsZoom(bounds, inside, padding) { bounds = new LatLngBounds(bounds); padding = new Point(padding ?? [0, 0]); let zoom = this.getZoom() ?? 0; const min = this.getMinZoom(), max = this.getMaxZoom(), nw = bounds.getNorthWest(), se = bounds.getSouthEast(), size = this.getSize().subtract(padding), boundsSize = new Bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(), snap = this.options.zoomSnap, scalex = size.x / boundsSize.x, scaley = size.y / boundsSize.y, scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley); zoom = this.getScaleZoom(scale, zoom); if (snap) { zoom = Math.round(zoom / (snap / 100)) * (snap / 100); zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap; } return Math.max(min, Math.min(max, zoom)); } getSize() { if (!this._size || this._sizeChanged) { this._size = new Point(this._container.clientWidth || 0, this._container.clientHeight || 0); this._sizeChanged = false; } return new Point(this._size); } getPixelBounds(center, zoom) { const topLeftPoint = this._getTopLeftPoint(center, zoom); return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); } getPixelOrigin() { this._checkIfLoaded(); return this._pixelOrigin; } getPixelWorldBounds(zoom) { return this.options.crs.getProjectedBounds(zoom ?? this.getZoom()); } getPane(pane) { return typeof pane === "string" ? this._panes[pane] : pane; } getPanes() { return this._panes; } getContainer() { return this._container; } getZoomScale(toZoom, fromZoom) { const crs = this.options.crs; fromZoom ??= this._zoom; return crs.scale(toZoom) / crs.scale(fromZoom); } getScaleZoom(scale, fromZoom) { const crs = this.options.crs; fromZoom ??= this._zoom; const zoom = crs.zoom(scale * crs.scale(fromZoom)); return isNaN(zoom) ? Infinity : zoom; } project(latlng, zoom) { zoom ??= this._zoom; return this.options.crs.latLngToPoint(new LatLng(latlng), zoom); } unproject(point, zoom) { zoom ??= this._zoom; return this.options.crs.pointToLatLng(new Point(point), zoom); } layerPointToLatLng(point) { const projectedPoint = new Point(point).add(this.getPixelOrigin()); return this.unproject(projectedPoint); } latLngToLayerPoint(latlng) { return this.project(new LatLng(latlng))._round()._subtract(this.getPixelOrigin()); } wrapLatLng(latlng) { return this.options.crs.wrapLatLng(new LatLng(latlng)); } wrapLatLngBounds(bounds) { return this.options.crs.wrapLatLngBounds(new LatLngBounds(bounds)); } distance(latlng1, latlng2) { return this.options.crs.distance(new LatLng(latlng1), new LatLng(latlng2)); } containerPointToLayerPoint(point) { return new Point(point).subtract(this._getMapPanePos()); } layerPointToContainerPoint(point) { return new Point(point).add(this._getMapPanePos()); } containerPointToLatLng(point) { const layerPoint = this.containerPointToLayerPoint(new Point(point)); return this.layerPointToLatLng(layerPoint); } latLngToContainerPoint(latlng) { return this.layerPointToContainerPoint(this.latLngToLayerPoint(new LatLng(latlng))); } pointerEventToContainerPoint(e) { return getPointerPosition(e, this._container); } pointerEventToLayerPoint(e) { return this.containerPointToLayerPoint(this.pointerEventToContainerPoint(e)); } pointerEventToLatLng(e) { return this.layerPointToLatLng(this.pointerEventToLayerPoint(e)); } _initContainer(id) { const container = this._container = get(id); if (!container) throw new Error("Map container not found."); else if (container._maplair_id) throw new Error("Map container is already initialized."); enablePointerDetection(container); on(container, "scroll", this._onScroll, this); this._containerId = stamp(container); } _initLayout() { const container = this._container; this._fadeAnimated = this.options.fadeAnimation; const classes = ["maplair-container", "maplair-touch"]; if (Browser_default.retina) classes.push("maplair-retina"); if (Browser_default.safari) classes.push("maplair-safari"); if (this._fadeAnimated) classes.push("maplair-fade-anim"); container.classList.add(...classes); const { position } = getComputedStyle(container); if (position !== "absolute" && position !== "relative" && position !== "fixed" && position !== "sticky") container.style.position = "relative"; this._initPanes(); if (this._initControlPos) this._initControlPos(); } _initPanes() { const panes = this._panes = {}; this._paneRenderers = {}; this._mapPane = this.createPane("mapPane", this._container); setPosition(this._mapPane, new Point(0, 0)); this.createPane("tilePane"); this.createPane("overlayPane"); this.createPane("shadowPane"); this.createPane("markerPane"); this.createPane("tooltipPane"); this.createPane("popupPane"); if (!this.options.markerZoomAnimation) { panes.markerPane.classList.add("maplair-zoom-hide"); panes.shadowPane.classList.add("maplair-zoom-hide"); } } _resetView(center, zoom, noMoveStart) { setPosition(this._mapPane, new Point(0, 0)); const loading = !this._loaded; this._loaded = true; zoom = this._limitZoom(zoom); this.fire("viewprereset"); const zoomChanged = this._zoom !== zoom; this._moveStart(zoomChanged, noMoveStart)._move(center, zoom)._moveEnd(zoomChanged); this.fire("viewreset"); if (loading) this.fire("load"); } _moveStart(zoomChanged, noMoveStart) { if (zoomChanged) this.fire("zoomstart"); if (!noMoveStart) this.fire("movestart"); return this; } _move(center, zoom, data, suppressEvent) { if (zoom === void 0) zoom = this._zoom; const zoomChanged = this._zoom !== zoom; this._zoom = zoom; this._lastCenter = center; this._pixelOrigin = this._getNewPixelOrigin(center); if (!suppressEvent) { if (zoomChanged || data?.pinch) this.fire("zoom", data); this.fire("move", data); } else if (data?.pinch) this.fire("zoom", data); return this; } _moveEnd(zoomChanged) { if (zoomChanged) this.fire("zoomend"); return this.fire("moveend"); } _stop() { cancelAnimationFrame(this._flyToFrame); cancelAnimationFrame(this._zoomAnimFrame); this._panAnim?.stop(); return this; } _rawPanBy(offset) { setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); } _getZoomSpan() { return this.getMaxZoom() - this.getMinZoom(); } _panInsideMaxBounds() { if (!this._enforcingBounds) this.panInsideBounds(this.options.maxBounds); } _checkIfLoaded() { if (!this._loaded) throw new Error("Set map center and zoom first."); } _initEvents(remove) { this._targets = {}; this._targets[stamp(this._container)] = this; (remove ? off : on)(this._container, "click dblclick pointerdown pointerup pointerover pointerout pointermove contextmenu keypress keydown keyup", this._handleDOMEvent, this); if (this.options.trackResize) { if (!remove) { if (!this._resizeObserver) this._resizeObserver = new ResizeObserver(this._onResize.bind(this)); this._resizeObserver.observe(this._container); } else this._resizeObserver.disconnect(); } if (this.options.transform3DLimit) (remove ? this.off : this.on).call(this, "moveend", this._onMoveEnd); } _onResize() { cancelAnimationFrame(this._resizeRequest); this._resizeRequest = requestAnimationFrame(() => { this.invalidateSize({ debounceMoveend: true }); }); } _onScroll() { this._container.scrollTop = 0; this._container.scrollLeft = 0; } _onMoveEnd() { const pos = this._getMapPanePos(); if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) this._resetView(this.getCenter(), this.getZoom()); } _findEventTargets(e, type) { let targets = [], target, src = e.target || e.srcElement, dragging = false; const isHover = type === "pointerout" || type === "pointerover"; while (src) { target = this._targets[stamp(src)]; if (target && (type === "click" || type === "preclick") && this._draggableMoved(target)) { dragging = true; break; } if (target && target.listens(type, true)) { if (isHover && !isExternalTarget(src, e)) break; targets.push(target); if (isHover) break; } if (src === this._container) break; src = src.parentNode; } if (!targets.length && !dragging && !isHover && this.listens(type, true)) targets = [this]; return targets; } _isClickDisabled(el) { while (el && el !== this._container) { if (el["_maplair_disable_click"] || !el.parentNode) return true; el = el.parentNode; } } _handleDOMEvent(e) { const el = e.target ?? e.srcElement; if (!this._loaded || el["_maplair_disable_events"] || e.type === "click" && this._isClickDisabled(el)) return; const type = e.type; if (type === "pointerdown") preventOutline(el); this._fireDOMEvent(e, type); } static _pointerEvents = [ "click", "dblclick", "pointerover", "pointerout", "contextmenu" ]; _fireDOMEvent(e, type, canvasTargets) { if (type === "click") this._fireDOMEvent(e, "preclick", canvasTargets); let targets = this._findEventTargets(e, type); if (canvasTargets) targets = [...canvasTargets.filter((t) => t.listens(type, true)), ...targets]; if (!targets.length) return; if (type === "contextmenu") e.preventDefault(); const target = targets[0]; const data = { originalEvent: e }; if (e.type !== "keypress" && e.type !== "keydown" && e.type !== "keyup") { const isMarker = target.getLatLng && (!target._pxRadius || target._pxRadius <= 10); data.containerPoint = isMarker ? this.latLngToContainerPoint(target.getLatLng()) : this.pointerEventToContainerPoint(e); data.layerPoint = this.containerPointToLayerPoint(data.containerPoint); data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint); } for (const t of targets) { t.fire(type, data, true); if (data.originalEvent._stopped || t.options.bubblingPointerEvents === false && MaplairMap._pointerEvents.includes(type)) return; } } _draggableMoved(obj) { obj = obj.dragging?.enabled() ? obj : this; return obj.dragging?.moved() || this.boxZoom?.moved(); } _clearHandlers() { for (const handler of this._handlers) handler.disable(); } whenReady(callback, context) { if (this._loaded) callback.call(context || this, { target: this }); else this.on("load", callback, context); return this; } _getMapPanePos() { return getPosition(this._mapPane); } _moved() { const pos = this._getMapPanePos(); return pos && !pos.equals([0, 0]); } _getTopLeftPoint(center, zoom) { return (center && zoom !== void 0 ? this._getNewPixelOrigin(center, zoom) : this.getPixelOrigin()).subtract(this._getMapPanePos()); } _getNewPixelOrigin(center, zoom) { const viewHalf = this.getSize()._divideBy(2); return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round(); } _latLngToNewLayerPoint(latlng, zoom, center) { const topLeft = this._getNewPixelOrigin(center, zoom); return this.project(latlng, zoom)._subtract(topLeft); } _latLngBoundsToNewLayerBounds(latLngBounds, zoom, center) { const topLeft = this._getNewPixelOrigin(center, zoom); return new Bounds([ this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft), this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft), this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft), this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft) ]); } _getCenterLayerPoint() { return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); } _getCenterOffset(latlng) { return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); } _limitCenter(center, zoom, bounds) { if (!bounds) return center; const centerPoint = this.project(center, zoom), viewHalf = this.getSize().divideBy(2), viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), offset = this._getBoundsOffset(viewBounds, bounds, zoom); if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) return center; return this.unproject(centerPoint.add(offset), zoom); } _limitOffset(offset, bounds) { if (!bounds) return offset; const viewBounds = this.getPixelBounds(), newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); return offset.add(this._getBoundsOffset(newBounds, bounds)); } _getBoundsOffset(pxBounds, maxBounds, zoom) { const projectedMaxBounds = new Bounds(this.project(maxBounds.getNorthEast(), zoom), this.project(maxBounds.getSouthWest(), zoom)), minOffset = projectedMaxBounds.min.subtract(pxBounds.min), maxOffset = projectedMaxBounds.max.subtract(pxBounds.max), dx = this._rebound(minOffset.x, -maxOffset.x), dy = this._rebound(minOffset.y, -maxOffset.y); return new Point(dx, dy); } _rebound(left, right) { return left + right > 0 ? Math.round(left - right) / 2 : Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); } _limitZoom(zoom) { const min = this.getMinZoom(), max = this.getMaxZoom(), snap = this.options.zoomSnap; if (snap) zoom = Math.round(zoom / snap) * snap; return Math.max(min, Math.min(max, zoom)); } _onPanTransitionStep() { this.fire("move"); } _onPanTransitionEnd() { this._mapPane.classList.remove("maplair-pan-anim"); this.fire("moveend"); } _tryAnimatedPan(center, options) { const offset = this._getCenterOffset(center)._trunc(); if (options?.animate !== true && !this.getSize().contains(offset)) return false; this.panBy(offset, options); return true; } _createAnimProxy() { this._proxy = create("div", "maplair-proxy maplair-zoom-animated"); this._panes.mapPane.appendChild(this._proxy); this.on("zoomanim", this._animateProxyZoom, this); this.on("load moveend", this._animMoveEnd, this); on(this._proxy, "transitionend", this._catchTransitionEnd, this); } _animateProxyZoom(e) { const transform = this._proxy.style.transform; setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); if (transform === this._proxy.style.transform && this._animatingZoom) this._onZoomTransitionEnd(); } _animMoveEnd() { const c = this.getCenter(); const z = this.getZoom(); setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1)); } _destroyAnimProxy() { if (this._proxy) { off(this._proxy, "transitionend", this._catchTransitionEnd, this); this._proxy.remove(); this.off("zoomanim", this._animateProxyZoom, this); this.off("load moveend", this._animMoveEnd, this); delete this._proxy; } } _catchTransitionEnd(e) { if (this._animatingZoom && e.propertyName.includes("transform")) this._onZoomTransitionEnd(); } _nothingToAnimate() { return !this._container.getElementsByClassName("maplair-zoom-animated").length; } _tryAnimatedZoom(center, zoom, options) { if (this._animatingZoom) return true; options ??= {}; if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) return false; const scale = this.getZoomScale(zoom), offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); if (options.animate !== true && !this.getSize().contains(offset)) return false; this._zoomAnimFrame = requestAnimationFrame(() => { this._zoomAnimFrame = null; this._moveStart(true, options.noMoveStart ?? false)._animateZoom(center, zoom, true); }); return true; } _animateZoom(center, zoom, startAnim, noUpdate) { if (!this._mapPane) return; if (startAnim) { this._animatingZoom = true; this._animateToCenter = center; this._animateToZoom = zoom; this._mapPane.classList.add("maplair-zoom-anim"); } this.fire("zoomanim", { center, zoom, noUpdate }); if (!this._tempFireZoomEvent) this._tempFireZoomEvent = this._zoom !== this._animateToZoom; this._move(this._animateToCenter, this._animateToZoom, void 0, true); this._transitionEndTimer = setTimeout(this._onZoomTransitionEnd.bind(this), 250); } _onZoomTransitionEnd() { if (!this._animatingZoom) return; this._mapPane?.classList.remove("maplair-zoom-anim"); this._animatingZoom = false; this._move(this._animateToCenter, this._animateToZoom, void 0, true); if (this._tempFireZoomEvent) this.fire("zoom"); delete this._tempFireZoomEvent; this.fire("move"); this._moveEnd(true); } }; for (const feature of [ AttributionControl, Control, DivOverlay, Layer, BoxZoomHandler, DoubleClickZoomHandler, DragHandler, KeyboardHandler, PinchZoomHandler, ScrollWheelZoomHandler, TapHoldHandler, Popup, Tooltip, Renderer_getRenderer_exports, ZoomControl ]) feature.register(MaplairMap); const Map = MaplairMap; //#endregion export { Map, MaplairMap }; //# sourceMappingURL=Map.js.map