| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  | import BaseUIElement from "../BaseUIElement"; | 
					
						
							|  |  |  | import * as L from "leaflet"; | 
					
						
							| 
									
										
										
										
											2021-09-03 17:30:35 +02:00
										 |  |  | import {Map} from "leaflet"; | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  | import {UIEventSource} from "../../Logic/UIEventSource"; | 
					
						
							|  |  |  | import Loc from "../../Models/Loc"; | 
					
						
							|  |  |  | import BaseLayer from "../../Models/BaseLayer"; | 
					
						
							|  |  |  | import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  | import {Utils} from "../../Utils"; | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | export default class Minimap extends BaseUIElement { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private static _nextId = 0; | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |     public readonly leafletMap: UIEventSource<Map> | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |     private readonly _id: string; | 
					
						
							|  |  |  |     private readonly _background: UIEventSource<BaseLayer>; | 
					
						
							|  |  |  |     private readonly _location: UIEventSource<Loc>; | 
					
						
							|  |  |  |     private _isInited = false; | 
					
						
							|  |  |  |     private _allowMoving: boolean; | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  |     private readonly _leafletoptions: any; | 
					
						
							| 
									
										
										
										
											2021-07-28 02:51:07 +02:00
										 |  |  |     private readonly _onFullyLoaded: (leaflet: L.Map) => void | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |     private readonly _attribution: BaseUIElement | boolean; | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |     private readonly _lastClickLocation: UIEventSource<{ lat: number; lon: number }>; | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     constructor(options?: { | 
					
						
							|  |  |  |                     background?: UIEventSource<BaseLayer>, | 
					
						
							|  |  |  |                     location?: UIEventSource<Loc>, | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  |                     allowMoving?: boolean, | 
					
						
							| 
									
										
										
										
											2021-07-28 02:51:07 +02:00
										 |  |  |                     leafletOptions?: any, | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |                     attribution?: BaseUIElement | boolean, | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |                     onFullyLoaded?: (leaflet: L.Map) => void, | 
					
						
							|  |  |  |                     leafletMap?: UIEventSource<Map>, | 
					
						
							|  |  |  |                     lastClickLocation?: UIEventSource<{ lat: number, lon: number }> | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |                 } | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |         super() | 
					
						
							|  |  |  |         options = options ?? {} | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |         this.leafletMap = options.leafletMap ?? new UIEventSource<Map>(undefined) | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |         this._background = options?.background ?? new UIEventSource<BaseLayer>(AvailableBaseLayers.osmCarto) | 
					
						
							| 
									
										
										
										
											2021-07-13 16:11:57 +02:00
										 |  |  |         this._location = options?.location ?? new UIEventSource<Loc>({lat: 0, lon: 0, zoom: 1}) | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |         this._id = "minimap" + Minimap._nextId; | 
					
						
							|  |  |  |         this._allowMoving = options.allowMoving ?? true; | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  |         this._leafletoptions = options.leafletOptions ?? {} | 
					
						
							| 
									
										
										
										
											2021-07-28 02:51:07 +02:00
										 |  |  |         this._onFullyLoaded = options.onFullyLoaded | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |         this._attribution = options.attribution | 
					
						
							|  |  |  |         this._lastClickLocation = options.lastClickLocation; | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |         Minimap._nextId++ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |     protected InnerConstructElement(): HTMLElement { | 
					
						
							|  |  |  |         const div = document.createElement("div") | 
					
						
							|  |  |  |         div.id = this._id; | 
					
						
							|  |  |  |         div.style.height = "100%" | 
					
						
							|  |  |  |         div.style.width = "100%" | 
					
						
							|  |  |  |         div.style.minWidth = "40px" | 
					
						
							|  |  |  |         div.style.minHeight = "40px" | 
					
						
							| 
									
										
										
										
											2021-07-15 20:47:28 +02:00
										 |  |  |         div.style.position = "relative" | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |         const wrapper = document.createElement("div") | 
					
						
							|  |  |  |         wrapper.appendChild(div) | 
					
						
							|  |  |  |         const self = this; | 
					
						
							|  |  |  |         // @ts-ignore
 | 
					
						
							|  |  |  |         const resizeObserver = new ResizeObserver(_ => { | 
					
						
							|  |  |  |             self.InitMap(); | 
					
						
							|  |  |  |             self.leafletMap?.data?.invalidateSize() | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         resizeObserver.observe(div); | 
					
						
							|  |  |  |         return wrapper; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |     private InitMap() { | 
					
						
							|  |  |  |         if (this._constructedHtmlElement === undefined) { | 
					
						
							|  |  |  |             // This element isn't initialized yet
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (document.getElementById(this._id) === null) { | 
					
						
							|  |  |  |             // not yet attached, we probably got some other event
 | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (this._isInited) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         this._isInited = true; | 
					
						
							|  |  |  |         const location = this._location; | 
					
						
							| 
									
										
										
										
											2021-07-28 02:51:07 +02:00
										 |  |  |         const self = this; | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |         let currentLayer = this._background.data.layer() | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  |         const options = { | 
					
						
							| 
									
										
										
										
											2021-07-28 02:51:07 +02:00
										 |  |  |             center: <[number, number]>[location.data?.lat ?? 0, location.data?.lon ?? 0], | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |             zoom: location.data?.zoom ?? 2, | 
					
						
							|  |  |  |             layers: [currentLayer], | 
					
						
							|  |  |  |             zoomControl: false, | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |             attributionControl: this._attribution !== undefined, | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |             dragging: this._allowMoving, | 
					
						
							|  |  |  |             scrollWheelZoom: this._allowMoving, | 
					
						
							|  |  |  |             doubleClickZoom: this._allowMoving, | 
					
						
							|  |  |  |             keyboard: this._allowMoving, | 
					
						
							| 
									
										
										
										
											2021-07-10 21:07:27 +02:00
										 |  |  |             touchZoom: this._allowMoving, | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  |             // Disabling this breaks the geojson layer - don't ask me why!  zoomAnimation: this._allowMoving,
 | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |             fadeAnimation: this._allowMoving, | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-28 02:51:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  |         Utils.Merge(this._leafletoptions, options) | 
					
						
							| 
									
										
										
										
											2021-07-28 02:51:07 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-20 01:33:58 +02:00
										 |  |  |         const map = L.map(this._id, options); | 
					
						
							| 
									
										
										
										
											2021-07-28 02:51:07 +02:00
										 |  |  |         if (self._onFullyLoaded !== undefined) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             currentLayer.on("load", () => { | 
					
						
							|  |  |  |                 console.log("Fully loaded all tiles!") | 
					
						
							|  |  |  |                 self._onFullyLoaded(map) | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |         // Users are not allowed to zoom to the 'copies' on the left and the right, stuff goes wrong then
 | 
					
						
							|  |  |  |         // We give a bit of leeway for people on the edges
 | 
					
						
							|  |  |  |         // Also see: https://www.reddit.com/r/openstreetmap/comments/ih4zzc/mapcomplete_a_new_easytouse_editor/g31ubyv/
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |         map.setMaxBounds( | 
					
						
							|  |  |  |             [[-100, -200], [100, 200]] | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |         if (this._attribution !== undefined) { | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |             if (this._attribution === true) { | 
					
						
							|  |  |  |                 map.attributionControl.setPrefix(false) | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 map.attributionControl.setPrefix( | 
					
						
							|  |  |  |                     "<span id='leaflet-attribution'></span>"); | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |         this._background.addCallbackAndRun(layer => { | 
					
						
							|  |  |  |             const newLayer = layer.layer() | 
					
						
							|  |  |  |             if (currentLayer !== undefined) { | 
					
						
							|  |  |  |                 map.removeLayer(currentLayer); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             currentLayer = newLayer; | 
					
						
							| 
									
										
										
										
											2021-07-28 02:51:07 +02:00
										 |  |  |             if (self._onFullyLoaded !== undefined) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 currentLayer.on("load", () => { | 
					
						
							|  |  |  |                     console.log("Fully loaded all tiles!") | 
					
						
							|  |  |  |                     self._onFullyLoaded(map) | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |             map.addLayer(newLayer); | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |             map.setMaxZoom(layer.max_zoom ?? map.getMaxZoom()) | 
					
						
							| 
									
										
										
										
											2021-09-08 01:36:44 +02:00
										 |  |  |             if (self._attribution !== true && self._attribution !== false) { | 
					
						
							|  |  |  |                 self._attribution?.AttachTo('leaflet-attribution') | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let isRecursing = false; | 
					
						
							|  |  |  |         map.on("moveend", function () { | 
					
						
							|  |  |  |             if (isRecursing) { | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (map.getZoom() === location.data.zoom && | 
					
						
							|  |  |  |                 map.getCenter().lat === location.data.lat && | 
					
						
							|  |  |  |                 map.getCenter().lng === location.data.lon) { | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             location.data.zoom = map.getZoom(); | 
					
						
							|  |  |  |             location.data.lat = map.getCenter().lat; | 
					
						
							|  |  |  |             location.data.lon = map.getCenter().lng; | 
					
						
							|  |  |  |             isRecursing = true; | 
					
						
							|  |  |  |             location.ping(); | 
					
						
							|  |  |  |             isRecursing = false; // This is ugly, I know
 | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         location.addCallback(loc => { | 
					
						
							|  |  |  |             const mapLoc = map.getCenter() | 
					
						
							|  |  |  |             const dlat = Math.abs(loc.lat - mapLoc[0]) | 
					
						
							|  |  |  |             const dlon = Math.abs(loc.lon - mapLoc[1]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (dlat < 0.000001 && dlon < 0.000001 && map.getZoom() === loc.zoom) { | 
					
						
							|  |  |  |                 return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             map.setView([loc.lat, loc.lon], loc.zoom) | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         location.map(loc => loc.zoom) | 
					
						
							|  |  |  |             .addCallback(zoom => { | 
					
						
							|  |  |  |                 if (Math.abs(map.getZoom() - zoom) > 0.1) { | 
					
						
							|  |  |  |                     map.setZoom(zoom, {}); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-03 13:48:04 +02:00
										 |  |  |         if (this._lastClickLocation) { | 
					
						
							|  |  |  |             const lastClickLocation = this._lastClickLocation | 
					
						
							|  |  |  |             map.on("click", function (e) { | 
					
						
							|  |  |  |                 // @ts-ignore
 | 
					
						
							|  |  |  |                 lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}) | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             map.on("contextmenu", function (e) { | 
					
						
							|  |  |  |                 // @ts-ignore
 | 
					
						
							|  |  |  |                 lastClickLocation?.setData({lat: e.latlng.lat, lon: e.latlng.lng}); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-23 02:15:28 +02:00
										 |  |  |         this.leafletMap.setData(map) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |