Feature: smooth switching of raster layers, fix #1765
This commit is contained in:
parent
8612d33724
commit
129f5c3d1f
2 changed files with 222 additions and 164 deletions
|
@ -9,6 +9,7 @@ import SvelteUIElement from "../Base/SvelteUIElement"
|
||||||
import MaplibreMap from "./MaplibreMap.svelte"
|
import MaplibreMap from "./MaplibreMap.svelte"
|
||||||
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
|
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
|
||||||
import * as htmltoimage from "html-to-image"
|
import * as htmltoimage from "html-to-image"
|
||||||
|
import RasterLayerHandler from "./RasterLayerHandler"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`
|
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`
|
||||||
|
@ -41,7 +42,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
readonly minzoom: UIEventSource<number>
|
readonly minzoom: UIEventSource<number>
|
||||||
readonly maxzoom: UIEventSource<number>
|
readonly maxzoom: UIEventSource<number>
|
||||||
readonly rotation: UIEventSource<number>
|
readonly rotation: UIEventSource<number>
|
||||||
readonly animationRunning = new UIEventSource(false)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functions that are called when one of those actions has happened
|
* Functions that are called when one of those actions has happened
|
||||||
|
@ -50,11 +50,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
private _onKeyNavigation: ((event: KeyNavigationEvent) => void | boolean)[] = []
|
private _onKeyNavigation: ((event: KeyNavigationEvent) => void | boolean)[] = []
|
||||||
|
|
||||||
private readonly _maplibreMap: Store<MLMap>
|
private readonly _maplibreMap: Store<MLMap>
|
||||||
/**
|
|
||||||
* Used for internal bookkeeping (to remove a rasterLayer when done loading)
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private _currentRasterLayer: string
|
|
||||||
|
|
||||||
constructor(maplibreMap: Store<MLMap>, state?: Partial<MapProperties>) {
|
constructor(maplibreMap: Store<MLMap>, state?: Partial<MapProperties>) {
|
||||||
this._maplibreMap = maplibreMap
|
this._maplibreMap = maplibreMap
|
||||||
|
@ -90,6 +85,8 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
this.lastClickLocation = lastClickLocation
|
this.lastClickLocation = lastClickLocation
|
||||||
const self = this
|
const self = this
|
||||||
|
|
||||||
|
const rasterLayerHandler = new RasterLayerHandler(this._maplibreMap, this.rasterLayer)
|
||||||
|
|
||||||
function handleClick(e) {
|
function handleClick(e) {
|
||||||
if (e.originalEvent["consumed"]) {
|
if (e.originalEvent["consumed"]) {
|
||||||
// Workaround, 'ShowPointLayer' sets this flag
|
// Workaround, 'ShowPointLayer' sets this flag
|
||||||
|
@ -111,7 +108,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
self.setMinzoom(self.minzoom.data)
|
self.setMinzoom(self.minzoom.data)
|
||||||
self.setMaxzoom(self.maxzoom.data)
|
self.setMaxzoom(self.maxzoom.data)
|
||||||
self.setBounds(self.bounds.data)
|
self.setBounds(self.bounds.data)
|
||||||
self.setBackground()
|
rasterLayerHandler.setBackground()
|
||||||
this.updateStores(true)
|
this.updateStores(true)
|
||||||
})
|
})
|
||||||
self.MoveMapToCurrentLoc(self.location.data)
|
self.MoveMapToCurrentLoc(self.location.data)
|
||||||
|
@ -124,7 +121,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
self.setMaxzoom(self.maxzoom.data)
|
self.setMaxzoom(self.maxzoom.data)
|
||||||
self.setBounds(self.bounds.data)
|
self.setBounds(self.bounds.data)
|
||||||
self.SetRotation(self.rotation.data)
|
self.SetRotation(self.rotation.data)
|
||||||
self.setBackground()
|
rasterLayerHandler.setBackground()
|
||||||
this.updateStores(true)
|
this.updateStores(true)
|
||||||
map.on("moveend", () => this.updateStores())
|
map.on("moveend", () => this.updateStores())
|
||||||
map.on("click", (e) => {
|
map.on("click", (e) => {
|
||||||
|
@ -170,11 +167,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
this.rasterLayer.addCallbackAndRun((_) =>
|
this.rasterLayer.addCallbackAndRun((_) => rasterLayerHandler.setBackground())
|
||||||
self.setBackground().catch((_) => {
|
|
||||||
console.error("Could not set background")
|
|
||||||
})
|
|
||||||
)
|
|
||||||
this.location.addCallbackAndRunD((loc) => {
|
this.location.addCallbackAndRunD((loc) => {
|
||||||
self.MoveMapToCurrentLoc(loc)
|
self.MoveMapToCurrentLoc(loc)
|
||||||
})
|
})
|
||||||
|
@ -211,48 +204,12 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static prepareWmsSource(layer: RasterLayerProperties): SourceSpecification {
|
public static prepareWmsSource(layer: RasterLayerProperties): SourceSpecification {
|
||||||
return {
|
return RasterLayerHandler.prepareWmsSource(layer)
|
||||||
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,
|
|
||||||
// Bit of a hack, but seems to work
|
|
||||||
scheme: layer.url.includes("{-y}") ? "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): 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
|
|
||||||
|
|
||||||
const toReplace = {
|
|
||||||
"{bbox}": "{bbox-epsg-3857}",
|
|
||||||
"{proj}": "EPSG:3857",
|
|
||||||
"{width}": "" + size,
|
|
||||||
"{height}": "" + size,
|
|
||||||
"{zoom}": "{z}",
|
|
||||||
"{-y}": "{y}",
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key in toReplace) {
|
|
||||||
url = url.replace(new RegExp(key), toReplace[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
const subdomains = url.match(/\{switch:([a-zA-Z0-9,]*)}/)
|
|
||||||
if (subdomains !== null) {
|
|
||||||
const options = subdomains[1].split(",")
|
|
||||||
const option = options[Math.floor(Math.random() * options.length)]
|
|
||||||
url = url.replace(subdomains[0], option)
|
|
||||||
}
|
|
||||||
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async toBlob(canvas: HTMLCanvasElement): Promise<Blob> {
|
private static async toBlob(canvas: HTMLCanvasElement): Promise<Blob> {
|
||||||
return await new Promise<Blob>((resolve) => canvas.toBlob((blob) => resolve(blob)))
|
return await new Promise<Blob>((resolve) => canvas.toBlob((blob) => resolve(blob)))
|
||||||
|
@ -502,15 +459,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async awaitStyleIsLoaded(): Promise<void> {
|
|
||||||
const map = this._maplibreMap.data
|
|
||||||
if (!map) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
while (!map?.isStyleLoaded()) {
|
|
||||||
await Utils.waitFor(250)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public installCustomKeyboardHandler(viewportStore: UIEventSource<HTMLDivElement>) {
|
public installCustomKeyboardHandler(viewportStore: UIEventSource<HTMLDivElement>) {
|
||||||
viewportStore.mapD(
|
viewportStore.mapD(
|
||||||
(viewport) => {
|
(viewport) => {
|
||||||
|
@ -534,111 +482,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
|
||||||
[this._maplibreMap]
|
[this._maplibreMap]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
private removeCurrentLayer(map: MLMap): void {
|
|
||||||
if (this._currentRasterLayer) {
|
|
||||||
// hide the previous layer
|
|
||||||
try {
|
|
||||||
if (map.getLayer(this._currentRasterLayer)) {
|
|
||||||
map.removeLayer(this._currentRasterLayer)
|
|
||||||
}
|
|
||||||
if (map.getSource(this._currentRasterLayer)) {
|
|
||||||
map.removeSource(this._currentRasterLayer)
|
|
||||||
}
|
|
||||||
this._currentRasterLayer = undefined
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Could not remove the previous layer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setBackground(retryAttempts: number = 3): Promise<void> {
|
|
||||||
const map = this._maplibreMap.data
|
|
||||||
if (!map) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const background: RasterLayerProperties = this.rasterLayer?.data?.properties
|
|
||||||
if (!background) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this._currentRasterLayer === background.id) {
|
|
||||||
// already the correct background layer, nothing to do
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!background?.url) {
|
|
||||||
// no background to set
|
|
||||||
this.removeCurrentLayer(map)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (background.type === "vector") {
|
|
||||||
this.removeCurrentLayer(map)
|
|
||||||
map.setStyle(background.url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let addLayerBeforeId = "aeroway_fill" // this is the first non-landuse item in the stylesheet, we add the raster layer before the roads but above the landuse
|
|
||||||
if (background.category === "osmbasedmap" || background.category === "map") {
|
|
||||||
// The background layer is already an OSM-based map or another map, so we don't want anything from the baselayer
|
|
||||||
addLayerBeforeId = undefined
|
|
||||||
this.removeCurrentLayer(map)
|
|
||||||
} else {
|
|
||||||
// Make sure that the default maptiler style is loaded as it gives an overlay with roads
|
|
||||||
const maptiler = AvailableRasterLayers.maptilerDefaultLayer.properties
|
|
||||||
try {
|
|
||||||
|
|
||||||
await this.awaitStyleIsLoaded()
|
|
||||||
if (!map.getSource(maptiler.id)) {
|
|
||||||
this.removeCurrentLayer(map)
|
|
||||||
map.addSource(maptiler.id, MapLibreAdaptor.prepareWmsSource(maptiler))
|
|
||||||
map.setStyle(maptiler.url)
|
|
||||||
await this.awaitStyleIsLoaded()
|
|
||||||
}
|
|
||||||
}catch (e) {
|
|
||||||
if(retryAttempts > 0){
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
console.log("Retrying to set the background ("+retryAttempts+" attempts remaining)... Failed because",e)
|
|
||||||
this.setBackground(retryAttempts-1)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!map.getLayer(addLayerBeforeId)) {
|
|
||||||
addLayerBeforeId = undefined
|
|
||||||
}
|
|
||||||
await this.awaitStyleIsLoaded()
|
|
||||||
if (!map.getSource(background.id)) {
|
|
||||||
map.addSource(background.id, MapLibreAdaptor.prepareWmsSource(background))
|
|
||||||
}
|
|
||||||
if (!map.getLayer(background.id)) {
|
|
||||||
addLayerBeforeId ??= map
|
|
||||||
.getStyle()
|
|
||||||
.layers.find((l) => l.id.startsWith("mapcomplete_"))?.id
|
|
||||||
console.log(
|
|
||||||
"Adding background layer",
|
|
||||||
background.id,
|
|
||||||
"beforeId",
|
|
||||||
addLayerBeforeId,
|
|
||||||
"; all layers are",
|
|
||||||
map.getStyle().layers.map((l) => l.id)
|
|
||||||
)
|
|
||||||
map.addLayer(
|
|
||||||
{
|
|
||||||
id: background.id,
|
|
||||||
type: "raster",
|
|
||||||
source: background.id,
|
|
||||||
paint: {},
|
|
||||||
},
|
|
||||||
addLayerBeforeId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
await this.awaitStyleIsLoaded()
|
|
||||||
if (this._currentRasterLayer !== background?.id) {
|
|
||||||
this.removeCurrentLayer(map)
|
|
||||||
}
|
|
||||||
this._currentRasterLayer = background?.id
|
|
||||||
}
|
|
||||||
|
|
||||||
private setMaxBounds(bbox: undefined | BBox) {
|
private setMaxBounds(bbox: undefined | BBox) {
|
||||||
const map = this._maplibreMap.data
|
const map = this._maplibreMap.data
|
||||||
|
|
215
src/UI/Map/RasterLayerHandler.ts
Normal file
215
src/UI/Map/RasterLayerHandler.ts
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
import { Map as MLMap, SourceSpecification } from "maplibre-gl"
|
||||||
|
import { Store, Stores, UIEventSource } from "../../Logic/UIEventSource"
|
||||||
|
import { RasterLayerPolygon } from "../../Models/RasterLayers"
|
||||||
|
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
|
||||||
|
import { Utils } from "../../Utils"
|
||||||
|
|
||||||
|
class SingleBackgroundHandler {
|
||||||
|
// Value between 0 and 1.0
|
||||||
|
public opacity = new UIEventSource<number>(0.0)
|
||||||
|
private _map: Store<MLMap>
|
||||||
|
private _background: UIEventSource<RasterLayerPolygon | undefined>
|
||||||
|
private readonly _targetLayer: RasterLayerPolygon
|
||||||
|
private _deactivationTime: Date = undefined
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate a layer after 60 seconds
|
||||||
|
*/
|
||||||
|
public static readonly DEACTIVATE_AFTER = 60
|
||||||
|
private fadeStep = 0.1
|
||||||
|
constructor(
|
||||||
|
map: Store<MLMap>,
|
||||||
|
targetLayer: RasterLayerPolygon,
|
||||||
|
background: UIEventSource<RasterLayerPolygon | undefined>
|
||||||
|
) {
|
||||||
|
this._targetLayer = targetLayer
|
||||||
|
this._map = map
|
||||||
|
this._background = background
|
||||||
|
|
||||||
|
background.addCallback(async () => {
|
||||||
|
await this.update()
|
||||||
|
})
|
||||||
|
map.addCallbackAndRunD(async (map) => {
|
||||||
|
map.on("load", async () => {
|
||||||
|
await this.update()
|
||||||
|
})
|
||||||
|
await this.update()
|
||||||
|
map.on("moveend", () => this.onMove(map))
|
||||||
|
map.on("zoomend", () => this.onMove(map))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private onMove(map: MLMap) {
|
||||||
|
if (!this._deactivationTime) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// in seconds
|
||||||
|
const timeSinceDeactivation =
|
||||||
|
(new Date().getTime() - this._deactivationTime.getTime()) / 1000
|
||||||
|
|
||||||
|
if (timeSinceDeactivation < SingleBackgroundHandler.DEACTIVATE_AFTER) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug(
|
||||||
|
"Removing raster layer",
|
||||||
|
this._targetLayer.properties.id,
|
||||||
|
"map moved and not been used for",
|
||||||
|
SingleBackgroundHandler.DEACTIVATE_AFTER
|
||||||
|
)
|
||||||
|
if (map.getLayer(<string>this._targetLayer.properties.id)) {
|
||||||
|
map.removeLayer(<string>this._targetLayer.properties.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async update() {
|
||||||
|
const newTarget: RasterLayerPolygon | undefined = this._background.data
|
||||||
|
const targetLayer = this._targetLayer
|
||||||
|
if (newTarget?.properties?.id !== targetLayer.properties.id) {
|
||||||
|
this._deactivationTime = new Date()
|
||||||
|
await this.awaitStyleIsLoaded()
|
||||||
|
this.fadeOut()
|
||||||
|
} else {
|
||||||
|
this._deactivationTime = undefined
|
||||||
|
this.enable()
|
||||||
|
this.fadeIn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private async awaitStyleIsLoaded(): Promise<void> {
|
||||||
|
const map = this._map.data
|
||||||
|
if (!map) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
while (!map?.isStyleLoaded()) {
|
||||||
|
await Utils.waitFor(250)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enable() {
|
||||||
|
const map: MLMap = this._map.data
|
||||||
|
if (!map) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const background = this._targetLayer.properties
|
||||||
|
console.debug("Enabling", background.id)
|
||||||
|
let addLayerBeforeId = "aeroway_fill" // this is the first non-landuse item in the stylesheet, we add the raster layer before the roads but above the landuse
|
||||||
|
if (background.category === "osmbasedmap" || background.category === "map") {
|
||||||
|
// The background layer is already an OSM-based map or another map, so we don't want anything from the baselayer
|
||||||
|
addLayerBeforeId = undefined
|
||||||
|
}
|
||||||
|
if (!map.getSource(background.id)) {
|
||||||
|
map.addSource(background.id, RasterLayerHandler.prepareWmsSource(background))
|
||||||
|
}
|
||||||
|
if (!map.getLayer(background.id)) {
|
||||||
|
addLayerBeforeId ??= map
|
||||||
|
.getStyle()
|
||||||
|
.layers.find((l) => l.id.startsWith("mapcomplete_"))?.id
|
||||||
|
|
||||||
|
map.addLayer(
|
||||||
|
{
|
||||||
|
id: background.id,
|
||||||
|
type: "raster",
|
||||||
|
source: background.id,
|
||||||
|
paint: {
|
||||||
|
"raster-opacity": 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
addLayerBeforeId
|
||||||
|
)
|
||||||
|
|
||||||
|
this.opacity.addCallbackAndRun((o) => {
|
||||||
|
map.setPaintProperty(background.id, "raster-opacity", o)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fadeOut() {
|
||||||
|
Stores.Chronic(
|
||||||
|
8,
|
||||||
|
() => this.opacity.data > 0 && this._deactivationTime !== undefined
|
||||||
|
).addCallback((_) => this.opacity.setData(Math.max(0, this.opacity.data - this.fadeStep)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fadeIn() {
|
||||||
|
Stores.Chronic(
|
||||||
|
8,
|
||||||
|
() => this.opacity.data < 1.0 && this._deactivationTime === undefined
|
||||||
|
).addCallback((_) => this.opacity.setData(Math.min(1.0, this.opacity.data + this.fadeStep)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class RasterLayerHandler {
|
||||||
|
private _map: Store<MLMap>
|
||||||
|
private _background: UIEventSource<RasterLayerPolygon | undefined>
|
||||||
|
private _singleLayerHandlers: Record<string, SingleBackgroundHandler> = {}
|
||||||
|
|
||||||
|
constructor(map: Store<MLMap>, background: UIEventSource<RasterLayerPolygon | undefined>) {
|
||||||
|
this._map = map
|
||||||
|
this._background = background
|
||||||
|
background.addCallbackAndRunD((l) => {
|
||||||
|
const key = l.properties.id
|
||||||
|
if (!this._singleLayerHandlers[key]) {
|
||||||
|
this._singleLayerHandlers[key] = new SingleBackgroundHandler(map, l, background)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
map.addCallback((map) => {
|
||||||
|
map.on("load", () => this.setBackground())
|
||||||
|
this.setBackground()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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: [RasterLayerHandler.prepareWmsURL(layer.url, layer["tile-size"] ?? 256)],
|
||||||
|
tileSize: layer["tile-size"] ?? 256,
|
||||||
|
minzoom: layer["min_zoom"] ?? 1,
|
||||||
|
maxzoom: layer["max_zoom"] ?? 25,
|
||||||
|
// Bit of a hack, but seems to work
|
||||||
|
scheme: layer.url.includes("{-y}") ? "tms" : "xyz",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
const toReplace = {
|
||||||
|
"{bbox}": "{bbox-epsg-3857}",
|
||||||
|
"{proj}": "EPSG:3857",
|
||||||
|
"{width}": "" + size,
|
||||||
|
"{height}": "" + size,
|
||||||
|
"{zoom}": "{z}",
|
||||||
|
"{-y}": "{y}",
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in toReplace) {
|
||||||
|
url = url.replace(new RegExp(key), toReplace[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
const subdomains = url.match(/\{switch:([a-zA-Z0-9,]*)}/)
|
||||||
|
if (subdomains !== null) {
|
||||||
|
const options = subdomains[1].split(",")
|
||||||
|
const option = options[Math.floor(Math.random() * options.length)]
|
||||||
|
url = url.replace(subdomains[0], option)
|
||||||
|
}
|
||||||
|
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs all necessary updates
|
||||||
|
*/
|
||||||
|
public setBackground() {
|
||||||
|
this.update().catch((e) => console.error(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async update() {
|
||||||
|
const map = this._map.data
|
||||||
|
if (!map) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue