diff --git a/website/package-lock.json b/website/package-lock.json index 6ebd6cc74..2c6b94be4 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -18,7 +18,7 @@ "gpx-wasm": "file:../wasm/pkg", "jszip": "^3.10.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^5.21.1", + "maplibre-gl": "^6.15.0", "sanitize-html": "^2.17.0", "sortablejs": "^1.15.6" }, @@ -1695,13 +1695,18 @@ } }, "node_modules/@mapbox/jsonlint-lines-primitives": { - "version": "2.0.2", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz", + "integrity": "sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 22" } }, "node_modules/@mapbox/point-geometry": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz", + "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==", "license": "ISC" }, "node_modules/@mapbox/sphericalmercator": { @@ -1712,24 +1717,32 @@ "license": "MIT" }, "node_modules/@mapbox/tiny-sdf": { - "version": "2.0.7", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.3.0.tgz", + "integrity": "sha512-gBvcNDBBB66bgQYvnaJ6Jk+VCaMmVq5+IZR2kWPisT88Z8s0kBQt6hfVi8QgRW8Vpg0UWZzBA4ZSyzHHC+VeRA==", "license": "BSD-2-Clause" }, "node_modules/@mapbox/unitbezier": { - "version": "0.0.1", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz", + "integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==", "license": "BSD-2-Clause" }, "node_modules/@mapbox/vector-tile": { - "version": "2.0.4", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-3.0.0.tgz", + "integrity": "sha512-Qf10S1uIHMk20ri/IVBnpS+esUEkVaR5Hftmz88jTInrpmWgPGJfPe3LVjjlE77trLx8tH6qjTG7uWH9hIq/0Q==", "license": "BSD-3-Clause", "dependencies": { "@mapbox/point-geometry": "~1.1.0", "@types/geojson": "^7946.0.16", - "pbf": "^4.0.1" + "pbf": "^5.0.0" } }, "node_modules/@mapbox/vector-tile/node_modules/pbf": { - "version": "4.0.1", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz", + "integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==", "license": "BSD-3-Clause", "dependencies": { "resolve-protobuf-schema": "^2.1.0" @@ -1738,18 +1751,13 @@ "pbf": "bin/pbf" } }, - "node_modules/@mapbox/whoots-js": { - "version": "3.1.0", - "license": "ISC", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@maplibre/geojson-vt": { - "version": "6.0.4", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.3.0.tgz", + "integrity": "sha512-3Zw2jsU9pIJG3G2CQFbLjWj2MlAHodhD7cSY+15iywUuLmmkgzcKRnueVRZ4AcC/5wOtfhidcJAAzBpoBNs3Xg==", "license": "ISC", "dependencies": { - "kdbush": "^4.0.2" + "kdbush": "^4.1.0" } }, "node_modules/@maplibre/maplibre-gl-geocoder": { @@ -1770,15 +1778,16 @@ } }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "24.8.1", + "version": "26.4.4", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-26.4.4.tgz", + "integrity": "sha512-hG4RZR4PBd/+6A3ODdZ3AnQRF106ZQk1y3bhwDg8bH/A4Oz2tnRUJVVWs9BZa6gXZ1d3UKe89C+Mm+olGBELvA==", "license": "ISC", "dependencies": { - "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/unitbezier": "^0.0.1", + "@mapbox/jsonlint-lines-primitives": "^2.0.3", + "@mapbox/unitbezier": "^1.0.0", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", "quickselect": "^3.0.0", - "rw": "^1.3.3", "tinyqueue": "^3.0.0" }, "bin": { @@ -1788,31 +1797,29 @@ } }, "node_modules/@maplibre/mlt": { - "version": "1.1.8", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.3.0.tgz", + "integrity": "sha512-7M2O7ABM80Oi8XPkfkrjKB8mOBIxGoVgkA52Mu4RMWoBvvMdagQ522+WxVYujZsMpqIr3BpzrTbXrm4k8lCzCQ==", "license": "(MIT OR Apache-2.0)", "dependencies": { "@mapbox/point-geometry": "^1.1.0" } }, "node_modules/@maplibre/vt-pbf": { - "version": "4.3.0", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.2.tgz", + "integrity": "sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==", "license": "MIT", "dependencies": { "@mapbox/point-geometry": "^1.1.0", - "@mapbox/vector-tile": "^2.0.4", - "@maplibre/geojson-vt": "^5.0.4", "@types/geojson": "^7946.0.16", - "@types/supercluster": "^7.1.3", - "pbf": "^4.0.1", - "supercluster": "^8.0.1" + "pbf": "^5.1.0" } }, - "node_modules/@maplibre/vt-pbf/node_modules/@maplibre/geojson-vt": { - "version": "5.0.4", - "license": "ISC" - }, "node_modules/@maplibre/vt-pbf/node_modules/pbf": { - "version": "4.0.1", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz", + "integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==", "license": "BSD-3-Clause", "dependencies": { "resolve-protobuf-schema": "^2.1.0" @@ -2758,13 +2765,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/supercluster": { - "version": "7.1.3", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, "node_modules/@types/three": { "version": "0.134.0", "license": "MIT" @@ -3098,6 +3098,15 @@ "node": "18 || 20 || >=22" } }, + "node_modules/bidi-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz", + "integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/bits-ui": { "version": "2.17.2", "dev": true, @@ -4177,6 +4186,8 @@ }, "node_modules/json-stringify-pretty-compact": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", "license": "MIT" }, "node_modules/jszip": { @@ -4190,7 +4201,9 @@ } }, "node_modules/kdbush": { - "version": "4.0.2", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz", + "integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==", "license": "ISC" }, "node_modules/keyv": { @@ -4611,25 +4624,26 @@ "license": "(MIT AND Zlib)" }, "node_modules/maplibre-gl": { - "version": "5.22.0", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-6.15.0.tgz", + "integrity": "sha512-kxFetbvUHDOVaAP1CYb226Mdk2QhzIPddqM0xJTFD35wu78Mpd6NM7+H68ALjc6Oa0kAOX5EAdMKyvD2u3qfcg==", "license": "BSD-3-Clause", "dependencies": { - "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/point-geometry": "^1.1.0", - "@mapbox/tiny-sdf": "^2.0.7", - "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^2.0.4", - "@mapbox/whoots-js": "^3.1.0", - "@maplibre/geojson-vt": "^6.0.4", - "@maplibre/maplibre-gl-style-spec": "^24.8.1", - "@maplibre/mlt": "^1.1.8", - "@maplibre/vt-pbf": "^4.3.0", + "@mapbox/tiny-sdf": "^2.3.0", + "@mapbox/unitbezier": "^1.0.0", + "@mapbox/vector-tile": "^3.0.0", + "@maplibre/geojson-vt": "^6.3.0", + "@maplibre/maplibre-gl-style-spec": "^26.4.4", + "@maplibre/mlt": "^1.3.0", + "@maplibre/vt-pbf": "^4.3.2", "@types/geojson": "^7946.0.16", - "earcut": "^3.0.2", + "bidi-js": "^1.1.0", + "earcut": "^3.2.4", "gl-matrix": "^3.4.4", - "kdbush": "^4.0.2", + "kdbush": "^4.1.0", "murmurhash-js": "^1.0.0", - "pbf": "^4.0.1", + "pbf": "^5.1.2", "potpack": "^2.1.0", "quickselect": "^3.0.0", "tinyqueue": "^3.0.0" @@ -4643,11 +4657,15 @@ } }, "node_modules/maplibre-gl/node_modules/earcut": { - "version": "3.0.2", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.2.4.tgz", + "integrity": "sha512-FHc6yNpnTORB0tM5kmZ03iD2mDuNakDE2lvf4Hw1XkfxtEHIGVi01B/GqUSwx+NcC1WGjidAfpTXzeALtPtfpg==", "license": "ISC" }, "node_modules/maplibre-gl/node_modules/pbf": { - "version": "4.0.1", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz", + "integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==", "license": "BSD-3-Clause", "dependencies": { "resolve-protobuf-schema": "^2.1.0" @@ -4708,6 +4726,8 @@ }, "node_modules/minimist": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5256,6 +5276,8 @@ }, "node_modules/quickselect": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", "license": "ISC" }, "node_modules/rbush": { @@ -5294,6 +5316,15 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -5391,10 +5422,6 @@ } } }, - "node_modules/rw": { - "version": "1.3.3", - "license": "BSD-3-Clause" - }, "node_modules/rxjs": { "version": "7.8.2", "license": "Apache-2.0", @@ -5628,13 +5655,6 @@ "xtend": "^4.0.0" } }, - "node_modules/supercluster": { - "version": "8.0.1", - "license": "ISC", - "dependencies": { - "kdbush": "^4.0.2" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5876,6 +5896,8 @@ }, "node_modules/tinyqueue": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", "license": "ISC" }, "node_modules/totalist": { diff --git a/website/package.json b/website/package.json index 2415d3ca0..f397cbbf8 100644 --- a/website/package.json +++ b/website/package.json @@ -78,7 +78,7 @@ "gpx-wasm": "file:../wasm/pkg", "jszip": "^3.10.1", "mapillary-js": "^4.1.2", - "maplibre-gl": "^5.21.1", + "maplibre-gl": "^6.15.0", "sanitize-html": "^2.17.0", "sortablejs": "^1.15.6" } diff --git a/website/src/lib/components/map/custom-control/CustomControl.svelte b/website/src/lib/components/map/custom-control/CustomControl.svelte index cab4348fb..c5c9ab2d1 100644 --- a/website/src/lib/components/map/custom-control/CustomControl.svelte +++ b/website/src/lib/components/map/custom-control/CustomControl.svelte @@ -2,6 +2,7 @@ import CustomControl from './custom-control'; import { map } from '$lib/components/map/map'; import { onMount, type Snippet } from 'svelte'; + import type { Map } from 'maplibre-gl'; let { position = 'top-right', @@ -17,7 +18,7 @@ let control: CustomControl | null = null; onMount(() => { - map.onLoad((map: maplibregl.Map) => { + map.onLoad((map: Map) => { if (position.includes('right')) container.classList.add('float-right'); else container.classList.add('float-left'); container.classList.remove('hidden'); diff --git a/website/src/lib/components/map/gpx-layer/gpx-layer-popup.ts b/website/src/lib/components/map/gpx-layer/gpx-layer-popup.ts index 42905788e..d3393792a 100644 --- a/website/src/lib/components/map/gpx-layer/gpx-layer-popup.ts +++ b/website/src/lib/components/map/gpx-layer/gpx-layer-popup.ts @@ -1,9 +1,10 @@ import { MapPopup } from '$lib/components/map/map-popup'; +import type { Map } from 'maplibre-gl'; export let waypointPopup: MapPopup | null = null; export let trackpointPopup: MapPopup | null = null; -export function createPopups(map: maplibregl.Map) { +export function createPopups(map: Map) { removePopups(); waypointPopup = new MapPopup(map, { closeButton: false, diff --git a/website/src/lib/components/map/gpx-layer/gpx-layer.ts b/website/src/lib/components/map/gpx-layer/gpx-layer.ts index e904ebe65..a6d86e967 100644 --- a/website/src/lib/components/map/gpx-layer/gpx-layer.ts +++ b/website/src/lib/components/map/gpx-layer/gpx-layer.ts @@ -1,9 +1,10 @@ import { get, type Readable } from 'svelte/store'; -import maplibregl, { +import { type GeoJSONSource, type FilterSpecification, type MapLayerMouseEvent, type MapLayerTouchEvent, + Point, } from 'maplibre-gl'; import { map } from '$lib/components/map/map'; import { waypointPopup, trackpointPopup } from './gpx-layer-popup'; @@ -61,7 +62,7 @@ export class GPXLayer { selected: boolean = false; currentWaypointData: GeoJSON.FeatureCollection | null = null; draggedWaypointIndex: number | null = null; - draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0); + draggingStartingPosition: Point = new Point(0, 0); unsubscribe: Function[] = []; updateBinded: () => void = this.update.bind(this); diff --git a/website/src/lib/components/map/layer-control/LayerControl.svelte b/website/src/lib/components/map/layer-control/LayerControl.svelte index 233389142..4f6b832cb 100644 --- a/website/src/lib/components/map/layer-control/LayerControl.svelte +++ b/website/src/lib/components/map/layer-control/LayerControl.svelte @@ -7,6 +7,7 @@ import { Layers } from '@lucide/svelte'; import { settings } from '$lib/state/settings'; import { map } from '$lib/components/map/map'; + import type { Map } from 'maplibre-gl'; let container: HTMLDivElement; let overpassLayer: OverpassLayer; @@ -21,7 +22,7 @@ selectedOverpassTree, } = settings; - map.onLoad((_map: maplibregl.Map) => { + map.onLoad((_map: Map) => { if (overpassLayer) { overpassLayer.remove(); } diff --git a/website/src/lib/components/map/layer-control/OverpassPopup.svelte b/website/src/lib/components/map/layer-control/OverpassPopup.svelte index 716c7a6a0..19b71cc12 100644 --- a/website/src/lib/components/map/layer-control/OverpassPopup.svelte +++ b/website/src/lib/components/map/layer-control/OverpassPopup.svelte @@ -15,7 +15,9 @@ poi: PopupItem; } = $props(); - let tags: Record = $derived(poi ? JSON.parse(poi.item.tags) : {}); + let tags: Record = $derived( + poi ? (typeof poi.item.tags == 'object' ? poi.item.tags : JSON.parse(poi.item.tags)) : {} + ); let name = $derived.by(() => { if (poi) { if (tags.name !== undefined && tags.name !== '') { diff --git a/website/src/lib/components/map/layer-control/overpass-layer.ts b/website/src/lib/components/map/layer-control/overpass-layer.ts index bed86b439..d026d84ab 100644 --- a/website/src/lib/components/map/layer-control/overpass-layer.ts +++ b/website/src/lib/components/map/layer-control/overpass-layer.ts @@ -4,7 +4,7 @@ import { get, writable } from 'svelte/store'; import { overpassQueryData } from '$lib/data/layers'; import { MapPopup } from '$lib/components/map/map-popup'; import { settings } from '$lib/state/settings'; -import type { GeoJSONSource } from 'maplibre-gl'; +import type { GeoJSONSource, Map as MapLibreMap } from 'maplibre-gl'; import { ANCHOR_LAYER_KEY } from '$lib/components/map/style'; import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager'; import { loadSVGIcon } from '$lib/utils'; @@ -23,7 +23,7 @@ export class OverpassLayer { overpassUrl = 'https://overpass.gpx.studio/api/interpreter'; minZoom = 12; queryZoom = 12; - map: maplibregl.Map; + map: MapLibreMap; layerEventManager: MapLayerEventManager; popup: MapPopup; @@ -39,7 +39,7 @@ export class OverpassLayer { updateBinded = this.update.bind(this); onHoverBinded = this.onHover.bind(this); - constructor(map: maplibregl.Map, layerEventManager: MapLayerEventManager) { + constructor(map: MapLibreMap, layerEventManager: MapLayerEventManager) { this.map = map; this.layerEventManager = layerEventManager; this.popup = new MapPopup(map, { diff --git a/website/src/lib/components/map/map-layer-event-manager.ts b/website/src/lib/components/map/map-layer-event-manager.ts index 2dd8e5cb9..043c5958d 100644 --- a/website/src/lib/components/map/map-layer-event-manager.ts +++ b/website/src/lib/components/map/map-layer-event-manager.ts @@ -1,11 +1,20 @@ import { engine } from '$lib/engine'; import { get } from 'svelte/store'; -import maplibregl from 'maplibre-gl'; +import { + LngLatBounds, + type Map, + type MapGeoJSONFeature, + type MapLayerMouseEvent, + type MapLayerTouchEvent, + MapMouseEvent, + MapTouchEvent, + type Point, +} from 'maplibre-gl'; -type MapLayerMouseEventListener = (e: maplibregl.MapLayerMouseEvent) => void; -type MapLayerTouchEventListener = (e: maplibregl.MapLayerTouchEvent) => void; +type MapLayerMouseEventListener = (e: MapLayerMouseEvent) => void; +type MapLayerTouchEventListener = (e: MapLayerTouchEvent) => void; type MapLayerListener = { - features: maplibregl.MapGeoJSONFeature[]; + features: MapGeoJSONFeature[]; mousemoves: MapLayerMouseEventListener[]; mouseenters: MapLayerMouseEventListener[]; mouseleaves: MapLayerMouseEventListener[]; @@ -16,10 +25,10 @@ type MapLayerListener = { }; export class MapLayerEventManager { - private _map: maplibregl.Map; + private _map: Map; private _listeners: Record = {}; - constructor(map: maplibregl.Map) { + constructor(map: Map) { this._map = map; this._map.on('mousemove', this._handleMouseMove.bind(this)); this._map.on('click', this._handleMouseClick.bind(this, 'click')); @@ -142,7 +151,7 @@ export class MapLayerEventManager { } } - private _handleMouseMove(e: maplibregl.MapMouseEvent) { + private _handleMouseMove(e: MapMouseEvent) { if (e.originalEvent.buttons > 0) return; const featuresByLayer = this._getRenderedFeaturesByLayer(e); Object.keys(this._listeners).forEach((layerId) => { @@ -151,29 +160,20 @@ export class MapLayerEventManager { if ((features.length == 0) != (listener.features.length == 0)) { if (features.length > 0) { if (listener.mouseenters.length > 0) { - const event = new maplibregl.MapMouseEvent( - 'mouseenter', - e.target, - e.originalEvent, - { - features: featuresByLayer[layerId]!, - } - ); + const event = new MapMouseEvent('mouseenter', e.target, e.originalEvent, { + features: featuresByLayer[layerId]!, + }); listener.mouseenters.forEach((l) => l(event)); } } else { if (listener.mouseleaves.length > 0) { - const event = new maplibregl.MapMouseEvent( - 'mouseleave', - e.target, - e.originalEvent - ); + const event = new MapMouseEvent('mouseleave', e.target, e.originalEvent); listener.mouseleaves.forEach((l) => l(event)); } } } if (features.length > 0 && listener.mousemoves.length > 0) { - const event = new maplibregl.MapMouseEvent('mousemove', e.target, e.originalEvent, { + const event = new MapMouseEvent('mousemove', e.target, e.originalEvent, { features: featuresByLayer[layerId]!, }); listener.mousemoves.forEach((l) => l(event)); @@ -182,49 +182,39 @@ export class MapLayerEventManager { }); } - private _handleMouseClick(type: string, e: maplibregl.MapMouseEvent) { + private _handleMouseClick(type: string, e: MapMouseEvent) { const featuresByLayer = this._getRenderedFeaturesByLayer(e); Object.keys(this._listeners).forEach((layerId) => { const features = featuresByLayer[layerId] || []; const listener = this._listeners[layerId]; if (features.length > 0) { if (type === 'click' && listener.clicks.length > 0) { - const event = new maplibregl.MapMouseEvent('click', e.target, e.originalEvent, { + const event = new MapMouseEvent('click', e.target, e.originalEvent, { features: features, }); listener.clicks.forEach((l) => l(event)); } else if (type === 'contextmenu' && listener.contextmenus.length > 0) { - const event = new maplibregl.MapMouseEvent( - 'contextmenu', - e.target, - e.originalEvent, - { - features: features, - } - ); + const event = new MapMouseEvent('contextmenu', e.target, e.originalEvent, { + features: features, + }); listener.contextmenus.forEach((l) => l(event)); } else if (type === 'mousedown' && listener.mousedowns.length > 0) { - const event = new maplibregl.MapMouseEvent( - 'mousedown', - e.target, - e.originalEvent, - { - features: features, - } - ); + const event = new MapMouseEvent('mousedown', e.target, e.originalEvent, { + features: features, + }); listener.mousedowns.forEach((l) => l(event)); } } }); } - private _handleTouchStart(e: maplibregl.MapTouchEvent) { + private _handleTouchStart(e: MapTouchEvent) { const featuresByLayer = this._getRenderedFeaturesByLayer(e); Object.keys(this._listeners).forEach((layerId) => { const features = featuresByLayer[layerId] || []; const listener = this._listeners[layerId]; if (features.length > 0) { - const event: maplibregl.MapLayerTouchEvent = new maplibregl.MapTouchEvent( + const event: MapLayerTouchEvent = new MapTouchEvent( 'touchstart', e.target, e.originalEvent @@ -235,18 +225,15 @@ export class MapLayerEventManager { }); } - private _getBounds(point: maplibregl.Point) { + private _getBounds(point: Point) { const delta = 30; - return new maplibregl.LngLatBounds( + return new LngLatBounds( this._map.unproject([point.x - delta, point.y + delta]), this._map.unproject([point.x + delta, point.y - delta]) ); } - private _filterLayersIntersectingBounds( - layerIds: string[], - bounds: maplibregl.LngLatBounds - ): string[] { + private _filterLayersIntersectingBounds(layerIds: string[], bounds: LngLatBounds): string[] { const files = get(engine.files); let result = layerIds.filter((layerId) => { if (!this._map.getLayer(layerId)) return false; @@ -268,7 +255,7 @@ export class MapLayerEventManager { return result; } - private _getRenderedFeaturesByLayer(e: maplibregl.MapMouseEvent | maplibregl.MapTouchEvent) { + private _getRenderedFeaturesByLayer(e: MapMouseEvent | MapTouchEvent) { const layerIds = this._filterLayersIntersectingBounds( Object.keys(this._listeners), this._getBounds(e.point) @@ -277,7 +264,7 @@ export class MapLayerEventManager { layerIds.length > 0 ? this._map.queryRenderedFeatures(e.point, { layers: layerIds }) : []; - const featuresByLayer: Record = {}; + const featuresByLayer: Record = {}; features.forEach((f) => { if (!featuresByLayer[f.layer.id]) { featuresByLayer[f.layer.id] = []; diff --git a/website/src/lib/components/map/map-popup.ts b/website/src/lib/components/map/map-popup.ts index a07fa72e2..389359fdd 100644 --- a/website/src/lib/components/map/map-popup.ts +++ b/website/src/lib/components/map/map-popup.ts @@ -1,4 +1,4 @@ -import maplibregl from 'maplibre-gl'; +import { LngLat, type Map, type MapMouseEvent, Popup, type PopupOptions } from 'maplibre-gl'; import { mount, tick, unmount } from 'svelte'; import { get, writable, type Writable } from 'svelte/store'; import MapPopupComponent from '$lib/components/map/MapPopup.svelte'; @@ -13,15 +13,15 @@ export type PopupItem = { }; export class MapPopup { - map: maplibregl.Map; - popup: maplibregl.Popup; + map: Map; + popup: Popup; item: Writable = writable(null); component: ReturnType; maybeHideBinded = this.maybeHide.bind(this); - constructor(map: maplibregl.Map, options?: maplibregl.PopupOptions) { + constructor(map: Map, options?: PopupOptions) { this.map = map; - this.popup = new maplibregl.Popup(options); + this.popup = new Popup(options); this.component = mount(MapPopupComponent, { target: document.body, props: { @@ -53,7 +53,7 @@ export class MapPopup { this.map.on('mousemove', this.maybeHideBinded); } - maybeHide(e: maplibregl.MapMouseEvent) { + maybeHide(e: MapMouseEvent) { const item = get(this.item); if (item === null) { this.hide(); @@ -77,8 +77,8 @@ export class MapPopup { getCoordinates() { const item = get(this.item); if (item === null) { - return new maplibregl.LngLat(0, 0); + return new LngLat(0, 0); } - return new maplibregl.LngLat(item.item.lng, item.item.lat); + return new LngLat(item.item.lng, item.item.lat); } } diff --git a/website/src/lib/components/map/map.ts b/website/src/lib/components/map/map.ts index cf2e7028e..4af23e4e3 100644 --- a/website/src/lib/components/map/map.ts +++ b/website/src/lib/components/map/map.ts @@ -1,5 +1,6 @@ -import maplibregl from 'maplibre-gl'; +import * as maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; +import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url'; import MaplibreGeocoder, { type MaplibreGeocoderFeatureResults, } from '@maplibre/maplibre-gl-geocoder'; @@ -39,6 +40,7 @@ export class MapLibreGLMap { geocoder: boolean, geolocate: boolean ) { + maplibregl.setWorkerUrl(workerUrl); this._maptilerKey = maptilerKey; this._styleManager = new StyleManager(this._mapStore, this._maptilerKey); // MapLibre gives Safari more workers, but only recognizes it by its user agent: not diff --git a/website/src/lib/components/map/street-view-control/StreetViewControl.svelte b/website/src/lib/components/map/street-view-control/StreetViewControl.svelte index 6884f850a..9a9d4ed61 100644 --- a/website/src/lib/components/map/street-view-control/StreetViewControl.svelte +++ b/website/src/lib/components/map/street-view-control/StreetViewControl.svelte @@ -9,6 +9,7 @@ import { i18n } from '$lib/i18n/i18n.svelte'; import { onMount } from 'svelte'; import ButtonWithTooltip from '$lib/components/ButtonWithTooltip.svelte'; + import type { Map } from 'maplibre-gl'; const { streetViewSource } = settings; @@ -20,7 +21,7 @@ let container: HTMLElement; onMount(() => { - map.onLoad((map_: maplibregl.Map) => { + map.onLoad((map_: Map) => { googleRedirect = new GoogleRedirect(map_); mapillaryLayer = new MapillaryLayer( map_, diff --git a/website/src/lib/components/map/street-view-control/google.ts b/website/src/lib/components/map/street-view-control/google.ts index 9d4c73b34..149607612 100644 --- a/website/src/lib/components/map/street-view-control/google.ts +++ b/website/src/lib/components/map/street-view-control/google.ts @@ -1,10 +1,11 @@ +import type { Map, MapMouseEvent } from 'maplibre-gl'; import { mapCursor, MapCursorState } from '$lib/state/map-cursor'; export class GoogleRedirect { - map: maplibregl.Map; + map: Map; enabled = false; - constructor(map: maplibregl.Map) { + constructor(map: Map) { this.map = map; } @@ -24,7 +25,7 @@ export class GoogleRedirect { this.map.off('click', this.openStreetView); } - openStreetView(e: maplibregl.MapMouseEvent) { + openStreetView(e: MapMouseEvent) { window.open( `https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${e.lngLat.lat},${e.lngLat.lng}` ); diff --git a/website/src/lib/components/map/street-view-control/mapillary.ts b/website/src/lib/components/map/street-view-control/mapillary.ts index a19d9688b..8e0f36318 100644 --- a/website/src/lib/components/map/street-view-control/mapillary.ts +++ b/website/src/lib/components/map/street-view-control/mapillary.ts @@ -1,4 +1,10 @@ -import maplibregl, { type LayerSpecification, type VectorSourceSpecification } from 'maplibre-gl'; +import { + Map, + Marker, + type LayerSpecification, + type MapLayerMouseEvent, + type VectorSourceSpecification, +} from 'maplibre-gl'; import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module'; import 'mapillary-js/dist/mapillary.css'; import { mapCursor, MapCursorState } from '$lib/state/map-cursor'; @@ -43,9 +49,9 @@ const mapillaryImageLayer: LayerSpecification = { }; export class MapillaryLayer { - map: maplibregl.Map; + map: Map; layerEventManager: MapLayerEventManager; - marker: maplibregl.Marker; + marker: Marker; viewer: Viewer; active = false; @@ -56,7 +62,7 @@ export class MapillaryLayer { onMouseLeaveBinded = this.onMouseLeave.bind(this); constructor( - map: maplibregl.Map, + map: Map, layerEventManager: MapLayerEventManager, container: HTMLElement, popupOpen: { value: boolean } @@ -75,7 +81,7 @@ export class MapillaryLayer { dot.className = 'maplibregl-user-location-dot'; element.appendChild(dot); - this.marker = new maplibregl.Marker({ + this.marker = new Marker({ rotationAlignment: 'map', element, }); @@ -140,7 +146,7 @@ export class MapillaryLayer { this.popupOpen.value = false; } - onMouseEnter(e: maplibregl.MapLayerMouseEvent) { + onMouseEnter(e: MapLayerMouseEvent) { if ( e.features && e.features.length > 0 && diff --git a/website/src/lib/components/map/style.ts b/website/src/lib/components/map/style.ts index bc8228b22..8fc770b9d 100644 --- a/website/src/lib/components/map/style.ts +++ b/website/src/lib/components/map/style.ts @@ -1,3 +1,10 @@ +import type { + GeoJSONSourceSpecification, + LayerSpecification, + Map, + SkySpecification, + StyleSpecification, +} from 'maplibre-gl'; import { settings } from '$lib/state/settings'; import { get, type Writable } from 'svelte/store'; import { @@ -13,13 +20,23 @@ import { i18n } from '$lib/i18n/i18n.svelte'; const { currentBasemap, currentOverlays, customLayers, opacities, terrainSource, distanceUnits } = settings; -const emptySource: maplibregl.GeoJSONSourceSpecification = { +const emptySource: GeoJSONSourceSpecification = { type: 'geojson', data: { type: 'FeatureCollection', features: [], }, }; +/** + * The sky above the horizon when the map is tilted, without the atmosphere around the globe. + */ +const sky: SkySpecification = { + 'sky-color': '#88C6FC', + 'horizon-color': '#ffffff', + 'sky-horizon-blend': 0.8, + 'atmosphere-blend': 0, +}; + export const ANCHOR_LAYER_KEY = { overlays: 'overlays-end', mapillary: 'mapillary-end', @@ -32,18 +49,18 @@ export const ANCHOR_LAYER_KEY = { waypoints: 'waypoints-end', routingControls: 'routing-controls-end', }; -const anchorLayers: maplibregl.LayerSpecification[] = Object.values(ANCHOR_LAYER_KEY).map((id) => ({ +const anchorLayers: LayerSpecification[] = Object.values(ANCHOR_LAYER_KEY).map((id) => ({ id: id, type: 'symbol', source: 'empty-source', })); export class StyleManager { - private _map: Writable; + private _map: Writable; private _maptilerKey: string; private _pastOverlays: Set = new Set(); - constructor(map: Writable, maptilerKey: string) { + constructor(map: Writable, maptilerKey: string) { this._map = map; this._maptilerKey = maptilerKey; this._map.subscribe((map_) => { @@ -58,6 +75,7 @@ export class StyleManager { opacities.subscribe(() => this.updateOverlays()); terrainSource.subscribe(() => this.updateTerrain()); customLayers.subscribe(() => this.updateBasemap()); + i18n.subscribe(() => this.updateBasemap()); distanceUnits.subscribe(() => { const map = get(this._map); if (map && (map.getLayer('contours_m') || map.getLayer('contours_ft'))) { @@ -75,10 +93,10 @@ export class StyleManager { }); } - async buildStyle(basemap: string): Promise { + async buildStyle(basemap: string): Promise { const custom = get(customLayers); - const style: maplibregl.StyleSpecification = { + const style: StyleSpecification = { version: 8, projection: { type: 'globe', @@ -91,7 +109,7 @@ export class StyleManager { const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap]; - let basemapStyle = basemaps.openStreetMap as maplibregl.StyleSpecification; + let basemapStyle = basemaps.openStreetMap as StyleSpecification; try { basemapStyle = await this.get(basemapInfo); for (const source in basemapStyle.sources) { @@ -110,6 +128,7 @@ export class StyleManager { console.error(e.message); } this.merge(style, basemapStyle); + style.sky ??= sky; const terrain = this.getCurrentTerrain(); style.sources[terrain.source] = terrainSources[terrain.source]; @@ -202,9 +221,7 @@ export class StyleManager { } } - async get( - styleInfo: maplibregl.StyleSpecification | string - ): Promise { + async get(styleInfo: StyleSpecification | string): Promise { if (typeof styleInfo === 'string') { let styleUrl = styleInfo as string; const response = await fetch(styleUrl, { cache: 'force-cache' }); @@ -218,7 +235,7 @@ export class StyleManager { } } - merge(style: maplibregl.StyleSpecification, other: maplibregl.StyleSpecification) { + merge(style: StyleSpecification, other: StyleSpecification) { style.sources = { ...style.sources, ...other.sources }; const units = get(distanceUnits); for (let layer of other.layers ?? []) { @@ -270,6 +287,9 @@ export class StyleManager { if (other.glyphs && !style.glyphs) { style.glyphs = other.glyphs; } + if (other.sky && !style.sky) { + style.sky = other.sky; + } } getCurrentTerrain() { diff --git a/website/src/lib/components/toolbar/ToolbarItemMenu.svelte b/website/src/lib/components/toolbar/ToolbarItemMenu.svelte index 9ecaa125f..83bcf9c5a 100644 --- a/website/src/lib/components/toolbar/ToolbarItemMenu.svelte +++ b/website/src/lib/components/toolbar/ToolbarItemMenu.svelte @@ -11,7 +11,7 @@ import Clean from '$lib/components/toolbar/tools/Clean.svelte'; import Reduce from '$lib/components/toolbar/tools/reduce/Reduce.svelte'; import RoutingControlPopup from '$lib/components/toolbar/tools/routing/RoutingControlPopup.svelte'; - import maplibregl from 'maplibre-gl'; + import { Popup } from 'maplibre-gl'; import { settings } from '$lib/state/settings'; let { @@ -23,11 +23,11 @@ const { minimizeRoutingMenu } = settings; let popupElement: HTMLDivElement | undefined = $state(undefined); - let popup: maplibregl.Popup | undefined = $derived.by(() => { + let popup: Popup | undefined = $derived.by(() => { if (!popupElement) { return undefined; } - let popup = new maplibregl.Popup({ + let popup = new Popup({ closeButton: false, maxWidth: undefined, }); diff --git a/website/src/lib/components/toolbar/tools/Clean.svelte b/website/src/lib/components/toolbar/tools/Clean.svelte index 2642cbc7f..cee19b8cd 100644 --- a/website/src/lib/components/toolbar/tools/Clean.svelte +++ b/website/src/lib/components/toolbar/tools/Clean.svelte @@ -16,7 +16,7 @@ import { getURLForLanguage } from '$lib/utils'; import { Trash2 } from '@lucide/svelte'; import { map } from '$lib/components/map/map'; - import type { GeoJSONSource } from 'maplibre-gl'; + import type { GeoJSONSource, LngLat } from 'maplibre-gl'; import { engine } from '$lib/engine'; import { mapCursor, MapCursorState } from '$lib/state/map-cursor'; import { ANCHOR_LAYER_KEY } from '$lib/components/map/style'; @@ -28,7 +28,7 @@ let cleanType = $state(CleanType.INSIDE); let deleteTrackpoints = $state(true); let deleteWaypoints = $state(true); - let rectangleCoordinates: maplibregl.LngLat[] = $state([]); + let rectangleCoordinates: LngLat[] = $state([]); $effect(() => { if ($map) { diff --git a/website/src/lib/components/toolbar/tools/routing/Routing.svelte b/website/src/lib/components/toolbar/tools/routing/Routing.svelte index 5cf2d358e..ad8594174 100644 --- a/website/src/lib/components/toolbar/tools/routing/Routing.svelte +++ b/website/src/lib/components/toolbar/tools/routing/Routing.svelte @@ -1,4 +1,5 @@