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)
|
const source = self._elements.get(id)
|
||||||
if (source === undefined) {
|
if (source === undefined) {
|
||||||
console.log("Adding feature store for", id)
|
|
||||||
self._elements.set(id, new UIEventSource<any>(feature.properties))
|
self._elements.set(id, new UIEventSource<any>(feature.properties))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ export interface MapProperties {
|
||||||
readonly location: UIEventSource<{ lon: number; lat: number }>
|
readonly location: UIEventSource<{ lon: number; lat: number }>
|
||||||
readonly zoom: UIEventSource<number>
|
readonly zoom: UIEventSource<number>
|
||||||
readonly minzoom: UIEventSource<number>
|
readonly minzoom: UIEventSource<number>
|
||||||
|
readonly maxzoom: UIEventSource<number>
|
||||||
readonly bounds: UIEventSource<BBox>
|
readonly bounds: UIEventSource<BBox>
|
||||||
readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined>
|
readonly rasterLayer: UIEventSource<RasterLayerPolygon | undefined>
|
||||||
readonly maxbounds: UIEventSource<undefined | BBox>
|
readonly maxbounds: UIEventSource<undefined | BBox>
|
||||||
|
|
|
@ -123,7 +123,9 @@ export interface RasterLayerProperties {
|
||||||
/**
|
/**
|
||||||
* The name of the imagery source
|
* The name of the imagery source
|
||||||
*/
|
*/
|
||||||
readonly name: string
|
readonly name: string | Record<string, string>
|
||||||
|
|
||||||
|
readonly isOverlay?: boolean
|
||||||
|
|
||||||
readonly id: string
|
readonly id: string
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { LayerConfigJson } from "./LayerConfigJson"
|
import { LayerConfigJson } from "./LayerConfigJson"
|
||||||
import TilesourceConfigJson from "./TilesourceConfigJson"
|
|
||||||
import ExtraLinkConfigJson from "./ExtraLinkConfigJson"
|
import ExtraLinkConfigJson from "./ExtraLinkConfigJson"
|
||||||
|
import { RasterLayerProperties } from "../../RasterLayers"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the entire theme.
|
* Defines the entire theme.
|
||||||
|
@ -148,7 +148,7 @@ export interface LayoutConfigJson {
|
||||||
/**
|
/**
|
||||||
* Define some (overlay) slippy map tilesources
|
* Define some (overlay) slippy map tilesources
|
||||||
*/
|
*/
|
||||||
tileLayerSources?: TilesourceConfigJson[]
|
tileLayerSources?: (RasterLayerProperties & { defaultState?: true | boolean })[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The layers to display.
|
* The layers to display.
|
||||||
|
|
|
@ -3,11 +3,12 @@ import { LayoutConfigJson } from "./Json/LayoutConfigJson"
|
||||||
import LayerConfig from "./LayerConfig"
|
import LayerConfig from "./LayerConfig"
|
||||||
import { LayerConfigJson } from "./Json/LayerConfigJson"
|
import { LayerConfigJson } from "./Json/LayerConfigJson"
|
||||||
import Constants from "../Constants"
|
import Constants from "../Constants"
|
||||||
import TilesourceConfig from "./TilesourceConfig"
|
|
||||||
import { ExtractImages } from "./Conversion/FixImages"
|
import { ExtractImages } from "./Conversion/FixImages"
|
||||||
import ExtraLinkConfig from "./ExtraLinkConfig"
|
import ExtraLinkConfig from "./ExtraLinkConfig"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import LanguageUtils from "../../Utils/LanguageUtils"
|
import LanguageUtils from "../../Utils/LanguageUtils"
|
||||||
|
import { RasterLayerProperties } from "../RasterLayers"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal information about a theme
|
* Minimal information about a theme
|
||||||
**/
|
**/
|
||||||
|
@ -39,7 +40,7 @@ export default class LayoutConfig implements LayoutInformation {
|
||||||
public widenFactor: number
|
public widenFactor: number
|
||||||
public defaultBackgroundId?: string
|
public defaultBackgroundId?: string
|
||||||
public layers: LayerConfig[]
|
public layers: LayerConfig[]
|
||||||
public tileLayerSources: TilesourceConfig[]
|
public tileLayerSources: (RasterLayerProperties & { defaultState?: true | boolean })[]
|
||||||
public readonly hideFromOverview: boolean
|
public readonly hideFromOverview: boolean
|
||||||
public lockLocation: boolean | [[number, number], [number, number]]
|
public lockLocation: boolean | [[number, number], [number, number]]
|
||||||
public readonly enableUserBadge: boolean
|
public readonly enableUserBadge: boolean
|
||||||
|
@ -161,9 +162,7 @@ export default class LayoutConfig implements LayoutInformation {
|
||||||
this.widenFactor = json.widenFactor ?? 1.5
|
this.widenFactor = json.widenFactor ?? 1.5
|
||||||
|
|
||||||
this.defaultBackgroundId = json.defaultBackgroundId
|
this.defaultBackgroundId = json.defaultBackgroundId
|
||||||
this.tileLayerSources = (json.tileLayerSources ?? []).map(
|
this.tileLayerSources = json.tileLayerSources ?? []
|
||||||
(config, i) => new TilesourceConfig(config, `${this.id}.tileLayerSources[${i}]`)
|
|
||||||
)
|
|
||||||
// At this point, layers should be expanded and validated either by the generateScript or the LegacyJsonConvert
|
// At this point, layers should be expanded and validated either by the generateScript or the LegacyJsonConvert
|
||||||
this.layers = json.layers.map(
|
this.layers = json.layers.map(
|
||||||
(lyrJson) =>
|
(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 ChangeGeometryApplicator from "../Logic/FeatureSource/Sources/ChangeGeometryApplicator"
|
||||||
import { NewGeometryFromChangesFeatureSource } from "../Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource"
|
import { NewGeometryFromChangesFeatureSource } from "../Logic/FeatureSource/Sources/NewGeometryFromChangesFeatureSource"
|
||||||
import OsmObjectDownloader from "../Logic/Osm/OsmObjectDownloader"
|
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 geolocation: GeoLocationHandler
|
||||||
|
|
||||||
readonly lastClickObject: WritableFeatureSource
|
readonly lastClickObject: WritableFeatureSource
|
||||||
|
readonly overlayLayerStates: ReadonlyMap<
|
||||||
|
string,
|
||||||
|
{ readonly isDisplayed: UIEventSource<boolean> }
|
||||||
|
>
|
||||||
|
|
||||||
constructor(layout: LayoutConfig) {
|
constructor(layout: LayoutConfig) {
|
||||||
this.layout = layout
|
this.layout = layout
|
||||||
|
@ -125,6 +130,21 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
||||||
const self = this
|
const self = this
|
||||||
this.layerState = new LayerState(this.osmConnection, layout.layers, layout.id)
|
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
|
/* 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
|
* 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 { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
import type { Map as MLMap } from "maplibre-gl"
|
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 { RasterLayerPolygon, RasterLayerProperties } from "../../Models/RasterLayers"
|
||||||
import { Utils } from "../../Utils"
|
import { Utils } from "../../Utils"
|
||||||
import { BBox } from "../../Logic/BBox"
|
import { BBox } from "../../Logic/BBox"
|
||||||
|
@ -37,6 +37,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
readonly allowZooming: UIEventSource<true | boolean | undefined>
|
readonly allowZooming: UIEventSource<true | boolean | undefined>
|
||||||
readonly lastClickLocation: Store<undefined | { lon: number; lat: number }>
|
readonly lastClickLocation: Store<undefined | { lon: number; lat: number }>
|
||||||
readonly minzoom: UIEventSource<number>
|
readonly minzoom: UIEventSource<number>
|
||||||
|
readonly maxzoom: UIEventSource<number>
|
||||||
private readonly _maplibreMap: Store<MLMap>
|
private readonly _maplibreMap: Store<MLMap>
|
||||||
/**
|
/**
|
||||||
* Used for internal bookkeeping (to remove a rasterLayer when done loading)
|
* 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.location = state?.location ?? new UIEventSource({ lon: 0, lat: 0 })
|
||||||
this.zoom = state?.zoom ?? new UIEventSource(1)
|
this.zoom = state?.zoom ?? new UIEventSource(1)
|
||||||
this.minzoom = state?.minzoom ?? new UIEventSource(0)
|
this.minzoom = state?.minzoom ?? new UIEventSource(0)
|
||||||
|
this.maxzoom = state?.maxzoom ?? new UIEventSource(24)
|
||||||
this.zoom.addCallbackAndRunD((z) => {
|
this.zoom.addCallbackAndRunD((z) => {
|
||||||
if (z < this.minzoom.data) {
|
if (z < this.minzoom.data) {
|
||||||
this.zoom.setData(this.minzoom.data)
|
this.zoom.setData(this.minzoom.data)
|
||||||
}
|
}
|
||||||
if (z > 24) {
|
const max = Math.min(24, this.maxzoom.data ?? 24)
|
||||||
this.zoom.setData(24)
|
if (z > max) {
|
||||||
|
this.zoom.setData(max)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.maxbounds = state?.maxbounds ?? new UIEventSource(undefined)
|
this.maxbounds = state?.maxbounds ?? new UIEventSource(undefined)
|
||||||
|
@ -90,6 +93,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
self.setAllowMoving(self.allowMoving.data)
|
self.setAllowMoving(self.allowMoving.data)
|
||||||
self.setAllowZooming(self.allowZooming.data)
|
self.setAllowZooming(self.allowZooming.data)
|
||||||
self.setMinzoom(self.minzoom.data)
|
self.setMinzoom(self.minzoom.data)
|
||||||
|
self.setMaxzoom(self.maxzoom.data)
|
||||||
self.setBounds(self.bounds.data)
|
self.setBounds(self.bounds.data)
|
||||||
})
|
})
|
||||||
self.MoveMapToCurrentLoc(self.location.data)
|
self.MoveMapToCurrentLoc(self.location.data)
|
||||||
|
@ -98,6 +102,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
self.setAllowMoving(self.allowMoving.data)
|
self.setAllowMoving(self.allowMoving.data)
|
||||||
self.setAllowZooming(self.allowZooming.data)
|
self.setAllowZooming(self.allowZooming.data)
|
||||||
self.setMinzoom(self.minzoom.data)
|
self.setMinzoom(self.minzoom.data)
|
||||||
|
self.setMaxzoom(self.maxzoom.data)
|
||||||
self.setBounds(self.bounds.data)
|
self.setBounds(self.bounds.data)
|
||||||
this.updateStores()
|
this.updateStores()
|
||||||
map.on("moveend", () => 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
|
* 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
|
// 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
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
map.addSource(background.id, {
|
map.addSource(background.id, MapLibreAdaptor.prepareWmsSource(background))
|
||||||
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.addLayer(
|
map.addLayer(
|
||||||
{
|
{
|
||||||
|
@ -405,6 +414,14 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
map.setMinZoom(minzoom)
|
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) {
|
private setAllowZooming(allow: true | boolean | undefined) {
|
||||||
const map = this._maplibreMap.data
|
const map = this._maplibreMap.data
|
||||||
if (map === undefined) {
|
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 mlmap = new UIEventSource(undefined)
|
||||||
const mla = new MapLibreAdaptor(mlmap)
|
const mla = new MapLibreAdaptor(mlmap)
|
||||||
|
|
||||||
|
mla.maxzoom.setData(17)
|
||||||
let zoom = 18
|
let zoom = 18
|
||||||
if (args[0]) {
|
if (args[0]) {
|
||||||
const parsed = Number(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 Hotkeys from "./Base/Hotkeys";
|
||||||
import { VariableUiElement } from "./Base/VariableUIElement";
|
import { VariableUiElement } from "./Base/VariableUIElement";
|
||||||
import SvelteUIElement from "./Base/SvelteUIElement";
|
import SvelteUIElement from "./Base/SvelteUIElement";
|
||||||
import { onDestroy } from "svelte";
|
import OverlayToggle from "./BigComponents/OverlayToggle.svelte";
|
||||||
|
|
||||||
export let state: ThemeViewState;
|
export let state: ThemeViewState;
|
||||||
let layout = state.layout;
|
let layout = state.layout;
|
||||||
|
@ -51,9 +51,9 @@
|
||||||
if (selectedElement === undefined || layer === undefined) {
|
if (selectedElement === undefined || layer === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = state.featureProperties.getStore(selectedElement.properties.id);
|
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]);
|
}, [selectedLayer]);
|
||||||
|
|
||||||
|
|
||||||
|
@ -160,6 +160,14 @@
|
||||||
<Filterview zoomlevel={state.mapProperties.zoom} filteredLayer={state.layerState.filteredLayers.get(layer.id)}
|
<Filterview zoomlevel={state.mapProperties.zoom} filteredLayer={state.layerState.filteredLayers.get(layer.id)}
|
||||||
highlightedLayer={state.guistate.highlightedLayerInFilters}></Filterview>
|
highlightedLayer={state.guistate.highlightedLayerInFilters}></Filterview>
|
||||||
{/each}
|
{/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}>
|
<If condition={state.featureSwitches.featureSwitchBackgroundSelection}>
|
||||||
<RasterLayerPicker {availableLayers} value={mapproperties.rasterLayer}></RasterLayerPicker>
|
<RasterLayerPicker {availableLayers} value={mapproperties.rasterLayer}></RasterLayerPicker>
|
||||||
</If>
|
</If>
|
||||||
|
|
|
@ -30,10 +30,10 @@
|
||||||
"tileLayerSources": [
|
"tileLayerSources": [
|
||||||
{
|
{
|
||||||
"id": "property-boundaries",
|
"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,
|
"isOverlay": true,
|
||||||
"minZoom": 18,
|
"min_zoom": 18,
|
||||||
"maxZoom": 20,
|
"max_zoom": 20,
|
||||||
"defaultState": false,
|
"defaultState": false,
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Property boundaries by osmuk.org",
|
"en": "Property boundaries by osmuk.org",
|
||||||
|
@ -695,4 +695,4 @@
|
||||||
"enableShareScreen": false,
|
"enableShareScreen": false,
|
||||||
"enableMoreQuests": false,
|
"enableMoreQuests": false,
|
||||||
"credits": "Pieter Vander Vennet, Rob Nickerson, Russ Garrett"
|
"credits": "Pieter Vander Vennet, Rob Nickerson, Russ Garrett"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue