| 
									
										
										
										
											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"; | 
					
						
							| 
									
										
										
										
											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") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const available = AvailableBaseLayers | 
					
						
							|  |  |  |             .SelectBestLayerAccordingTo(locationControl, new UIEventSource<string | string[]>(options.preferredType)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let toggle: BaseUIElement = new Toggle( | 
					
						
							|  |  |  |             selected, | 
					
						
							|  |  |  |             unselected, | 
					
						
							|  |  |  |             options.currentBackground.map(bg => bg.category === options.preferredType) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +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 { | 
					
						
							| 
									
										
										
										
											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>, | 
					
						
							|  |  |  |         preferredCategory?: string | 
					
						
							| 
									
										
										
										
											2021-11-18 23:42:03 +01:00
										 |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |         const allowedCategories = ["osmbasedmap", "photo", "map"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const previousLayer = state.backgroundLayer.data | 
					
						
							|  |  |  |         const buttons = [] | 
					
						
							|  |  |  |         let activatePrevious: () => void = undefined | 
					
						
							|  |  |  |         for (const category of allowedCategories) { | 
					
						
							|  |  |  |             let preferredLayer = undefined | 
					
						
							|  |  |  |             if (previousLayer.category === category) { | 
					
						
							|  |  |  |                 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 | 
					
						
							| 
									
										
										
										
											2021-11-21 02:44:35 +01:00
										 |  |  |             if (category === preferredCategory) { | 
					
						
							|  |  |  |                 button.activate() | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             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
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |