forked from MapComplete/MapComplete
		
	Fix overlay layers
This commit is contained in:
		
							parent
							
								
									3aeedf22c8
								
							
						
					
					
						commit
						24f7610d0a
					
				
					 15 changed files with 216 additions and 184 deletions
				
			
		|  | @ -29,7 +29,6 @@ export default class FeaturePropertiesStore { | |||
| 
 | ||||
|                 const source = self._elements.get(id) | ||||
|                 if (source === undefined) { | ||||
|                     console.log("Adding feature store for", id) | ||||
|                     self._elements.set(id, new UIEventSource<any>(feature.properties)) | ||||
|                     continue | ||||
|                 } | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ export interface MapProperties { | |||
|     readonly location: UIEventSource<{ lon: number; lat: number }> | ||||
|     readonly zoom: UIEventSource<number> | ||||
|     readonly minzoom: UIEventSource<number> | ||||
|     readonly maxzoom: UIEventSource<number> | ||||
|     readonly bounds: UIEventSource<BBox> | ||||
|     readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined> | ||||
|     readonly maxbounds: UIEventSource<undefined | BBox> | ||||
|  |  | |||
|  | @ -123,7 +123,9 @@ export interface RasterLayerProperties { | |||
|     /** | ||||
|      * The name of the imagery source | ||||
|      */ | ||||
|     readonly name: string | ||||
|     readonly name: string | Record<string, string> | ||||
| 
 | ||||
|     readonly isOverlay?: boolean | ||||
| 
 | ||||
|     readonly id: string | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import { LayerConfigJson } from "./LayerConfigJson" | ||||
| import TilesourceConfigJson from "./TilesourceConfigJson" | ||||
| import ExtraLinkConfigJson from "./ExtraLinkConfigJson" | ||||
| import { RasterLayerProperties } from "../../RasterLayers" | ||||
| 
 | ||||
| /** | ||||
|  * Defines the entire theme. | ||||
|  | @ -148,7 +148,7 @@ export interface LayoutConfigJson { | |||
|     /** | ||||
|      * Define some (overlay) slippy map tilesources | ||||
|      */ | ||||
|     tileLayerSources?: TilesourceConfigJson[] | ||||
|     tileLayerSources?: (RasterLayerProperties & { defaultState?: true | boolean })[] | ||||
| 
 | ||||
|     /** | ||||
|      * The layers to display. | ||||
|  |  | |||
|  | @ -3,11 +3,12 @@ import { LayoutConfigJson } from "./Json/LayoutConfigJson" | |||
| import LayerConfig from "./LayerConfig" | ||||
| import { LayerConfigJson } from "./Json/LayerConfigJson" | ||||
| import Constants from "../Constants" | ||||
| import TilesourceConfig from "./TilesourceConfig" | ||||
| import { ExtractImages } from "./Conversion/FixImages" | ||||
| import ExtraLinkConfig from "./ExtraLinkConfig" | ||||
| import { Utils } from "../../Utils" | ||||
| import LanguageUtils from "../../Utils/LanguageUtils" | ||||
| import { RasterLayerProperties } from "../RasterLayers" | ||||
| 
 | ||||
| /** | ||||
|  * Minimal information about a theme | ||||
|  **/ | ||||
|  | @ -39,7 +40,7 @@ export default class LayoutConfig implements LayoutInformation { | |||
|     public widenFactor: number | ||||
|     public defaultBackgroundId?: string | ||||
|     public layers: LayerConfig[] | ||||
|     public tileLayerSources: TilesourceConfig[] | ||||
|     public tileLayerSources: (RasterLayerProperties & { defaultState?: true | boolean })[] | ||||
|     public readonly hideFromOverview: boolean | ||||
|     public lockLocation: boolean | [[number, number], [number, number]] | ||||
|     public readonly enableUserBadge: boolean | ||||
|  | @ -161,9 +162,7 @@ export default class LayoutConfig implements LayoutInformation { | |||
|         this.widenFactor = json.widenFactor ?? 1.5 | ||||
| 
 | ||||
|         this.defaultBackgroundId = json.defaultBackgroundId | ||||
|         this.tileLayerSources = (json.tileLayerSources ?? []).map( | ||||
|             (config, i) => new TilesourceConfig(config, `${this.id}.tileLayerSources[${i}]`) | ||||
|         ) | ||||
|         this.tileLayerSources = json.tileLayerSources ?? [] | ||||
|         // At this point, layers should be expanded and validated either by the generateScript or the LegacyJsonConvert
 | ||||
|         this.layers = json.layers.map( | ||||
|             (lyrJson) => | ||||
|  |  | |||
|  | @ -1,43 +0,0 @@ | |||
| import TilesourceConfigJson from "./Json/TilesourceConfigJson" | ||||
| import Translations from "../../UI/i18n/Translations" | ||||
| import { Translation } from "../../UI/i18n/Translation" | ||||
| 
 | ||||
| export default class TilesourceConfig { | ||||
|     public readonly source: string | ||||
|     public readonly id: string | ||||
|     public readonly isOverlay: boolean | ||||
|     public readonly name: Translation | ||||
|     public readonly minzoom: number | ||||
|     public readonly maxzoom: number | ||||
|     public readonly defaultState: boolean | ||||
| 
 | ||||
|     constructor(config: TilesourceConfigJson, ctx: string = "") { | ||||
|         this.id = config.id | ||||
|         this.source = config.source | ||||
|         this.isOverlay = config.isOverlay ?? false | ||||
|         this.name = Translations.T(config.name) | ||||
|         this.minzoom = config.minZoom ?? 0 | ||||
|         this.maxzoom = config.maxZoom ?? 999 | ||||
|         this.defaultState = config.defaultState ?? true | ||||
|         if (this.id === undefined) { | ||||
|             throw "An id is obligated" | ||||
|         } | ||||
|         if (this.minzoom > this.maxzoom) { | ||||
|             throw ( | ||||
|                 "Invalid tilesourceConfig: minzoom should be smaller then maxzoom (at " + ctx + ")" | ||||
|             ) | ||||
|         } | ||||
|         if (this.minzoom < 0) { | ||||
|             throw "minzoom should be > 0 (at " + ctx + ")" | ||||
|         } | ||||
|         if (this.maxzoom < 0) { | ||||
|             throw "maxzoom should be > 0 (at " + ctx + ")" | ||||
|         } | ||||
|         if (this.source.indexOf("{zoom}") >= 0) { | ||||
|             throw "Invalid source url: use {z} instead of {zoom}  (at " + ctx + ".source)" | ||||
|         } | ||||
|         if (!this.defaultState && config.name === undefined) { | ||||
|             throw "Disabling an overlay without a name is not possible" | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -43,6 +43,7 @@ import MetaTagging from "../Logic/MetaTagging" | |||
| import ChangeGeometryApplicator from "../Logic/FeatureSource/Sources/ChangeGeometryApplicator" | ||||
| import { NewGeometryFromChangesFeatureSource } from "../Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource" | ||||
| import OsmObjectDownloader from "../Logic/Osm/OsmObjectDownloader" | ||||
| import ShowOverlayRasterLayer from "../UI/Map/ShowOverlayRasterLayer" | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  | @ -82,6 +83,10 @@ export default class ThemeViewState implements SpecialVisualizationState { | |||
|     readonly geolocation: GeoLocationHandler | ||||
| 
 | ||||
|     readonly lastClickObject: WritableFeatureSource | ||||
|     readonly overlayLayerStates: ReadonlyMap< | ||||
|         string, | ||||
|         { readonly isDisplayed: UIEventSource<boolean> } | ||||
|     > | ||||
| 
 | ||||
|     constructor(layout: LayoutConfig) { | ||||
|         this.layout = layout | ||||
|  | @ -125,6 +130,21 @@ export default class ThemeViewState implements SpecialVisualizationState { | |||
|         const self = this | ||||
|         this.layerState = new LayerState(this.osmConnection, layout.layers, layout.id) | ||||
| 
 | ||||
|         { | ||||
|             const overlayLayerStates = new Map<string, { isDisplayed: UIEventSource<boolean> }>() | ||||
|             for (const rasterInfo of this.layout.tileLayerSources) { | ||||
|                 const isDisplayed = QueryParameters.GetBooleanQueryParameter( | ||||
|                     "overlay-" + rasterInfo.id, | ||||
|                     rasterInfo.defaultState ?? true, | ||||
|                     "Wether or not overlayer layer " + rasterInfo.id + " is shown" | ||||
|                 ) | ||||
|                 const state = { isDisplayed } | ||||
|                 overlayLayerStates.set(rasterInfo.id, state) | ||||
|                 new ShowOverlayRasterLayer(rasterInfo, this.map, this.mapProperties, state) | ||||
|             } | ||||
|             this.overlayLayerStates = overlayLayerStates | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             /* Setup the layout source | ||||
|              * A bit tricky, as this is heavily intertwined with the 'changes'-element, which generate a stream of new and changed features too | ||||
|  |  | |||
|  | @ -1,68 +0,0 @@ | |||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import Combine from "../Base/Combine" | ||||
| import Translations from "../i18n/Translations" | ||||
| import { Translation } from "../i18n/Translation" | ||||
| import Svg from "../../Svg" | ||||
| import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import FilteredLayer from "../../Models/FilteredLayer" | ||||
| import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig" | ||||
| import Loc from "../../Models/Loc" | ||||
| 
 | ||||
| export default class FilterView extends VariableUiElement { | ||||
|     constructor( | ||||
|         filteredLayer: Store<FilteredLayer[]>, | ||||
|         tileLayers: { config: TilesourceConfig; isDisplayed: UIEventSource<boolean> }[], | ||||
|         state: { | ||||
|             readonly availableBackgroundLayers?: Store<BaseLayer[]> | ||||
|             readonly featureSwitchBackgroundSelection?: UIEventSource<boolean> | ||||
|             readonly featureSwitchIsDebugging?: UIEventSource<boolean> | ||||
|             readonly locationControl?: UIEventSource<Loc> | ||||
|             readonly featureSwitchMoreQuests: Store<boolean> | ||||
|         } | ||||
|     ) { | ||||
|         super( | ||||
|             filteredLayer.map((filteredLayers) => { | ||||
|                 // Create the views which toggle layers (and filters them) ...
 | ||||
|                 let elements = filteredLayers | ||||
|                     ?.map((l) => | ||||
|                         FilterView.createOneFilteredLayerElement(l, state)?.SetClass("filter-panel") | ||||
|                     ) | ||||
|                     ?.filter((l) => l !== undefined) | ||||
|                 elements[0].SetClass("first-filter-panel") | ||||
| 
 | ||||
|                 // ... create views for non-interactive layers ...
 | ||||
|                 elements = elements.concat( | ||||
|                     tileLayers.map((tl) => FilterView.createOverlayToggle(state, tl)) | ||||
|                 ) | ||||
| 
 | ||||
|                 return elements | ||||
|             }) | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     private static createOverlayToggle( | ||||
|         state: { locationControl?: UIEventSource<Loc> }, | ||||
|         config: { config: TilesourceConfig; isDisplayed: UIEventSource<boolean> } | ||||
|     ) { | ||||
|         const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem;flex-shrink: 0;" | ||||
| 
 | ||||
|         const icon = new Combine([Svg.checkbox_filled]).SetStyle(iconStyle) | ||||
|         const iconUnselected = new Combine([Svg.checkbox_empty]).SetStyle(iconStyle) | ||||
|         const name: Translation = config.config.name | ||||
| 
 | ||||
|         const styledNameChecked = name.Clone().SetStyle("font-size:large").SetClass("ml-2") | ||||
|         const styledNameUnChecked = name.Clone().SetStyle("font-size:large").SetClass("ml-2") | ||||
| 
 | ||||
|         const style = "display:flex;align-items:center;padding:0.5rem 0;" | ||||
|         const layerChecked = new Combine([icon, styledNameChecked]) | ||||
|             .SetStyle(style) | ||||
|             .onClick(() => config.isDisplayed.setData(false)) | ||||
| 
 | ||||
|         const layerNotChecked = new Combine([iconUnselected, styledNameUnChecked]) | ||||
|             .SetStyle(style) | ||||
|             .onClick(() => config.isDisplayed.setData(true)) | ||||
| 
 | ||||
|         return new Toggle(layerChecked, layerNotChecked, config.isDisplayed) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										47
									
								
								UI/BigComponents/OverlayToggle.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								UI/BigComponents/OverlayToggle.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | |||
| <script lang="ts">/** | ||||
|  * The OverlayToggle shows a single toggle to enable or disable an overlay | ||||
|  */ | ||||
| import Checkbox from "../Base/Checkbox.svelte"; | ||||
| import { onDestroy } from "svelte"; | ||||
| import { UIEventSource } from "../../Logic/UIEventSource"; | ||||
| import Tr from "../Base/Tr.svelte"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import type { RasterLayerProperties } from "../../Models/RasterLayers"; | ||||
| import { Translation } from "../i18n/Translation"; | ||||
| 
 | ||||
| export let layerproperties : RasterLayerProperties | ||||
| export let state: {isDisplayed: UIEventSource<boolean>}; | ||||
| export let zoomlevel: UIEventSource<number>; | ||||
| export let highlightedLayer: UIEventSource<string> | undefined; | ||||
| 
 | ||||
| let isDisplayed: boolean = state.isDisplayed.data; | ||||
| onDestroy(state.isDisplayed.addCallbackAndRunD(d => { | ||||
|   isDisplayed = d; | ||||
|   return false; | ||||
| })); | ||||
| 
 | ||||
| 
 | ||||
| let mainElem: HTMLElement; | ||||
| $:  onDestroy( | ||||
|   highlightedLayer.addCallbackAndRun(highlightedLayer => { | ||||
|     if (highlightedLayer === layerproperties.id) { | ||||
|       mainElem?.classList?.add("glowing-shadow"); | ||||
|     } else { | ||||
|       mainElem?.classList?.remove("glowing-shadow"); | ||||
|     } | ||||
|   }) | ||||
| ); | ||||
| </script> | ||||
| {#if layerproperties.name} | ||||
|   <div bind:this={mainElem}> | ||||
|     <label class="flex gap-1"> | ||||
|       <Checkbox selected={state.isDisplayed} /> | ||||
|       <Tr t={new Translation(layerproperties.name)}/> | ||||
|       {#if $zoomlevel < layerproperties.min_zoom} | ||||
|         <span class="alert"> | ||||
|           <Tr t={Translations.t.general.layerSelection.zoomInToSeeThisLayer} /> | ||||
|         </span> | ||||
|       {/if} | ||||
|     </label> | ||||
|   </div> | ||||
| {/if} | ||||
|  | @ -1,6 +1,6 @@ | |||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import type { Map as MLMap } from "maplibre-gl" | ||||
| import { Map as MlMap } from "maplibre-gl" | ||||
| import { Map as MlMap, SourceSpecification } from "maplibre-gl" | ||||
| import { RasterLayerPolygon, RasterLayerProperties } from "../../Models/RasterLayers" | ||||
| import { Utils } from "../../Utils" | ||||
| import { BBox } from "../../Logic/BBox" | ||||
|  | @ -37,6 +37,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|     readonly allowZooming: UIEventSource<true | boolean | undefined> | ||||
|     readonly lastClickLocation: Store<undefined | { lon: number; lat: number }> | ||||
|     readonly minzoom: UIEventSource<number> | ||||
|     readonly maxzoom: UIEventSource<number> | ||||
|     private readonly _maplibreMap: Store<MLMap> | ||||
|     /** | ||||
|      * Used for internal bookkeeping (to remove a rasterLayer when done loading) | ||||
|  | @ -50,12 +51,14 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|         this.location = state?.location ?? new UIEventSource({ lon: 0, lat: 0 }) | ||||
|         this.zoom = state?.zoom ?? new UIEventSource(1) | ||||
|         this.minzoom = state?.minzoom ?? new UIEventSource(0) | ||||
|         this.maxzoom = state?.maxzoom ?? new UIEventSource(24) | ||||
|         this.zoom.addCallbackAndRunD((z) => { | ||||
|             if (z < this.minzoom.data) { | ||||
|                 this.zoom.setData(this.minzoom.data) | ||||
|             } | ||||
|             if (z > 24) { | ||||
|                 this.zoom.setData(24) | ||||
|             const max = Math.min(24, this.maxzoom.data ?? 24) | ||||
|             if (z > max) { | ||||
|                 this.zoom.setData(max) | ||||
|             } | ||||
|         }) | ||||
|         this.maxbounds = state?.maxbounds ?? new UIEventSource(undefined) | ||||
|  | @ -90,6 +93,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|                 self.setAllowMoving(self.allowMoving.data) | ||||
|                 self.setAllowZooming(self.allowZooming.data) | ||||
|                 self.setMinzoom(self.minzoom.data) | ||||
|                 self.setMaxzoom(self.maxzoom.data) | ||||
|                 self.setBounds(self.bounds.data) | ||||
|             }) | ||||
|             self.MoveMapToCurrentLoc(self.location.data) | ||||
|  | @ -98,6 +102,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|             self.setAllowMoving(self.allowMoving.data) | ||||
|             self.setAllowZooming(self.allowZooming.data) | ||||
|             self.setMinzoom(self.minzoom.data) | ||||
|             self.setMaxzoom(self.maxzoom.data) | ||||
|             self.setBounds(self.bounds.data) | ||||
|             this.updateStores() | ||||
|             map.on("moveend", () => this.updateStores()) | ||||
|  | @ -146,10 +151,23 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static prepareWmsSource(layer: RasterLayerProperties): SourceSpecification { | ||||
|         return { | ||||
|             type: "raster", | ||||
|             // use the tiles option to specify a 256WMS tile source URL
 | ||||
|             // https://maplibre.org/maplibre-gl-js-docs/style-spec/sources/
 | ||||
|             tiles: [MapLibreAdaptor.prepareWmsURL(layer.url, layer["tile-size"] ?? 256)], | ||||
|             tileSize: layer["tile-size"] ?? 256, | ||||
|             minzoom: layer["min_zoom"] ?? 1, | ||||
|             maxzoom: layer["max_zoom"] ?? 25, | ||||
|             //  scheme: background["type"] === "tms" ? "tms" : "xyz",
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Prepares an ELI-URL to be compatible with mapbox | ||||
|      */ | ||||
|     private static prepareWmsURL(url: string, size: number = 256) { | ||||
|     private static prepareWmsURL(url: string, size: number = 256): string { | ||||
|         // ELI:  LAYERS=OGWRGB13_15VL&STYLES=&FORMAT=image/jpeg&CRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}&VERSION=1.3.0&SERVICE=WMS&REQUEST=GetMap
 | ||||
|         // PROD: SERVICE=WMS&REQUEST=GetMap&LAYERS=OGWRGB13_15VL&STYLES=&FORMAT=image/jpeg&TRANSPARENT=false&VERSION=1.3.0&WIDTH=256&HEIGHT=256&CRS=EPSG:3857&BBOX=488585.4847988467,6590094.830634755,489196.9810251281,6590706.32686104
 | ||||
| 
 | ||||
|  | @ -342,16 +360,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|             return | ||||
|         } | ||||
| 
 | ||||
|         map.addSource(background.id, { | ||||
|             type: "raster", | ||||
|             // use the tiles option to specify a 256WMS tile source URL
 | ||||
|             // https://maplibre.org/maplibre-gl-js-docs/style-spec/sources/
 | ||||
|             tiles: [MapLibreAdaptor.prepareWmsURL(background.url, background["tile-size"] ?? 256)], | ||||
|             tileSize: background["tile-size"] ?? 256, | ||||
|             minzoom: background["min_zoom"] ?? 1, | ||||
|             maxzoom: background["max_zoom"] ?? 25, | ||||
|             //  scheme: background["type"] === "tms" ? "tms" : "xyz",
 | ||||
|         }) | ||||
|         map.addSource(background.id, MapLibreAdaptor.prepareWmsSource(background)) | ||||
| 
 | ||||
|         map.addLayer( | ||||
|             { | ||||
|  | @ -405,6 +414,14 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|         map.setMinZoom(minzoom) | ||||
|     } | ||||
| 
 | ||||
|     private setMaxzoom(maxzoom: number) { | ||||
|         const map = this._maplibreMap.data | ||||
|         if (map === undefined) { | ||||
|             return | ||||
|         } | ||||
|         map.setMaxZoom(maxzoom) | ||||
|     } | ||||
| 
 | ||||
|     private setAllowZooming(allow: true | boolean | undefined) { | ||||
|         const map = this._maplibreMap.data | ||||
|         if (map === undefined) { | ||||
|  |  | |||
							
								
								
									
										92
									
								
								UI/Map/ShowOverlayRasterLayer.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								UI/Map/ShowOverlayRasterLayer.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | |||
| import { RasterLayerProperties } from "../../Models/RasterLayers" | ||||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import { Map as MlMap } from "maplibre-gl" | ||||
| import { Utils } from "../../Utils" | ||||
| import { MapLibreAdaptor } from "./MapLibreAdaptor" | ||||
| 
 | ||||
| export default class ShowOverlayRasterLayer { | ||||
|     private readonly _map: UIEventSource<MlMap> | ||||
|     private readonly _layer: RasterLayerProperties | ||||
|     private readonly _mapProperties?: { zoom: Store<number> } | ||||
|     private _mllayer | ||||
|     private readonly _isDisplayed?: Store<boolean> | ||||
| 
 | ||||
|     constructor( | ||||
|         layer: RasterLayerProperties, | ||||
|         map: UIEventSource<MlMap>, | ||||
|         mapProperties?: { zoom: Store<number> }, | ||||
|         options?: { | ||||
|             isDisplayed?: Store<boolean> | ||||
|         } | ||||
|     ) { | ||||
|         this._mapProperties = mapProperties | ||||
|         this._layer = layer | ||||
|         this._map = map | ||||
|         this._isDisplayed = options?.isDisplayed | ||||
|         const self = this | ||||
|         map.addCallbackAndRunD((map) => { | ||||
|             self.addLayer() | ||||
|             map.on("load", () => { | ||||
|                 self.addLayer() | ||||
|             }) | ||||
|         }) | ||||
|         this.addLayer() | ||||
| 
 | ||||
|         options?.isDisplayed?.addCallbackAndRun(() => { | ||||
|             self.setVisibility() | ||||
|         }) | ||||
| 
 | ||||
|         mapProperties?.zoom?.addCallbackAndRun(() => { | ||||
|             self.setVisibility() | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private setVisibility() { | ||||
|         let zoom = this._mapProperties?.zoom?.data | ||||
|         let withinRange = zoom === undefined || zoom > this._layer.min_zoom | ||||
|         let isDisplayed = (this._isDisplayed?.data ?? true) && withinRange | ||||
|         this._map.data?.setLayoutProperty( | ||||
|             this._layer.id, | ||||
|             "visibility", | ||||
|             isDisplayed ? "visible" : "none" | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     private async awaitStyleIsLoaded(): Promise<void> { | ||||
|         const map = this._map.data | ||||
|         if (map === undefined) { | ||||
|             return | ||||
|         } | ||||
|         while (!map?.isStyleLoaded()) { | ||||
|             await Utils.waitFor(250) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private async addLayer() { | ||||
|         const map = this._map.data | ||||
|         console.log("Attempting to add ", this._layer.id) | ||||
|         if (map === undefined) { | ||||
|             return | ||||
|         } | ||||
|         await this.awaitStyleIsLoaded() | ||||
|         if (this._mllayer) { | ||||
|             // Already initialized
 | ||||
|             return | ||||
|         } | ||||
|         const background: RasterLayerProperties = this._layer | ||||
| 
 | ||||
|         map.addSource(background.id, MapLibreAdaptor.prepareWmsSource(background)) | ||||
|         this._mllayer = map.addLayer({ | ||||
|             id: background.id, | ||||
|             type: "raster", | ||||
|             source: background.id, | ||||
|             paint: {}, | ||||
|         }) | ||||
|         map.setLayoutProperty( | ||||
|             this._layer.id, | ||||
|             "visibility", | ||||
|             this._isDisplayed?.data ?? true ? "visible" : "none" | ||||
|         ) | ||||
|         this.setVisibility() | ||||
|     } | ||||
| } | ||||
|  | @ -75,6 +75,7 @@ export class MinimapViz implements SpecialVisualization { | |||
|         const mlmap = new UIEventSource(undefined) | ||||
|         const mla = new MapLibreAdaptor(mlmap) | ||||
| 
 | ||||
|         mla.maxzoom.setData(17) | ||||
|         let zoom = 18 | ||||
|         if (args[0]) { | ||||
|             const parsed = Number(args[0]) | ||||
|  |  | |||
|  | @ -1,43 +0,0 @@ | |||
| import * as L from "leaflet" | ||||
| import TilesourceConfig from "../../Models/ThemeConfig/TilesourceConfig" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import ShowOverlayLayer from "./ShowOverlayLayer" | ||||
| 
 | ||||
| // TODO port this to maplibre!
 | ||||
| export default class ShowOverlayLayerImplementation { | ||||
|     public static Implement() { | ||||
|         ShowOverlayLayer.implementation = ShowOverlayLayerImplementation.AddToMap | ||||
|     } | ||||
| 
 | ||||
|     public static AddToMap( | ||||
|         config: TilesourceConfig, | ||||
|         leafletMap: UIEventSource<any>, | ||||
|         isShown: UIEventSource<boolean> = undefined | ||||
|     ) { | ||||
|         leafletMap.map((leaflet) => { | ||||
|             if (leaflet === undefined) { | ||||
|                 return | ||||
|             } | ||||
| 
 | ||||
|             const tileLayer = L.tileLayer(config.source, { | ||||
|                 attribution: "", | ||||
|                 maxZoom: config.maxzoom, | ||||
|                 minZoom: config.minzoom, | ||||
|                 // @ts-ignore
 | ||||
|                 wmts: false, | ||||
|             }) | ||||
| 
 | ||||
|             if (isShown === undefined) { | ||||
|                 tileLayer.addTo(leaflet) | ||||
|             } | ||||
| 
 | ||||
|             isShown?.addCallbackAndRunD((isShown) => { | ||||
|                 if (isShown) { | ||||
|                     tileLayer.addTo(leaflet) | ||||
|                 } else { | ||||
|                     leaflet.removeLayer(tileLayer) | ||||
|                 } | ||||
|             }) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | @ -34,7 +34,7 @@ | |||
|   import Hotkeys from "./Base/Hotkeys"; | ||||
|   import { VariableUiElement } from "./Base/VariableUIElement"; | ||||
|   import SvelteUIElement from "./Base/SvelteUIElement"; | ||||
|   import { onDestroy } from "svelte"; | ||||
|   import OverlayToggle from "./BigComponents/OverlayToggle.svelte"; | ||||
| 
 | ||||
|   export let state: ThemeViewState; | ||||
|   let layout = state.layout; | ||||
|  | @ -53,7 +53,7 @@ | |||
|     } | ||||
| 
 | ||||
|     const tags = state.featureProperties.getStore(selectedElement.properties.id); | ||||
|     return new SvelteUIElement(SelectedElementView, {state, layer, selectedElement, tags}) | ||||
|     return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags }); | ||||
|   }, [selectedLayer]); | ||||
| 
 | ||||
| 
 | ||||
|  | @ -160,6 +160,14 @@ | |||
|           <Filterview zoomlevel={state.mapProperties.zoom} filteredLayer={state.layerState.filteredLayers.get(layer.id)} | ||||
|                       highlightedLayer={state.guistate.highlightedLayerInFilters}></Filterview> | ||||
|         {/each} | ||||
|         {#each layout.tileLayerSources as tilesource} | ||||
|           <OverlayToggle | ||||
|             layerproperties={tilesource} | ||||
|             state={state.overlayLayerStates.get(tilesource.id)} | ||||
|             highlightedLayer={state.guistate.highlightedLayerInFilters} | ||||
|             zoomlevel={state.mapProperties.zoom} | ||||
|              /> | ||||
|         {/each} | ||||
|         <If condition={state.featureSwitches.featureSwitchBackgroundSelection}> | ||||
|           <RasterLayerPicker {availableLayers} value={mapproperties.rasterLayer}></RasterLayerPicker> | ||||
|         </If> | ||||
|  |  | |||
|  | @ -30,10 +30,10 @@ | |||
|   "tileLayerSources": [ | ||||
|     { | ||||
|       "id": "property-boundaries", | ||||
|       "source": "https://tiles.osmuk.org/PropertyBoundaries/{z}/{x}/{y}.png", | ||||
|       "url": "https://tiles.osmuk.org/PropertyBoundaries/{z}/{x}/{y}.png", | ||||
|       "isOverlay": true, | ||||
|       "minZoom": 18, | ||||
|       "maxZoom": 20, | ||||
|       "min_zoom": 18, | ||||
|       "max_zoom": 20, | ||||
|       "defaultState": false, | ||||
|       "name": { | ||||
|         "en": "Property boundaries by osmuk.org", | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue