forked from MapComplete/MapComplete
		
	Refactoring: new background selector
This commit is contained in:
		
							parent
							
								
									5427a4cb05
								
							
						
					
					
						commit
						82093ffdf4
					
				
					 25 changed files with 658 additions and 269 deletions
				
			
		|  | @ -275,7 +275,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|         return new Promise<Blob>((resolve) => drawOn.toBlob((data) => resolve(data))) | ||||
|     } | ||||
| 
 | ||||
|     private updateStores() { | ||||
|     private updateStores(): void { | ||||
|         const map = this._maplibreMap.data | ||||
|         if (!map) { | ||||
|             return | ||||
|  | @ -293,7 +293,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|         this.bounds.setData(bbox) | ||||
|     } | ||||
| 
 | ||||
|     private SetZoom(z: number) { | ||||
|     private SetZoom(z: number): void { | ||||
|         const map = this._maplibreMap.data | ||||
|         if (!map || z === undefined) { | ||||
|             return | ||||
|  | @ -303,7 +303,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private MoveMapToCurrentLoc(loc: { lat: number; lon: number }) { | ||||
|     private MoveMapToCurrentLoc(loc: { lat: number; lon: number }): void { | ||||
|         const map = this._maplibreMap.data | ||||
|         if (!map || loc === undefined) { | ||||
|             return | ||||
|  | @ -325,7 +325,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private removeCurrentLayer(map: MLMap) { | ||||
|     private removeCurrentLayer(map: MLMap): void { | ||||
|         if (this._currentRasterLayer) { | ||||
|             // hide the previous layer
 | ||||
|             map.removeLayer(this._currentRasterLayer) | ||||
|  | @ -333,7 +333,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private async setBackground() { | ||||
|     private async setBackground(): Promise<void> { | ||||
|         const map = this._maplibreMap.data | ||||
|         if (!map) { | ||||
|             return | ||||
|  | @ -363,6 +363,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
| 
 | ||||
|         map.addSource(background.id, MapLibreAdaptor.prepareWmsSource(background)) | ||||
| 
 | ||||
|         map.resize() | ||||
|         map.addLayer( | ||||
|             { | ||||
|                 id: background.id, | ||||
|  |  | |||
							
								
								
									
										69
									
								
								UI/Map/OverlayMap.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								UI/Map/OverlayMap.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | |||
| <script lang="ts"> | ||||
|     /** | ||||
|      * The overlay map is a bit a weird map: | ||||
|      * it is a HTML-component which is intended to be placed _over_ another map. | ||||
|      * It will align itself in order to seamlessly show the same location; but possibly in a different style | ||||
|      */ | ||||
|     import MaplibreMap from "./MaplibreMap.svelte"; | ||||
|     import {Store, UIEventSource} from "../../Logic/UIEventSource"; | ||||
|     import {Map as MlMap} from "maplibre-gl"; | ||||
|     import {MapLibreAdaptor} from "./MapLibreAdaptor"; | ||||
|     import type {MapProperties} from "../../Models/MapProperties"; | ||||
|     import {onDestroy} from "svelte"; | ||||
|     import type {RasterLayerPolygon} from "../../Models/RasterLayers"; | ||||
| 
 | ||||
| 
 | ||||
|     export let placedOverMapProperties: MapProperties | ||||
|     export let placedOverMap: UIEventSource<MlMap> | ||||
| 
 | ||||
|     export let rasterLayer: UIEventSource<RasterLayerPolygon> | ||||
| 
 | ||||
|     export let visible: Store<boolean> = undefined | ||||
|     let altmap: UIEventSource<MlMap> = new UIEventSource(undefined) | ||||
|     let altproperties = new MapLibreAdaptor(altmap, { | ||||
|         rasterLayer, | ||||
|         zoom: UIEventSource.feedFrom(placedOverMapProperties.zoom) | ||||
|     }) | ||||
|     altproperties.allowMoving.setData(false) | ||||
|     altproperties.allowZooming.setData(false) | ||||
| 
 | ||||
|     function pixelCenterOf(map: UIEventSource<MlMap>): [number, number] { | ||||
|         const rect = map?.data?.getCanvas()?.getBoundingClientRect() | ||||
|         if (!rect) { | ||||
|             return undefined | ||||
|         } | ||||
|         const x = (rect.left + rect.right) / 2 | ||||
|         const y = (rect.top + rect.bottom) / 2 | ||||
|         return [x, y] | ||||
|     } | ||||
| 
 | ||||
|     function updateLocation() { | ||||
|         if (!placedOverMap.data || !altmap.data) { | ||||
|             return | ||||
|         } | ||||
|         altmap.data.resize() | ||||
|         const {lon, lat} = placedOverMapProperties.location.data | ||||
|         const altMapCenter = pixelCenterOf(altmap) | ||||
|         const c = placedOverMap.data.unproject(altMapCenter) | ||||
|         altproperties.location.setData({lon: c.lng, lat: c.lat}) | ||||
|     } | ||||
| 
 | ||||
|     onDestroy(placedOverMapProperties.location.addCallbackAndRunD(updateLocation)) | ||||
|     updateLocation() | ||||
|     window.setTimeout(updateLocation, 150) | ||||
|     window.setTimeout(updateLocation, 500) | ||||
| 
 | ||||
|     if (visible) { | ||||
|         onDestroy(visible?.addCallbackAndRunD(v => { | ||||
|             if (!v) { | ||||
|                 return | ||||
|             } | ||||
|             updateLocation() | ||||
|             window.setTimeout(updateLocation, 150) | ||||
|             window.setTimeout(updateLocation, 500) | ||||
|         })) | ||||
|     } | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <MaplibreMap map={altmap}/> | ||||
							
								
								
									
										73
									
								
								UI/Map/RasterLayerOverview.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								UI/Map/RasterLayerOverview.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | |||
| <script lang="ts"> | ||||
|     /** | ||||
|      * The RasterLayerOverview shows the available 4 categories of maps with a RasterLayerPicker | ||||
|      */ | ||||
|     import {Store, UIEventSource} from "../../Logic/UIEventSource"; | ||||
|     import type {RasterLayerPolygon} from "../../Models/RasterLayers"; | ||||
|     import type {MapProperties} from "../../Models/MapProperties"; | ||||
|     import {Map as MlMap} from "maplibre-gl"; | ||||
|     import RasterLayerPicker from "./RasterLayerPicker.svelte"; | ||||
|     import type {EliCategory} from "../../Models/RasterLayerProperties"; | ||||
|     import UserRelatedState from "../../Logic/State/UserRelatedState"; | ||||
|     import Translations from "../i18n/Translations"; | ||||
|     import Tr from "../Base/Tr.svelte"; | ||||
| 
 | ||||
|     export let availableLayers: Store<RasterLayerPolygon[]> | ||||
|     export let mapproperties: MapProperties | ||||
|     export let userstate: UserRelatedState | ||||
|     export let map: Store<MlMap> | ||||
|     /** | ||||
|      * Used to toggle the background layers on/off | ||||
|      */ | ||||
|     export let visible: UIEventSource<boolean> = undefined | ||||
| 
 | ||||
|     type CategoryType = "photo" | "map" | "other" | "osmbasedmap" | ||||
|     const categories: Record<CategoryType, EliCategory[]> = { | ||||
|         "photo": ["photo", "historicphoto"], | ||||
|         "map": ["map", "historicmap"], | ||||
|         "other": ["other", "elevation"], | ||||
|         "osmbasedmap": ["osmbasedmap"] | ||||
|     } | ||||
| 
 | ||||
|     function availableForCategory(type: CategoryType): Store<RasterLayerPolygon[]> { | ||||
|         const keywords = categories[type] | ||||
|         return availableLayers.mapD(available => available.filter(layer => | ||||
|             keywords.indexOf(<EliCategory>layer.properties.category) >= 0 | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     const mapLayers = availableForCategory("map") | ||||
|     const osmbasedmapLayers = availableForCategory("osmbasedmap") | ||||
|     const photoLayers = availableForCategory("photo") | ||||
|     const otherLayers = availableForCategory("other") | ||||
| 
 | ||||
|     function onApply() { | ||||
|         visible.setData(false) | ||||
|     } | ||||
| 
 | ||||
|     function getPref(type: CategoryType): UIEventSource<string> { | ||||
|         return userstate.osmConnection.GetPreference("preferred-layer-" + type) | ||||
|     } | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <div class="h-full flex flex-col"> | ||||
|     <slot name="title"> | ||||
|         <h2> | ||||
|             <Tr t={Translations.t.general.backgroundMap}/> | ||||
|         </h2> | ||||
|     </slot> | ||||
| 
 | ||||
|     <div class="grid grid-cols-1 md:grid-cols-2 gap-2 h-full w-full"> | ||||
|         <RasterLayerPicker availableLayers={photoLayers} favourite={getPref("photo")} {map} {mapproperties} | ||||
|                            on:appliedLayer={onApply} {visible}/> | ||||
|         <RasterLayerPicker availableLayers={mapLayers} favourite={getPref("map")} {map} {mapproperties} | ||||
|                            on:appliedLayer={onApply} {visible}/> | ||||
|         <RasterLayerPicker availableLayers={osmbasedmapLayers} favourite={getPref("osmbasedmap")} | ||||
|                            {map} {mapproperties} on:appliedLayer={onApply} {visible}/> | ||||
|         <RasterLayerPicker availableLayers={otherLayers} favourite={getPref("other")} {map} {mapproperties} | ||||
|                            on:appliedLayer={onApply} {visible}/> | ||||
|     </div> | ||||
| 
 | ||||
| </div> | ||||
|  | @ -1,18 +1,77 @@ | |||
| <script lang="ts"> | ||||
|   import type { Readable, Writable } from "svelte/store"; | ||||
|   import type { RasterLayerPolygon } from "../../Models/RasterLayers"; | ||||
|     import type {RasterLayerPolygon} from "../../Models/RasterLayers"; | ||||
|     import OverlayMap from "./OverlayMap.svelte"; | ||||
|     import type {MapProperties} from "../../Models/MapProperties"; | ||||
|     import {Store, UIEventSource} from "../../Logic/UIEventSource"; | ||||
|     import {Map as MlMap} from "maplibre-gl" | ||||
|     import {createEventDispatcher, onDestroy} from "svelte"; | ||||
| 
 | ||||
|   /*** | ||||
|    * Chooses a background-layer out of available options | ||||
|    */ | ||||
|   export let availableLayers: Readable<RasterLayerPolygon[]> | ||||
|   export let value: Writable<RasterLayerPolygon> | ||||
|     /*** | ||||
|      * Chooses a background-layer out of available options | ||||
|      */ | ||||
|     export let availableLayers: Store<RasterLayerPolygon[]> | ||||
|     export let mapproperties: MapProperties | ||||
|     export let map: Store<MlMap> | ||||
| 
 | ||||
|     export let visible: Store<boolean> = undefined | ||||
|      | ||||
|     let dispatch = createEventDispatcher<{appliedLayer}>() | ||||
|      | ||||
|     export let favourite : UIEventSource<string> = undefined | ||||
|      | ||||
| 
 | ||||
|     let rasterLayer = new UIEventSource<RasterLayerPolygon>(availableLayers.data?.[0]) | ||||
|     let hasLayers = true | ||||
|     onDestroy(availableLayers.addCallbackAndRun(layers => { | ||||
|         if (layers === undefined || layers.length === 0) { | ||||
|             hasLayers = false | ||||
|             return | ||||
|         } | ||||
|         hasLayers = true | ||||
|         rasterLayer.setData(layers[0]) | ||||
|     })) | ||||
|      | ||||
|     if(favourite){ | ||||
|         onDestroy(favourite.addCallbackAndRunD(favourite => { | ||||
|             const fav = availableLayers.data?.find(l => l.properties.id === favourite) | ||||
|             if(!fav){ | ||||
|                 return | ||||
|             } | ||||
|             rasterLayer.setData(fav) | ||||
|         })) | ||||
|     } | ||||
|      | ||||
|     onDestroy(rasterLayer.addCallbackAndRunD(selected => { | ||||
|         favourite?.setData(selected.properties.id) | ||||
|     })) | ||||
| 
 | ||||
|     let rasterLayerOnMap = UIEventSource.feedFrom(rasterLayer) | ||||
|      | ||||
|     if (visible) { | ||||
|         onDestroy(visible?.addCallbackAndRunD(visible => { | ||||
|             if (visible) { | ||||
|                 rasterLayerOnMap.setData(rasterLayer.data ?? availableLayers.data[0]) | ||||
|             } else { | ||||
|                 rasterLayerOnMap.setData(undefined) | ||||
|             } | ||||
|         })) | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| <select bind:value={$value}> | ||||
|   {#each $availableLayers as availableLayer } | ||||
|     <option value={availableLayer}> | ||||
|       {availableLayer.properties.name} | ||||
|     </option> | ||||
|   {/each} | ||||
| </select> | ||||
| {#if hasLayers} | ||||
|     <div class="h-full w-full flex flex-col"> | ||||
|         <button on:click={() => {mapproperties.rasterLayer.setData(rasterLayer.data); | ||||
|             dispatch("appliedLayer") | ||||
|         }} class="w-full h-full m-0 p-0"> | ||||
|             <OverlayMap rasterLayer={rasterLayerOnMap} placedOverMap={map} placedOverMapProperties={mapproperties} | ||||
|                         {visible}/> | ||||
|         </button> | ||||
|         <select bind:value={$rasterLayer} class="w-full"> | ||||
|             {#each $availableLayers as availableLayer } | ||||
|                 <option value={availableLayer}> | ||||
|                     {availableLayer.properties.name} | ||||
|                 </option> | ||||
|             {/each} | ||||
|         </select> | ||||
|     </div> | ||||
| {/if} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue