refactoring(maplibre): add RasterLayer info, add background switch, add default global layers

This commit is contained in:
Pieter Vander Vennet 2023-03-11 02:37:07 +01:00
parent 703d561324
commit 4f2bbf4b54
14 changed files with 1103 additions and 472 deletions

View file

@ -1,51 +0,0 @@
import BaseLayer from "../../Models/BaseLayer"
import { ImmutableStore, Store, UIEventSource } from "../UIEventSource"
import Loc from "../../Models/Loc"
export interface AvailableBaseLayersObj {
readonly osmCarto: BaseLayer
layerOverview: BaseLayer[]
AvailableLayersAt(location: Store<Loc>): Store<BaseLayer[]>
SelectBestLayerAccordingTo(
location: Store<Loc>,
preferedCategory: Store<string | string[]>
): Store<BaseLayer>
}
/**
* Calculates which layers are available at the current location
* Changes the basemap
*/
export default class AvailableBaseLayers {
public static layerOverview: BaseLayer[]
public static osmCarto: BaseLayer
private static implementation: AvailableBaseLayersObj
static AvailableLayersAt(location: Store<Loc>): Store<BaseLayer[]> {
return (
AvailableBaseLayers.implementation?.AvailableLayersAt(location) ??
new ImmutableStore<BaseLayer[]>([])
)
}
static SelectBestLayerAccordingTo(
location: Store<Loc>,
preferedCategory: UIEventSource<string | string[]>
): Store<BaseLayer> {
return (
AvailableBaseLayers.implementation?.SelectBestLayerAccordingTo(
location,
preferedCategory
) ?? new ImmutableStore<BaseLayer>(undefined)
)
}
public static implement(backend: AvailableBaseLayersObj) {
AvailableBaseLayers.layerOverview = backend.layerOverview
AvailableBaseLayers.osmCarto = backend.osmCarto
AvailableBaseLayers.implementation = backend
}
}

View file

@ -1,309 +0,0 @@
import BaseLayer from "../../Models/BaseLayer"
import { Store, Stores } from "../UIEventSource"
import Loc from "../../Models/Loc"
import { GeoOperations } from "../GeoOperations"
import * as editorlayerindex from "../../assets/editor-layer-index.json"
import * as L from "leaflet"
import { TileLayer } from "leaflet"
import * as X from "leaflet-providers"
import { Utils } from "../../Utils"
import { AvailableBaseLayersObj } from "./AvailableBaseLayers"
import { BBox } from "../BBox"
export default class AvailableBaseLayersImplementation implements AvailableBaseLayersObj {
public readonly osmCarto: BaseLayer = {
id: "osm",
name: "OpenStreetMap",
layer: () =>
AvailableBaseLayersImplementation.CreateBackgroundLayer(
"osm",
"OpenStreetMap",
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
"OpenStreetMap",
"https://openStreetMap.org/copyright",
19,
false,
false
),
feature: null,
max_zoom: 19,
min_zoom: 0,
isBest: true, // Of course, OpenStreetMap is the best map!
category: "osmbasedmap",
}
public readonly layerOverview = AvailableBaseLayersImplementation.LoadRasterIndex().concat(
AvailableBaseLayersImplementation.LoadProviderIndex()
)
public readonly globalLayers = this.layerOverview.filter(
(layer) => layer.feature?.geometry === undefined || layer.feature?.geometry === null
)
public readonly localLayers = this.layerOverview.filter(
(layer) => layer.feature?.geometry !== undefined && layer.feature?.geometry !== null
)
private static LoadRasterIndex(): BaseLayer[] {
const layers: BaseLayer[] = []
// @ts-ignore
const features = editorlayerindex.features
for (const i in features) {
const layer = features[i]
const props = layer.properties
if (props.type === "bing") {
// A lot of work to implement - see https://github.com/pietervdvn/MapComplete/issues/648
continue
}
if (props.id === "MAPNIK") {
// Already added by default
continue
}
if (props.overlay) {
continue
}
if (props.url.toLowerCase().indexOf("apikey") > 0) {
continue
}
if (props.max_zoom < 19) {
// We want users to zoom to level 19 when adding a point
// If they are on a layer which hasn't enough precision, they can not zoom far enough. This is confusing, so we don't use this layer
continue
}
if (props.name === undefined) {
console.warn("Editor layer index: name not defined on ", props)
continue
}
const leafletLayer: () => TileLayer = () =>
AvailableBaseLayersImplementation.CreateBackgroundLayer(
props.id,
props.name,
props.url,
props.name,
props.license_url,
props.max_zoom,
props.type.toLowerCase() === "wms",
props.type.toLowerCase() === "wmts"
)
// Note: if layer.geometry is null, there is global coverage for this layer
layers.push({
id: props.id,
max_zoom: props.max_zoom ?? 19,
min_zoom: props.min_zoom ?? 1,
name: props.name,
layer: leafletLayer,
feature: layer.geometry !== null ? layer : null,
isBest: props.best ?? false,
category: props.category,
})
}
return layers
}
private static LoadProviderIndex(): BaseLayer[] {
// @ts-ignore
X // Import X to make sure the namespace is not optimized away
function l(id: string, name: string): BaseLayer {
try {
const layer: any = L.tileLayer.provider(id, undefined)
return {
feature: null,
id: id,
name: name,
layer: () =>
L.tileLayer.provider(id, {
maxNativeZoom: layer.options?.maxZoom,
maxZoom: Math.max(layer.options?.maxZoom ?? 19, 21),
}),
min_zoom: 1,
max_zoom: layer.options.maxZoom,
category: "osmbasedmap",
isBest: false,
}
} catch (e) {
console.error("Could not find provided layer", name, e)
return null
}
}
const layers = [
l("Stamen.TonerLite", "Toner Lite (by Stamen)"),
l("Stamen.TonerBackground", "Toner Background - no labels (by Stamen)"),
l("Stamen.Watercolor", "Watercolor (by Stamen)"),
l("CartoDB.Positron", "Positron (by CartoDB)"),
l("CartoDB.PositronNoLabels", "Positron - no labels (by CartoDB)"),
l("CartoDB.Voyager", "Voyager (by CartoDB)"),
l("CartoDB.VoyagerNoLabels", "Voyager - no labels (by CartoDB)"),
l("CartoDB.DarkMatter", "Dark Matter (by CartoDB)"),
l("CartoDB.DarkMatterNoLabels", "Dark Matter - no labels (by CartoDB)"),
]
return Utils.NoNull(layers)
}
/**
* Converts a layer from the editor-layer-index into a tilelayer usable by leaflet
*/
private static CreateBackgroundLayer(
id: string,
name: string,
url: string,
attribution: string,
attributionUrl: string,
maxZoom: number,
isWms: boolean,
isWMTS?: boolean
): TileLayer {
url = url.replace("{zoom}", "{z}").replace("&BBOX={bbox}", "").replace("&bbox={bbox}", "")
const subdomainsMatch = url.match(/{switch:[^}]*}/)
let domains: string[] = []
if (subdomainsMatch !== null) {
let domainsStr = subdomainsMatch[0].substr("{switch:".length)
domainsStr = domainsStr.substr(0, domainsStr.length - 1)
domains = domainsStr.split(",")
url = url.replace(/{switch:[^}]*}/, "{s}")
}
if (isWms) {
url = url.replace("&SRS={proj}", "")
url = url.replace("&srs={proj}", "")
const paramaters = [
"format",
"layers",
"version",
"service",
"request",
"styles",
"transparent",
"version",
]
const urlObj = new URL(url)
const isUpper = urlObj.searchParams["LAYERS"] !== null
const options = {
maxZoom: Math.max(maxZoom ?? 19, 21),
maxNativeZoom: maxZoom ?? 19,
attribution: attribution + " | ",
subdomains: domains,
uppercase: isUpper,
transparent: false,
}
for (const paramater of paramaters) {
let p = paramater
if (isUpper) {
p = paramater.toUpperCase()
}
options[paramater] = urlObj.searchParams.get(p)
}
if (options.transparent === null) {
options.transparent = false
}
return L.tileLayer.wms(urlObj.protocol + "//" + urlObj.host + urlObj.pathname, options)
}
if (attributionUrl) {
attribution = `<a href='${attributionUrl}' target='_blank'>${attribution}</a>`
}
return L.tileLayer(url, {
attribution: attribution,
maxZoom: Math.max(21, maxZoom ?? 19),
maxNativeZoom: maxZoom ?? 19,
minZoom: 1,
// @ts-ignore
wmts: isWMTS ?? false,
subdomains: domains,
})
}
public AvailableLayersAt(location: Store<Loc>): Store<BaseLayer[]> {
return Stores.ListStabilized(
location.map((currentLocation) => {
if (currentLocation === undefined) {
return this.layerOverview
}
return this.CalculateAvailableLayersAt(currentLocation?.lon, currentLocation?.lat)
})
)
}
public SelectBestLayerAccordingTo(
location: Store<Loc>,
preferedCategory: Store<string | string[]>
): Store<BaseLayer> {
return this.AvailableLayersAt(location).map(
(available) => {
// First float all 'best layers' to the top
available.sort((a, b) => {
if (a.isBest && b.isBest) {
return 0
}
if (!a.isBest) {
return 1
}
return -1
})
if (preferedCategory.data === undefined) {
return available[0]
}
let prefered: string[]
if (typeof preferedCategory.data === "string") {
prefered = [preferedCategory.data]
} else {
prefered = preferedCategory.data
}
prefered.reverse(/*New list, inplace reverse is fine*/)
for (const category of prefered) {
//Then sort all 'photo'-layers to the top. Stability of the sorting will force a 'best' photo layer on top
available.sort((a, b) => {
if (a.category === category && b.category === category) {
return 0
}
if (a.category !== category) {
return 1
}
return -1
})
}
return available[0]
},
[preferedCategory]
)
}
private CalculateAvailableLayersAt(lon: number, lat: number): BaseLayer[] {
const availableLayers = [this.osmCarto]
if (lon === undefined || lat === undefined) {
return availableLayers.concat(this.globalLayers)
}
const lonlat: [number, number] = [lon, lat]
for (const layerOverviewItem of this.localLayers) {
const layer = layerOverviewItem
const bbox = BBox.get(layer.feature)
if (!bbox.contains(lonlat)) {
continue
}
if (GeoOperations.inside(lonlat, layer.feature)) {
availableLayers.push(layer)
}
}
return availableLayers.concat(this.globalLayers)
}
}

View file

@ -1,49 +1,42 @@
import { UIEventSource } from "../UIEventSource"
import BaseLayer from "../../Models/BaseLayer"
import AvailableBaseLayers from "./AvailableBaseLayers"
import Loc from "../../Models/Loc"
import { Store, UIEventSource } from "../UIEventSource"
import { Utils } from "../../Utils"
import {
AvailableRasterLayers,
RasterLayerPolygon,
RasterLayerUtils,
} from "../../Models/RasterLayers"
/**
* Sets the current background layer to a layer that is actually available
* When a user pans around on the map, they might pan out of the range of the current background raster layer.
* This actor will then quickly select a (best) raster layer of the same category which is available
*/
export default class BackgroundLayerResetter {
constructor(
currentBackgroundLayer: UIEventSource<BaseLayer>,
location: UIEventSource<Loc>,
availableLayers: UIEventSource<BaseLayer[]>,
defaultLayerId: string = undefined
currentBackgroundLayer: UIEventSource<RasterLayerPolygon>,
availableLayers: Store<RasterLayerPolygon[]>
) {
if (Utils.runningFromConsole) {
return
}
defaultLayerId = defaultLayerId ?? AvailableBaseLayers.osmCarto.id
// Change the baseLayer back to OSM if we go out of the current range of the layer
availableLayers.addCallbackAndRunD((availableLayers) => {
// We only check on move/on change of the availableLayers
const currentBgPolygon: RasterLayerPolygon | undefined = currentBackgroundLayer.data
// Change the baselayer back to OSM if we go out of the current range of the layer
availableLayers.addCallbackAndRun((availableLayers) => {
let defaultLayer = undefined
const currentLayer = currentBackgroundLayer.data.id
for (const availableLayer of availableLayers) {
if (availableLayer.id === currentLayer) {
if (availableLayer.max_zoom < location.data.zoom) {
break
}
if (availableLayer.min_zoom > location.data.zoom) {
break
}
if (availableLayer.id === defaultLayerId) {
defaultLayer = availableLayer
}
return // All good - the current layer still works!
}
if (availableLayers.findIndex((available) => currentBgPolygon == available) >= 0) {
// Still available!
return
}
// Oops, we panned out of range for this layer!
console.log(
"AvailableBaseLayers-actor: detected that the current bounds aren't sufficient anymore - reverting to OSM standard"
// What is the 'best' map of the same category which is available?
const availableInSameCat = RasterLayerUtils.SelectBestLayerAccordingTo(
availableLayers,
currentBgPolygon?.properties?.category ?? "osmbasedmap"
)
currentBackgroundLayer.setData(defaultLayer ?? AvailableBaseLayers.osmCarto)
console.log("Selecting a different layer:", availableInSameCat.properties.id)
currentBackgroundLayer.setData(availableInSameCat ?? AvailableRasterLayers.osmCarto)
})
}
}

View file

@ -1,10 +0,0 @@
export default interface BaseLayer {
id: string
name: string
layer: () => any /*leaflet.TileLayer - not importing as it breaks scripts*/
max_zoom: number
min_zoom: number
feature: any
isBest?: boolean
category?: "map" | "osmbasedmap" | "photo" | "historicphoto" | string
}

276
Models/RasterLayers.ts Normal file
View file

@ -0,0 +1,276 @@
import { Feature, Polygon } from "geojson"
import * as editorlayerindex from "../assets/editor-layer-index.json"
import * as globallayers from "../assets/global-raster-layers.json"
import { BBox } from "../Logic/BBox"
export class AvailableRasterLayers {
public static EditorLayerIndex: (Feature<Polygon, EditorLayerIndexProperties> &
RasterLayerPolygon)[] = <any>editorlayerindex.features
public static globalLayers: RasterLayerPolygon[] = globallayers.layers.map(
(properties) =>
<RasterLayerPolygon>{
type: "Feature",
properties,
geometry: BBox.global.asGeometry(),
}
)
public static readonly osmCartoProperties: RasterLayerProperties = {
id: "osm",
name: "OpenStreetMap",
url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
attribution: {
text: "OpenStreetMap",
url: "https://openStreetMap.org/copyright",
},
best: true,
max_zoom: 19,
min_zoom: 0,
category: "osmbasedmap",
}
public static readonly osmCarto: RasterLayerPolygon = {
type: "Feature",
properties: AvailableRasterLayers.osmCartoProperties,
geometry: BBox.global.asGeometry(),
}
}
export class RasterLayerUtils {
public static SelectBestLayerAccordingTo(
available: RasterLayerPolygon[],
preferredCategory: string | string[]
): RasterLayerPolygon {
available = [...available]
if (preferredCategory === undefined) {
return available[0]
}
let prefered: string[]
if (typeof preferredCategory === "string") {
prefered = [preferredCategory]
} else {
prefered = preferredCategory
}
for (let i = prefered.length - 1; i >= 0; i--) {
const category = prefered[i]
//Then sort all layers of the preferred type to the top. Stability of the sorting will force a 'best' photo layer on top
available.sort((ap, bp) => {
const a = ap.properties
const b = bp.properties
if (a.category === category && b.category === category) {
return 0
}
if (a.category !== category) {
return 1
}
return -1
})
}
const best = available.find((l) => l.properties.best)
if (best) {
return best
}
return available[0]
}
}
export type RasterLayerPolygon = Feature<Polygon, RasterLayerProperties>
export interface RasterLayerProperties {
/**
* The name of the imagery source
*/
readonly name: string
readonly id: string
readonly url: string
readonly category?:
| string
| "photo"
| "map"
| "historicmap"
| "osmbasedmap"
| "historicphoto"
| "qa"
| "elevation"
| "other"
readonly attribution?: {
readonly url?: string
readonly text?: string
readonly html?: string
readonly required?: boolean
}
readonly min_zoom?: number
readonly max_zoom?: number
readonly best?: boolean
}
/**
* Information about a raster tile layer
*
* Based on the spec here https://github.com/osmlab/editor-layer-index/blob/gh-pages/schema.json
* which was then converted with http://borischerny.com/json-schema-to-typescript-browser/
*/
export interface EditorLayerIndexProperties extends RasterLayerProperties {
/**
* The name of the imagery source
*/
readonly name: string
/**
* Whether the imagery name should be translated
*/
readonly i18n?: boolean
readonly type: "tms" | "wms" | "bing" | "scanex" | "wms_endpoint" | "wmts"
/**
* A rough categorisation of different types of layers. See https://github.com/osmlab/editor-layer-index/blob/gh-pages/CONTRIBUTING.md#categories for a description of the individual categories.
*/
readonly category?:
| "photo"
| "map"
| "historicmap"
| "osmbasedmap"
| "historicphoto"
| "qa"
| "elevation"
| "other"
/**
* A URL template for imagery tiles
*/
readonly url: string
readonly min_zoom?: number
readonly max_zoom?: number
/**
* explicit/implicit permission by the owner for use in OSM
*/
readonly permission_osm?: "explicit" | "implicit" | "no"
/**
* A URL for the license or permissions for the imagery
*/
readonly license_url?: string
/**
* A URL for the privacy policy of the operator or false if there is no existing privacy policy for tis imagery.
*/
readonly privacy_policy_url?: string | boolean
/**
* A unique identifier for the source; used in imagery_used changeset tag
*/
readonly id: string
/**
* A short English-language description of the source
*/
readonly description?: string
/**
* The ISO 3166-1 alpha-2 two letter country code in upper case. Use ZZ for unknown or multiple.
*/
readonly country_code?: string
/**
* Whether this imagery should be shown in the default world-wide menu
*/
readonly default?: boolean
/**
* Whether this imagery is the best source for the region
*/
readonly best?: boolean
/**
* The age of the oldest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly start_date?: string
/**
* The age of the newest imagery or data in the source, as an RFC3339 date or leading portion of one
*/
readonly end_date?: string
/**
* HTTP header to check for information if the tile is invalid
*/
readonly no_tile_header?: {
/**
* This interface was referenced by `undefined`'s JSON-Schema definition
* via the `patternProperty` "^.*$".
*/
[k: string]: string[] | null
}
/**
* 'true' if tiles are transparent and can be overlaid on another source
*/
readonly overlay?: boolean & string
readonly available_projections?: string[]
readonly attribution?: {
readonly url?: string
readonly text?: string
readonly html?: string
readonly required?: boolean
}
/**
* A URL for an image, that can be displayed in the list of imagery layers next to the name
*/
readonly icon?: string
/**
* A link to an EULA text that has to be accepted by the user, before the imagery source is added. Can contain {lang} to be replaced by a current user language wiki code (like FR:) or an empty string for the default English text.
*/
readonly eula?: string
/**
* A URL for an image, that is displayed in the mapview for attribution
*/
readonly "logo-image"?: string
/**
* Customized text for the terms of use link (default is "Background Terms of Use")
*/
readonly "terms-of-use-text"?: string
/**
* Specify a checksum for tiles, which aren't real tiles. `type` is the digest type and can be MD5, SHA-1, SHA-256, SHA-384 and SHA-512, value is the hex encoded checksum in lower case. To create a checksum save the tile as file and upload it to e.g. https://defuse.ca/checksums.htm.
*/
readonly "no-tile-checksum"?: string
/**
* header-name attribute specifies a header returned by tile server, that will be shown as `metadata-key` attribute in Show Tile Info dialog
*/
readonly "metadata-header"?: string
/**
* Set to `true` if imagery source is properly aligned and does not need imagery offset adjustments. This is used for OSM based sources too.
*/
readonly "valid-georeference"?: boolean
/**
* Size of individual tiles delivered by a TMS service
*/
readonly "tile-size"?: number
/**
* Whether tiles status can be accessed by appending /status to the tile URL and can be submitted for re-rendering by appending /dirty.
*/
readonly "mod-tile-features"?: string
/**
* HTTP headers to be sent to server. It has two attributes header-name and header-value. May be specified multiple times.
*/
readonly "custom-http-headers"?: {
readonly "header-name"?: string
readonly "header-value"?: string
}
/**
* Default layer to open (when using WMS_ENDPOINT type). Contains list of layer tag with two attributes - name and style, e.g. `"default-layers": ["layer": { name="Basisdata_NP_Basiskart_JanMayen_WMTS_25829" "style":"default" } ]` (not allowed in `mirror` attribute)
*/
readonly "default-layers"?: {
layer?: {
"layer-name"?: string
"layer-style"?: string
[k: string]: unknown
}
[k: string]: unknown
}[]
/**
* format to use when connecting tile server (when using WMS_ENDPOINT type)
*/
readonly format?: string
/**
* If `true` transparent tiles will be requested from WMS server
*/
readonly transparent?: boolean & string
/**
* minimum expiry time for tiles in seconds. The larger the value, the longer entry in cache will be considered valid
*/
readonly "minimum-tile-expire"?: number
}

View file

@ -11,7 +11,6 @@ import { BBox } from "../../Logic/BBox"
import "leaflet-polylineoffset"
import { SimpleMapScreenshoter } from "leaflet-simple-map-screenshoter"
import BackgroundMapSwitch from "../BigComponents/BackgroundMapSwitch"
import AvailableBaseLayersImplementation from "../../Logic/Actors/AvailableBaseLayersImplementation"
import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"
import ShowDataLayerImplementation from "../ShowDataLayer/ShowDataLayerImplementation"
import FilteredLayer from "../../Models/FilteredLayer"
@ -127,7 +126,6 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
}
public static initialize() {
AvailableBaseLayers.implement(new AvailableBaseLayersImplementation())
Minimap.createMiniMap = (options) => new MinimapImplementation(options)
ShowDataLayer.actualContstructor = (options) => new ShowDataLayerImplementation(options)
StrayClickHandler.construct = (

View file

@ -1,41 +0,0 @@
import { DropDown } from "../Input/DropDown"
import Translations from "../i18n/Translations"
import State from "../../State"
import BaseLayer from "../../Models/BaseLayer"
import { VariableUiElement } from "../Base/VariableUIElement"
import { Store } from "../../Logic/UIEventSource"
export default class BackgroundSelector extends VariableUiElement {
constructor(state: { availableBackgroundLayers?: Store<BaseLayer[]> }) {
const available = state.availableBackgroundLayers?.map((available) => {
if (available === undefined) {
return undefined
}
const baseLayers: { value: BaseLayer; shown: string }[] = []
for (const i in available) {
if (!available.hasOwnProperty(i)) {
continue
}
const layer: BaseLayer = available[i]
baseLayers.push({ value: layer, shown: layer.name ?? "id:" + layer.id })
}
return baseLayers
})
super(
available?.map((baseLayers) => {
if (baseLayers === undefined || baseLayers.length <= 1) {
return undefined
}
return new DropDown(
Translations.t.general.backgroundMap.Clone(),
baseLayers,
State.state.backgroundLayer,
{
select_class: "bg-indigo-100 p-1 rounded hover:bg-indigo-200 w-full",
}
)
})
)
}
}

172
UI/Map/MapLibreAdaptor.ts Normal file
View file

@ -0,0 +1,172 @@
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import type { Map as MLMap } from "maplibre-gl"
import {
EditorLayerIndexProperties,
RasterLayerPolygon,
RasterLayerProperties,
} from "../../Models/RasterLayers"
import { Utils } from "../../Utils"
import Loc from "../../Models/Loc"
export class MapLibreAdaptor {
private readonly _maplibreMap: Store<MLMap>
private readonly _backgroundLayer?: Store<RasterLayerPolygon>
private _currentRasterLayer: string = undefined
constructor(
maplibreMap: Store<MLMap>,
state?: {
// availableBackgroundLayers: Store<BaseLayer[]>
/**
* The current background layer
*/
readonly backgroundLayer?: Store<RasterLayerPolygon>
readonly locationControl?: UIEventSource<Loc>
}
) {
this._maplibreMap = maplibreMap
this._backgroundLayer = state.backgroundLayer
const self = this
this._backgroundLayer?.addCallback((_) => self.setBackground())
maplibreMap.addCallbackAndRunD((map) => {
map.on("load", () => {
self.setBackground()
})
if (state.locationControl) {
self.MoveMapToCurrentLoc(state.locationControl.data)
map.on("moveend", () => {
const dt = state.locationControl.data
dt.lon = map.getCenter().lng
dt.lat = map.getCenter().lat
dt.zoom = map.getZoom()
state.locationControl.ping()
})
}
})
state.locationControl.addCallbackAndRunD((loc) => {
self.MoveMapToCurrentLoc(loc)
})
}
private MoveMapToCurrentLoc(loc: Loc) {
const map = this._maplibreMap.data
if (map === undefined || loc === undefined) {
return
}
if (map.getZoom() !== loc.zoom) {
map.setZoom(loc.zoom)
}
const center = map.getCenter()
if (center.lng !== loc.lon || center.lat !== loc.lat) {
map.setCenter({ lng: loc.lon, lat: loc.lat })
}
}
/**
* Prepares an ELI-URL to be compatible with mapbox
*/
private static prepareWmsURL(url: string, size: number = 256) {
// 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}",
}
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) {
console.log("Found a switch:", subdomains)
const options = subdomains[1].split(",")
const option = options[Math.floor(Math.random() * options.length)]
url = url.replace(subdomains[0], option)
}
return url
}
private async awaitStyleIsLoaded(): Promise<void> {
const map = this._maplibreMap.data
if (map === undefined) {
return
}
while (!map.isStyleLoaded()) {
await Utils.waitFor(250)
}
}
private removeCurrentLayer(map: MLMap) {
if (this._currentRasterLayer) {
// hide the previous layer
console.log("Removing previous layer", this._currentRasterLayer)
map.removeLayer(this._currentRasterLayer)
map.removeSource(this._currentRasterLayer)
}
}
private async setBackground() {
const map = this._maplibreMap.data
if (map === undefined) {
return
}
const background: RasterLayerProperties = this._backgroundLayer?.data?.properties
if (background !== undefined && this._currentRasterLayer === background.id) {
// already the correct background layer, nothing to do
return
}
await this.awaitStyleIsLoaded()
if (background !== this._backgroundLayer?.data?.properties) {
// User selected another background in the meantime... abort
return
}
if (background !== undefined && this._currentRasterLayer === background.id) {
// already the correct background layer, nothing to do
return
}
if (background === undefined) {
// no background to set
this.removeCurrentLayer(map)
this._currentRasterLayer = undefined
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.addLayer(
{
id: background.id,
type: "raster",
source: background.id,
paint: {},
},
background.category === "osmbasedmap" || background.category === "map"
? undefined
: "aeroway_fill"
)
await this.awaitStyleIsLoaded()
this.removeCurrentLayer(map)
this._currentRasterLayer = background?.id
}
}

43
UI/Map/MaplibreMap.svelte Normal file
View file

@ -0,0 +1,43 @@
<script lang="ts">
/**
* The 'MaplibreMap' maps various event sources onto MapLibre.
*
* As it replaces the old 'MinimapObj' onto MapLibre and the existing codebase, this is sometimes a bit awkward
*/
import { onMount } from "svelte";
import { Map } from "@onsvisual/svelte-maps";
import type { Map as MaplibreMap } from "maplibre-gl";
import type { Writable } from "svelte/store";
import type Loc from "../../Models/Loc";
import { UIEventSource } from "../../Logic/UIEventSource";
/**
* Beware: this map will _only_ be set by this component
* It should thus be treated as a 'store' by external parties
*/
export let map: Writable<MaplibreMap>
let center = {};
onMount(() => {
$map.on("load", function() {
$map.resize();
});
});
const styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=GvoVAJgu46I5rZapJuAy";
</script>
<main>
<Map bind:center={center}
bind:map={$map}
controls="true"
id="map" location={{lng: 0, lat: 0, zoom: 0}} maxzoom=24 style={styleUrl} />
</main>
<style>
main {
width: 100%;
height: 100%;
position: relative;
}
</style>

View file

@ -0,0 +1,18 @@
<script lang="ts">
import type { Readable, Writable } from "svelte/store";
import type { RasterLayerPolygon } from "../../Models/RasterLayers";
/***
* Chooses a background-layer out of available options
*/
export let availableLayers: Readable<RasterLayerPolygon[]>
export let value: Writable<RasterLayerPolygon>
</script>
<select bind:value={$value}>
{#each $availableLayers as availableLayer }
<option value={availableLayer}>
{availableLayer.properties.name}
</option>
{/each}
</select>

View file

@ -0,0 +1,97 @@
{
"layers": [
{
"id": "Stamen.TonerLite",
"name": "Toner Lite (by Stamen)",
"url": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/toner-lite/{z}/{x}/{y}.png",
"category": "osmbasedmap",
"attribution": {
"html": "Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a> &mdash; Map data {attribution.OpenStreetMap}"
},
"min_zoom": 0,
"max_zoom": 20
},
{
"id": "Stamen.TonerBackground",
"name": "Toner Background - no labels (by Stamen)",
"category": "osmbasedmap",
"url": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/toner-background/{z}/{x}/{y}.png",
"attribution": {
"html": "Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a> &mdash; Map data {attribution.OpenStreetMap}"
},
"min_zoom": 0,
"max_zoom": 20
},
{
"id": "Stamen.Watercolor",
"name": "Watercolor (by Stamen)",
"category": "osmbasedmap",
"url": "https://stamen-tiles-{switch:a,b,c,d}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png",
"attribution": {
"html": "Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a> &mdash; Map data {attribution.OpenStreetMap}"
},
"min_zoom": 0,
"max_zoom": 20
},
{
"id": "CartoDB.Positron",
"name": "Positron (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20,
"category": "osmbasedmap"
},
{
"id": "CartoDB.PositronNoLabels",
"name": "Positron - no labels (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png",
"category": "osmbasedmap",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
},
{
"id": "CartoDB.Voyager",
"name": "Voyager (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png",
"category": "osmbasedmap",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
},
{
"id": "CartoDB.VoyagerNoLabels",
"name": "Voyager - no labels (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png",
"category": "osmbasedmap",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
},
{
"id": "CartoDB.DarkMatter",
"name": "Dark Matter (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png",
"category": "osmbasedmap",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
},
{
"id": "CartoDB.DarkMatterNoLabels",
"name": "Dark Matter - no labels (by CartoDB)",
"url": "https://{switch:a,b,c,d}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png",
"category": "osmbasedmap",
"attribution": {
"html": "<a href=\"https://carto.com/attributions\">CARTO</a>"
},
"max_zoom": 20
}
]
}

418
package-lock.json generated
View file

@ -9,6 +9,7 @@
"version": "0.25.1",
"license": "GPL-3.0-or-later",
"dependencies": {
"@onsvisual/svelte-maps": "^1.1.6",
"@rollup/plugin-typescript": "^11.0.0",
"@turf/boolean-intersects": "^6.5.0",
"@turf/buffer": "^6.5.0",
@ -37,6 +38,7 @@
"libphonenumber-js": "^1.10.8",
"lz-string": "^1.4.4",
"mangrove-reviews-typescript": "^1.1.0",
"maplibre-gl": "^2.4.0",
"opening_hours": "^3.6.0",
"osm-auth": "^1.0.2",
"osmtogeojson": "^3.0.0-beta.5",
@ -1799,6 +1801,24 @@
"geojson-rewind": "geojson-rewind"
}
},
"node_modules/@mapbox/jsonlint-lines-primitives": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/@mapbox/mapbox-gl-supported": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz",
"integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ=="
},
"node_modules/@mapbox/point-geometry": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="
},
"node_modules/@mapbox/sphericalmercator": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@mapbox/sphericalmercator/-/sphericalmercator-1.2.0.tgz",
@ -1810,6 +1830,32 @@
"xyz": "bin/xyz.js"
}
},
"node_modules/@mapbox/tiny-sdf": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz",
"integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA=="
},
"node_modules/@mapbox/unitbezier": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
"integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw=="
},
"node_modules/@mapbox/vector-tile": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
"integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==",
"dependencies": {
"@mapbox/point-geometry": "~0.1.0"
}
},
"node_modules/@mapbox/whoots-js": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -1842,6 +1888,16 @@
"node": ">= 8"
}
},
"node_modules/@onsvisual/svelte-maps": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@onsvisual/svelte-maps/-/svelte-maps-1.1.6.tgz",
"integrity": "sha512-qrt/Z7SvOLzPdz+q3vL6VEVQp3ayavmPE7Rqbtbhicq3JFx/l/1W2VS8ryHgFGXi5nuuZtAzxW7/p8ZM0L/VOw==",
"peerDependencies": {
"maplibre-gl": "^2.4.0",
"pmtiles": "^2.7.0",
"svelte": "^3.32.2"
}
},
"node_modules/@parcel/service-worker": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/@parcel/service-worker/-/service-worker-2.8.2.tgz",
@ -3572,8 +3628,7 @@
"node_modules/@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
"devOptional": true
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA=="
},
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.4",
@ -3642,6 +3697,21 @@
"integrity": "sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow==",
"dev": true
},
"node_modules/@types/mapbox__point-geometry": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz",
"integrity": "sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA=="
},
"node_modules/@types/mapbox__vector-tile": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz",
"integrity": "sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g==",
"dependencies": {
"@types/geojson": "*",
"@types/mapbox__point-geometry": "*",
"@types/pbf": "*"
}
},
"node_modules/@types/node": {
"version": "18.11.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
@ -3656,6 +3726,11 @@
"@types/node": "*"
}
},
"node_modules/@types/pbf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.2.tgz",
"integrity": "sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ=="
},
"node_modules/@types/prompt-sync": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.2.0.tgz",
@ -4811,6 +4886,11 @@
"utrie": "^1.0.2"
}
},
"node_modules/csscolorparser": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz",
"integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w=="
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@ -5975,6 +6055,11 @@
"quickselect": "^2.0.0"
}
},
"node_modules/geojson-vt": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
"integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg=="
},
"node_modules/geojson2svg": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/geojson2svg/-/geojson2svg-1.3.3.tgz",
@ -6037,6 +6122,11 @@
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
"dev": true
},
"node_modules/gl-matrix": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
"integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
},
"node_modules/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
@ -6076,6 +6166,30 @@
"process": "^0.11.10"
}
},
"node_modules/global-prefix": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
"integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
"dependencies": {
"ini": "^1.3.5",
"kind-of": "^6.0.2",
"which": "^1.3.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/global-prefix/node_modules/which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"which": "bin/which"
}
},
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@ -6487,8 +6601,7 @@
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"node_modules/inquirer": {
"version": "8.2.0",
@ -7144,6 +7257,14 @@
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-1.0.1.tgz",
"integrity": "sha512-Y75c18KdvLKRmqHc0u2WUYud1vEj54i+8SNBxsowr6LJJsnNUJ8KK8cH7uHDpC5U66NNlieEzVxeWipZaYfN0w=="
},
"node_modules/kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/kleur": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@ -7382,6 +7503,43 @@
"typescript": "^4.9.4"
}
},
"node_modules/maplibre-gl": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-2.4.0.tgz",
"integrity": "sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA==",
"hasInstallScript": true,
"dependencies": {
"@mapbox/geojson-rewind": "^0.5.2",
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
"@mapbox/mapbox-gl-supported": "^2.0.1",
"@mapbox/point-geometry": "^0.1.0",
"@mapbox/tiny-sdf": "^2.0.5",
"@mapbox/unitbezier": "^0.0.1",
"@mapbox/vector-tile": "^1.3.1",
"@mapbox/whoots-js": "^3.1.0",
"@types/geojson": "^7946.0.10",
"@types/mapbox__point-geometry": "^0.1.2",
"@types/mapbox__vector-tile": "^1.3.0",
"@types/pbf": "^3.0.2",
"csscolorparser": "~1.0.3",
"earcut": "^2.2.4",
"geojson-vt": "^3.2.1",
"gl-matrix": "^3.4.3",
"global-prefix": "^3.0.0",
"murmurhash-js": "^1.0.0",
"pbf": "^3.2.1",
"potpack": "^1.0.2",
"quickselect": "^2.0.0",
"supercluster": "^7.1.5",
"tinyqueue": "^2.0.3",
"vt-pbf": "^3.1.3"
}
},
"node_modules/maplibre-gl/node_modules/quickselect": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -7667,6 +7825,11 @@
"resolved": "https://registry.npmjs.org/multigeojson/-/multigeojson-0.0.1.tgz",
"integrity": "sha512-FbCR4K9xp+0lbcHmJk1TLjXW+l82VcEhDDIU7g3DWm47WyGSpuGX8lJx58pOPa61T0b1zQUJVjllPJ6eXe54lg=="
},
"node_modules/murmurhash-js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw=="
},
"node_modules/mute-stream": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
@ -8195,6 +8358,21 @@
"pathe": "^1.0.0"
}
},
"node_modules/pmtiles": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-2.7.0.tgz",
"integrity": "sha512-/0WERHBKCt9P8dlQaROQd1CE/J1sqYhdGkDgOROaxCjnpm/U+guWNNxZPPduUy6Wu7wQtPghA7sS4t0T18FKAA==",
"peer": true,
"dependencies": {
"fflate": "^0.7.3"
}
},
"node_modules/pmtiles/node_modules/fflate": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz",
"integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==",
"peer": true
},
"node_modules/point-in-polygon": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
@ -8220,6 +8398,11 @@
"node": ">=4"
}
},
"node_modules/potpack": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="
},
"node_modules/prebuild-install": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
@ -9466,6 +9649,19 @@
"resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.9.0.tgz",
"integrity": "sha512-vMJ8Byp1uIPoj+wb9c1AdK4jpkSKVAywgHX0lqY7zt6+EWRRC3Z+0Ucfjy/0yxTVO1hwwchZe4uoFNqrIC24+A=="
},
"node_modules/supercluster": {
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz",
"integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==",
"dependencies": {
"kdbush": "^3.0.0"
}
},
"node_modules/supercluster/node_modules/kdbush": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz",
"integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew=="
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@ -9493,7 +9689,6 @@
"version": "3.55.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
"dev": true,
"engines": {
"node": ">= 8"
}
@ -11446,6 +11641,16 @@
"node": ">=0.4.0"
}
},
"node_modules/vt-pbf": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
"integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==",
"dependencies": {
"@mapbox/point-geometry": "0.1.0",
"@mapbox/vector-tile": "^1.3.1",
"pbf": "^3.2.1"
}
},
"node_modules/w3c-xmlserializer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
@ -13036,11 +13241,49 @@
"minimist": "^1.2.6"
}
},
"@mapbox/jsonlint-lines-primitives": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ=="
},
"@mapbox/mapbox-gl-supported": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz",
"integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ=="
},
"@mapbox/point-geometry": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="
},
"@mapbox/sphericalmercator": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@mapbox/sphericalmercator/-/sphericalmercator-1.2.0.tgz",
"integrity": "sha512-ZTOuuwGuMOJN+HEmG/68bSEw15HHaMWmQ5gdTsWdWsjDe56K1kGvLOK6bOSC8gWgIvEO0w6un/2Gvv1q5hJSkQ=="
},
"@mapbox/tiny-sdf": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz",
"integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA=="
},
"@mapbox/unitbezier": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz",
"integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw=="
},
"@mapbox/vector-tile": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
"integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==",
"requires": {
"@mapbox/point-geometry": "~0.1.0"
}
},
"@mapbox/whoots-js": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q=="
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -13064,6 +13307,12 @@
"fastq": "^1.6.0"
}
},
"@onsvisual/svelte-maps": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@onsvisual/svelte-maps/-/svelte-maps-1.1.6.tgz",
"integrity": "sha512-qrt/Z7SvOLzPdz+q3vL6VEVQp3ayavmPE7Rqbtbhicq3JFx/l/1W2VS8ryHgFGXi5nuuZtAzxW7/p8ZM0L/VOw==",
"requires": {}
},
"@parcel/service-worker": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/@parcel/service-worker/-/service-worker-2.8.2.tgz",
@ -14417,8 +14666,7 @@
"@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
"devOptional": true
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA=="
},
"@types/istanbul-lib-coverage": {
"version": "2.0.4",
@ -14486,6 +14734,21 @@
"integrity": "sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow==",
"dev": true
},
"@types/mapbox__point-geometry": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz",
"integrity": "sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA=="
},
"@types/mapbox__vector-tile": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz",
"integrity": "sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g==",
"requires": {
"@types/geojson": "*",
"@types/mapbox__point-geometry": "*",
"@types/pbf": "*"
}
},
"@types/node": {
"version": "18.11.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
@ -14500,6 +14763,11 @@
"@types/node": "*"
}
},
"@types/pbf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.2.tgz",
"integrity": "sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ=="
},
"@types/prompt-sync": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.2.0.tgz",
@ -15382,6 +15650,11 @@
"utrie": "^1.0.2"
}
},
"csscolorparser": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz",
"integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w=="
},
"cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@ -16288,6 +16561,11 @@
}
}
},
"geojson-vt": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
"integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg=="
},
"geojson2svg": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/geojson2svg/-/geojson2svg-1.3.3.tgz",
@ -16335,6 +16613,11 @@
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
"dev": true
},
"gl-matrix": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz",
"integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA=="
},
"glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
@ -16365,6 +16648,26 @@
"process": "^0.11.10"
}
},
"global-prefix": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
"integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
"requires": {
"ini": "^1.3.5",
"kind-of": "^6.0.2",
"which": "^1.3.1"
},
"dependencies": {
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"requires": {
"isexe": "^2.0.0"
}
}
}
},
"globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@ -16668,8 +16971,7 @@
"ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"inquirer": {
"version": "8.2.0",
@ -17149,6 +17451,11 @@
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-1.0.1.tgz",
"integrity": "sha512-Y75c18KdvLKRmqHc0u2WUYud1vEj54i+8SNBxsowr6LJJsnNUJ8KK8cH7uHDpC5U66NNlieEzVxeWipZaYfN0w=="
},
"kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
},
"kleur": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@ -17335,6 +17642,44 @@
"typescript": "^4.9.4"
}
},
"maplibre-gl": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-2.4.0.tgz",
"integrity": "sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA==",
"requires": {
"@mapbox/geojson-rewind": "^0.5.2",
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
"@mapbox/mapbox-gl-supported": "^2.0.1",
"@mapbox/point-geometry": "^0.1.0",
"@mapbox/tiny-sdf": "^2.0.5",
"@mapbox/unitbezier": "^0.0.1",
"@mapbox/vector-tile": "^1.3.1",
"@mapbox/whoots-js": "^3.1.0",
"@types/geojson": "^7946.0.10",
"@types/mapbox__point-geometry": "^0.1.2",
"@types/mapbox__vector-tile": "^1.3.0",
"@types/pbf": "^3.0.2",
"csscolorparser": "~1.0.3",
"earcut": "^2.2.4",
"geojson-vt": "^3.2.1",
"gl-matrix": "^3.4.3",
"global-prefix": "^3.0.0",
"murmurhash-js": "^1.0.0",
"pbf": "^3.2.1",
"potpack": "^1.0.2",
"quickselect": "^2.0.0",
"supercluster": "^7.1.5",
"tinyqueue": "^2.0.3",
"vt-pbf": "^3.1.3"
},
"dependencies": {
"quickselect": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
}
}
},
"merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -17547,6 +17892,11 @@
"resolved": "https://registry.npmjs.org/multigeojson/-/multigeojson-0.0.1.tgz",
"integrity": "sha512-FbCR4K9xp+0lbcHmJk1TLjXW+l82VcEhDDIU7g3DWm47WyGSpuGX8lJx58pOPa61T0b1zQUJVjllPJ6eXe54lg=="
},
"murmurhash-js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
"integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw=="
},
"mute-stream": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
@ -17945,6 +18295,23 @@
"pathe": "^1.0.0"
}
},
"pmtiles": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-2.7.0.tgz",
"integrity": "sha512-/0WERHBKCt9P8dlQaROQd1CE/J1sqYhdGkDgOROaxCjnpm/U+guWNNxZPPduUy6Wu7wQtPghA7sS4t0T18FKAA==",
"peer": true,
"requires": {
"fflate": "^0.7.3"
},
"dependencies": {
"fflate": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz",
"integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==",
"peer": true
}
}
},
"point-in-polygon": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
@ -17967,6 +18334,11 @@
"util-deprecate": "^1.0.2"
}
},
"potpack": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
"integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="
},
"prebuild-install": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
@ -18902,6 +19274,21 @@
"resolved": "https://registry.npmjs.org/suncalc/-/suncalc-1.9.0.tgz",
"integrity": "sha512-vMJ8Byp1uIPoj+wb9c1AdK4jpkSKVAywgHX0lqY7zt6+EWRRC3Z+0Ucfjy/0yxTVO1hwwchZe4uoFNqrIC24+A=="
},
"supercluster": {
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz",
"integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==",
"requires": {
"kdbush": "^3.0.0"
},
"dependencies": {
"kdbush": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz",
"integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew=="
}
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@ -18919,8 +19306,7 @@
"svelte": {
"version": "3.55.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
"dev": true
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ=="
},
"svelte-check": {
"version": "3.0.3",
@ -20360,6 +20746,16 @@
}
}
},
"vt-pbf": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
"integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==",
"requires": {
"@mapbox/point-geometry": "0.1.0",
"@mapbox/vector-tile": "^1.3.1",
"pbf": "^3.2.1"
}
},
"w3c-xmlserializer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",

View file

@ -61,6 +61,7 @@
"not op_mini all"
],
"dependencies": {
"@onsvisual/svelte-maps": "^1.1.6",
"@rollup/plugin-typescript": "^11.0.0",
"@turf/boolean-intersects": "^6.5.0",
"@turf/buffer": "^6.5.0",
@ -89,6 +90,7 @@
"libphonenumber-js": "^1.10.8",
"lz-string": "^1.4.4",
"mangrove-reviews-typescript": "^1.1.0",
"maplibre-gl": "^2.4.0",
"opening_hours": "^3.6.0",
"osm-auth": "^1.0.2",
"osmtogeojson": "^3.0.0-beta.5",

81
test.ts
View file

@ -1,25 +1,72 @@
import ContactLink from "./UI/BigComponents/ContactLink.svelte"
import SvelteUIElement from "./UI/Base/SvelteUIElement"
import { Utils } from "./Utils"
import List from "./UI/Base/List"
import MaplibreMap from "./UI/Map/MaplibreMap.svelte"
import { Store, Stores, UIEventSource } from "./Logic/UIEventSource"
import { MapLibreAdaptor } from "./UI/Map/MapLibreAdaptor"
import {
EditorLayerIndexProperties,
RasterLayerPolygon,
RasterLayerProperties,
} from "./Models/RasterLayers"
import type { Map as MlMap } from "maplibre-gl"
import { AvailableRasterLayers } from "./Models/RasterLayers"
import Loc from "./Models/Loc"
import { BBox } from "./Logic/BBox"
import { GeoOperations } from "./Logic/GeoOperations"
import { Tiles } from "./Models/TileRange"
import { Stores } from "./Logic/UIEventSource"
import RasterLayerPicker from "./UI/Map/RasterLayerPicker.svelte"
import BackgroundLayerResetter from "./Logic/Actors/BackgroundLayerResetter"
async function main() {
const location: [number, number] = [3.21, 51.2]
const t = Tiles.embedded_tile(location[1], location[0], 6)
const url = `https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/community_index/tile_${t.z}_${t.x}_${t.y}.geojson`
const be = Stores.FromPromise(Utils.downloadJson(url)).mapD(
(data) => data.features.find((f) => GeoOperations.inside(location, f)).properties
const mlmap = new UIEventSource<MlMap>(undefined)
const locationControl = new UIEventSource<Loc>({
zoom: 14,
lat: 51.1,
lon: 3.1,
})
new SvelteUIElement(MaplibreMap, {
map: mlmap,
})
.SetClass("border border-black")
.SetStyle("height: 50vh; width: 90%; margin: 1%")
.AttachTo("maindiv")
const bg = new UIEventSource<RasterLayerPolygon>(undefined)
new MapLibreAdaptor(mlmap, {
backgroundLayer: bg,
locationControl,
})
const availableLayersBboxes = Stores.ListStabilized(
locationControl.mapD((loc) => {
const lonlat: [number, number] = [loc.lon, loc.lat]
return AvailableRasterLayers.EditorLayerIndex.filter((eliPolygon) =>
BBox.get(eliPolygon).contains(lonlat)
)
})
)
new SvelteUIElement(ContactLink, { country: be }).AttachTo("maindiv")
/*
const links = data.features
.filter((f) => GeoOperations.inside(location, f))
.map((f) => new SvelteUIElement(ContactLink, { country: f.properties }))
new List(links).AttachTo("maindiv")
//*/
const availableLayers: Store<RasterLayerPolygon[]> = Stores.ListStabilized(
availableLayersBboxes.map((eliPolygons) => {
const loc = locationControl.data
const lonlat: [number, number] = [loc.lon, loc.lat]
const matching: RasterLayerPolygon[] = eliPolygons.filter((eliPolygon) => {
if (eliPolygon.geometry === null) {
return true // global ELI-layer
}
return GeoOperations.inside(lonlat, eliPolygon)
})
matching.unshift(AvailableRasterLayers.osmCarto)
matching.push(...AvailableRasterLayers.globalLayers)
return matching
})
)
availableLayers.map((a) =>
console.log(
"Availabe layers at current location:",
a.map((al) => al.properties.id)
)
)
new BackgroundLayerResetter(bg, availableLayers)
new SvelteUIElement(RasterLayerPicker, { availableLayers, value: bg }).AttachTo("extradiv")
}
main().then((_) => {})