| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  | import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource" | 
					
						
							| 
									
										
										
										
											2024-06-20 03:30:14 +02:00
										 |  |  | import maplibregl, { Map as MLMap, Map as MlMap, SourceSpecification } from "maplibre-gl" | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  | import { RasterLayerPolygon } from "../../Models/RasterLayers" | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  | import { Utils } from "../../Utils" | 
					
						
							|  |  |  | import { BBox } from "../../Logic/BBox" | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  | import { ExportableMap, KeyNavigationEvent, MapProperties } from "../../Models/MapProperties" | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  | import SvelteUIElement from "../Base/SvelteUIElement" | 
					
						
							|  |  |  | import MaplibreMap from "./MaplibreMap.svelte" | 
					
						
							|  |  |  | import { RasterLayerProperties } from "../../Models/RasterLayerProperties" | 
					
						
							|  |  |  | import * as htmltoimage from "html-to-image" | 
					
						
							| 
									
										
										
										
											2024-01-28 00:04:24 +01:00
										 |  |  | import RasterLayerHandler from "./RasterLayerHandler" | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  | import Constants from "../../Models/Constants" | 
					
						
							| 
									
										
										
										
											2024-03-30 13:07:26 +01:00
										 |  |  | import { Protocol } from "pmtiles" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties` | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  | export class MapLibreAdaptor implements MapProperties, ExportableMap { | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |     private static maplibre_control_handlers = [ | 
					
						
							| 
									
										
										
										
											2023-03-25 02:48:24 +01:00
										 |  |  |         // "scrollZoom",
 | 
					
						
							|  |  |  |         // "boxZoom",
 | 
					
						
							|  |  |  |         // "doubleClickZoom",
 | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |         "dragRotate", | 
					
						
							|  |  |  |         "dragPan", | 
					
						
							|  |  |  |         "keyboard", | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |         "touchZoomRotate" | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |     ] | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |     private static maplibre_zoom_handlers = [ | 
					
						
							|  |  |  |         "scrollZoom", | 
					
						
							|  |  |  |         "boxZoom", | 
					
						
							|  |  |  |         "doubleClickZoom", | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |         "touchZoomRotate" | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |     ] | 
					
						
							| 
									
										
										
										
											2023-03-23 00:58:21 +01:00
										 |  |  |     readonly location: UIEventSource<{ lon: number; lat: number }> | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |     private readonly isFlying = new UIEventSource(false) | 
					
						
							| 
									
										
										
										
											2023-03-23 00:58:21 +01:00
										 |  |  |     readonly zoom: UIEventSource<number> | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |     readonly bounds: UIEventSource<BBox> | 
					
						
							| 
									
										
										
										
											2023-03-23 00:58:21 +01:00
										 |  |  |     readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined> | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |     readonly maxbounds: UIEventSource<BBox | undefined> | 
					
						
							|  |  |  |     readonly allowMoving: UIEventSource<true | boolean | undefined> | 
					
						
							| 
									
										
										
										
											2023-07-18 01:26:04 +02:00
										 |  |  |     readonly allowRotating: UIEventSource<true | boolean | undefined> | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |     readonly allowZooming: UIEventSource<true | boolean | undefined> | 
					
						
							| 
									
										
										
										
											2024-06-20 04:21:29 +02:00
										 |  |  |     readonly lastClickLocation: Store< | 
					
						
							|  |  |  |         undefined | { lon: number; lat: number; mode: "left" | "right" | "middle" } | 
					
						
							|  |  |  |     > | 
					
						
							| 
									
										
										
										
											2023-04-06 01:33:08 +02:00
										 |  |  |     readonly minzoom: UIEventSource<number> | 
					
						
							| 
									
										
										
										
											2023-04-21 01:53:24 +02:00
										 |  |  |     readonly maxzoom: UIEventSource<number> | 
					
						
							| 
									
										
										
										
											2023-12-19 22:21:34 +01:00
										 |  |  |     readonly rotation: UIEventSource<number> | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |     readonly pitch: UIEventSource<number> | 
					
						
							|  |  |  |     readonly useTerrain: Store<boolean> | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-30 13:07:26 +01:00
										 |  |  |     private static pmtilesInited = false | 
					
						
							| 
									
										
										
										
											2023-11-16 03:32:04 +01:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  |      * Functions that are called when one of those actions has happened | 
					
						
							|  |  |  |      * @private | 
					
						
							| 
									
										
										
										
											2023-11-16 03:32:04 +01:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  |     private _onKeyNavigation: ((event: KeyNavigationEvent) => void | boolean)[] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  |     private readonly _maplibreMap: Store<MLMap> | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-29 17:21:20 +02:00
										 |  |  |     constructor(maplibreMap: Store<MLMap>, state?: Partial<MapProperties>) { | 
					
						
							| 
									
										
										
										
											2024-03-30 13:07:26 +01:00
										 |  |  |         if (!MapLibreAdaptor.pmtilesInited) { | 
					
						
							|  |  |  |             maplibregl.addProtocol("pmtiles", new Protocol().tile) | 
					
						
							| 
									
										
										
										
											2024-03-21 12:16:00 +01:00
										 |  |  |             MapLibreAdaptor.pmtilesInited = true | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  |         this._maplibreMap = maplibreMap | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-07 22:46:41 +02:00
										 |  |  |         this.location = state?.location ?? new UIEventSource(undefined) | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |         if (this.location.data) { | 
					
						
							| 
									
										
										
										
											2023-05-19 10:56:30 +02:00
										 |  |  |             // The MapLibre adaptor updates the element in the location and then pings them
 | 
					
						
							|  |  |  |             // Often, code setting this up doesn't expect the object they pass in to be changed, so we create a copy
 | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |             this.location.setData({ ...this.location.data }) | 
					
						
							| 
									
										
										
										
											2023-05-19 10:56:30 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-23 00:58:21 +01:00
										 |  |  |         this.zoom = state?.zoom ?? new UIEventSource(1) | 
					
						
							| 
									
										
										
										
											2024-06-20 04:21:29 +02:00
										 |  |  |         this.minzoom = state?.minzoom ?? new UIEventSource(0.5) // 0.5 is the maplibre minzoom
 | 
					
						
							| 
									
										
										
										
											2023-04-21 01:53:24 +02:00
										 |  |  |         this.maxzoom = state?.maxzoom ?? new UIEventSource(24) | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |         this.zoom.addCallbackAndRunD((z) => { | 
					
						
							| 
									
										
										
										
											2023-04-06 01:33:08 +02:00
										 |  |  |             if (z < this.minzoom.data) { | 
					
						
							|  |  |  |                 this.zoom.setData(this.minzoom.data) | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-04-21 01:53:24 +02:00
										 |  |  |             const max = Math.min(24, this.maxzoom.data ?? 24) | 
					
						
							|  |  |  |             if (z > max) { | 
					
						
							|  |  |  |                 this.zoom.setData(max) | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         this.maxbounds = state?.maxbounds ?? new UIEventSource(undefined) | 
					
						
							|  |  |  |         this.allowMoving = state?.allowMoving ?? new UIEventSource(true) | 
					
						
							| 
									
										
										
										
											2023-07-18 01:26:04 +02:00
										 |  |  |         this.allowRotating = state?.allowRotating ?? new UIEventSource<boolean>(true) | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |         this.allowZooming = state?.allowZooming ?? new UIEventSource(true) | 
					
						
							| 
									
										
										
										
											2023-04-06 01:33:08 +02:00
										 |  |  |         this.bounds = state?.bounds ?? new UIEventSource(undefined) | 
					
						
							| 
									
										
										
										
											2023-12-19 22:21:34 +01:00
										 |  |  |         this.rotation = state?.rotation ?? new UIEventSource<number>(0) | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |         this.pitch = state?.pitch ?? new UIEventSource<number>(0) | 
					
						
							|  |  |  |         this.useTerrain = state?.useTerrain ?? new ImmutableStore<boolean>(false) | 
					
						
							| 
									
										
										
										
											2023-03-23 00:58:21 +01:00
										 |  |  |         this.rasterLayer = | 
					
						
							|  |  |  |             state?.rasterLayer ?? new UIEventSource<RasterLayerPolygon | undefined>(undefined) | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-20 04:21:29 +02:00
										 |  |  |         const lastClickLocation = new UIEventSource<{ | 
					
						
							|  |  |  |             lat: number | 
					
						
							|  |  |  |             lon: number | 
					
						
							|  |  |  |             mode: "left" | "right" | "middle" | 
					
						
							|  |  |  |         }>(undefined) | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |         this.lastClickLocation = lastClickLocation | 
					
						
							| 
									
										
										
										
											2023-03-23 00:58:21 +01:00
										 |  |  |         const self = this | 
					
						
							| 
									
										
										
										
											2023-04-16 03:42:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-21 17:18:09 +01:00
										 |  |  |         new RasterLayerHandler(this._maplibreMap, this.rasterLayer) | 
					
						
							| 
									
										
										
										
											2024-01-28 00:04:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-20 04:21:29 +02:00
										 |  |  |         const clickmodes = ["left", "middle", "right"] as const | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-20 03:30:14 +02:00
										 |  |  |         function handleClick(e: maplibregl.MapMouseEvent, mode?: "left" | "right" | "middle") { | 
					
						
							| 
									
										
										
										
											2023-04-16 03:42:26 +02:00
										 |  |  |             if (e.originalEvent["consumed"]) { | 
					
						
							|  |  |  |                 // Workaround, 'ShowPointLayer' sets this flag
 | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             const lon = e.lngLat.lng | 
					
						
							|  |  |  |             const lat = e.lngLat.lat | 
					
						
							| 
									
										
										
										
											2024-06-20 03:30:14 +02:00
										 |  |  |             const mouseEvent: MouseEvent = e.originalEvent | 
					
						
							|  |  |  |             mode = mode ?? clickmodes[mouseEvent.button] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             lastClickLocation.setData({ lon, lat, mode }) | 
					
						
							| 
									
										
										
										
											2023-04-16 03:42:26 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  |         maplibreMap.addCallbackAndRunD((map) => { | 
					
						
							|  |  |  |             map.on("load", () => { | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |                 self.MoveMapToCurrentLoc(self.location.data) | 
					
						
							|  |  |  |                 self.SetZoom(self.zoom.data) | 
					
						
							|  |  |  |                 self.setMaxBounds(self.maxbounds.data) | 
					
						
							|  |  |  |                 self.setAllowMoving(self.allowMoving.data) | 
					
						
							| 
									
										
										
										
											2023-07-18 01:26:04 +02:00
										 |  |  |                 self.setAllowRotating(self.allowRotating.data) | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |                 self.setAllowZooming(self.allowZooming.data) | 
					
						
							| 
									
										
										
										
											2023-04-06 01:33:08 +02:00
										 |  |  |                 self.setMinzoom(self.minzoom.data) | 
					
						
							| 
									
										
										
										
											2023-04-21 01:53:24 +02:00
										 |  |  |                 self.setMaxzoom(self.maxzoom.data) | 
					
						
							| 
									
										
										
										
											2023-04-20 01:52:23 +02:00
										 |  |  |                 self.setBounds(self.bounds.data) | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |                 self.setTerrain(self.useTerrain.data) | 
					
						
							| 
									
										
										
										
											2023-05-18 23:42:03 +02:00
										 |  |  |                 this.updateStores(true) | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |             self.MoveMapToCurrentLoc(self.location.data) | 
					
						
							|  |  |  |             self.SetZoom(self.zoom.data) | 
					
						
							|  |  |  |             self.setMaxBounds(self.maxbounds.data) | 
					
						
							|  |  |  |             self.setAllowMoving(self.allowMoving.data) | 
					
						
							| 
									
										
										
										
											2023-07-18 01:26:04 +02:00
										 |  |  |             self.setAllowRotating(self.allowRotating.data) | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |             self.setAllowZooming(self.allowZooming.data) | 
					
						
							| 
									
										
										
										
											2023-04-06 01:33:08 +02:00
										 |  |  |             self.setMinzoom(self.minzoom.data) | 
					
						
							| 
									
										
										
										
											2023-04-21 01:53:24 +02:00
										 |  |  |             self.setMaxzoom(self.maxzoom.data) | 
					
						
							| 
									
										
										
										
											2023-04-20 01:52:23 +02:00
										 |  |  |             self.setBounds(self.bounds.data) | 
					
						
							| 
									
										
										
										
											2023-12-19 22:21:34 +01:00
										 |  |  |             self.SetRotation(self.rotation.data) | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |             self.setTerrain(self.useTerrain.data) | 
					
						
							| 
									
										
										
										
											2023-05-18 23:42:03 +02:00
										 |  |  |             this.updateStores(true) | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |             map.on("movestart", () => { | 
					
						
							|  |  |  |                 this.isFlying.setData(true) | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |             map.on("moveend", () => { | 
					
						
							|  |  |  |                 this.isFlying.setData(false) | 
					
						
							|  |  |  |                 this.updateStores() | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |             map.on("click", (e) => { | 
					
						
							| 
									
										
										
										
											2023-04-16 03:42:26 +02:00
										 |  |  |                 handleClick(e) | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2024-06-21 02:36:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // map.on("contextmenu", ...) only works on desktop, hence we listen to the container:
 | 
					
						
							| 
									
										
										
										
											2024-06-20 03:30:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             map._container.addEventListener("contextmenu", (e) => { | 
					
						
							| 
									
										
										
										
											2024-06-20 04:21:29 +02:00
										 |  |  |                 const lngLat = map.unproject([e.x, e.y]) | 
					
						
							|  |  |  |                 lastClickLocation.setData({ lon: lngLat.lng, lat: lngLat.lat, mode: "right" }) | 
					
						
							| 
									
										
										
										
											2023-04-16 03:42:26 +02:00
										 |  |  |             }) | 
					
						
							|  |  |  |             map.on("dblclick", (e) => { | 
					
						
							| 
									
										
										
										
											2024-06-20 03:30:14 +02:00
										 |  |  |                 handleClick(e, "left") | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |             map.on("touchend", (e) => { | 
					
						
							|  |  |  |                 const touchEvent = e.originalEvent | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2024-01-24 23:45:20 +01:00
										 |  |  |             map.on("rotateend", (_) => { | 
					
						
							| 
									
										
										
										
											2023-12-19 22:21:34 +01:00
										 |  |  |                 this.updateStores() | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |             map.on("pitchend", () => { | 
					
						
							|  |  |  |                 this.updateStores() | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2023-11-16 03:32:04 +01:00
										 |  |  |             map.getContainer().addEventListener("keydown", (event) => { | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  |                 let locked: "islocked" = undefined | 
					
						
							|  |  |  |                 if (!this.allowMoving.data) { | 
					
						
							|  |  |  |                     locked = "islocked" | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 switch (event.key) { | 
					
						
							|  |  |  |                     case "ArrowUp": | 
					
						
							|  |  |  |                         this.pingKeycodeEvent(locked ?? "north") | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                     case "ArrowRight": | 
					
						
							|  |  |  |                         this.pingKeycodeEvent(locked ?? "east") | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                     case "ArrowDown": | 
					
						
							|  |  |  |                         this.pingKeycodeEvent(locked ?? "south") | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                     case "ArrowLeft": | 
					
						
							|  |  |  |                         this.pingKeycodeEvent(locked ?? "west") | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                     case "+": | 
					
						
							|  |  |  |                         this.pingKeycodeEvent("in") | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                     case "=": | 
					
						
							|  |  |  |                         this.pingKeycodeEvent("in") | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                     case "-": | 
					
						
							|  |  |  |                         this.pingKeycodeEvent("out") | 
					
						
							|  |  |  |                         break | 
					
						
							| 
									
										
										
										
											2023-11-16 03:32:04 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-23 00:58:21 +01:00
										 |  |  |         this.location.addCallbackAndRunD((loc) => { | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  |             self.MoveMapToCurrentLoc(loc) | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2023-03-23 00:58:21 +01:00
										 |  |  |         this.zoom.addCallbackAndRunD((z) => self.SetZoom(z)) | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |         this.maxbounds.addCallbackAndRun((bbox) => self.setMaxBounds(bbox)) | 
					
						
							| 
									
										
										
										
											2023-12-19 22:21:34 +01:00
										 |  |  |         this.rotation.addCallbackAndRunD((bearing) => self.SetRotation(bearing)) | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  |         this.allowMoving.addCallbackAndRun((allowMoving) => { | 
					
						
							|  |  |  |             self.setAllowMoving(allowMoving) | 
					
						
							|  |  |  |             self.pingKeycodeEvent(allowMoving ? "unlocked" : "locked") | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2023-07-18 01:26:04 +02:00
										 |  |  |         this.allowRotating.addCallbackAndRunD((allowRotating) => | 
					
						
							|  |  |  |             self.setAllowRotating(allowRotating) | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |         this.allowZooming.addCallbackAndRun((allowZooming) => self.setAllowZooming(allowZooming)) | 
					
						
							|  |  |  |         this.bounds.addCallbackAndRunD((bounds) => self.setBounds(bounds)) | 
					
						
							| 
									
										
										
										
											2024-02-20 13:33:38 +01:00
										 |  |  |         this.useTerrain?.addCallbackAndRun((useTerrain) => self.setTerrain(useTerrain)) | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-23 00:58:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Convenience constructor | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public static construct(): { | 
					
						
							|  |  |  |         map: Store<MLMap> | 
					
						
							|  |  |  |         ui: SvelteUIElement | 
					
						
							|  |  |  |         mapproperties: MapProperties | 
					
						
							|  |  |  |     } { | 
					
						
							|  |  |  |         const mlmap = new UIEventSource<MlMap>(undefined) | 
					
						
							|  |  |  |         return { | 
					
						
							|  |  |  |             map: mlmap, | 
					
						
							|  |  |  |             ui: new SvelteUIElement(MaplibreMap, { | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |                 map: mlmap | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |             }), | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |             mapproperties: new MapLibreAdaptor(mlmap) | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-21 01:53:24 +02:00
										 |  |  |     public static prepareWmsSource(layer: RasterLayerProperties): SourceSpecification { | 
					
						
							| 
									
										
										
										
											2024-03-21 12:16:00 +01:00
										 |  |  |         return RasterLayerHandler.prepareSource(layer) | 
					
						
							| 
									
										
										
										
											2023-04-21 01:53:24 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Prepares an ELI-URL to be compatible with mapbox | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |     private static async toBlob(canvas: HTMLCanvasElement): Promise<Blob> { | 
					
						
							|  |  |  |         return await new Promise<Blob>((resolve) => canvas.toBlob((blob) => resolve(blob))) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private static async createImage(url: string): Promise<HTMLImageElement> { | 
					
						
							|  |  |  |         return new Promise((resolve, reject) => { | 
					
						
							|  |  |  |             const img = new Image() | 
					
						
							|  |  |  |             img.decode = () => resolve(img) as any | 
					
						
							|  |  |  |             img.onload = () => resolve(img) | 
					
						
							|  |  |  |             img.onerror = reject | 
					
						
							|  |  |  |             img.crossOrigin = "anonymous" | 
					
						
							|  |  |  |             img.decoding = "async" | 
					
						
							|  |  |  |             img.src = url | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  |     public onKeyNavigationEvent(f: (event: KeyNavigationEvent) => void | boolean) { | 
					
						
							|  |  |  |         this._onKeyNavigation.push(f) | 
					
						
							|  |  |  |         return () => { | 
					
						
							|  |  |  |             this._onKeyNavigation.splice(this._onKeyNavigation.indexOf(f), 1) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |     public async exportAsPng( | 
					
						
							|  |  |  |         rescaleIcons: number = 1, | 
					
						
							|  |  |  |         progress: UIEventSource<{ current: number; total: number }> = undefined | 
					
						
							|  |  |  |     ): Promise<Blob> { | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |         const map = this._maplibreMap.data | 
					
						
							| 
									
										
										
										
											2023-05-05 02:03:41 +02:00
										 |  |  |         if (!map) { | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |             return undefined | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |         const drawOn = document.createElement("canvas", {}) | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |         const ctx = drawOn.getContext("2d") | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |         // The width/height has been set in 'mm' on the parent element and converted to pixels by the browser
 | 
					
						
							|  |  |  |         const w = map.getContainer().getBoundingClientRect().width | 
					
						
							|  |  |  |         const h = map.getContainer().getBoundingClientRect().height | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |         const dpi = map.getPixelRatio() | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |         // The 'css'-size stays constant...
 | 
					
						
							|  |  |  |         drawOn.style.width = w + "px" | 
					
						
							|  |  |  |         drawOn.style.height = h + "px" | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |         // ...but the number of pixels is increased
 | 
					
						
							|  |  |  |         drawOn.width = Math.ceil(w * dpi) | 
					
						
							|  |  |  |         drawOn.height = Math.ceil(h * dpi) | 
					
						
							| 
									
										
										
										
											2023-11-13 15:14:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |         await this.exportBackgroundOnCanvas(ctx) | 
					
						
							|  |  |  |         await this.drawMarkers(ctx, rescaleIcons, progress) | 
					
						
							|  |  |  |         return await MapLibreAdaptor.toBlob(drawOn) | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  |     private pingKeycodeEvent( | 
					
						
							|  |  |  |         key: "north" | "east" | "south" | "west" | "in" | "out" | "islocked" | "locked" | "unlocked" | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |         const event = { | 
					
						
							|  |  |  |             date: new Date(), | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |             key: key | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (let i = 0; i < this._onKeyNavigation.length; i++) { | 
					
						
							|  |  |  |             const f = this._onKeyNavigation[i] | 
					
						
							|  |  |  |             const unregister = f(event) | 
					
						
							|  |  |  |             if (unregister === true) { | 
					
						
							|  |  |  |                 this._onKeyNavigation.splice(i, 1) | 
					
						
							|  |  |  |                 i-- | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Exports the background map and lines to PNG. | 
					
						
							|  |  |  |      * Markers are _not_ rendered | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-06-04 22:52:13 +02:00
										 |  |  |     private async exportBackgroundOnCanvas(ctx: CanvasRenderingContext2D): Promise<void> { | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |         const map = this._maplibreMap.data | 
					
						
							|  |  |  |         // We draw the maplibre-map onto the canvas. This does not export markers
 | 
					
						
							|  |  |  |         // Inspiration by https://github.com/mapbox/mapbox-gl-js/issues/2766
 | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |         // Total hack - see https://stackoverflow.com/questions/42483449/mapbox-gl-js-export-map-to-png-or-pdf
 | 
					
						
							|  |  |  |         const promise = new Promise<void>((resolve) => { | 
					
						
							|  |  |  |             map.once("render", () => { | 
					
						
							|  |  |  |                 ctx.drawImage(map.getCanvas(), 0, 0) | 
					
						
							|  |  |  |                 resolve() | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |         }) | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |         while (!map.isStyleLoaded()) { | 
					
						
							|  |  |  |             console.log("Waiting to fully load the style...") | 
					
						
							|  |  |  |             await Utils.waitFor(100) | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |         map.triggerRepaint() | 
					
						
							|  |  |  |         await promise | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  |     private async drawElement( | 
					
						
							|  |  |  |         drawOn: CanvasRenderingContext2D, | 
					
						
							|  |  |  |         element: HTMLElement, | 
					
						
							|  |  |  |         rescaleIcons: number, | 
					
						
							|  |  |  |         pixelRatio: number | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2024-03-30 13:07:26 +01:00
										 |  |  |         { | 
					
						
							|  |  |  |             const allimages = element.getElementsByTagName("img") | 
					
						
							|  |  |  |             for (const img of Array.from(allimages)) { | 
					
						
							|  |  |  |                 let isLoaded: boolean = false | 
					
						
							|  |  |  |                 while (!isLoaded) { | 
					
						
							| 
									
										
										
										
											2024-04-13 02:40:21 +02:00
										 |  |  |                     console.log( | 
					
						
							|  |  |  |                         "Waiting for image", | 
					
						
							|  |  |  |                         img.src, | 
					
						
							|  |  |  |                         "to load", | 
					
						
							|  |  |  |                         img.complete, | 
					
						
							|  |  |  |                         img.naturalWidth, | 
					
						
							|  |  |  |                         img | 
					
						
							|  |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2024-03-30 13:07:26 +01:00
										 |  |  |                     await Utils.waitFor(250) | 
					
						
							| 
									
										
										
										
											2024-03-31 21:14:29 +02:00
										 |  |  |                     isLoaded = img.complete && img.width > 0 | 
					
						
							| 
									
										
										
										
											2024-03-30 13:07:26 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-06 17:27:30 +01:00
										 |  |  |         const style = element.style.transform | 
					
						
							|  |  |  |         let x = element.getBoundingClientRect().x | 
					
						
							|  |  |  |         let y = element.getBoundingClientRect().y | 
					
						
							|  |  |  |         element.style.transform = "" | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  |         const offset = style.match(/translate\(([-0-9]+)%, ?([-0-9]+)%\)/) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-13 02:40:21 +02:00
										 |  |  |         let labels = <HTMLElement[]>Array.from(element.getElementsByClassName("marker-label")) | 
					
						
							|  |  |  |         const origLabelTransforms = labels.map((l) => l.style.transform) | 
					
						
							| 
									
										
										
										
											2024-03-31 21:14:29 +02:00
										 |  |  |         // We save the original width (`w`) and height (`h`) in order to restore them later on
 | 
					
						
							| 
									
										
										
										
											2023-12-06 17:27:30 +01:00
										 |  |  |         const w = element.style.width | 
					
						
							| 
									
										
										
										
											2024-03-31 21:14:29 +02:00
										 |  |  |         const h = Number(element.style.height) | 
					
						
							| 
									
										
										
										
											2024-04-13 02:40:21 +02:00
										 |  |  |         const targetW = Math.max( | 
					
						
							|  |  |  |             element.getBoundingClientRect().width * 4, | 
					
						
							|  |  |  |             ...labels.map((l) => l.getBoundingClientRect().width) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         const targetH = | 
					
						
							|  |  |  |             element.getBoundingClientRect().height + | 
					
						
							|  |  |  |             Math.max( | 
					
						
							|  |  |  |                 ...labels.map( | 
					
						
							|  |  |  |                     (l) => | 
					
						
							|  |  |  |                         l.getBoundingClientRect().height * | 
					
						
							|  |  |  |                         2 /* A bit of buffer to catch eventual 'margin-top'*/ | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2024-04-01 01:39:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  |         // Force a wider view for icon badges
 | 
					
						
							| 
									
										
										
										
											2024-03-31 21:14:29 +02:00
										 |  |  |         element.style.width = targetW + "px" | 
					
						
							| 
									
										
										
										
											2024-03-30 13:07:26 +01:00
										 |  |  |         // Force more height to include labels
 | 
					
						
							| 
									
										
										
										
											2024-03-31 21:14:29 +02:00
										 |  |  |         element.style.height = targetH + "px" | 
					
						
							|  |  |  |         element.classList.add("w-full", "flex", "flex-col", "items-center") | 
					
						
							| 
									
										
										
										
											2024-04-13 02:40:21 +02:00
										 |  |  |         labels.forEach((l) => { | 
					
						
							| 
									
										
										
										
											2024-03-31 21:14:29 +02:00
										 |  |  |             l.style.transform = "" | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         await Utils.awaitAnimationFrame() | 
					
						
							| 
									
										
										
										
											2023-12-06 17:27:30 +01:00
										 |  |  |         const svgSource = await htmltoimage.toSvg(element) | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  |         const img = await MapLibreAdaptor.createImage(svgSource) | 
					
						
							| 
									
										
										
										
											2024-03-31 21:14:29 +02:00
										 |  |  |         for (let i = 0; i < labels.length; i++) { | 
					
						
							|  |  |  |             labels[i].style.transform = origLabelTransforms[i] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         element.style.width = "" + w | 
					
						
							|  |  |  |         element.style.height = "" + h | 
					
						
							| 
									
										
										
										
											2023-12-06 17:27:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  |         if (offset && rescaleIcons !== 1) { | 
					
						
							|  |  |  |             const [_, __, relYStr] = offset | 
					
						
							|  |  |  |             const relY = Number(relYStr) | 
					
						
							|  |  |  |             y += img.height * (relY / 100) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         x *= pixelRatio | 
					
						
							|  |  |  |         y *= pixelRatio | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							| 
									
										
										
										
											2024-04-13 02:40:21 +02:00
										 |  |  |             const xdiff = (img.width * rescaleIcons) / 2 | 
					
						
							| 
									
										
										
										
											2024-03-30 13:07:26 +01:00
										 |  |  |             drawOn.drawImage(img, x - xdiff, y, img.width * rescaleIcons, img.height * rescaleIcons) | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  |         } catch (e) { | 
					
						
							|  |  |  |             console.log("Could not draw image because of", e) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-03-31 21:14:29 +02:00
										 |  |  |         element.classList.remove("w-full", "flex", "flex-col", "items-center") | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Draws the markers of the current map on the specified canvas. | 
					
						
							|  |  |  |      * The DPIfactor is used to calculate the correct position, whereas 'rescaleIcons' can be used to make the icons smaller | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private async drawMarkers( | 
					
						
							|  |  |  |         drawOn: CanvasRenderingContext2D, | 
					
						
							|  |  |  |         rescaleIcons: number = 1, | 
					
						
							|  |  |  |         progress: UIEventSource<{ current: number; total: number }> | 
					
						
							|  |  |  |     ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |         const map = this._maplibreMap.data | 
					
						
							|  |  |  |         if (!map) { | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |             console.error("There is no map to export from") | 
					
						
							| 
									
										
										
										
											2023-06-04 00:43:32 +02:00
										 |  |  |             return undefined | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-13 15:14:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const container = map.getContainer() | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  |         const pixelRatio = map.getPixelRatio() | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-13 15:14:41 +01:00
										 |  |  |         function isDisplayed(el: Element) { | 
					
						
							|  |  |  |             const r1 = el.getBoundingClientRect() | 
					
						
							|  |  |  |             const r2 = container.getBoundingClientRect() | 
					
						
							|  |  |  |             return !( | 
					
						
							|  |  |  |                 r2.left > r1.right || | 
					
						
							|  |  |  |                 r2.right < r1.left || | 
					
						
							|  |  |  |                 r2.top > r1.bottom || | 
					
						
							|  |  |  |                 r2.bottom < r1.top | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-13 15:14:41 +01:00
										 |  |  |         const markers = Array.from(container.getElementsByClassName("marker")) | 
					
						
							|  |  |  |         for (let i = 0; i < markers.length; i++) { | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |             const marker = <HTMLElement>markers[i] | 
					
						
							|  |  |  |             const style = marker.style.transform | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (isDisplayed(marker)) { | 
					
						
							|  |  |  |                 await this.drawElement(drawOn, marker, rescaleIcons, pixelRatio) | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (progress) { | 
					
						
							|  |  |  |                 progress.setData({ current: i, total: markers.length }) | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-12-06 04:01:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 16:14:27 +01:00
										 |  |  |             marker.style.transform = style | 
					
						
							| 
									
										
										
										
											2023-11-13 15:14:41 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-18 23:42:03 +02:00
										 |  |  |     private updateStores(isSetup: boolean = false): void { | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |         const map = this._maplibreMap.data | 
					
						
							| 
									
										
										
										
											2023-04-20 17:42:07 +02:00
										 |  |  |         if (!map) { | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |         const { lng, lat } = map.getCenter() | 
					
						
							| 
									
										
										
										
											2023-06-07 22:46:41 +02:00
										 |  |  |         if (lng === 0 && lat === 0) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (this.location.data === undefined) { | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |             this.location.setData({ lon: lng, lat }) | 
					
						
							| 
									
										
										
										
											2023-06-07 22:46:41 +02:00
										 |  |  |         } else if (!isSetup) { | 
					
						
							| 
									
										
										
										
											2023-06-14 23:21:19 +02:00
										 |  |  |             const lon = map.getCenter().lng | 
					
						
							|  |  |  |             const lat = map.getCenter().lat | 
					
						
							|  |  |  |             this.location.setData({ lon, lat }) | 
					
						
							| 
									
										
										
										
											2023-05-18 23:42:03 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |         this.zoom.setData(Math.round(map.getZoom() * 10) / 10) | 
					
						
							|  |  |  |         const bounds = map.getBounds() | 
					
						
							|  |  |  |         const bbox = new BBox([ | 
					
						
							|  |  |  |             [bounds.getEast(), bounds.getNorth()], | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |             [bounds.getWest(), bounds.getSouth()] | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |         ]) | 
					
						
							| 
									
										
										
										
											2023-05-18 23:42:03 +02:00
										 |  |  |         if (this.bounds.data === undefined || !isSetup) { | 
					
						
							|  |  |  |             this.bounds.setData(bbox) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-12-19 22:21:34 +01:00
										 |  |  |         this.rotation.setData(map.getBearing()) | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |         this.pitch.setData(map.getPitch()) | 
					
						
							| 
									
										
										
										
											2023-04-19 03:20:49 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-18 15:44:54 +02:00
										 |  |  |     private SetZoom(z: number): void { | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |         const map = this._maplibreMap.data | 
					
						
							|  |  |  |         if (!map || z === undefined) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |         if (this.isFlying.data) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |         if (Math.abs(map.getZoom() - z) > 0.01) { | 
					
						
							|  |  |  |             map.setZoom(z) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-19 22:21:34 +01:00
										 |  |  |     private SetRotation(bearing: number): void { | 
					
						
							|  |  |  |         const map = this._maplibreMap.data | 
					
						
							|  |  |  |         if (!map || bearing === undefined) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         map.rotateTo(bearing, { duration: 0 }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-18 15:44:54 +02:00
										 |  |  |     private MoveMapToCurrentLoc(loc: { lat: number; lon: number }): void { | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |         const map = this._maplibreMap.data | 
					
						
							|  |  |  |         if (!map || loc === undefined) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const center = map.getCenter() | 
					
						
							|  |  |  |         if (center.lng !== loc.lon || center.lat !== loc.lat) { | 
					
						
							| 
									
										
										
										
											2023-07-08 01:21:11 +02:00
										 |  |  |             if (isNaN(loc.lon) || isNaN(loc.lat)) { | 
					
						
							|  |  |  |                 console.error("Got invalid lat or lon, not setting") | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 map.setCenter({ lng: loc.lon, lat: loc.lat }) | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-01 03:29:57 +01:00
										 |  |  |     public installCustomKeyboardHandler(viewportStore: UIEventSource<HTMLDivElement>) { | 
					
						
							|  |  |  |         viewportStore.mapD( | 
					
						
							| 
									
										
										
										
											2023-12-22 18:50:22 +01:00
										 |  |  |             (viewport) => { | 
					
						
							|  |  |  |                 const map = this._maplibreMap.data | 
					
						
							|  |  |  |                 if (!map) { | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 const oldKeyboard = map.keyboard | 
					
						
							| 
									
										
										
										
											2024-01-01 03:29:57 +01:00
										 |  |  |                 const w = viewport.getBoundingClientRect().width | 
					
						
							|  |  |  |                 if (w < 10) { | 
					
						
							|  |  |  |                     /// this is weird, but definitively wrong!
 | 
					
						
							|  |  |  |                     console.log("Got a very small bound", w, viewport) | 
					
						
							|  |  |  |                     // We try again later on
 | 
					
						
							|  |  |  |                     window.requestAnimationFrame(() => { | 
					
						
							|  |  |  |                         viewportStore.ping() | 
					
						
							|  |  |  |                     }) | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 oldKeyboard._panStep = w | 
					
						
							| 
									
										
										
										
											2023-12-22 18:50:22 +01:00
										 |  |  |             }, | 
					
						
							|  |  |  |             [this._maplibreMap] | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     private setMaxBounds(bbox: undefined | BBox) { | 
					
						
							|  |  |  |         const map = this._maplibreMap.data | 
					
						
							| 
									
										
										
										
											2023-05-05 02:03:41 +02:00
										 |  |  |         if (!map) { | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (bbox) { | 
					
						
							| 
									
										
										
										
											2023-04-06 01:33:08 +02:00
										 |  |  |             map?.setMaxBounds(bbox.toLngLat()) | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2023-04-06 01:33:08 +02:00
										 |  |  |             map?.setMaxBounds(null) | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-18 01:26:04 +02:00
										 |  |  |     private setAllowRotating(allow: true | boolean | undefined) { | 
					
						
							|  |  |  |         const map = this._maplibreMap.data | 
					
						
							|  |  |  |         if (!map) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (allow === false) { | 
					
						
							|  |  |  |             map.rotateTo(0, { duration: 0 }) | 
					
						
							|  |  |  |             map.setPitch(0) | 
					
						
							|  |  |  |             map.dragRotate.disable() | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  |             map.keyboard.disableRotation() | 
					
						
							| 
									
										
										
										
											2023-09-28 23:50:27 +02:00
										 |  |  |             map.touchZoomRotate.disableRotation() | 
					
						
							| 
									
										
										
										
											2023-07-18 01:26:04 +02:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             map.dragRotate.enable() | 
					
						
							| 
									
										
										
										
											2023-12-15 01:46:01 +01:00
										 |  |  |             map.keyboard.enableRotation() | 
					
						
							| 
									
										
										
										
											2023-09-28 23:50:27 +02:00
										 |  |  |             map.touchZoomRotate.enableRotation() | 
					
						
							| 
									
										
										
										
											2023-07-18 01:26:04 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |     private setAllowMoving(allow: true | boolean | undefined) { | 
					
						
							|  |  |  |         const map = this._maplibreMap.data | 
					
						
							| 
									
										
										
										
											2023-05-05 02:03:41 +02:00
										 |  |  |         if (!map) { | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (allow === false) { | 
					
						
							|  |  |  |             for (const id of MapLibreAdaptor.maplibre_control_handlers) { | 
					
						
							|  |  |  |                 map[id].disable() | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             for (const id of MapLibreAdaptor.maplibre_control_handlers) { | 
					
						
							|  |  |  |                 map[id].enable() | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-07-18 01:26:04 +02:00
										 |  |  |         this.setAllowRotating(this.allowRotating.data) | 
					
						
							| 
									
										
										
										
											2023-03-24 19:21:15 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-06 01:33:08 +02:00
										 |  |  |     private setMinzoom(minzoom: number) { | 
					
						
							|  |  |  |         const map = this._maplibreMap.data | 
					
						
							| 
									
										
										
										
											2023-05-05 02:03:41 +02:00
										 |  |  |         if (!map) { | 
					
						
							| 
									
										
										
										
											2023-04-06 01:33:08 +02:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         map.setMinZoom(minzoom) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-21 01:53:24 +02:00
										 |  |  |     private setMaxzoom(maxzoom: number) { | 
					
						
							|  |  |  |         const map = this._maplibreMap.data | 
					
						
							| 
									
										
										
										
											2023-05-05 02:03:41 +02:00
										 |  |  |         if (!map) { | 
					
						
							| 
									
										
										
										
											2023-04-21 01:53:24 +02:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         map.setMaxZoom(maxzoom) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |     private setAllowZooming(allow: true | boolean | undefined) { | 
					
						
							|  |  |  |         const map = this._maplibreMap.data | 
					
						
							| 
									
										
										
										
											2023-05-05 02:03:41 +02:00
										 |  |  |         if (!map) { | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (allow === false) { | 
					
						
							|  |  |  |             for (const id of MapLibreAdaptor.maplibre_zoom_handlers) { | 
					
						
							|  |  |  |                 map[id].disable() | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             for (const id of MapLibreAdaptor.maplibre_zoom_handlers) { | 
					
						
							|  |  |  |                 map[id].enable() | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private setBounds(bounds: BBox) { | 
					
						
							|  |  |  |         const map = this._maplibreMap.data | 
					
						
							| 
									
										
										
										
											2023-05-05 02:03:41 +02:00
										 |  |  |         if (!map || bounds === undefined) { | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         const oldBounds = map.getBounds() | 
					
						
							|  |  |  |         const e = 0.0000001 | 
					
						
							|  |  |  |         const hasDiff = | 
					
						
							|  |  |  |             Math.abs(oldBounds.getWest() - bounds.getWest()) > e && | 
					
						
							|  |  |  |             Math.abs(oldBounds.getEast() - bounds.getEast()) > e && | 
					
						
							|  |  |  |             Math.abs(oldBounds.getNorth() - bounds.getNorth()) > e && | 
					
						
							|  |  |  |             Math.abs(oldBounds.getSouth() - bounds.getSouth()) > e | 
					
						
							|  |  |  |         if (!hasDiff) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |         this.lockZoom() | 
					
						
							| 
									
										
										
										
											2023-03-28 05:13:48 +02:00
										 |  |  |         map.fitBounds(bounds.toLngLat()) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Should be called before making an animation. | 
					
						
							|  |  |  |      * First, 'isFlying' is set to true. This will disable the zoom control | 
					
						
							|  |  |  |      * Then, zoom is set to '1', which is very low. This will generally disable all layers, after which this function will return | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Then, a zoom/pan/... animation can be made; after which a 'moveEnd'-event will trigger the 'isFlying' to be set to false and the zoom to be set correctly | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     private lockZoom() { | 
					
						
							|  |  |  |         this.isFlying.setData(true) | 
					
						
							|  |  |  |         this.zoom.setData(1) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |     private async setTerrain(useTerrain: boolean) { | 
					
						
							|  |  |  |         const map = this._maplibreMap.data | 
					
						
							|  |  |  |         if (!map) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         const id = "maptiler-terrain-data" | 
					
						
							|  |  |  |         if (useTerrain) { | 
					
						
							| 
									
										
										
										
											2024-02-20 13:33:38 +01:00
										 |  |  |             if (map.getTerrain()) { | 
					
						
							|  |  |  |                 return | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |             map.addSource(id, { | 
					
						
							| 
									
										
										
										
											2024-02-20 13:33:38 +01:00
										 |  |  |                 type: "raster-dem", | 
					
						
							|  |  |  |                 url: | 
					
						
							|  |  |  |                     "https://api.maptiler.com/tiles/terrain-rgb/tiles.json?key=" + | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |                     Constants.maptilerApiKey | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2024-02-20 13:33:38 +01:00
										 |  |  |             try { | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |                 while (!map?.isStyleLoaded()) { | 
					
						
							|  |  |  |                     await Utils.waitFor(250) | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2024-02-20 13:33:38 +01:00
										 |  |  |                 map.setTerrain({ | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |                     source: id | 
					
						
							| 
									
										
										
										
											2024-02-20 13:33:38 +01:00
										 |  |  |                 }) | 
					
						
							|  |  |  |             } catch (e) { | 
					
						
							| 
									
										
										
										
											2024-02-03 14:33:10 +01:00
										 |  |  |                 console.error(e) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-08-15 01:51:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-22 02:54:46 +02:00
										 |  |  |     public flyTo(lon: number, lat: number, zoom: number) { | 
					
						
							|  |  |  |         this.lockZoom() | 
					
						
							|  |  |  |         window.requestAnimationFrame(() => { | 
					
						
							|  |  |  |             this._maplibreMap.data?.flyTo({ | 
					
						
							|  |  |  |                 zoom, | 
					
						
							|  |  |  |                 center: [lon, lat] | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2024-08-15 01:51:33 +02:00
										 |  |  |         }) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-11 02:37:07 +01:00
										 |  |  | } |