Feature: add Protomaps as background layers

This commit is contained in:
Pieter Vander Vennet 2024-03-21 12:16:00 +01:00
parent 4ff67addf7
commit f2a6225c80
8 changed files with 272 additions and 104 deletions

View file

@ -7,6 +7,10 @@ export type EliCategory =
| "qa"
| "elevation"
| "other"
/**
* This class has grown beyond the point of only containing Raster Layers
*/
export interface RasterLayerProperties {
/**
* The name of the imagery source
@ -19,7 +23,8 @@ export interface RasterLayerProperties {
readonly url: string
readonly category?: string | EliCategory
readonly type?: "vector" | string
readonly type?: "vector" | "raster" | string
readonly style?: string,
readonly attribution?: {
readonly url?: string

View file

@ -1,6 +1,7 @@
import { ImmutableStore, 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 maplibregl from "maplibre-gl";
import { RasterLayerPolygon } from "../../Models/RasterLayers"
import { Utils } from "../../Utils"
import { BBox } from "../../Logic/BBox"
@ -11,7 +12,7 @@ import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
import * as htmltoimage from "html-to-image"
import RasterLayerHandler from "./RasterLayerHandler"
import Constants from "../../Models/Constants"
import { Protocol } from "pmtiles";
/**
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`
*/
@ -46,6 +47,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
readonly pitch: UIEventSource<number>
readonly useTerrain: Store<boolean>
private static pmtilesInited = false
/**
* Functions that are called when one of those actions has happened
* @private
@ -55,6 +57,10 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
private readonly _maplibreMap: Store<MLMap>
constructor(maplibreMap: Store<MLMap>, state?: Partial<MapProperties>) {
if(!MapLibreAdaptor.pmtilesInited){
maplibregl.addProtocol("pmtiles",new Protocol().tile);
MapLibreAdaptor.pmtilesInited = true
}
this._maplibreMap = maplibreMap
this.location = state?.location ?? new UIEventSource(undefined)
@ -103,6 +109,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
}
maplibreMap.addCallbackAndRunD((map) => {
map.on("load", () => {
self.MoveMapToCurrentLoc(self.location.data)
self.SetZoom(self.zoom.data)
@ -212,7 +219,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
}
public static prepareWmsSource(layer: RasterLayerProperties): SourceSpecification {
return RasterLayerHandler.prepareWmsSource(layer)
return RasterLayerHandler.prepareSource(layer)
}
/**

View file

@ -1,8 +1,9 @@
import { Map as MLMap, SourceSpecification } from "maplibre-gl"
import { Map as MLMap, RasterSourceSpecification, VectorTileSource } from "maplibre-gl"
import { Store, Stores, UIEventSource } from "../../Logic/UIEventSource"
import { RasterLayerPolygon } from "../../Models/RasterLayers"
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
import { Utils } from "../../Utils"
import { VectorSourceSpecification } from "@maplibre/maplibre-gl-style-spec"
class SingleBackgroundHandler {
// Value between 0 and 1.0
@ -17,6 +18,7 @@ class SingleBackgroundHandler {
*/
public static readonly DEACTIVATE_AFTER = 60
private fadeStep = 0.1
constructor(
map: Store<MLMap>,
targetLayer: RasterLayerPolygon,
@ -75,6 +77,7 @@ class SingleBackgroundHandler {
this.fadeIn()
}
}
private async awaitStyleIsLoaded(): Promise<void> {
const map = this._map.data
if (!map) {
@ -85,11 +88,11 @@ class SingleBackgroundHandler {
}
}
private async enable(){
private async enable() {
let ttl = 15
await this.awaitStyleIsLoaded()
while(!this.tryEnable() && ttl > 0){
ttl --;
while (!this.tryEnable() && ttl > 0) {
ttl--
await Utils.waitFor(250)
}
}
@ -110,9 +113,10 @@ class SingleBackgroundHandler {
// 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)) {
try {
map.addSource(background.id, RasterLayerHandler.prepareWmsSource(background))
map.addSource(background.id, RasterLayerHandler.prepareSource(background))
} catch (e) {
return false
}
@ -122,21 +126,25 @@ class SingleBackgroundHandler {
.getStyle()
.layers.find((l) => l.id.startsWith("mapcomplete_"))?.id
map.addLayer(
{
id: background.id,
type: "raster",
source: background.id,
paint: {
"raster-opacity": 0,
},
},
addLayerBeforeId
)
if (background.type === "vector") {
map.setStyle(background["style"])
} else {
this.opacity.addCallbackAndRun((o) => {
map.setPaintProperty(background.id, "raster-opacity", o)
})
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)
})
}
}
return true
}
@ -168,7 +176,14 @@ export default class RasterLayerHandler {
})
}
public static prepareWmsSource(layer: RasterLayerProperties): SourceSpecification {
public static prepareSource(layer: RasterLayerProperties): RasterSourceSpecification | VectorSourceSpecification {
if (layer.type === "vector") {
const vs: VectorSourceSpecification = {
type: "vector",
url: layer.url
}
return vs
}
return {
type: "raster",
// use the tiles option to specify a 256WMS tile source URL
@ -178,7 +193,7 @@ export default class RasterLayerHandler {
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",
scheme: layer.url.includes("{-y}") ? "tms" : "xyz"
}
}
@ -192,7 +207,7 @@ export default class RasterLayerHandler {
"{width}": "" + size,
"{height}": "" + size,
"{zoom}": "{z}",
"{-y}": "{y}",
"{-y}": "{y}"
}
for (const key in toReplace) {

View file

@ -88,7 +88,8 @@
"text": "Stamen/Stadiamaps",
"url": "https://stadiamaps.com/"
}
}, {
},
{
"name": "Stamen Watercolor",
"url": "https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg?key=14c5a900-7137-42f7-9cb9-fff0f4696f75",
"category": "osmbasedmap",
@ -142,8 +143,6 @@
"url": "https://carto.com/"
}
},
{
"url": "https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json?key=eyJhbGciOiJIUzI1NiJ9.eyJhIjoiYWNfdW4ybmhlbTciLCJqdGkiOiIwZGQxNjJmNyJ9.uATJpa6QcrtXhph3Bzvk2nX3QsxEw-Q8dj5khUG6hGk",
"name": "Carto Positron (no labels)",
@ -176,6 +175,66 @@
"text": "<a href=\"https://carto.com/about-carto/\" target=\"_blank\" rel=\"noopener\">CARTO</a>",
"url": "https://carto.com/"
}
},
{
"url": "pmtiles://https://api.protomaps.com/tiles/v3/{z}/{x}/{y}.mvt?key=2af8b969a9e8b692",
"style": "https://api.protomaps.com/styles/v2/white.json?key=2af8b969a9e8b692",
"id": "protomaps.white",
"name": "Protomaps White",
"type": "vector",
"category": "osmbasedmap",
"attribution": {
"text": "Protomaps",
"url": "https://protomaps.com/"
}
},
{
"url": "pmtiles://https://api.protomaps.com/tiles/v3/{z}/{x}/{y}.mvt?key=2af8b969a9e8b692",
"style": "https://api.protomaps.com/styles/v2/light.json?key=2af8b969a9e8b692",
"id": "protomaps.light",
"name": "Protomaps Light",
"type": "vector",
"category": "osmbasedmap",
"attribution": {
"text": "Protomaps",
"url": "https://protomaps.com/"
}
},
{
"url": "pmtiles://https://api.protomaps.com/tiles/v3/{z}/{x}/{y}.mvt?key=2af8b969a9e8b692",
"style": "https://api.protomaps.com/styles/v2/grayscale.json?key=2af8b969a9e8b692",
"id": "protomaps.grayscale",
"name": "Protomaps Grayscale",
"type": "vector",
"category": "osmbasedmap",
"attribution": {
"text": "Protomaps",
"url": "https://protomaps.com/"
}
},
{
"url": "pmtiles://https://api.protomaps.com/tiles/v3/{z}/{x}/{y}.mvt?key=2af8b969a9e8b692",
"style": "https://api.protomaps.com/styles/v2/dark.json?key=2af8b969a9e8b692",
"id": "protomaps.dark",
"name": "Protomaps Dark",
"type": "vector",
"category": "osmbasedmap",
"attribution": {
"text": "Protomaps",
"url": "https://protomaps.com/"
}
},
{
"url": "pmtiles://https://api.protomaps.com/tiles/v3/{z}/{x}/{y}.mvt?key=2af8b969a9e8b692",
"style": "https://api.protomaps.com/styles/v2/black.json?key=2af8b969a9e8b692",
"id": "protomaps.black",
"name": "Protomaps Black",
"type": "vector",
"category": "osmbasedmap",
"attribution": {
"text": "Protomaps",
"url": "https://protomaps.com/"
}
}
]
}