| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  | import Combine from "../Base/Combine" | 
					
						
							|  |  |  | import { UIEventSource } from "../../Logic/UIEventSource" | 
					
						
							|  |  |  | import Loc from "../../Models/Loc" | 
					
						
							|  |  |  | import Svg from "../../Svg" | 
					
						
							|  |  |  | import Toggle from "../Input/Toggle" | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  | import BaseLayer from "../../Models/BaseLayer" | 
					
						
							|  |  |  | import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers" | 
					
						
							|  |  |  | import BaseUIElement from "../BaseUIElement" | 
					
						
							|  |  |  | import { GeoOperations } from "../../Logic/GeoOperations" | 
					
						
							| 
									
										
										
										
											2022-12-24 03:44:21 +01:00
										 |  |  | import Hotkeys from "../Base/Hotkeys" | 
					
						
							| 
									
										
										
										
											2022-12-31 01:40:27 +01:00
										 |  |  | import Translations from "../i18n/Translations" | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class SingleLayerSelectionButton extends Toggle { | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |     public readonly activate: () => void | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * The SingeLayerSelectionButton also acts as an actor to keep the layers in check | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * It works the following way: | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * - It has a boolean state to indicate wether or not the button is active | 
					
						
							|  |  |  |      * - It keeps track of the available layers | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     constructor( | 
					
						
							|  |  |  |         locationControl: UIEventSource<Loc>, | 
					
						
							|  |  |  |         options: { | 
					
						
							|  |  |  |             currentBackground: UIEventSource<BaseLayer> | 
					
						
							|  |  |  |             preferredType: string | 
					
						
							|  |  |  |             preferredLayer?: BaseLayer | 
					
						
							|  |  |  |             notAvailable?: () => void | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |         const prefered = options.preferredType | 
					
						
							|  |  |  |         const previousLayer = new UIEventSource(options.preferredLayer) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const unselected = SingleLayerSelectionButton.getIconFor(prefered).SetClass( | 
					
						
							|  |  |  |             "rounded-lg p-1 h-12 w-12 overflow-hidden subtle-background border-invisible" | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const selected = SingleLayerSelectionButton.getIconFor(prefered).SetClass( | 
					
						
							|  |  |  |             "rounded-lg p-1 h-12 w-12 overflow-hidden subtle-background border-attention-catch" | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const available = AvailableBaseLayers.SelectBestLayerAccordingTo( | 
					
						
							|  |  |  |             locationControl, | 
					
						
							|  |  |  |             new UIEventSource<string | string[]>(options.preferredType) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         let toggle: BaseUIElement = new Toggle( | 
					
						
							|  |  |  |             selected, | 
					
						
							|  |  |  |             unselected, | 
					
						
							| 
									
										
										
										
											2022-12-24 03:44:21 +01:00
										 |  |  |             options.currentBackground.map((bg) => bg?.category === options.preferredType) | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |         super( | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |             toggle, | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |             undefined, | 
					
						
							| 
									
										
										
										
											2022-12-24 03:44:21 +01:00
										 |  |  |             available.map((av) => av?.category === options.preferredType) | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * Checks that the previous layer is still usable on the current location. | 
					
						
							|  |  |  |          * If not, clears the 'previousLayer' | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         function checkPreviousLayer() { | 
					
						
							|  |  |  |             if (previousLayer.data === undefined) { | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (previousLayer.data.feature === null || previousLayer.data.feature === undefined) { | 
					
						
							|  |  |  |                 // Global layer
 | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             const loc = locationControl.data | 
					
						
							|  |  |  |             if (!GeoOperations.inside([loc.lon, loc.lat], previousLayer.data.feature)) { | 
					
						
							|  |  |  |                 // The previous layer is out of bounds
 | 
					
						
							|  |  |  |                 previousLayer.setData(undefined) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         unselected.onClick(() => { | 
					
						
							|  |  |  |             // Note: a check if 'available' has the correct type is not needed:
 | 
					
						
							|  |  |  |             // Unselected will _not_ be visible if availableBaseLayer has a wrong type!
 | 
					
						
							|  |  |  |             checkPreviousLayer() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             previousLayer.setData(previousLayer.data ?? available.data) | 
					
						
							|  |  |  |             options.currentBackground.setData(previousLayer.data) | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 00:02:40 +01:00
										 |  |  |         options.currentBackground.addCallbackAndRunD((background) => { | 
					
						
							|  |  |  |             if (background.category === options.preferredType) { | 
					
						
							|  |  |  |                 previousLayer.setData(background) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 00:02:40 +01:00
										 |  |  |         available.addCallbackD((availableLayer) => { | 
					
						
							|  |  |  |             // Called whenever a better layer is available
 | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (previousLayer.data === undefined) { | 
					
						
							|  |  |  |                 // PreviousLayer is unset -> we definitively weren't using this category -> no need to switch
 | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (options.currentBackground.data?.id !== previousLayer.data?.id) { | 
					
						
							|  |  |  |                 // The previously used layer doesn't match the current layer -> no need to switch
 | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 00:02:40 +01:00
										 |  |  |             // Is the previous layer still valid? If so, we don't bother to switch
 | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |             if ( | 
					
						
							|  |  |  |                 previousLayer.data.feature === null || | 
					
						
							|  |  |  |                 GeoOperations.inside(locationControl.data, previousLayer.data.feature) | 
					
						
							|  |  |  |             ) { | 
					
						
							| 
									
										
										
										
											2021-11-22 00:02:40 +01:00
										 |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (availableLayer.category === options.preferredType) { | 
					
						
							|  |  |  |                 // Allright, we can set this different layer
 | 
					
						
							|  |  |  |                 options.currentBackground.setData(availableLayer) | 
					
						
							|  |  |  |                 previousLayer.setData(availableLayer) | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 // Uh oh - no correct layer is available... We pass the torch!
 | 
					
						
							|  |  |  |                 if (options.notAvailable !== undefined) { | 
					
						
							|  |  |  |                     options.notAvailable() | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     // Fallback to OSM carto
 | 
					
						
							|  |  |  |                     options.currentBackground.setData(AvailableBaseLayers.osmCarto) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.activate = () => { | 
					
						
							|  |  |  |             checkPreviousLayer() | 
					
						
							|  |  |  |             if (available.data.category !== options.preferredType) { | 
					
						
							|  |  |  |                 // This object can't help either - pass the torch!
 | 
					
						
							|  |  |  |                 if (options.notAvailable !== undefined) { | 
					
						
							|  |  |  |                     options.notAvailable() | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     // Fallback to OSM carto
 | 
					
						
							|  |  |  |                     options.currentBackground.setData(AvailableBaseLayers.osmCarto) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             previousLayer.setData(previousLayer.data ?? available.data) | 
					
						
							|  |  |  |             options.currentBackground.setData(previousLayer.data) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private static getIconFor(type: string) { | 
					
						
							|  |  |  |         switch (type) { | 
					
						
							|  |  |  |             case "map": | 
					
						
							|  |  |  |                 return Svg.generic_map_svg() | 
					
						
							|  |  |  |             case "photo": | 
					
						
							|  |  |  |                 return Svg.satellite_svg() | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |             case "osmbasedmap": | 
					
						
							|  |  |  |                 return Svg.osm_logo_svg() | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |             default: | 
					
						
							|  |  |  |                 return Svg.generic_map_svg() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  | export default class BackgroundMapSwitch extends Combine { | 
					
						
							| 
									
										
										
										
											2022-07-08 03:14:55 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Three buttons to easily switch map layers between OSM, aerial and some map. | 
					
						
							|  |  |  |      * @param state | 
					
						
							|  |  |  |      * @param currentBackground | 
					
						
							|  |  |  |      * @param options | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |     constructor( | 
					
						
							|  |  |  |         state: { | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |             locationControl: UIEventSource<Loc> | 
					
						
							|  |  |  |             backgroundLayer: UIEventSource<BaseLayer> | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |         }, | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |         currentBackground: UIEventSource<BaseLayer>, | 
					
						
							| 
									
										
										
										
											2022-06-19 18:15:33 +02:00
										 |  |  |         options?: { | 
					
						
							|  |  |  |             preferredCategory?: string | 
					
						
							|  |  |  |             allowedCategories?: ("osmbasedmap" | "photo" | "map")[] | 
					
						
							| 
									
										
										
										
											2022-12-24 03:44:21 +01:00
										 |  |  |             enableHotkeys?: boolean | 
					
						
							| 
									
										
										
										
											2022-06-19 18:15:33 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2022-06-19 18:15:33 +02:00
										 |  |  |         const allowedCategories = options?.allowedCategories ?? ["osmbasedmap", "photo", "map"] | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const previousLayer = state.backgroundLayer.data | 
					
						
							|  |  |  |         const buttons = [] | 
					
						
							|  |  |  |         let activatePrevious: () => void = undefined | 
					
						
							|  |  |  |         for (const category of allowedCategories) { | 
					
						
							|  |  |  |             let preferredLayer = undefined | 
					
						
							| 
									
										
										
										
											2022-12-24 03:44:21 +01:00
										 |  |  |             if (previousLayer?.category === category) { | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |                 preferredLayer = previousLayer | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             const button = new SingleLayerSelectionButton(state.locationControl, { | 
					
						
							|  |  |  |                 preferredType: category, | 
					
						
							|  |  |  |                 preferredLayer: preferredLayer, | 
					
						
							|  |  |  |                 currentBackground: currentBackground, | 
					
						
							|  |  |  |                 notAvailable: activatePrevious, | 
					
						
							|  |  |  |             }) | 
					
						
							| 
									
										
										
										
											2021-12-09 01:22:57 +01:00
										 |  |  |             // Fall back to the first option: OSM
 | 
					
						
							|  |  |  |             activatePrevious = activatePrevious ?? button.activate | 
					
						
							| 
									
										
										
										
											2022-06-19 18:15:33 +02:00
										 |  |  |             if (category === options?.preferredCategory) { | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |                 button.activate() | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-12-24 03:44:21 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (options?.enableHotkeys) { | 
					
						
							|  |  |  |                 Hotkeys.RegisterHotkey( | 
					
						
							|  |  |  |                     { nomod: category.charAt(0).toUpperCase() }, | 
					
						
							| 
									
										
										
										
											2022-12-31 01:40:27 +01:00
										 |  |  |                     Translations.t.hotkeyDocumentation.selectBackground.Subs({ category }), | 
					
						
							| 
									
										
										
										
											2022-12-24 03:44:21 +01:00
										 |  |  |                     () => { | 
					
						
							|  |  |  |                         button.activate() | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |             buttons.push(button) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |         // Selects the initial map
 | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |         super(buttons) | 
					
						
							|  |  |  |         this.SetClass("flex") | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } |