2023-10-16 14:27:05 +02:00
import { Store , UIEventSource } from "../../Logic/UIEventSource"
import type { Map as MLMap } from "maplibre-gl"
import { Map as MlMap , SourceSpecification } from "maplibre-gl"
import { AvailableRasterLayers , RasterLayerPolygon } from "../../Models/RasterLayers"
import { Utils } from "../../Utils"
import { BBox } from "../../Logic/BBox"
import { ExportableMap , MapProperties } from "../../Models/MapProperties"
import SvelteUIElement from "../Base/SvelteUIElement"
import MaplibreMap from "./MaplibreMap.svelte"
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
import * as htmltoimage from "html-to-image"
2023-11-14 16:14:27 +01:00
import { draw } from "svelte/transition"
2023-03-23 00:58:21 +01:00
2023-03-24 19:21:15 +01:00
/ * *
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the ` MapProperties `
* /
2023-04-19 03:20:49 +02:00
export class MapLibreAdaptor implements MapProperties , ExportableMap {
2023-03-24 19:21:15 +01:00
private static maplibre_control_handlers = [
2023-03-25 02:48:24 +01:00
// "scrollZoom",
// "boxZoom",
// "doubleClickZoom",
2023-03-24 19:21:15 +01:00
"dragRotate" ,
"dragPan" ,
"keyboard" ,
"touchZoomRotate" ,
]
2023-03-28 05:13:48 +02:00
private static maplibre_zoom_handlers = [
"scrollZoom" ,
"boxZoom" ,
"doubleClickZoom" ,
"touchZoomRotate" ,
]
2023-03-23 00:58:21 +01:00
readonly location : UIEventSource < { lon : number ; lat : number } >
readonly zoom : UIEventSource < number >
2023-03-28 05:13:48 +02:00
readonly bounds : UIEventSource < BBox >
2023-03-23 00:58:21 +01:00
readonly rasterLayer : UIEventSource < RasterLayerPolygon | undefined >
2023-03-24 19:21:15 +01:00
readonly maxbounds : UIEventSource < BBox | undefined >
readonly allowMoving : UIEventSource < true | boolean | undefined >
2023-07-18 01:26:04 +02:00
readonly allowRotating : UIEventSource < true | boolean | undefined >
2023-03-28 05:13:48 +02:00
readonly allowZooming : UIEventSource < true | boolean | undefined >
readonly lastClickLocation : Store < undefined | { lon : number ; lat : number } >
2023-04-06 01:33:08 +02:00
readonly minzoom : UIEventSource < number >
2023-04-21 01:53:24 +02:00
readonly maxzoom : UIEventSource < number >
2023-03-11 02:37:07 +01:00
private readonly _maplibreMap : Store < MLMap >
2023-03-23 00:58:21 +01:00
/ * *
* Used for internal bookkeeping ( to remove a rasterLayer when done loading )
* @private
* /
private _currentRasterLayer : string
2023-03-24 19:21:15 +01:00
2023-03-29 17:21:20 +02:00
constructor ( maplibreMap : Store < MLMap > , state? : Partial < MapProperties > ) {
2023-03-11 02:37:07 +01:00
this . _maplibreMap = maplibreMap
2023-06-07 22:46:41 +02:00
this . location = state ? . location ? ? new UIEventSource ( undefined )
2023-06-04 00:43:32 +02:00
if ( this . location . data ) {
2023-05-19 10:56:30 +02:00
// The MapLibre adaptor updates the element in the location and then pings them
// Often, code setting this up doesn't expect the object they pass in to be changed, so we create a copy
2023-06-14 20:39:36 +02:00
this . location . setData ( { . . . this . location . data } )
2023-05-19 10:56:30 +02:00
}
2023-03-23 00:58:21 +01:00
this . zoom = state ? . zoom ? ? new UIEventSource ( 1 )
2023-04-06 01:33:08 +02:00
this . minzoom = state ? . minzoom ? ? new UIEventSource ( 0 )
2023-04-21 01:53:24 +02:00
this . maxzoom = state ? . maxzoom ? ? new UIEventSource ( 24 )
2023-03-24 19:21:15 +01:00
this . zoom . addCallbackAndRunD ( ( z ) = > {
2023-04-06 01:33:08 +02:00
if ( z < this . minzoom . data ) {
this . zoom . setData ( this . minzoom . data )
2023-03-24 19:21:15 +01:00
}
2023-04-21 01:53:24 +02:00
const max = Math . min ( 24 , this . maxzoom . data ? ? 24 )
if ( z > max ) {
this . zoom . setData ( max )
2023-03-24 19:21:15 +01:00
}
} )
this . maxbounds = state ? . maxbounds ? ? new UIEventSource ( undefined )
this . allowMoving = state ? . allowMoving ? ? new UIEventSource ( true )
2023-07-18 01:26:04 +02:00
this . allowRotating = state ? . allowRotating ? ? new UIEventSource < boolean > ( true )
2023-03-28 05:13:48 +02:00
this . allowZooming = state ? . allowZooming ? ? new UIEventSource ( true )
2023-04-06 01:33:08 +02:00
this . bounds = state ? . bounds ? ? new UIEventSource ( undefined )
2023-03-23 00:58:21 +01:00
this . rasterLayer =
state ? . rasterLayer ? ? new UIEventSource < RasterLayerPolygon | undefined > ( undefined )
2023-03-11 02:37:07 +01:00
2023-03-28 05:13:48 +02:00
const lastClickLocation = new UIEventSource < { lon : number ; lat : number } > ( undefined )
this . lastClickLocation = lastClickLocation
2023-03-23 00:58:21 +01:00
const self = this
2023-04-16 03:42:26 +02:00
function handleClick ( e ) {
if ( e . originalEvent [ "consumed" ] ) {
// Workaround, 'ShowPointLayer' sets this flag
return
}
const lon = e . lngLat . lng
const lat = e . lngLat . lat
2023-06-14 20:39:36 +02:00
lastClickLocation . setData ( { lon , lat } )
2023-04-16 03:42:26 +02:00
}
2023-03-11 02:37:07 +01:00
maplibreMap . addCallbackAndRunD ( ( map ) = > {
map . on ( "load" , ( ) = > {
2023-09-24 16:34:36 +02:00
map . resize ( )
2023-03-24 19:21:15 +01:00
self . MoveMapToCurrentLoc ( self . location . data )
self . SetZoom ( self . zoom . data )
self . setMaxBounds ( self . maxbounds . data )
self . setAllowMoving ( self . allowMoving . data )
2023-07-18 01:26:04 +02:00
self . setAllowRotating ( self . allowRotating . data )
2023-03-28 05:13:48 +02:00
self . setAllowZooming ( self . allowZooming . data )
2023-04-06 01:33:08 +02:00
self . setMinzoom ( self . minzoom . data )
2023-04-21 01:53:24 +02:00
self . setMaxzoom ( self . maxzoom . data )
2023-04-20 01:52:23 +02:00
self . setBounds ( self . bounds . data )
2023-09-24 16:34:36 +02:00
self . setBackground ( )
2023-05-18 23:42:03 +02:00
this . updateStores ( true )
2023-03-11 02:37:07 +01:00
} )
2023-09-24 16:34:36 +02:00
map . resize ( )
2023-03-24 19:21:15 +01:00
self . MoveMapToCurrentLoc ( self . location . data )
self . SetZoom ( self . zoom . data )
self . setMaxBounds ( self . maxbounds . data )
self . setAllowMoving ( self . allowMoving . data )
2023-07-18 01:26:04 +02:00
self . setAllowRotating ( self . allowRotating . data )
2023-03-28 05:13:48 +02:00
self . setAllowZooming ( self . allowZooming . data )
2023-04-06 01:33:08 +02:00
self . setMinzoom ( self . minzoom . data )
2023-04-21 01:53:24 +02:00
self . setMaxzoom ( self . maxzoom . data )
2023-04-20 01:52:23 +02:00
self . setBounds ( self . bounds . data )
2023-09-24 16:34:36 +02:00
self . setBackground ( )
2023-05-18 23:42:03 +02:00
this . updateStores ( true )
2023-04-06 01:33:08 +02:00
map . on ( "moveend" , ( ) = > this . updateStores ( ) )
2023-03-28 05:13:48 +02:00
map . on ( "click" , ( e ) = > {
2023-04-16 03:42:26 +02:00
handleClick ( e )
} )
map . on ( "contextmenu" , ( e ) = > {
handleClick ( e )
} )
map . on ( "dblclick" , ( e ) = > {
handleClick ( e )
2023-03-28 05:13:48 +02:00
} )
2023-03-11 02:37:07 +01:00
} )
2023-09-24 16:34:36 +02:00
this . rasterLayer . addCallbackAndRun ( ( _ ) = >
2023-03-24 19:21:15 +01:00
self . setBackground ( ) . catch ( ( _ ) = > {
2023-03-23 00:58:21 +01:00
console . error ( "Could not set background" )
} )
)
this . location . addCallbackAndRunD ( ( loc ) = > {
2023-03-11 02:37:07 +01:00
self . MoveMapToCurrentLoc ( loc )
} )
2023-03-23 00:58:21 +01:00
this . zoom . addCallbackAndRunD ( ( z ) = > self . SetZoom ( z ) )
2023-03-24 19:21:15 +01:00
this . maxbounds . addCallbackAndRun ( ( bbox ) = > self . setMaxBounds ( bbox ) )
this . allowMoving . addCallbackAndRun ( ( allowMoving ) = > self . setAllowMoving ( allowMoving ) )
2023-07-18 01:26:04 +02:00
this . allowRotating . addCallbackAndRunD ( ( allowRotating ) = >
self . setAllowRotating ( allowRotating )
)
2023-03-28 05:13:48 +02:00
this . allowZooming . addCallbackAndRun ( ( allowZooming ) = > self . setAllowZooming ( allowZooming ) )
this . bounds . addCallbackAndRunD ( ( bounds ) = > self . setBounds ( bounds ) )
2023-03-11 02:37:07 +01:00
}
2023-03-23 00:58:21 +01:00
2023-03-24 19:21:15 +01:00
/ * *
* Convenience constructor
* /
public static construct ( ) : {
map : Store < MLMap >
ui : SvelteUIElement
mapproperties : MapProperties
} {
const mlmap = new UIEventSource < MlMap > ( undefined )
return {
map : mlmap ,
ui : new SvelteUIElement ( MaplibreMap , {
map : mlmap ,
} ) ,
mapproperties : new MapLibreAdaptor ( mlmap ) ,
2023-03-11 02:37:07 +01:00
}
}
2023-04-21 01:53:24 +02:00
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 ,
2023-09-02 23:23:46 +02:00
// Bit of a hack, but seems to work
scheme : layer.url.includes ( "{-y}" ) ? "tms" : "xyz" ,
2023-04-21 01:53:24 +02:00
}
}
2023-03-11 02:37:07 +01:00
/ * *
* Prepares an ELI - URL to be compatible with mapbox
* /
2023-04-21 01:53:24 +02:00
private static prepareWmsURL ( url : string , size : number = 256 ) : string {
2023-03-11 02:37:07 +01:00
// 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}" ,
2023-09-02 23:23:46 +02:00
"{-y}" : "{y}" ,
2023-03-11 02:37:07 +01:00
}
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
}
2023-11-14 16:14:27 +01:00
private static async toBlob ( canvas : HTMLCanvasElement ) : Promise < Blob > {
return await new Promise < Blob > ( ( resolve ) = > canvas . toBlob ( ( blob ) = > resolve ( blob ) ) )
}
private static async createImage ( url : string ) : Promise < HTMLImageElement > {
return new Promise ( ( resolve , reject ) = > {
const img = new Image ( )
img . decode = ( ) = > resolve ( img ) as any
img . onload = ( ) = > resolve ( img )
img . onerror = reject
img . crossOrigin = "anonymous"
img . decoding = "async"
img . src = url
} )
}
public async exportAsPng (
rescaleIcons : number = 1 ,
progress : UIEventSource < { current : number ; total : number } > = undefined
) : Promise < Blob > {
2023-04-19 03:20:49 +02:00
const map = this . _maplibreMap . data
2023-05-05 02:03:41 +02:00
if ( ! map ) {
2023-04-19 03:20:49 +02:00
return undefined
}
2023-11-14 16:14:27 +01:00
const drawOn = document . createElement ( "canvas" , { } )
2023-06-04 00:43:32 +02:00
const ctx = drawOn . getContext ( "2d" )
2023-11-14 16:14:27 +01:00
// The width/height has been set in 'mm' on the parent element and converted to pixels by the browser
const w = map . getContainer ( ) . getBoundingClientRect ( ) . width
const h = map . getContainer ( ) . getBoundingClientRect ( ) . height
2023-04-19 03:20:49 +02:00
2023-11-14 16:14:27 +01:00
let dpi = map . getPixelRatio ( )
console . log ( "Sizes:" , {
dpi ,
w ,
h ,
origSizeW : drawOn.style.width ,
origSizeH : drawOn.style.height ,
} )
// The 'css'-size stays constant...
drawOn . style . width = w + "px"
drawOn . style . height = h + "px"
2023-04-19 03:20:49 +02:00
2023-11-14 16:14:27 +01:00
// ...but the number of pixels is increased
drawOn . width = Math . ceil ( w * dpi )
drawOn . height = Math . ceil ( h * dpi )
2023-11-13 15:14:41 +01:00
2023-11-14 16:14:27 +01:00
await this . exportBackgroundOnCanvas ( ctx )
await this . drawMarkers ( ctx , rescaleIcons , progress )
return await MapLibreAdaptor . toBlob ( drawOn )
2023-06-04 00:43:32 +02:00
}
2023-04-19 03:20:49 +02:00
2023-06-04 00:43:32 +02:00
/ * *
* Exports the background map and lines to PNG .
* Markers are _not_ rendered
* /
2023-06-04 22:52:13 +02:00
private async exportBackgroundOnCanvas ( ctx : CanvasRenderingContext2D ) : Promise < void > {
2023-06-04 00:43:32 +02:00
const map = this . _maplibreMap . data
// We draw the maplibre-map onto the canvas. This does not export markers
// Inspiration by https://github.com/mapbox/mapbox-gl-js/issues/2766
2023-04-19 03:20:49 +02:00
2023-06-04 00:43:32 +02:00
// Total hack - see https://stackoverflow.com/questions/42483449/mapbox-gl-js-export-map-to-png-or-pdf
const promise = new Promise < void > ( ( resolve ) = > {
map . once ( "render" , ( ) = > {
ctx . drawImage ( map . getCanvas ( ) , 0 , 0 )
resolve ( )
2023-04-19 03:20:49 +02:00
} )
2023-06-04 00:43:32 +02:00
} )
2023-04-19 03:20:49 +02:00
2023-06-04 00:43:32 +02:00
while ( ! map . isStyleLoaded ( ) ) {
console . log ( "Waiting to fully load the style..." )
await Utils . waitFor ( 100 )
2023-04-19 03:20:49 +02:00
}
2023-06-04 00:43:32 +02:00
map . triggerRepaint ( )
await promise
map . resize ( )
}
2023-04-19 03:20:49 +02:00
2023-11-14 16:14:27 +01:00
/ * *
* Draws the markers of the current map on the specified canvas .
* The DPIfactor is used to calculate the correct position , whereas 'rescaleIcons' can be used to make the icons smaller
* @param drawOn
* @param rescaleIcons
* @private
* /
private async drawMarkers (
drawOn : CanvasRenderingContext2D ,
rescaleIcons : number = 1 ,
progress : UIEventSource < { current : number ; total : number } >
) : Promise < void > {
2023-06-04 00:43:32 +02:00
const map = this . _maplibreMap . data
if ( ! map ) {
2023-11-14 16:14:27 +01:00
console . error ( "There is no map to export from" )
2023-06-04 00:43:32 +02:00
return undefined
2023-04-19 03:20:49 +02:00
}
2023-11-13 15:14:41 +01:00
const container = map . getContainer ( )
2023-11-14 16:14:27 +01:00
2023-11-13 15:14:41 +01:00
function isDisplayed ( el : Element ) {
const r1 = el . getBoundingClientRect ( )
const r2 = container . getBoundingClientRect ( )
return ! (
r2 . left > r1 . right ||
r2 . right < r1 . left ||
r2 . top > r1 . bottom ||
r2 . bottom < r1 . top
)
}
2023-11-14 16:14:27 +01:00
2023-11-13 15:14:41 +01:00
const markers = Array . from ( container . getElementsByClassName ( "marker" ) )
for ( let i = 0 ; i < markers . length ; i ++ ) {
2023-11-14 16:14:27 +01:00
const marker = < HTMLElement > markers [ i ]
2023-11-13 15:14:41 +01:00
if ( ! isDisplayed ( marker ) ) {
continue
}
2023-11-14 16:14:27 +01:00
const pixelRatio = map . getPixelRatio ( )
let x = marker . getBoundingClientRect ( ) . x
let y = marker . getBoundingClientRect ( ) . y
const style = marker . style . transform
marker . style . transform = ""
const offset = style . match ( /translate\(([-0-9]+)%, ?([-0-9]+)%\)/ )
console . log ( "MARKER" , marker )
const w = marker . style . width
// Force a wider view for icon badges
marker . style . width = marker . getBoundingClientRect ( ) . width * 4 + "px"
const svgSource = await htmltoimage . toSvg ( marker )
const img = await MapLibreAdaptor . createImage ( svgSource )
marker . style . width = w
if ( offset && rescaleIcons !== 1 ) {
const [ _ , relXStr , relYStr ] = offset
const relX = Number ( relXStr )
const relY = Number ( relYStr )
console . log ( "Moving icon with" , relX , relY , img . width , img . height , x , y )
// x += img.width * (relX / 100)
y += img . height * ( relY / 100 )
}
x *= pixelRatio
y *= pixelRatio
if ( progress ) {
progress . setData ( { current : i , total : markers.length } )
}
2023-11-13 15:14:41 +01:00
try {
2023-11-14 16:14:27 +01:00
drawOn . drawImage ( img , x , y , img . width * rescaleIcons , img . height * rescaleIcons )
2023-11-13 15:14:41 +01:00
} catch ( e ) {
console . log ( "Could not draw image because of" , e )
}
2023-11-14 16:14:27 +01:00
marker . style . transform = style
2023-11-13 15:14:41 +01:00
}
2023-04-19 03:20:49 +02:00
}
2023-05-18 23:42:03 +02:00
private updateStores ( isSetup : boolean = false ) : void {
2023-04-19 03:20:49 +02:00
const map = this . _maplibreMap . data
2023-04-20 17:42:07 +02:00
if ( ! map ) {
2023-04-19 03:20:49 +02:00
return
}
2023-06-14 20:39:36 +02:00
const { lng , lat } = map . getCenter ( )
2023-06-07 22:46:41 +02:00
if ( lng === 0 && lat === 0 ) {
return
}
if ( this . location . data === undefined ) {
2023-06-14 20:39:36 +02:00
this . location . setData ( { lon : lng , lat } )
2023-06-07 22:46:41 +02:00
} else if ( ! isSetup ) {
2023-06-14 23:21:19 +02:00
const lon = map . getCenter ( ) . lng
const lat = map . getCenter ( ) . lat
this . location . setData ( { lon , lat } )
2023-05-18 23:42:03 +02:00
}
2023-04-19 03:20:49 +02:00
this . zoom . setData ( Math . round ( map . getZoom ( ) * 10 ) / 10 )
const bounds = map . getBounds ( )
const bbox = new BBox ( [
[ bounds . getEast ( ) , bounds . getNorth ( ) ] ,
[ bounds . getWest ( ) , bounds . getSouth ( ) ] ,
] )
2023-05-18 23:42:03 +02:00
if ( this . bounds . data === undefined || ! isSetup ) {
this . bounds . setData ( bbox )
}
2023-04-19 03:20:49 +02:00
}
2023-05-18 15:44:54 +02:00
private SetZoom ( z : number ) : void {
2023-03-24 19:21:15 +01:00
const map = this . _maplibreMap . data
if ( ! map || z === undefined ) {
return
}
if ( Math . abs ( map . getZoom ( ) - z ) > 0.01 ) {
map . setZoom ( z )
}
}
2023-05-18 15:44:54 +02:00
private MoveMapToCurrentLoc ( loc : { lat : number ; lon : number } ) : void {
2023-03-24 19:21:15 +01:00
const map = this . _maplibreMap . data
if ( ! map || loc === undefined ) {
return
}
const center = map . getCenter ( )
if ( center . lng !== loc . lon || center . lat !== loc . lat ) {
2023-07-08 01:21:11 +02:00
if ( isNaN ( loc . lon ) || isNaN ( loc . lat ) ) {
console . error ( "Got invalid lat or lon, not setting" )
} else {
map . setCenter ( { lng : loc.lon , lat : loc.lat } )
}
2023-03-24 19:21:15 +01:00
}
}
2023-03-11 02:37:07 +01:00
private async awaitStyleIsLoaded ( ) : Promise < void > {
const map = this . _maplibreMap . data
2023-05-05 02:03:41 +02:00
if ( ! map ) {
2023-03-11 02:37:07 +01:00
return
}
2023-04-06 01:33:08 +02:00
while ( ! map ? . isStyleLoaded ( ) ) {
2023-03-11 02:37:07 +01:00
await Utils . waitFor ( 250 )
}
}
2023-05-18 15:44:54 +02:00
private removeCurrentLayer ( map : MLMap ) : void {
2023-03-11 02:37:07 +01:00
if ( this . _currentRasterLayer ) {
// hide the previous layer
2023-06-29 00:24:19 +02:00
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" )
}
2023-03-11 02:37:07 +01:00
}
}
2023-05-18 15:44:54 +02:00
private async setBackground ( ) : Promise < void > {
2023-03-11 02:37:07 +01:00
const map = this . _maplibreMap . data
2023-05-05 02:03:41 +02:00
if ( ! map ) {
2023-03-11 02:37:07 +01:00
return
}
2023-03-23 00:58:21 +01:00
const background : RasterLayerProperties = this . rasterLayer ? . data ? . properties
2023-06-29 00:24:19 +02:00
if ( ! background ) {
2023-03-11 02:37:07 +01:00
return
}
2023-06-29 00:24:19 +02:00
if ( this . _currentRasterLayer === background . id ) {
2023-03-11 02:37:07 +01:00
// already the correct background layer, nothing to do
return
}
2023-06-29 00:24:19 +02:00
2023-03-28 05:13:48 +02:00
if ( ! background ? . url ) {
2023-03-11 02:37:07 +01:00
// no background to set
this . removeCurrentLayer ( map )
return
}
2023-06-14 20:39:36 +02:00
if ( background . type === "vector" ) {
2023-06-29 00:24:19 +02:00
this . removeCurrentLayer ( map )
2023-06-14 00:47:09 +02:00
map . setStyle ( background . url )
return
}
2023-06-14 20:39:36 +02:00
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
2023-06-07 22:46:41 +02:00
if ( background . category === "osmbasedmap" || background . category === "map" ) {
2023-06-07 14:34:58 +02:00
// The background layer is already an OSM-based map or another map, so we don't want anything from the baselayer
2023-06-29 00:24:19 +02:00
addLayerBeforeId = undefined
this . removeCurrentLayer ( map )
} else {
// Make sure that the default maptiler style is loaded as it gives an overlay with roads
2023-09-24 18:24:10 +02:00
const maptiler = AvailableRasterLayers . maptilerDefaultLayer . properties
2023-06-29 00:24:19 +02:00
if ( ! map . getSource ( maptiler . id ) ) {
this . removeCurrentLayer ( map )
map . addSource ( maptiler . id , MapLibreAdaptor . prepareWmsSource ( maptiler ) )
map . setStyle ( maptiler . url )
await this . awaitStyleIsLoaded ( )
}
2023-06-07 14:34:58 +02:00
}
2023-06-29 00:24:19 +02:00
if ( ! map . getLayer ( addLayerBeforeId ) ) {
addLayerBeforeId = undefined
}
2023-10-06 02:23:24 +02:00
await this . awaitStyleIsLoaded ( )
2023-06-29 00:24:19 +02:00
if ( ! map . getSource ( background . id ) ) {
map . addSource ( background . id , MapLibreAdaptor . prepareWmsSource ( background ) )
}
if ( ! map . getLayer ( background . id ) ) {
2023-10-10 01:52:02 +02:00
addLayerBeforeId ? ? = map
. getStyle ( )
. layers . find ( ( l ) = > l . id . startsWith ( "mapcomplete_" ) ) ? . id
2023-10-06 03:34:26 +02:00
console . log (
"Adding background layer" ,
background . id ,
"beforeId" ,
addLayerBeforeId ,
"; all layers are" ,
map . getStyle ( ) . layers . map ( ( l ) = > l . id )
)
2023-06-29 00:24:19 +02:00
map . addLayer (
{
id : background.id ,
type : "raster" ,
source : background.id ,
paint : { } ,
} ,
addLayerBeforeId
)
}
2023-03-11 02:37:07 +01:00
await this . awaitStyleIsLoaded ( )
2023-09-28 23:50:27 +02:00
if ( this . _currentRasterLayer !== background ? . id ) {
2023-09-24 16:34:36 +02:00
this . removeCurrentLayer ( map )
}
2023-03-11 02:37:07 +01:00
this . _currentRasterLayer = background ? . id
}
2023-03-24 19:21:15 +01:00
private setMaxBounds ( bbox : undefined | BBox ) {
const map = this . _maplibreMap . data
2023-05-05 02:03:41 +02:00
if ( ! map ) {
2023-03-24 19:21:15 +01:00
return
}
if ( bbox ) {
2023-04-06 01:33:08 +02:00
map ? . setMaxBounds ( bbox . toLngLat ( ) )
2023-03-24 19:21:15 +01:00
} else {
2023-04-06 01:33:08 +02:00
map ? . setMaxBounds ( null )
2023-03-24 19:21:15 +01:00
}
}
2023-07-18 01:26:04 +02:00
private setAllowRotating ( allow : true | boolean | undefined ) {
const map = this . _maplibreMap . data
if ( ! map ) {
return
}
if ( allow === false ) {
map . rotateTo ( 0 , { duration : 0 } )
map . setPitch ( 0 )
map . dragRotate . disable ( )
2023-09-28 23:50:27 +02:00
map . touchZoomRotate . disableRotation ( )
2023-07-18 01:26:04 +02:00
} else {
map . dragRotate . enable ( )
2023-09-28 23:50:27 +02:00
map . touchZoomRotate . enableRotation ( )
2023-07-18 01:26:04 +02:00
}
}
2023-03-24 19:21:15 +01:00
private setAllowMoving ( allow : true | boolean | undefined ) {
const map = this . _maplibreMap . data
2023-05-05 02:03:41 +02:00
if ( ! map ) {
2023-03-24 19:21:15 +01:00
return
}
if ( allow === false ) {
for ( const id of MapLibreAdaptor . maplibre_control_handlers ) {
map [ id ] . disable ( )
}
} else {
for ( const id of MapLibreAdaptor . maplibre_control_handlers ) {
map [ id ] . enable ( )
}
}
2023-07-18 01:26:04 +02:00
this . setAllowRotating ( this . allowRotating . data )
2023-03-24 19:21:15 +01:00
}
2023-03-28 05:13:48 +02:00
2023-04-06 01:33:08 +02:00
private setMinzoom ( minzoom : number ) {
const map = this . _maplibreMap . data
2023-05-05 02:03:41 +02:00
if ( ! map ) {
2023-04-06 01:33:08 +02:00
return
}
map . setMinZoom ( minzoom )
}
2023-04-21 01:53:24 +02:00
private setMaxzoom ( maxzoom : number ) {
const map = this . _maplibreMap . data
2023-05-05 02:03:41 +02:00
if ( ! map ) {
2023-04-21 01:53:24 +02:00
return
}
map . setMaxZoom ( maxzoom )
}
2023-03-28 05:13:48 +02:00
private setAllowZooming ( allow : true | boolean | undefined ) {
const map = this . _maplibreMap . data
2023-05-05 02:03:41 +02:00
if ( ! map ) {
2023-03-28 05:13:48 +02:00
return
}
if ( allow === false ) {
for ( const id of MapLibreAdaptor . maplibre_zoom_handlers ) {
map [ id ] . disable ( )
}
} else {
for ( const id of MapLibreAdaptor . maplibre_zoom_handlers ) {
map [ id ] . enable ( )
}
}
}
private setBounds ( bounds : BBox ) {
const map = this . _maplibreMap . data
2023-05-05 02:03:41 +02:00
if ( ! map || bounds === undefined ) {
2023-03-28 05:13:48 +02:00
return
}
const oldBounds = map . getBounds ( )
const e = 0.0000001
const hasDiff =
Math . abs ( oldBounds . getWest ( ) - bounds . getWest ( ) ) > e &&
Math . abs ( oldBounds . getEast ( ) - bounds . getEast ( ) ) > e &&
Math . abs ( oldBounds . getNorth ( ) - bounds . getNorth ( ) ) > e &&
Math . abs ( oldBounds . getSouth ( ) - bounds . getSouth ( ) ) > e
if ( ! hasDiff ) {
return
}
map . fitBounds ( bounds . toLngLat ( ) )
}
2023-03-11 02:37:07 +01:00
}