diff --git a/website/src/lib/components/map/gpx-layer/gpx-layer.ts b/website/src/lib/components/map/gpx-layer/gpx-layer.ts index cbc1c8655..0a85b8a98 100644 --- a/website/src/lib/components/map/gpx-layer/gpx-layer.ts +++ b/website/src/lib/components/map/gpx-layer/gpx-layer.ts @@ -174,8 +174,9 @@ export class GPXLayer { update() { const _map = get(map); + const layerEventManager = map.layerEventManager; let file = get(this.file)?.file; - if (!_map || !file) { + if (!_map || !layerEventManager || !file) { return; } @@ -220,11 +221,11 @@ export class GPXLayer { ANCHOR_LAYER_KEY.tracks ); - _map.on('click', this.fileId, this.layerOnClickBinded); - _map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded); - _map.on('mouseenter', this.fileId, this.layerOnMouseEnterBinded); - _map.on('mouseleave', this.fileId, this.layerOnMouseLeaveBinded); - _map.on('mousemove', this.fileId, this.layerOnMouseMoveBinded); + layerEventManager.on('click', this.fileId, this.layerOnClickBinded); + layerEventManager.on('contextmenu', this.fileId, this.layerOnContextMenuBinded); + layerEventManager.on('mouseenter', this.fileId, this.layerOnMouseEnterBinded); + layerEventManager.on('mouseleave', this.fileId, this.layerOnMouseLeaveBinded); + layerEventManager.on('mousemove', this.fileId, this.layerOnMouseMoveBinded); } let waypointSource = _map.getSource(this.fileId + '-waypoints') as | GeoJSONSource @@ -256,23 +257,27 @@ export class GPXLayer { ANCHOR_LAYER_KEY.waypoints ); - _map.on( + layerEventManager.on( 'mouseenter', this.fileId + '-waypoints', this.waypointLayerOnMouseEnterBinded ); - _map.on( + layerEventManager.on( 'mouseleave', this.fileId + '-waypoints', this.waypointLayerOnMouseLeaveBinded ); - _map.on('click', this.fileId + '-waypoints', this.waypointLayerOnClickBinded); - _map.on( + layerEventManager.on( + 'click', + this.fileId + '-waypoints', + this.waypointLayerOnClickBinded + ); + layerEventManager.on( 'mousedown', this.fileId + '-waypoints', this.waypointLayerOnMouseDownBinded ); - _map.on( + layerEventManager.on( 'touchstart', this.fileId + '-waypoints', this.waypointLayerOnTouchStartBinded @@ -350,32 +355,47 @@ export class GPXLayer { remove() { const _map = get(map); - if (_map) { - _map.off('click', this.fileId, this.layerOnClickBinded); - _map.off('contextmenu', this.fileId, this.layerOnContextMenuBinded); - _map.off('mouseenter', this.fileId, this.layerOnMouseEnterBinded); - _map.off('mouseleave', this.fileId, this.layerOnMouseLeaveBinded); - _map.off('mousemove', this.fileId, this.layerOnMouseMoveBinded); - _map.off('style.load', this.updateBinded); - _map.off( + if (_map) { + _map.off('style.load', this.updateBinded); + } + + const layerEventManager = map.layerEventManager; + if (layerEventManager) { + layerEventManager.off('click', this.fileId, this.layerOnClickBinded); + layerEventManager.off('contextmenu', this.fileId, this.layerOnContextMenuBinded); + layerEventManager.off('mouseenter', this.fileId, this.layerOnMouseEnterBinded); + layerEventManager.off('mouseleave', this.fileId, this.layerOnMouseLeaveBinded); + layerEventManager.off('mousemove', this.fileId, this.layerOnMouseMoveBinded); + + layerEventManager.off( 'mouseenter', this.fileId + '-waypoints', this.waypointLayerOnMouseEnterBinded ); - _map.off( + layerEventManager.off( 'mouseleave', this.fileId + '-waypoints', this.waypointLayerOnMouseLeaveBinded ); - _map.off('click', this.fileId + '-waypoints', this.waypointLayerOnClickBinded); - _map.off('mousedown', this.fileId + '-waypoints', this.waypointLayerOnMouseDownBinded); - _map.off( + layerEventManager.off( + 'click', + this.fileId + '-waypoints', + this.waypointLayerOnClickBinded + ); + layerEventManager.off( + 'mousedown', + this.fileId + '-waypoints', + this.waypointLayerOnMouseDownBinded + ); + layerEventManager.off( 'touchstart', this.fileId + '-waypoints', this.waypointLayerOnTouchStartBinded ); + } + if (_map) { if (_map.getLayer(this.fileId + '-direction')) { _map.removeLayer(this.fileId + '-direction'); } @@ -581,6 +601,7 @@ export class GPXLayer { } e.preventDefault(); + _map.dragPan.disable(); this.draggedWaypointIndex = e.features![0].properties!.waypointIndex; this.draggingStartingPosition = e.point; @@ -604,6 +625,7 @@ export class GPXLayer { waypointPopup?.hide(); e.preventDefault(); + _map.dragPan.disable(); _map.on('touchmove', this.waypointLayerOnMouseMoveBinded); _map.once('touchend', this.waypointLayerOnMouseUpBinded); @@ -631,8 +653,15 @@ export class GPXLayer { waypointLayerOnMouseUp(e: MapLayerMouseEvent | MapLayerTouchEvent) { mapCursor.notify(MapCursorState.WAYPOINT_DRAGGING, false); - get(map)?.off('mousemove', this.waypointLayerOnMouseMoveBinded); - get(map)?.off('touchmove', this.waypointLayerOnMouseMoveBinded); + const _map = get(map); + if (!_map) { + return; + } + + _map.dragPan.enable(); + + _map.off('mousemove', this.waypointLayerOnMouseMoveBinded); + _map.off('touchmove', this.waypointLayerOnMouseMoveBinded); if (this.draggedWaypointIndex === null) { return; diff --git a/website/src/lib/components/map/layer-control/LayerControl.svelte b/website/src/lib/components/map/layer-control/LayerControl.svelte index ee0ebe688..e91dec96f 100644 --- a/website/src/lib/components/map/layer-control/LayerControl.svelte +++ b/website/src/lib/components/map/layer-control/LayerControl.svelte @@ -25,7 +25,7 @@ if (overpassLayer) { overpassLayer.remove(); } - overpassLayer = new OverpassLayer(_map); + overpassLayer = new OverpassLayer(_map, map.layerEventManager!); overpassLayer.add(); }); diff --git a/website/src/lib/components/map/layer-control/overpass-layer.ts b/website/src/lib/components/map/layer-control/overpass-layer.ts index b17f8a178..78d7739d2 100644 --- a/website/src/lib/components/map/layer-control/overpass-layer.ts +++ b/website/src/lib/components/map/layer-control/overpass-layer.ts @@ -8,6 +8,7 @@ import { settings } from '$lib/logic/settings'; import { db } from '$lib/db'; import type { GeoJSONSource } from 'maplibre-gl'; import { ANCHOR_LAYER_KEY } from '../style'; +import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager'; const { currentOverpassQueries } = settings; @@ -27,6 +28,7 @@ export class OverpassLayer { queryZoom = 12; expirationTime = 7 * 24 * 3600 * 1000; map: maplibregl.Map; + layerEventManager: MapLayerEventManager; popup: MapPopup; currentQueries: Set = new Set(); @@ -37,8 +39,9 @@ export class OverpassLayer { updateBinded = this.update.bind(this); onHoverBinded = this.onHover.bind(this); - constructor(map: maplibregl.Map) { + constructor(map: maplibregl.Map, layerEventManager: MapLayerEventManager) { this.map = map; + this.layerEventManager = layerEventManager; this.popup = new MapPopup(map, { closeButton: false, focusAfterOpen: false, @@ -102,8 +105,8 @@ export class OverpassLayer { ANCHOR_LAYER_KEY.overpass ); - this.map.on('mouseenter', 'overpass', this.onHoverBinded); - this.map.on('click', 'overpass', this.onHoverBinded); + this.layerEventManager.on('mouseenter', 'overpass', this.onHoverBinded); + this.layerEventManager.on('click', 'overpass', this.onHoverBinded); } this.map.setFilter('overpass', ['in', 'query', ...getCurrentQueries()], { @@ -117,6 +120,8 @@ export class OverpassLayer { remove() { this.map.off('moveend', this.queryIfNeededBinded); this.map.off('style.load', this.updateBinded); + this.layerEventManager.off('mouseenter', 'overpass', this.onHoverBinded); + this.layerEventManager.off('click', 'overpass', this.onHoverBinded); this.unsubscribes.forEach((unsubscribe) => unsubscribe()); try { diff --git a/website/src/lib/components/map/map-layer-event-manager.ts b/website/src/lib/components/map/map-layer-event-manager.ts new file mode 100644 index 000000000..e5f14f3e0 --- /dev/null +++ b/website/src/lib/components/map/map-layer-event-manager.ts @@ -0,0 +1,253 @@ +import maplibregl from 'maplibre-gl'; + +type MapLayerMouseEventListener = (e: maplibregl.MapLayerMouseEvent) => void; +type MapLayerTouchEventListener = (e: maplibregl.MapLayerTouchEvent) => void; +type MapLayerListener = { + features: maplibregl.MapGeoJSONFeature[]; + mousemoves: MapLayerMouseEventListener[]; + mouseenters: MapLayerMouseEventListener[]; + mouseleaves: MapLayerMouseEventListener[]; + mousedowns: MapLayerMouseEventListener[]; + clicks: MapLayerMouseEventListener[]; + contextmenus: MapLayerMouseEventListener[]; + touchstarts: MapLayerTouchEventListener[]; +}; + +export class MapLayerEventManager { + private _map: maplibregl.Map; + private _listeners: Record = {}; + + constructor(map: maplibregl.Map) { + this._map = map; + this._map.on('mousemove', this._handleMouseMove.bind(this)); + this._map.on('click', this._handleMouseClick.bind(this, 'click')); + this._map.on('contextmenu', this._handleMouseClick.bind(this, 'contextmenu')); + this._map.on('mousedown', this._handleMouseClick.bind(this, 'mousedown')); + this._map.on('touchstart', this._handleTouchStart.bind(this)); + } + + on( + eventType: + | 'mousemove' + | 'mouseenter' + | 'mouseleave' + | 'mousedown' + | 'click' + | 'contextmenu' + | 'touchstart', + + layerId: string, + listener: MapLayerMouseEventListener | MapLayerTouchEventListener + ) { + if (!this._listeners[layerId]) { + this._listeners[layerId] = { + features: [], + mousemoves: [], + mouseenters: [], + mouseleaves: [], + mousedowns: [], + clicks: [], + contextmenus: [], + touchstarts: [], + }; + } + switch (eventType) { + case 'mousemove': + this._listeners[layerId].mousemoves.push(listener as MapLayerMouseEventListener); + break; + case 'mouseenter': + this._listeners[layerId].mouseenters.push(listener as MapLayerMouseEventListener); + break; + case 'mouseleave': + this._listeners[layerId].mouseleaves.push(listener as MapLayerMouseEventListener); + break; + case 'mousedown': + this._listeners[layerId].mousedowns.push(listener as MapLayerMouseEventListener); + break; + case 'click': + this._listeners[layerId].clicks.push(listener as MapLayerMouseEventListener); + break; + case 'contextmenu': + this._listeners[layerId].contextmenus.push(listener as MapLayerMouseEventListener); + break; + case 'touchstart': + this._listeners[layerId].touchstarts.push(listener as MapLayerTouchEventListener); + break; + } + } + + off( + eventType: + | 'mousemove' + | 'mouseenter' + | 'mouseleave' + | 'mousedown' + | 'click' + | 'contextmenu' + | 'touchstart', + layerId: string, + listener: MapLayerMouseEventListener | MapLayerTouchEventListener + ) { + if (this._listeners[layerId]) { + switch (eventType) { + case 'mousemove': + this._listeners[layerId].mousemoves = this._listeners[ + layerId + ].mousemoves.filter((l) => l !== listener); + break; + case 'mouseenter': + this._listeners[layerId].mouseenters = this._listeners[ + layerId + ].mouseenters.filter((l) => l !== listener); + break; + case 'mouseleave': + this._listeners[layerId].mouseleaves = this._listeners[ + layerId + ].mouseleaves.filter((l) => l !== listener); + break; + case 'mousedown': + this._listeners[layerId].mousedowns = this._listeners[ + layerId + ].mousedowns.filter((l) => l !== listener); + break; + case 'click': + this._listeners[layerId].clicks = this._listeners[layerId].clicks.filter( + (l) => l !== listener + ); + break; + case 'contextmenu': + this._listeners[layerId].contextmenus = this._listeners[ + layerId + ].contextmenus.filter((l) => l !== listener); + break; + case 'touchstart': + this._listeners[layerId].touchstarts = this._listeners[ + layerId + ].touchstarts.filter((l) => l !== listener); + break; + } + if ( + this._listeners[layerId].mousemoves.length === 0 && + this._listeners[layerId].mouseenters.length === 0 && + this._listeners[layerId].mouseleaves.length === 0 && + this._listeners[layerId].mousedowns.length === 0 && + this._listeners[layerId].clicks.length === 0 && + this._listeners[layerId].contextmenus.length === 0 && + this._listeners[layerId].touchstarts.length === 0 + ) { + delete this._listeners[layerId]; + } + } + } + + private _handleMouseMove(e: maplibregl.MapMouseEvent) { + const layerIds = Object.keys(this._listeners); + const features = + layerIds.length > 0 + ? this._map.queryRenderedFeatures(e.point, { layers: layerIds }) + : []; + const featuresByLayer: Record = {}; + features.forEach((f) => { + if (!featuresByLayer[f.layer.id]) { + featuresByLayer[f.layer.id] = []; + } + featuresByLayer[f.layer.id].push(f); + }); + Object.keys(this._listeners).forEach((layerId) => { + const features = featuresByLayer[layerId] || []; + const listener = this._listeners[layerId]; + if ((features.length == 0) != (listener.features.length == 0)) { + if (features.length > 0) { + if (listener.mouseenters.length > 0) { + const event = new maplibregl.MapMouseEvent( + 'mouseenter', + e.target, + e.originalEvent, + { + features: featuresByLayer[layerId]!, + } + ); + listener.mouseenters.forEach((l) => l(event)); + } + } else { + if (listener.mouseleaves.length > 0) { + const event = new maplibregl.MapMouseEvent( + 'mouseleave', + e.target, + e.originalEvent + ); + listener.mouseleaves.forEach((l) => l(event)); + } + } + listener.features = features; + } + if (features.length > 0 && listener.mousemoves.length > 0) { + const event = new maplibregl.MapMouseEvent('mousemove', e.target, e.originalEvent, { + features: featuresByLayer[layerId]!, + }); + listener.mousemoves.forEach((l) => l(event)); + } + }); + } + + private _handleMouseClick(type: string, e: maplibregl.MapMouseEvent) { + Object.values(this._listeners).forEach((listener) => { + if (listener.features.length > 0) { + if (type === 'click' && listener.clicks.length > 0) { + const event = new maplibregl.MapMouseEvent('click', e.target, e.originalEvent, { + features: listener.features, + }); + listener.clicks.forEach((l) => l(event)); + } else if (type === 'contextmenu' && listener.contextmenus.length > 0) { + const event = new maplibregl.MapMouseEvent( + 'contextmenu', + e.target, + e.originalEvent, + { + features: listener.features, + } + ); + listener.contextmenus.forEach((l) => l(event)); + } else if (type === 'mousedown' && listener.mousedowns.length > 0) { + const event = new maplibregl.MapMouseEvent( + 'mousedown', + e.target, + e.originalEvent, + { + features: listener.features, + } + ); + listener.mousedowns.forEach((l) => l(event)); + } + } + }); + } + + private _handleTouchStart(e: maplibregl.MapTouchEvent) { + const layerIds = Object.keys(this._listeners).filter( + (layerId) => this._listeners[layerId].touchstarts.length > 0 + ); + if (layerIds.length === 0) return; + const features = this._map.queryRenderedFeatures(e.points[0], { layers: layerIds }); + const featuresByLayer: Record = {}; + features.forEach((f) => { + if (!featuresByLayer[f.layer.id]) { + featuresByLayer[f.layer.id] = []; + } + featuresByLayer[f.layer.id].push(f); + }); + Object.keys(this._listeners).forEach((layerId) => { + const features = featuresByLayer[layerId] || []; + const listener = this._listeners[layerId]; + if (features.length > 0) { + const event: maplibregl.MapLayerTouchEvent = new maplibregl.MapTouchEvent( + 'touchstart', + e.target, + e.originalEvent + ); + event.features = featuresByLayer[layerId]!; + listener.touchstarts.forEach((l) => l(event)); + } + }); + } +} diff --git a/website/src/lib/components/map/map.ts b/website/src/lib/components/map/map.ts index d05689ca3..b7b298087 100644 --- a/website/src/lib/components/map/map.ts +++ b/website/src/lib/components/map/map.ts @@ -8,6 +8,7 @@ import { get, writable, type Writable } from 'svelte/store'; import { settings } from '$lib/logic/settings'; import { tick } from 'svelte'; import { ANCHOR_LAYER_KEY, StyleManager } from '$lib/components/map/style'; +import { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager'; const { treeFileView, elevationProfile, bottomPanelSize, rightPanelSize, distanceUnits } = settings; @@ -25,6 +26,7 @@ export class MapLibreGLMap { private _onLoadCallbacks: ((map: maplibregl.Map) => void)[] = []; private _unsubscribes: (() => void)[] = []; private callOnLoadBinded: () => void = this.callOnLoad.bind(this); + public layerEventManager: MapLayerEventManager | null = null; subscribe(run: (value: maplibregl.Map | null) => void, invalidate?: () => void) { return this._mapStore.subscribe(run, invalidate); @@ -54,6 +56,7 @@ export class MapLibreGLMap { boxZoom: false, maxPitch: 85, }); + this.layerEventManager = new MapLayerEventManager(map); map.addControl( new maplibregl.NavigationControl({ visualizePitch: true, diff --git a/website/src/lib/components/map/street-view-control/StreetViewControl.svelte b/website/src/lib/components/map/street-view-control/StreetViewControl.svelte index f50ba355f..3e5e0f13f 100644 --- a/website/src/lib/components/map/street-view-control/StreetViewControl.svelte +++ b/website/src/lib/components/map/street-view-control/StreetViewControl.svelte @@ -20,9 +20,14 @@ let container: HTMLElement; onMount(() => { - map.onLoad((map: maplibregl.Map) => { - googleRedirect = new GoogleRedirect(map); - mapillaryLayer = new MapillaryLayer(map, container, mapillaryOpen); + map.onLoad((map_: maplibregl.Map) => { + googleRedirect = new GoogleRedirect(map_); + mapillaryLayer = new MapillaryLayer( + map_, + map.layerEventManager!, + container, + mapillaryOpen + ); }); }); diff --git a/website/src/lib/components/map/street-view-control/mapillary.ts b/website/src/lib/components/map/street-view-control/mapillary.ts index 8c309d73d..816852372 100644 --- a/website/src/lib/components/map/street-view-control/mapillary.ts +++ b/website/src/lib/components/map/street-view-control/mapillary.ts @@ -3,6 +3,7 @@ import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.mod import 'mapillary-js/dist/mapillary.css'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { ANCHOR_LAYER_KEY } from '../style'; +import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager'; const mapillarySource: VectorSourceSpecification = { type: 'vector', @@ -43,6 +44,7 @@ const mapillaryImageLayer: LayerSpecification = { export class MapillaryLayer { map: maplibregl.Map; + layerEventManager: MapLayerEventManager; marker: maplibregl.Marker; viewer: Viewer; @@ -53,8 +55,14 @@ export class MapillaryLayer { onMouseEnterBinded = this.onMouseEnter.bind(this); onMouseLeaveBinded = this.onMouseLeave.bind(this); - constructor(map: maplibregl.Map, container: HTMLElement, popupOpen: { value: boolean }) { + constructor( + map: maplibregl.Map, + layerEventManager: MapLayerEventManager, + container: HTMLElement, + popupOpen: { value: boolean } + ) { this.map = map; + this.layerEventManager = layerEventManager; this.viewer = new Viewer({ accessToken: 'MLY|4381405525255083|3204871ec181638c3c31320490f03011', @@ -103,14 +111,14 @@ export class MapillaryLayer { this.map.addLayer(mapillaryImageLayer, ANCHOR_LAYER_KEY.mapillary); } this.map.on('style.load', this.addBinded); - this.map.on('mouseenter', 'mapillary-image', this.onMouseEnterBinded); - this.map.on('mouseleave', 'mapillary-image', this.onMouseLeaveBinded); + this.layerEventManager.on('mouseenter', 'mapillary-image', this.onMouseEnterBinded); + this.layerEventManager.on('mouseleave', 'mapillary-image', this.onMouseLeaveBinded); } remove() { this.map.off('style.load', this.addBinded); - this.map.off('mouseenter', 'mapillary-image', this.onMouseEnterBinded); - this.map.off('mouseleave', 'mapillary-image', this.onMouseLeaveBinded); + this.layerEventManager.off('mouseenter', 'mapillary-image', this.onMouseEnterBinded); + this.layerEventManager.off('mouseleave', 'mapillary-image', this.onMouseLeaveBinded); if (this.map.getLayer('mapillary-image')) { this.map.removeLayer('mapillary-image'); diff --git a/website/src/lib/components/map/style.ts b/website/src/lib/components/map/style.ts index c4c9660cc..228943dc9 100644 --- a/website/src/lib/components/map/style.ts +++ b/website/src/lib/components/map/style.ts @@ -45,22 +45,51 @@ export class StyleManager { this._maptilerKey = maptilerKey; this._map.subscribe((map_) => { if (map_) { - this.update(); + this.updateBasemap(); map_.on('style.load', () => this.updateOverlays()); map_.on('pitch', () => this.updateTerrain()); } }); - currentBasemap.subscribe(() => this.update()); - customBasemapUpdate.subscribe(() => this.update()); + currentBasemap.subscribe(() => this.updateBasemap()); + customBasemapUpdate.subscribe(() => this.updateBasemap()); currentOverlays.subscribe(() => this.updateOverlays()); opacities.subscribe(() => this.updateOverlays()); terrainSource.subscribe(() => this.updateTerrain()); } - update() { + updateBasemap() { const map_ = get(this._map); if (!map_) return; - this.build().then((style) => map_.setStyle(style)); + this.buildStyle().then((style) => map_.setStyle(style)); + } + + async buildStyle(): Promise { + const custom = get(customLayers); + + const style: maplibregl.StyleSpecification = { + version: 8, + projection: { + type: 'globe', + }, + sources: { + 'empty-source': emptySource, + }, + layers: [], + }; + + let basemap = get(currentBasemap); + const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap]; + const basemapStyle = await this.get(basemapInfo); + + this.merge(style, basemapStyle); + + const terrain = this.getCurrentTerrain(); + style.sources[terrain.source] = terrainSources[terrain.source]; + style.terrain = terrain.exaggeration > 0 ? terrain : undefined; + + style.layers.push(...anchorLayers); + + return style; } async updateOverlays() { @@ -127,38 +156,14 @@ export class StyleManager { const mapTerrain = map_.getTerrain(); const terrain = this.getCurrentTerrain(); if (JSON.stringify(mapTerrain) !== JSON.stringify(terrain)) { - map_.setTerrain(terrain); + if (terrain.exaggeration > 0) { + map_.setTerrain(terrain); + } else { + map_.setTerrain(null); + } } } - async build(): Promise { - const custom = get(customLayers); - - const style: maplibregl.StyleSpecification = { - version: 8, - projection: { - type: 'globe', - }, - sources: { - 'empty-source': emptySource, - }, - layers: [], - }; - - let basemap = get(currentBasemap); - const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap]; - const basemapStyle = await this.get(basemapInfo); - - this.merge(style, basemapStyle); - - style.terrain = this.getCurrentTerrain(); - style.sources[style.terrain.source] = terrainSources[style.terrain.source]; - - style.layers.push(...anchorLayers); - - return style; - } - async get( styleInfo: maplibregl.StyleSpecification | string ): Promise { diff --git a/website/src/lib/components/toolbar/tools/routing/routing-controls.ts b/website/src/lib/components/toolbar/tools/routing/routing-controls.ts index ba7e891e1..76ac8ce47 100644 --- a/website/src/lib/components/toolbar/tools/routing/routing-controls.ts +++ b/website/src/lib/components/toolbar/tools/routing/routing-controls.ts @@ -94,7 +94,8 @@ export class RoutingControls { add() { const map_ = get(map); - if (!map_) { + const layerEventManager = map.layerEventManager; + if (!map_ || !layerEventManager) { return; } @@ -102,8 +103,8 @@ export class RoutingControls { map_.on('move', this.toggleAnchorsForZoomLevelAndBoundsBinded); map_.on('click', this.appendAnchorBinded); - map_.on('mousemove', this.fileId, this.showTemporaryAnchorBinded); - map_.on('click', this.fileId, stopPropagation); + layerEventManager.on('mousemove', this.fileId, this.showTemporaryAnchorBinded); + layerEventManager.on('click', this.fileId, stopPropagation); this.fileUnsubscribe = this.file.subscribe(this.updateControls.bind(this)); } @@ -152,20 +153,18 @@ export class RoutingControls { remove() { const map_ = get(map); - if (!map_) { - return; - } + const layerEventManager = map.layerEventManager; this.active = false; for (let anchor of this.anchors) { anchor.marker.remove(); } - map_.off('move', this.toggleAnchorsForZoomLevelAndBoundsBinded); - map_.off('click', this.appendAnchorBinded); - map_.off('mousemove', this.fileId, this.showTemporaryAnchorBinded); - map_.off('click', this.fileId, stopPropagation); - map_.off('mousemove', this.updateTemporaryAnchorBinded); + map_?.off('move', this.toggleAnchorsForZoomLevelAndBoundsBinded); + map_?.off('click', this.appendAnchorBinded); + layerEventManager?.off('mousemove', this.fileId, this.showTemporaryAnchorBinded); + layerEventManager?.off('click', this.fileId, stopPropagation); + map_?.off('mousemove', this.updateTemporaryAnchorBinded); this.temporaryAnchor.marker.remove(); this.fileUnsubscribe(); diff --git a/website/src/lib/components/toolbar/tools/scissors/Scissors.svelte b/website/src/lib/components/toolbar/tools/scissors/Scissors.svelte index 6b9aef8e6..be2548980 100644 --- a/website/src/lib/components/toolbar/tools/scissors/Scissors.svelte +++ b/website/src/lib/components/toolbar/tools/scissors/Scissors.svelte @@ -36,7 +36,7 @@ onMount(() => { if ($map) { - splitControls = new SplitControls($map); + splitControls = new SplitControls($map, map.layerEventManager!); } }); diff --git a/website/src/lib/components/toolbar/tools/scissors/split-controls.ts b/website/src/lib/components/toolbar/tools/scissors/split-controls.ts index 65583e10a..2d59a57ef 100644 --- a/website/src/lib/components/toolbar/tools/scissors/split-controls.ts +++ b/website/src/lib/components/toolbar/tools/scissors/split-controls.ts @@ -10,17 +10,20 @@ import { fileActions } from '$lib/logic/file-actions'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import type { GeoJSONSource } from 'maplibre-gl'; import { ANCHOR_LAYER_KEY } from '$lib/components/map/style'; +import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager'; export class SplitControls { map: maplibregl.Map; + layerEventManager: MapLayerEventManager; unsubscribes: Function[] = []; layerOnMouseEnterBinded: (e: any) => void = this.layerOnMouseEnter.bind(this); layerOnMouseLeaveBinded: () => void = this.layerOnMouseLeave.bind(this); layerOnClickBinded: (e: any) => void = this.layerOnClick.bind(this); - constructor(map: maplibregl.Map) { + constructor(map: maplibregl.Map, layerEventManager: MapLayerEventManager) { this.map = map; + this.layerEventManager = layerEventManager; if (!this.map.hasImage('split-control')) { let icon = new Image(100, 100); @@ -125,9 +128,17 @@ export class SplitControls { ANCHOR_LAYER_KEY.interactions ); - this.map.on('mouseenter', 'split-controls', this.layerOnMouseEnterBinded); - this.map.on('mouseleave', 'split-controls', this.layerOnMouseLeaveBinded); - this.map.on('click', 'split-controls', this.layerOnClickBinded); + this.layerEventManager.on( + 'mouseenter', + 'split-controls', + this.layerOnMouseEnterBinded + ); + this.layerEventManager.on( + 'mouseleave', + 'split-controls', + this.layerOnMouseLeaveBinded + ); + this.layerEventManager.on('click', 'split-controls', this.layerOnClickBinded); } } catch (e) { // No reliable way to check if the map is ready to add sources and layers @@ -135,9 +146,9 @@ export class SplitControls { } remove() { - this.map.off('mouseenter', 'split-controls', this.layerOnMouseEnterBinded); - this.map.off('mouseleave', 'split-controls', this.layerOnMouseLeaveBinded); - this.map.off('click', 'split-controls', this.layerOnClickBinded); + this.layerEventManager.off('mouseenter', 'split-controls', this.layerOnMouseEnterBinded); + this.layerEventManager.off('mouseleave', 'split-controls', this.layerOnMouseLeaveBinded); + this.layerEventManager.off('click', 'split-controls', this.layerOnClickBinded); try { if (this.map.getLayer('split-controls')) {