16 Commits
Author SHA1 Message Date
vcoppe c89a6148f2 New translations edit.mdx (Russian)
[ci skip]
2026-09-21 22:13:00 +02:00
vcoppe fa732b0343 New translations en.json (Portuguese)
[ci skip]
2026-09-02 03:11:59 +02:00
vcoppe 142ee61484 New translations en.json (Portuguese)
[ci skip]
2026-09-02 02:14:31 +02:00
vcoppe 873059cfcc New translations view.mdx (Chinese Traditional, Hong Kong)
[ci skip]
2026-08-14 12:27:32 +02:00
vcoppe 9e380e597e New translations settings.mdx (Chinese Traditional, Hong Kong)
[ci skip]
2026-08-14 12:27:30 +02:00
vcoppe f4dc516fe5 New translations files-and-stats.mdx (Chinese Traditional, Hong Kong)
[ci skip]
2026-08-14 10:41:57 +02:00
vcoppe bca6c32193 New translations routing.mdx (Russian)
[ci skip]
2026-08-11 14:53:35 +02:00
vcoppe 9f52d4d60d New translations elevation.mdx (Dutch)
[ci skip]
2026-08-10 14:56:55 +02:00
vcoppe a1475ab7ba New translations en.json (Dutch)
[ci skip]
2026-08-10 14:56:54 +02:00
vcoppe 0987c6a72e New translations elevation.mdx (Basque)
[ci skip]
2026-08-06 17:52:04 +02:00
vcoppe a02de61d0a New translations merge.mdx (Basque)
[ci skip]
2026-08-06 17:52:03 +02:00
vcoppe 75e477ba52 New translations view.mdx (Basque)
[ci skip]
2026-08-06 17:52:01 +02:00
vcoppe b72cebc186 New translations map-controls.mdx (Basque)
[ci skip]
2026-08-06 17:51:59 +02:00
vcoppe 5dd53cd9eb New translations integration.mdx (Basque)
[ci skip]
2026-08-06 17:51:58 +02:00
vcoppe 2c82444832 New translations elevation.mdx (German)
[ci skip]
2026-08-01 13:11:15 +02:00
vcoppe 93ac92969d New translations en.json (Spanish)
[ci skip]
2026-07-29 17:33:54 +02:00
40 changed files with 266 additions and 280 deletions

No files matched your search

+67 -70
View File
@@ -20,7 +20,7 @@
"immer": "^10.1.1", "immer": "^10.1.1",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"mapillary-js": "^4.1.2", "mapillary-js": "^4.1.2",
"maplibre-gl": "^6.0.0", "maplibre-gl": "^5.21.1",
"sanitize-html": "^2.17.0", "sanitize-html": "^2.17.0",
"sortablejs": "^1.15.6" "sortablejs": "^1.15.6"
}, },
@@ -3236,18 +3236,13 @@
} }
}, },
"node_modules/@mapbox/jsonlint-lines-primitives": { "node_modules/@mapbox/jsonlint-lines-primitives": {
"version": "2.0.3", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz",
"integrity": "sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 22" "node": ">= 0.6"
} }
}, },
"node_modules/@mapbox/point-geometry": { "node_modules/@mapbox/point-geometry": {
"version": "1.1.0", "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" "license": "ISC"
}, },
"node_modules/@mapbox/sphericalmercator": { "node_modules/@mapbox/sphericalmercator": {
@@ -3258,32 +3253,24 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@mapbox/tiny-sdf": { "node_modules/@mapbox/tiny-sdf": {
"version": "2.2.0", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz",
"integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==",
"license": "BSD-2-Clause" "license": "BSD-2-Clause"
}, },
"node_modules/@mapbox/unitbezier": { "node_modules/@mapbox/unitbezier": {
"version": "1.0.0", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz",
"integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==",
"license": "BSD-2-Clause" "license": "BSD-2-Clause"
}, },
"node_modules/@mapbox/vector-tile": { "node_modules/@mapbox/vector-tile": {
"version": "3.0.0", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-3.0.0.tgz",
"integrity": "sha512-Qf10S1uIHMk20ri/IVBnpS+esUEkVaR5Hftmz88jTInrpmWgPGJfPe3LVjjlE77trLx8tH6qjTG7uWH9hIq/0Q==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"@mapbox/point-geometry": "~1.1.0", "@mapbox/point-geometry": "~1.1.0",
"@types/geojson": "^7946.0.16", "@types/geojson": "^7946.0.16",
"pbf": "^5.0.0" "pbf": "^4.0.1"
} }
}, },
"node_modules/@mapbox/vector-tile/node_modules/pbf": { "node_modules/@mapbox/vector-tile/node_modules/pbf": {
"version": "5.1.2", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz",
"integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"resolve-protobuf-schema": "^2.1.0" "resolve-protobuf-schema": "^2.1.0"
@@ -3292,13 +3279,18 @@
"pbf": "bin/pbf" "pbf": "bin/pbf"
} }
}, },
"node_modules/@mapbox/whoots-js": {
"version": "3.1.0",
"license": "ISC",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@maplibre/geojson-vt": { "node_modules/@maplibre/geojson-vt": {
"version": "6.1.1", "version": "6.0.4",
"resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.1.1.tgz",
"integrity": "sha512-FVMOcmSP/yqol45t7StApEyTL5/vmqBCuFhH9n+fFuINenhaX+YgHHIt1yJ86S8kln3uJLcMvmEU2cfn6E2eCQ==",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"kdbush": "^4.1.0" "kdbush": "^4.0.2"
} }
}, },
"node_modules/@maplibre/maplibre-gl-geocoder": { "node_modules/@maplibre/maplibre-gl-geocoder": {
@@ -3319,16 +3311,15 @@
} }
}, },
"node_modules/@maplibre/maplibre-gl-style-spec": { "node_modules/@maplibre/maplibre-gl-style-spec": {
"version": "26.2.1", "version": "24.8.1",
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-26.2.1.tgz",
"integrity": "sha512-QFKCXkOeSzOr8jF75jm6kySOg+dUvOehPhRi68gcOYPHb7U5JloUq0dJW0Y5/fZV8ygfT0Vp2RWodvq+fyxFWA==",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@mapbox/jsonlint-lines-primitives": "^2.0.3", "@mapbox/jsonlint-lines-primitives": "~2.0.2",
"@mapbox/unitbezier": "^1.0.0", "@mapbox/unitbezier": "^0.0.1",
"json-stringify-pretty-compact": "^4.0.0", "json-stringify-pretty-compact": "^4.0.0",
"minimist": "^1.2.8", "minimist": "^1.2.8",
"quickselect": "^3.0.0", "quickselect": "^3.0.0",
"rw": "^1.3.3",
"tinyqueue": "^3.0.0" "tinyqueue": "^3.0.0"
}, },
"bin": { "bin": {
@@ -3338,29 +3329,31 @@
} }
}, },
"node_modules/@maplibre/mlt": { "node_modules/@maplibre/mlt": {
"version": "1.1.12", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.12.tgz",
"integrity": "sha512-ZeK5w2TTeHOajcLaEQs1KZXw2V9wIKo1PmThlxlsHoXsQsYlBqLJzPOd6tJHRtGTChUY3DPPmjXRArYVvAbmZw==",
"license": "(MIT OR Apache-2.0)", "license": "(MIT OR Apache-2.0)",
"dependencies": { "dependencies": {
"@mapbox/point-geometry": "^1.1.0" "@mapbox/point-geometry": "^1.1.0"
} }
}, },
"node_modules/@maplibre/vt-pbf": { "node_modules/@maplibre/vt-pbf": {
"version": "4.3.2", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.2.tgz",
"integrity": "sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@mapbox/point-geometry": "^1.1.0", "@mapbox/point-geometry": "^1.1.0",
"@mapbox/vector-tile": "^2.0.4",
"@maplibre/geojson-vt": "^5.0.4",
"@types/geojson": "^7946.0.16", "@types/geojson": "^7946.0.16",
"pbf": "^5.1.0" "@types/supercluster": "^7.1.3",
"pbf": "^4.0.1",
"supercluster": "^8.0.1"
} }
}, },
"node_modules/@maplibre/vt-pbf/node_modules/@maplibre/geojson-vt": {
"version": "5.0.4",
"license": "ISC"
},
"node_modules/@maplibre/vt-pbf/node_modules/pbf": { "node_modules/@maplibre/vt-pbf/node_modules/pbf": {
"version": "5.1.2", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz",
"integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"resolve-protobuf-schema": "^2.1.0" "resolve-protobuf-schema": "^2.1.0"
@@ -4296,6 +4289,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/supercluster": {
"version": "7.1.3",
"license": "MIT",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/three": { "node_modules/@types/three": {
"version": "0.134.0", "version": "0.134.0",
"license": "MIT" "license": "MIT"
@@ -5720,8 +5720,6 @@
}, },
"node_modules/json-stringify-pretty-compact": { "node_modules/json-stringify-pretty-compact": {
"version": "4.0.0", "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" "license": "MIT"
}, },
"node_modules/jszip": { "node_modules/jszip": {
@@ -5735,9 +5733,7 @@
} }
}, },
"node_modules/kdbush": { "node_modules/kdbush": {
"version": "4.1.0", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz",
"integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/keyv": { "node_modules/keyv": {
@@ -6158,25 +6154,25 @@
"license": "(MIT AND Zlib)" "license": "(MIT AND Zlib)"
}, },
"node_modules/maplibre-gl": { "node_modules/maplibre-gl": {
"version": "6.0.0", "version": "5.22.0",
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-6.0.0.tgz",
"integrity": "sha512-1wBgEhzTZfA+cDpFdt0fM1mJA5mJ90fifORJ7D8JcKLJCvPT/iTx9bNxkP7DqEYde65DJd7gE7h83khwNRqdyg==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
"@mapbox/point-geometry": "^1.1.0", "@mapbox/point-geometry": "^1.1.0",
"@mapbox/tiny-sdf": "^2.2.0", "@mapbox/tiny-sdf": "^2.0.7",
"@mapbox/unitbezier": "^1.0.0", "@mapbox/unitbezier": "^0.0.1",
"@mapbox/vector-tile": "^3.0.0", "@mapbox/vector-tile": "^2.0.4",
"@maplibre/geojson-vt": "^6.1.1", "@mapbox/whoots-js": "^3.1.0",
"@maplibre/maplibre-gl-style-spec": "^26.1.0", "@maplibre/geojson-vt": "^6.0.4",
"@maplibre/mlt": "^1.1.12", "@maplibre/maplibre-gl-style-spec": "^24.8.1",
"@maplibre/vt-pbf": "^4.3.2", "@maplibre/mlt": "^1.1.8",
"@maplibre/vt-pbf": "^4.3.0",
"@types/geojson": "^7946.0.16", "@types/geojson": "^7946.0.16",
"earcut": "^3.2.3", "earcut": "^3.0.2",
"gl-matrix": "^3.4.4", "gl-matrix": "^3.4.4",
"kdbush": "^4.1.0", "kdbush": "^4.0.2",
"murmurhash-js": "^1.0.0", "murmurhash-js": "^1.0.0",
"pbf": "^5.1.2", "pbf": "^4.0.1",
"potpack": "^2.1.0", "potpack": "^2.1.0",
"quickselect": "^3.0.0", "quickselect": "^3.0.0",
"tinyqueue": "^3.0.0" "tinyqueue": "^3.0.0"
@@ -6190,15 +6186,11 @@
} }
}, },
"node_modules/maplibre-gl/node_modules/earcut": { "node_modules/maplibre-gl/node_modules/earcut": {
"version": "3.2.3", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/earcut/-/earcut-3.2.3.tgz",
"integrity": "sha512-vnS4AVwp1KHAF13i1vp1/2D5evWy3k5u/iW/B81QVsUZtV8cv2tU0b2VNFlqvh4kYwrFMDdjPCfAmfyJW9y14Q==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/maplibre-gl/node_modules/pbf": { "node_modules/maplibre-gl/node_modules/pbf": {
"version": "5.1.2", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz",
"integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"resolve-protobuf-schema": "^2.1.0" "resolve-protobuf-schema": "^2.1.0"
@@ -6259,8 +6251,6 @@
}, },
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@@ -6795,8 +6785,6 @@
}, },
"node_modules/quickselect": { "node_modules/quickselect": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz",
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/rbush": { "node_modules/rbush": {
@@ -6932,6 +6920,10 @@
} }
} }
}, },
"node_modules/rw": {
"version": "1.3.3",
"license": "BSD-3-Clause"
},
"node_modules/rxjs": { "node_modules/rxjs": {
"version": "7.8.2", "version": "7.8.2",
"license": "Apache-2.0", "license": "Apache-2.0",
@@ -7165,6 +7157,13 @@
"xtend": "^4.0.0" "xtend": "^4.0.0"
} }
}, },
"node_modules/supercluster": {
"version": "8.0.1",
"license": "ISC",
"dependencies": {
"kdbush": "^4.0.2"
}
},
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -7406,8 +7405,6 @@
}, },
"node_modules/tinyqueue": { "node_modules/tinyqueue": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz",
"integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/totalist": { "node_modules/totalist": {
+1 -1
View File
@@ -75,7 +75,7 @@
"immer": "^10.1.1", "immer": "^10.1.1",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"mapillary-js": "^4.1.2", "mapillary-js": "^4.1.2",
"maplibre-gl": "^6.0.0", "maplibre-gl": "^5.21.1",
"sanitize-html": "^2.17.0", "sanitize-html": "^2.17.0",
"sortablejs": "^1.15.6" "sortablejs": "^1.15.6"
} }
+1 -1
View File
@@ -1168,7 +1168,7 @@ export type CustomLayer = {
maxZoom: number; maxZoom: number;
layerType: 'basemap' | 'overlay'; layerType: 'basemap' | 'overlay';
resourceType: 'raster' | 'vector'; resourceType: 'raster' | 'vector';
value: string | StyleSpecification; value: string | maplibregl.StyleSpecification;
}; };
type OverpassQueryData = { type OverpassQueryData = {
@@ -2,7 +2,6 @@
import CustomControl from './custom-control'; import CustomControl from './custom-control';
import { map } from '$lib/components/map/map'; import { map } from '$lib/components/map/map';
import { onMount, type Snippet } from 'svelte'; import { onMount, type Snippet } from 'svelte';
import type { Map } from 'maplibre-gl';
let { let {
position = 'top-right', position = 'top-right',
@@ -18,7 +17,7 @@
let control: CustomControl | null = null; let control: CustomControl | null = null;
onMount(() => { onMount(() => {
map.onLoad((map: Map) => { map.onLoad((map: maplibregl.Map) => {
if (position.includes('right')) container.classList.add('float-right'); if (position.includes('right')) container.classList.add('float-right');
else container.classList.add('float-left'); else container.classList.add('float-left');
container.classList.remove('hidden'); container.classList.remove('hidden');
@@ -1,10 +1,9 @@
import { MapPopup } from '$lib/components/map/map-popup'; import { MapPopup } from '$lib/components/map/map-popup';
import type { Map } from 'maplibre-gl';
export let waypointPopup: MapPopup | null = null; export let waypointPopup: MapPopup | null = null;
export let trackpointPopup: MapPopup | null = null; export let trackpointPopup: MapPopup | null = null;
export function createPopups(map: Map) { export function createPopups(map: maplibregl.Map) {
removePopups(); removePopups();
waypointPopup = new MapPopup(map, { waypointPopup = new MapPopup(map, {
closeButton: false, closeButton: false,
@@ -1,10 +1,9 @@
import { get, type Readable } from 'svelte/store'; import { get, type Readable } from 'svelte/store';
import { import maplibregl, {
type GeoJSONSource, type GeoJSONSource,
type FilterSpecification, type FilterSpecification,
type MapLayerMouseEvent, type MapLayerMouseEvent,
type MapLayerTouchEvent, type MapLayerTouchEvent,
Point,
} from 'maplibre-gl'; } from 'maplibre-gl';
import { map } from '$lib/components/map/map'; import { map } from '$lib/components/map/map';
import { waypointPopup, trackpointPopup } from './gpx-layer-popup'; import { waypointPopup, trackpointPopup } from './gpx-layer-popup';
@@ -121,7 +120,7 @@ export class GPXLayer {
selected: boolean = false; selected: boolean = false;
currentWaypointData: GeoJSON.FeatureCollection | null = null; currentWaypointData: GeoJSON.FeatureCollection | null = null;
draggedWaypointIndex: number | null = null; draggedWaypointIndex: number | null = null;
draggingStartingPosition: Point = new Point(0, 0); draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0);
unsubscribe: Function[] = []; unsubscribe: Function[] = [];
updateBinded: () => void = this.update.bind(this); updateBinded: () => void = this.update.bind(this);
@@ -7,7 +7,6 @@
import { Layers } from '@lucide/svelte'; import { Layers } from '@lucide/svelte';
import { settings } from '$lib/logic/settings'; import { settings } from '$lib/logic/settings';
import { map } from '$lib/components/map/map'; import { map } from '$lib/components/map/map';
import type { Map } from 'maplibre-gl';
let container: HTMLDivElement; let container: HTMLDivElement;
let overpassLayer: OverpassLayer; let overpassLayer: OverpassLayer;
@@ -22,7 +21,7 @@
selectedOverpassTree, selectedOverpassTree,
} = settings; } = settings;
map.onLoad((_map: Map) => { map.onLoad((_map: maplibregl.Map) => {
if (overpassLayer) { if (overpassLayer) {
overpassLayer.remove(); overpassLayer.remove();
} }
@@ -15,9 +15,7 @@
poi: PopupItem<any>; poi: PopupItem<any>;
} = $props(); } = $props();
let tags: Record<string, string> = $derived( let tags: Record<string, string> = $derived(poi ? JSON.parse(poi.item.tags) : {});
poi ? (typeof poi.item.tags == 'object' ? poi.item.tags : JSON.parse(poi.item.tags)) : {}
);
let name = $derived.by(() => { let name = $derived.by(() => {
if (poi) { if (poi) {
if (tags.name !== undefined && tags.name !== '') { if (tags.name !== undefined && tags.name !== '') {
@@ -6,7 +6,7 @@ import { overpassQueryData } from '$lib/assets/layers';
import { MapPopup } from '$lib/components/map/map-popup'; import { MapPopup } from '$lib/components/map/map-popup';
import { settings } from '$lib/logic/settings'; import { settings } from '$lib/logic/settings';
import { db } from '$lib/db'; import { db } from '$lib/db';
import type { Map as MapLibreMap, GeoJSONSource } from 'maplibre-gl'; import type { GeoJSONSource } from 'maplibre-gl';
import { ANCHOR_LAYER_KEY } from '$lib/components/map/style'; import { ANCHOR_LAYER_KEY } from '$lib/components/map/style';
import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager'; import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager';
import { loadSVGIcon } from '$lib/utils'; import { loadSVGIcon } from '$lib/utils';
@@ -28,7 +28,7 @@ export class OverpassLayer {
minZoom = 12; minZoom = 12;
queryZoom = 12; queryZoom = 12;
expirationTime = 7 * 24 * 3600 * 1000; expirationTime = 7 * 24 * 3600 * 1000;
map: MapLibreMap; map: maplibregl.Map;
layerEventManager: MapLayerEventManager; layerEventManager: MapLayerEventManager;
popup: MapPopup; popup: MapPopup;
@@ -40,7 +40,7 @@ export class OverpassLayer {
updateBinded = this.update.bind(this); updateBinded = this.update.bind(this);
onHoverBinded = this.onHover.bind(this); onHoverBinded = this.onHover.bind(this);
constructor(map: MapLibreMap, layerEventManager: MapLayerEventManager) { constructor(map: maplibregl.Map, layerEventManager: MapLayerEventManager) {
this.map = map; this.map = map;
this.layerEventManager = layerEventManager; this.layerEventManager = layerEventManager;
this.popup = new MapPopup(map, { this.popup = new MapPopup(map, {
@@ -1,19 +1,10 @@
import { fileStateCollection } from '$lib/logic/file-state'; import { fileStateCollection } from '$lib/logic/file-state';
import { import maplibregl from 'maplibre-gl';
LngLatBounds,
MapMouseEvent,
MapTouchEvent,
Point,
type Map,
type MapGeoJSONFeature,
type MapLayerMouseEvent,
type MapLayerTouchEvent,
} from 'maplibre-gl';
type MapLayerMouseEventListener = (e: MapLayerMouseEvent) => void; type MapLayerMouseEventListener = (e: maplibregl.MapLayerMouseEvent) => void;
type MapLayerTouchEventListener = (e: MapLayerTouchEvent) => void; type MapLayerTouchEventListener = (e: maplibregl.MapLayerTouchEvent) => void;
type MapLayerListener = { type MapLayerListener = {
features: MapGeoJSONFeature[]; features: maplibregl.MapGeoJSONFeature[];
mousemoves: MapLayerMouseEventListener[]; mousemoves: MapLayerMouseEventListener[];
mouseenters: MapLayerMouseEventListener[]; mouseenters: MapLayerMouseEventListener[];
mouseleaves: MapLayerMouseEventListener[]; mouseleaves: MapLayerMouseEventListener[];
@@ -24,10 +15,10 @@ type MapLayerListener = {
}; };
export class MapLayerEventManager { export class MapLayerEventManager {
private _map: Map; private _map: maplibregl.Map;
private _listeners: Record<string, MapLayerListener> = {}; private _listeners: Record<string, MapLayerListener> = {};
constructor(map: Map) { constructor(map: maplibregl.Map) {
this._map = map; this._map = map;
this._map.on('mousemove', this._handleMouseMove.bind(this)); this._map.on('mousemove', this._handleMouseMove.bind(this));
this._map.on('click', this._handleMouseClick.bind(this, 'click')); this._map.on('click', this._handleMouseClick.bind(this, 'click'));
@@ -150,7 +141,7 @@ export class MapLayerEventManager {
} }
} }
private _handleMouseMove(e: MapMouseEvent) { private _handleMouseMove(e: maplibregl.MapMouseEvent) {
if (e.originalEvent.buttons > 0) return; if (e.originalEvent.buttons > 0) return;
const featuresByLayer = this._getRenderedFeaturesByLayer(e); const featuresByLayer = this._getRenderedFeaturesByLayer(e);
Object.keys(this._listeners).forEach((layerId) => { Object.keys(this._listeners).forEach((layerId) => {
@@ -159,20 +150,29 @@ export class MapLayerEventManager {
if ((features.length == 0) != (listener.features.length == 0)) { if ((features.length == 0) != (listener.features.length == 0)) {
if (features.length > 0) { if (features.length > 0) {
if (listener.mouseenters.length > 0) { if (listener.mouseenters.length > 0) {
const event = new MapMouseEvent('mouseenter', e.target, e.originalEvent, { const event = new maplibregl.MapMouseEvent(
'mouseenter',
e.target,
e.originalEvent,
{
features: featuresByLayer[layerId]!, features: featuresByLayer[layerId]!,
}); }
);
listener.mouseenters.forEach((l) => l(event)); listener.mouseenters.forEach((l) => l(event));
} }
} else { } else {
if (listener.mouseleaves.length > 0) { if (listener.mouseleaves.length > 0) {
const event = new MapMouseEvent('mouseleave', e.target, e.originalEvent); const event = new maplibregl.MapMouseEvent(
'mouseleave',
e.target,
e.originalEvent
);
listener.mouseleaves.forEach((l) => l(event)); listener.mouseleaves.forEach((l) => l(event));
} }
} }
} }
if (features.length > 0 && listener.mousemoves.length > 0) { if (features.length > 0 && listener.mousemoves.length > 0) {
const event = new MapMouseEvent('mousemove', e.target, e.originalEvent, { const event = new maplibregl.MapMouseEvent('mousemove', e.target, e.originalEvent, {
features: featuresByLayer[layerId]!, features: featuresByLayer[layerId]!,
}); });
listener.mousemoves.forEach((l) => l(event)); listener.mousemoves.forEach((l) => l(event));
@@ -181,39 +181,49 @@ export class MapLayerEventManager {
}); });
} }
private _handleMouseClick(type: string, e: MapMouseEvent) { private _handleMouseClick(type: string, e: maplibregl.MapMouseEvent) {
const featuresByLayer = this._getRenderedFeaturesByLayer(e); const featuresByLayer = this._getRenderedFeaturesByLayer(e);
Object.keys(this._listeners).forEach((layerId) => { Object.keys(this._listeners).forEach((layerId) => {
const features = featuresByLayer[layerId] || []; const features = featuresByLayer[layerId] || [];
const listener = this._listeners[layerId]; const listener = this._listeners[layerId];
if (features.length > 0) { if (features.length > 0) {
if (type === 'click' && listener.clicks.length > 0) { if (type === 'click' && listener.clicks.length > 0) {
const event = new MapMouseEvent('click', e.target, e.originalEvent, { const event = new maplibregl.MapMouseEvent('click', e.target, e.originalEvent, {
features: features, features: features,
}); });
listener.clicks.forEach((l) => l(event)); listener.clicks.forEach((l) => l(event));
} else if (type === 'contextmenu' && listener.contextmenus.length > 0) { } else if (type === 'contextmenu' && listener.contextmenus.length > 0) {
const event = new MapMouseEvent('contextmenu', e.target, e.originalEvent, { const event = new maplibregl.MapMouseEvent(
'contextmenu',
e.target,
e.originalEvent,
{
features: features, features: features,
}); }
);
listener.contextmenus.forEach((l) => l(event)); listener.contextmenus.forEach((l) => l(event));
} else if (type === 'mousedown' && listener.mousedowns.length > 0) { } else if (type === 'mousedown' && listener.mousedowns.length > 0) {
const event = new MapMouseEvent('mousedown', e.target, e.originalEvent, { const event = new maplibregl.MapMouseEvent(
'mousedown',
e.target,
e.originalEvent,
{
features: features, features: features,
}); }
);
listener.mousedowns.forEach((l) => l(event)); listener.mousedowns.forEach((l) => l(event));
} }
} }
}); });
} }
private _handleTouchStart(e: MapTouchEvent) { private _handleTouchStart(e: maplibregl.MapTouchEvent) {
const featuresByLayer = this._getRenderedFeaturesByLayer(e); const featuresByLayer = this._getRenderedFeaturesByLayer(e);
Object.keys(this._listeners).forEach((layerId) => { Object.keys(this._listeners).forEach((layerId) => {
const features = featuresByLayer[layerId] || []; const features = featuresByLayer[layerId] || [];
const listener = this._listeners[layerId]; const listener = this._listeners[layerId];
if (features.length > 0) { if (features.length > 0) {
const event: MapLayerTouchEvent = new MapTouchEvent( const event: maplibregl.MapLayerTouchEvent = new maplibregl.MapTouchEvent(
'touchstart', 'touchstart',
e.target, e.target,
e.originalEvent e.originalEvent
@@ -224,15 +234,18 @@ export class MapLayerEventManager {
}); });
} }
private _getBounds(point: Point) { private _getBounds(point: maplibregl.Point) {
const delta = 30; const delta = 30;
return new LngLatBounds( return new maplibregl.LngLatBounds(
this._map.unproject([point.x - delta, point.y + delta]), this._map.unproject([point.x - delta, point.y + delta]),
this._map.unproject([point.x + delta, point.y - delta]) this._map.unproject([point.x + delta, point.y - delta])
); );
} }
private _filterLayersIntersectingBounds(layerIds: string[], bounds: LngLatBounds): string[] { private _filterLayersIntersectingBounds(
layerIds: string[],
bounds: maplibregl.LngLatBounds
): string[] {
let result = layerIds.filter((layerId) => { let result = layerIds.filter((layerId) => {
if (!this._map.getLayer(layerId)) return false; if (!this._map.getLayer(layerId)) return false;
const fileId = layerId.replace('-waypoints', ''); const fileId = layerId.replace('-waypoints', '');
@@ -248,7 +261,7 @@ export class MapLayerEventManager {
return result; return result;
} }
private _getRenderedFeaturesByLayer(e: MapMouseEvent | MapTouchEvent) { private _getRenderedFeaturesByLayer(e: maplibregl.MapMouseEvent | maplibregl.MapTouchEvent) {
const layerIds = this._filterLayersIntersectingBounds( const layerIds = this._filterLayersIntersectingBounds(
Object.keys(this._listeners), Object.keys(this._listeners),
this._getBounds(e.point) this._getBounds(e.point)
@@ -257,7 +270,7 @@ export class MapLayerEventManager {
layerIds.length > 0 layerIds.length > 0
? this._map.queryRenderedFeatures(e.point, { layers: layerIds }) ? this._map.queryRenderedFeatures(e.point, { layers: layerIds })
: []; : [];
const featuresByLayer: Record<string, MapGeoJSONFeature[]> = {}; const featuresByLayer: Record<string, maplibregl.MapGeoJSONFeature[]> = {};
features.forEach((f) => { features.forEach((f) => {
if (!featuresByLayer[f.layer.id]) { if (!featuresByLayer[f.layer.id]) {
featuresByLayer[f.layer.id] = []; featuresByLayer[f.layer.id] = [];
+8 -8
View File
@@ -1,5 +1,5 @@
import { TrackPoint, Waypoint } from 'gpx'; import { TrackPoint, Waypoint } from 'gpx';
import { LngLat, Map, MapMouseEvent, Popup, type PopupOptions } from 'maplibre-gl'; import maplibregl from 'maplibre-gl';
import { mount, tick, unmount } from 'svelte'; import { mount, tick, unmount } from 'svelte';
import { get, writable, type Writable } from 'svelte/store'; import { get, writable, type Writable } from 'svelte/store';
import MapPopupComponent from '$lib/components/map/MapPopup.svelte'; import MapPopupComponent from '$lib/components/map/MapPopup.svelte';
@@ -11,15 +11,15 @@ export type PopupItem<T = Waypoint | TrackPoint | any> = {
}; };
export class MapPopup { export class MapPopup {
map: Map; map: maplibregl.Map;
popup: Popup; popup: maplibregl.Popup;
item: Writable<PopupItem | null> = writable(null); item: Writable<PopupItem | null> = writable(null);
component: ReturnType<typeof mount>; component: ReturnType<typeof mount>;
maybeHideBinded = this.maybeHide.bind(this); maybeHideBinded = this.maybeHide.bind(this);
constructor(map: Map, options?: PopupOptions) { constructor(map: maplibregl.Map, options?: maplibregl.PopupOptions) {
this.map = map; this.map = map;
this.popup = new Popup(options); this.popup = new maplibregl.Popup(options);
this.component = mount(MapPopupComponent, { this.component = mount(MapPopupComponent, {
target: document.body, target: document.body,
props: { props: {
@@ -51,7 +51,7 @@ export class MapPopup {
this.map.on('mousemove', this.maybeHideBinded); this.map.on('mousemove', this.maybeHideBinded);
} }
maybeHide(e: MapMouseEvent) { maybeHide(e: maplibregl.MapMouseEvent) {
const item = get(this.item); const item = get(this.item);
if (item === null) { if (item === null) {
this.hide(); this.hide();
@@ -75,10 +75,10 @@ export class MapPopup {
getCoordinates() { getCoordinates() {
const item = get(this.item); const item = get(this.item);
if (item === null) { if (item === null) {
return new LngLat(0, 0); return new maplibregl.LngLat(0, 0);
} }
return item.item instanceof Waypoint || item.item instanceof TrackPoint return item.item instanceof Waypoint || item.item instanceof TrackPoint
? item.item.getCoordinates() ? item.item.getCoordinates()
: new LngLat(item.item.lon, item.item.lat); : new maplibregl.LngLat(item.item.lon, item.item.lat);
} }
} }
+1 -3
View File
@@ -1,6 +1,5 @@
import * as maplibregl from 'maplibre-gl'; import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css'; import 'maplibre-gl/dist/maplibre-gl.css';
import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
import MaplibreGeocoder, { import MaplibreGeocoder, {
type MaplibreGeocoderFeatureResults, type MaplibreGeocoderFeatureResults,
} from '@maplibre/maplibre-gl-geocoder'; } from '@maplibre/maplibre-gl-geocoder';
@@ -40,7 +39,6 @@ export class MapLibreGLMap {
geocoder: boolean, geocoder: boolean,
geolocate: boolean geolocate: boolean
) { ) {
maplibregl.setWorkerUrl(workerUrl);
this._maptilerKey = maptilerKey; this._maptilerKey = maptilerKey;
this._styleManager = new StyleManager(this._mapStore, this._maptilerKey); this._styleManager = new StyleManager(this._mapStore, this._maptilerKey);
const map = new maplibregl.Map({ const map = new maplibregl.Map({
@@ -9,7 +9,6 @@
import { i18n } from '$lib/i18n.svelte'; import { i18n } from '$lib/i18n.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import ButtonWithTooltip from '$lib/components/ButtonWithTooltip.svelte'; import ButtonWithTooltip from '$lib/components/ButtonWithTooltip.svelte';
import type { Map } from 'maplibre-gl';
const { streetViewSource } = settings; const { streetViewSource } = settings;
@@ -21,7 +20,7 @@
let container: HTMLElement; let container: HTMLElement;
onMount(() => { onMount(() => {
map.onLoad((map_: Map) => { map.onLoad((map_: maplibregl.Map) => {
googleRedirect = new GoogleRedirect(map_); googleRedirect = new GoogleRedirect(map_);
mapillaryLayer = new MapillaryLayer( mapillaryLayer = new MapillaryLayer(
map_, map_,
@@ -1,11 +1,10 @@
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import type { Map, MapMouseEvent } from 'maplibre-gl';
export class GoogleRedirect { export class GoogleRedirect {
map: Map; map: maplibregl.Map;
enabled = false; enabled = false;
constructor(map: Map) { constructor(map: maplibregl.Map) {
this.map = map; this.map = map;
} }
@@ -25,7 +24,7 @@ export class GoogleRedirect {
this.map.off('click', this.openStreetView); this.map.off('click', this.openStreetView);
} }
openStreetView(e: MapMouseEvent) { openStreetView(e: maplibregl.MapMouseEvent) {
window.open( window.open(
`https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${e.lngLat.lat},${e.lngLat.lng}` `https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${e.lngLat.lat},${e.lngLat.lng}`
); );
@@ -1,10 +1,4 @@
import { import maplibregl, { type LayerSpecification, type VectorSourceSpecification } from 'maplibre-gl';
Map,
Marker,
type LayerSpecification,
type MapLayerMouseEvent,
type VectorSourceSpecification,
} from 'maplibre-gl';
import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module'; import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module';
import 'mapillary-js/dist/mapillary.css'; import 'mapillary-js/dist/mapillary.css';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
@@ -49,9 +43,9 @@ const mapillaryImageLayer: LayerSpecification = {
}; };
export class MapillaryLayer { export class MapillaryLayer {
map: Map; map: maplibregl.Map;
layerEventManager: MapLayerEventManager; layerEventManager: MapLayerEventManager;
marker: Marker; marker: maplibregl.Marker;
viewer: Viewer; viewer: Viewer;
active = false; active = false;
@@ -62,7 +56,7 @@ export class MapillaryLayer {
onMouseLeaveBinded = this.onMouseLeave.bind(this); onMouseLeaveBinded = this.onMouseLeave.bind(this);
constructor( constructor(
map: Map, map: maplibregl.Map,
layerEventManager: MapLayerEventManager, layerEventManager: MapLayerEventManager,
container: HTMLElement, container: HTMLElement,
popupOpen: { value: boolean } popupOpen: { value: boolean }
@@ -81,7 +75,7 @@ export class MapillaryLayer {
dot.className = 'maplibregl-user-location-dot'; dot.className = 'maplibregl-user-location-dot';
element.appendChild(dot); element.appendChild(dot);
this.marker = new Marker({ this.marker = new maplibregl.Marker({
rotationAlignment: 'map', rotationAlignment: 'map',
element, element,
}); });
@@ -146,7 +140,7 @@ export class MapillaryLayer {
this.popupOpen.value = false; this.popupOpen.value = false;
} }
onMouseEnter(e: MapLayerMouseEvent) { onMouseEnter(e: maplibregl.MapLayerMouseEvent) {
if ( if (
e.features && e.features &&
e.features.length > 0 && e.features.length > 0 &&
+11 -15
View File
@@ -9,17 +9,11 @@ import {
} from '$lib/assets/layers'; } from '$lib/assets/layers';
import { getLayers } from '$lib/components/map/layer-control/utils'; import { getLayers } from '$lib/components/map/layer-control/utils';
import { i18n } from '$lib/i18n.svelte'; import { i18n } from '$lib/i18n.svelte';
import type {
Map,
GeoJSONSourceSpecification,
LayerSpecification,
StyleSpecification,
} from 'maplibre-gl';
const { currentBasemap, currentOverlays, customLayers, opacities, terrainSource, distanceUnits } = const { currentBasemap, currentOverlays, customLayers, opacities, terrainSource, distanceUnits } =
settings; settings;
const emptySource: GeoJSONSourceSpecification = { const emptySource: maplibregl.GeoJSONSourceSpecification = {
type: 'geojson', type: 'geojson',
data: { data: {
type: 'FeatureCollection', type: 'FeatureCollection',
@@ -38,18 +32,18 @@ export const ANCHOR_LAYER_KEY = {
waypoints: 'waypoints-end', waypoints: 'waypoints-end',
routingControls: 'routing-controls-end', routingControls: 'routing-controls-end',
}; };
const anchorLayers: LayerSpecification[] = Object.values(ANCHOR_LAYER_KEY).map((id) => ({ const anchorLayers: maplibregl.LayerSpecification[] = Object.values(ANCHOR_LAYER_KEY).map((id) => ({
id: id, id: id,
type: 'symbol', type: 'symbol',
source: 'empty-source', source: 'empty-source',
})); }));
export class StyleManager { export class StyleManager {
private _map: Writable<Map | null>; private _map: Writable<maplibregl.Map | null>;
private _maptilerKey: string; private _maptilerKey: string;
private _pastOverlays: Set<string> = new Set(); private _pastOverlays: Set<string> = new Set();
constructor(map: Writable<Map | null>, maptilerKey: string) { constructor(map: Writable<maplibregl.Map | null>, maptilerKey: string) {
this._map = map; this._map = map;
this._maptilerKey = maptilerKey; this._maptilerKey = maptilerKey;
this._map.subscribe((map_) => { this._map.subscribe((map_) => {
@@ -82,10 +76,10 @@ export class StyleManager {
}); });
} }
async buildStyle(basemap: string): Promise<StyleSpecification> { async buildStyle(basemap: string): Promise<maplibregl.StyleSpecification> {
const custom = get(customLayers); const custom = get(customLayers);
const style: StyleSpecification = { const style: maplibregl.StyleSpecification = {
version: 8, version: 8,
projection: { projection: {
type: 'globe', type: 'globe',
@@ -98,7 +92,7 @@ export class StyleManager {
const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap]; const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap];
let basemapStyle = basemaps.openStreetMap as StyleSpecification; let basemapStyle = basemaps.openStreetMap as maplibregl.StyleSpecification;
try { try {
basemapStyle = await this.get(basemapInfo); basemapStyle = await this.get(basemapInfo);
for (const source in basemapStyle.sources) { for (const source in basemapStyle.sources) {
@@ -209,7 +203,9 @@ export class StyleManager {
} }
} }
async get(styleInfo: StyleSpecification | string): Promise<StyleSpecification> { async get(
styleInfo: maplibregl.StyleSpecification | string
): Promise<maplibregl.StyleSpecification> {
if (typeof styleInfo === 'string') { if (typeof styleInfo === 'string') {
let styleUrl = styleInfo as string; let styleUrl = styleInfo as string;
const response = await fetch(styleUrl, { cache: 'force-cache' }); const response = await fetch(styleUrl, { cache: 'force-cache' });
@@ -223,7 +219,7 @@ export class StyleManager {
} }
} }
merge(style: StyleSpecification, other: StyleSpecification) { merge(style: maplibregl.StyleSpecification, other: maplibregl.StyleSpecification) {
style.sources = { ...style.sources, ...other.sources }; style.sources = { ...style.sources, ...other.sources };
const units = get(distanceUnits); const units = get(distanceUnits);
for (let layer of other.layers ?? []) { for (let layer of other.layers ?? []) {
@@ -11,7 +11,7 @@
import Clean from '$lib/components/toolbar/tools/Clean.svelte'; import Clean from '$lib/components/toolbar/tools/Clean.svelte';
import Reduce from '$lib/components/toolbar/tools/reduce/Reduce.svelte'; import Reduce from '$lib/components/toolbar/tools/reduce/Reduce.svelte';
import RoutingControlPopup from '$lib/components/toolbar/tools/routing/RoutingControlPopup.svelte'; import RoutingControlPopup from '$lib/components/toolbar/tools/routing/RoutingControlPopup.svelte';
import { Popup } from 'maplibre-gl'; import maplibregl from 'maplibre-gl';
import { settings } from '$lib/logic/settings'; import { settings } from '$lib/logic/settings';
let { let {
@@ -23,11 +23,11 @@
const { minimizeRoutingMenu } = settings; const { minimizeRoutingMenu } = settings;
let popupElement: HTMLDivElement | undefined = $state(undefined); let popupElement: HTMLDivElement | undefined = $state(undefined);
let popup: Popup | undefined = $derived.by(() => { let popup: maplibregl.Popup | undefined = $derived.by(() => {
if (!popupElement) { if (!popupElement) {
return undefined; return undefined;
} }
let popup = new Popup({ let popup = new maplibregl.Popup({
closeButton: false, closeButton: false,
maxWidth: undefined, maxWidth: undefined,
}); });
@@ -16,7 +16,7 @@
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
import { Trash2 } from '@lucide/svelte'; import { Trash2 } from '@lucide/svelte';
import { map } from '$lib/components/map/map'; import { map } from '$lib/components/map/map';
import type { GeoJSONSource, LngLat } from 'maplibre-gl'; import type { GeoJSONSource } from 'maplibre-gl';
import { selection } from '$lib/logic/selection'; import { selection } from '$lib/logic/selection';
import { fileActions } from '$lib/logic/file-actions'; import { fileActions } from '$lib/logic/file-actions';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
@@ -29,7 +29,7 @@
let cleanType = $state(CleanType.INSIDE); let cleanType = $state(CleanType.INSIDE);
let deleteTrackpoints = $state(true); let deleteTrackpoints = $state(true);
let deleteWaypoints = $state(true); let deleteWaypoints = $state(true);
let rectangleCoordinates: LngLat[] = $state([]); let rectangleCoordinates: maplibregl.LngLat[] = $state([]);
$effect(() => { $effect(() => {
if ($map) { if ($map) {
@@ -41,7 +41,6 @@
import { fileActions, getFileIds, newGPXFile } from '$lib/logic/file-actions'; import { fileActions, getFileIds, newGPXFile } from '$lib/logic/file-actions';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import { RoutingControls, routingControls } from './routing-controls'; import { RoutingControls, routingControls } from './routing-controls';
import type { Popup } from 'maplibre-gl';
let { let {
minimized = $bindable(false), minimized = $bindable(false),
@@ -52,7 +51,7 @@
}: { }: {
minimized?: boolean; minimized?: boolean;
minimizable?: boolean; minimizable?: boolean;
popup?: Popup; popup?: maplibregl.Popup;
popupElement?: HTMLDivElement; popupElement?: HTMLDivElement;
class?: string; class?: string;
} = $props(); } = $props();
@@ -1,12 +1,10 @@
import { distance, type Coordinates, TrackPoint, TrackSegment, Track, projectedPoint } from 'gpx'; import { distance, type Coordinates, TrackPoint, TrackSegment, Track, projectedPoint } from 'gpx';
import { get, writable, type Readable } from 'svelte/store'; import { get, writable, type Readable } from 'svelte/store';
import { import maplibregl, {
type MapMouseEvent, type MapMouseEvent,
type GeoJSONSource, type GeoJSONSource,
type MapLayerMouseEvent, type MapLayerMouseEvent,
type MapLayerTouchEvent, type MapLayerTouchEvent,
Popup,
Point,
} from 'maplibre-gl'; } from 'maplibre-gl';
import { route } from './routing'; import { route } from './routing';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
@@ -52,7 +50,7 @@ export class RoutingControls {
} }
> = new Map(); > = new Map();
anchors: GeoJSON.Feature<GeoJSON.Point, AnchorProperties>[] = []; anchors: GeoJSON.Feature<GeoJSON.Point, AnchorProperties>[] = [];
popup: Popup; popup: maplibregl.Popup;
popupElement: HTMLElement; popupElement: HTMLElement;
fileUnsubscribe: () => void = () => {}; fileUnsubscribe: () => void = () => {};
unsubscribes: Function[] = []; unsubscribes: Function[] = [];
@@ -63,7 +61,7 @@ export class RoutingControls {
draggedAnchorIndex: number | null = null; draggedAnchorIndex: number | null = null;
lastDraggedAnchorEventTime: number = 0; lastDraggedAnchorEventTime: number = 0;
draggingStartingPosition: Point = new Point(0, 0); draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0);
onMouseEnterBinded: () => void = this.onMouseEnter.bind(this); onMouseEnterBinded: () => void = this.onMouseEnter.bind(this);
onMouseLeaveBinded: () => void = this.onMouseLeave.bind(this); onMouseLeaveBinded: () => void = this.onMouseLeave.bind(this);
onClickBinded: (e: MapLayerMouseEvent) => void = this.onClick.bind(this); onClickBinded: (e: MapLayerMouseEvent) => void = this.onClick.bind(this);
@@ -82,7 +80,7 @@ export class RoutingControls {
constructor( constructor(
fileId: string, fileId: string,
file: Readable<GPXFileWithStatistics | undefined>, file: Readable<GPXFileWithStatistics | undefined>,
popup: Popup, popup: maplibregl.Popup,
popupElement: HTMLElement popupElement: HTMLElement
) { ) {
this.fileId = fileId; this.fileId = fileId;
@@ -185,7 +183,7 @@ export class RoutingControls {
this.layers.forEach((layer, zoom) => { this.layers.forEach((layer, zoom) => {
try { try {
let source = map_.getSource(layer.id) as GeoJSONSource | undefined; let source = map_.getSource(layer.id) as maplibregl.GeoJSONSource | undefined;
if (source) { if (source) {
source.setData({ source.setData({
type: 'FeatureCollection', type: 'FeatureCollection',
@@ -522,7 +520,7 @@ export class RoutingControls {
}); });
} }
async appendAnchor(e: MapMouseEvent) { async appendAnchor(e: maplibregl.MapMouseEvent) {
// Add a new anchor to the end of the last segment // Add a new anchor to the end of the last segment
if (get(streetViewEnabled) && get(streetViewSource) === 'google') { if (get(streetViewEnabled) && get(streetViewSource) === 'google') {
return; return;
@@ -611,7 +609,7 @@ export class RoutingControls {
await this.routeBetweenAnchors([lastAnchor, newAnchor], [lastAnchorPoint, newAnchorPoint]); await this.routeBetweenAnchors([lastAnchor, newAnchor], [lastAnchorPoint, newAnchorPoint]);
} }
addIntermediateAnchor(e: MapMouseEvent) { addIntermediateAnchor(e: maplibregl.MapMouseEvent) {
e.preventDefault(); e.preventDefault();
if (this.temporaryAnchor !== null) { if (this.temporaryAnchor !== null) {
@@ -8,13 +8,13 @@ import { get } from 'svelte/store';
import { fileStateCollection } from '$lib/logic/file-state'; import { fileStateCollection } from '$lib/logic/file-state';
import { fileActions } from '$lib/logic/file-actions'; import { fileActions } from '$lib/logic/file-actions';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import type { Map, GeoJSONSource, MapLayerMouseEvent } from 'maplibre-gl'; import type { GeoJSONSource } from 'maplibre-gl';
import { ANCHOR_LAYER_KEY } from '$lib/components/map/style'; import { ANCHOR_LAYER_KEY } from '$lib/components/map/style';
import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager'; import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager';
import { loadSVGIcon } from '$lib/utils'; import { loadSVGIcon } from '$lib/utils';
export class SplitControls { export class SplitControls {
map: Map; map: maplibregl.Map;
layerEventManager: MapLayerEventManager; layerEventManager: MapLayerEventManager;
unsubscribes: Function[] = []; unsubscribes: Function[] = [];
@@ -22,7 +22,7 @@ export class SplitControls {
layerOnMouseLeaveBinded: () => void = this.layerOnMouseLeave.bind(this); layerOnMouseLeaveBinded: () => void = this.layerOnMouseLeave.bind(this);
layerOnClickBinded: (e: any) => void = this.layerOnClick.bind(this); layerOnClickBinded: (e: any) => void = this.layerOnClick.bind(this);
constructor(map: Map, layerEventManager: MapLayerEventManager) { constructor(map: maplibregl.Map, layerEventManager: MapLayerEventManager) {
this.map = map; this.map = map;
this.layerEventManager = layerEventManager; this.layerEventManager = layerEventManager;
loadSVGIcon( loadSVGIcon(
@@ -160,7 +160,7 @@ export class SplitControls {
mapCursor.notify(MapCursorState.SPLIT_CONTROL, false); mapCursor.notify(MapCursorState.SPLIT_CONTROL, false);
} }
layerOnClick(e: MapLayerMouseEvent) { layerOnClick(e: maplibregl.MapLayerMouseEvent) {
let coordinates = (e.features![0].geometry as GeoJSON.Point).coordinates; let coordinates = (e.features![0].geometry as GeoJSON.Point).coordinates;
fileActions.split( fileActions.split(
get(splitAs), get(splitAs),
@@ -16,7 +16,7 @@
import { fileActions } from '$lib/logic/file-actions'; import { fileActions } from '$lib/logic/file-actions';
import { map } from '$lib/components/map/map'; import { map } from '$lib/components/map/map';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import { Marker } from 'maplibre-gl'; import maplibregl from 'maplibre-gl';
import { getSvgForSymbol } from '$lib/components/map/gpx-layer/gpx-layer'; import { getSvgForSymbol } from '$lib/components/map/gpx-layer/gpx-layer';
let props: { let props: {
@@ -41,7 +41,7 @@
}) })
); );
let marker: Marker | null = null; let marker: maplibregl.Marker | null = null;
function reset() { function reset() {
if ($selectedWaypoint) { if ($selectedWaypoint) {
@@ -125,7 +125,7 @@
let element = document.createElement('div'); let element = document.createElement('div');
element.classList.add('w-8', 'h-8'); element.classList.add('w-8', 'h-8');
element.innerHTML = getSvgForSymbol(symbolKey); element.innerHTML = getSvgForSymbol(symbolKey);
marker = new Marker({ marker = new maplibregl.Marker({
element, element,
anchor: 'bottom', anchor: 'bottom',
}) })
@@ -19,6 +19,6 @@ Mit diesem Tool kannst du Höhendaten zu Routen und [Points of Interest] (../gpx
<DocsNote> <DocsNote>
Elevation data is provided by <a href="https://mapterhorn.com" target="_blank">Mapterhorn</a>. Elevation data is provided by <a href="https://mapterhorn.com" target="_blank">Mapterhorn</a>.
You can learn more about its origin and accuracy in the <a href="https://mapterhorn.com/attribution/" target="_blank">documentation</a>. Mehr über die Herkunft und Genauigkeit erfährst du in der <a href="https://mapterhorn.com/attribution/" target="_blank">Dokumentation</a>.
</DocsNote> </DocsNote>
+2 -2
View File
@@ -13,8 +13,8 @@ title: Integrazioa
Behar duzun guztia hau da: Behar duzun guztia hau da:
1. GPX files hosted on your server or on Google Drive, or accessible via a public URL; 1. Zure zerbitzarian edo Google Drive-n ostatatutako GPX fitxategiak, edo URL publiko baten bidez eskura daitezke;
2. _Optional:_ a <a href="https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/" target="_blank">MapTiler key</a> to load MapTiler maps. 2. _Aukerakoa:_ <a href="https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/" target="_blank">MapTiler tekla</a> MapTiler mapak kargatzeko.
Ondoren, konfiguratzailearekin jolastu dezakezu zure mapa pertsonalizatzeko eta dagokion HTML kodea sortzeko. Ondoren, konfiguratzailearekin jolastu dezakezu zure mapa pertsonalizatzeko eta dagokion HTML kodea sortzeko.
+2 -2
View File
@@ -59,7 +59,7 @@ Maparen geruzen botoiak mapa-oinarri desberdinen artean aldatzeko aukera ematen
<DocsLayers /> <DocsLayers />
<span class="text-sm text-center mt-2"> <span class="text-sm text-center mt-2">
Hover over the map to show the <a href="https://hiking.waymarkedtrails.org" target="_blank">Waymarked Trails hiking</a> overlay on top of the topographic basemap. Pasa ezazu maparen gainean <a href="https://hiking.waymarkedtrails.org" target="_blank">markatutako mendi-ibiliak</a> gainjartzeko oinarrizko mapa topografikoaren gainean.
</span> </span>
</div> </div>
@@ -69,4 +69,4 @@ Global eta tokiko mapa oinarri bilduma handia dago **gpx.studio**-n eskuragarri,
Ezarpen horietan, gainjartzeen opakutasuna ere kudeatu dezakezu. Ezarpen horietan, gainjartzeen opakutasuna ere kudeatu dezakezu.
For advanced users, it is possible to add custom basemaps and overlays by providing <a href="https://en.wikipedia.org/wiki/Web_Map_Tile_Service" target="_blank">WMTS</a>, <a href="https://en.wikipedia.org/wiki/Web_Map_Service" target="_blank">WMS</a>, or <a href="https://maplibre.org/maplibre-style-spec/" target="_blank">MapLibre style JSON</a> URLs. Erabiltzaile aurreratuentzat, oinarrizko mapa pertsonalizatuak eta gainjartzeak gehi daitezke <a href="https://en.wikipedia.org/wiki/Web_Map_Tile_Service" target="_blank">WMTS</a>, <a href="https://en.wikipedia.org/wiki/Web_Map_Service" target="_blank">WMS</a> edo <a href="https://en.wikipedia.org/wiki/Web_Map_Service" target="_blank">WMS</a>https:///malibrea-spec.org/malibrea-spec. target="_blank">MapLibre estiloko JSON</a> URLak.
+2 -3
View File
@@ -48,7 +48,6 @@ Maparen orientazioa eta okertzea kontrolatzeko, mapa ere arrasta dezakezu <kbd>
</DocsNote> </DocsNote>
### <Maximize2 size="16" class="inline-block" style="margin-bottom: 2px" /> Full screen ### <Maximize2 size="16" class="inline-block" style="margin-bottom: 2px" /> Pantaila osoa
Enter or exit full screen mode. Sartu edo irten pantaila osoko moduan.<kbd>F11</kbd> ere saka dezakezu txandakatzeko, edo <kbd>Ihes</kbd> irteteko.
You can also press <kbd>F11</kbd> to toggle, or <kbd>Escape</kbd> to exit.
@@ -18,7 +18,7 @@ Tresna honen bidez elebazio datuak gehitzen ahal dira ibilbidetan eta [interesgu
<DocsNote> <DocsNote>
Elevation data is provided by <a href="https://mapterhorn.com" target="_blank">Mapterhorn</a>. Altueraren datuak <a href="https://mapterhorn.com" target="_blank">Mapterhorn</a>k ematen ditu.
You can learn more about its origin and accuracy in the <a href="https://mapterhorn.com/attribution/" target="_blank">documentation</a>. Haren jatorriari eta zehaztasunari buruz gehiago jakin dezakezu <a href="https://mapterhorn.com/attribution/" target="_blank">dokumentazioan</a>.
</DocsNote> </DocsNote>
+2 -2
View File
@@ -18,8 +18,8 @@ Tresna hau erabiltzeko [hautatu](../files-and-stats) behar duzu hainbat fitxateg
<DocsNote> <DocsNote>
Selected items are merged in the order they appear in the files list. Hautatutako elementuak fitxategien zerrendan agertzen diren ordenan batzen dira.
Reorder items by drag-and-drop if needed. Berrantolatu elementuak arrastatu eta jareginez, behar izanez gero.
</DocsNote> </DocsNote>
@@ -10,7 +10,7 @@ title: Stijging
# <MountainSnow size="24" class="inline-block" style="margin-bottom: 5px" /> { title } # <MountainSnow size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
Met dit gereedschap kun je hoogtegegevens toevoegen aan sporen en [interessante punten](../gpx), of de bestaande gegevens vervangen. Met deze tool kunt u hoogtegegevens toevoegen aan sporen en [punten van belang](../gpx) of de bestaande gegevens vervangen.
<div class="flex flex-row justify-center"> <div class="flex flex-row justify-center">
<Elevation class="text-foreground p-3 border rounded-md shadow-lg" /> <Elevation class="text-foreground p-3 border rounded-md shadow-lg" />
+36 -36
View File
@@ -9,88 +9,88 @@ title: Редактировать действия
# { title } # { title }
Unlike the file actions, the edit actions can potentially modify the content of the currently selected files. В отличие от действий с файлами действия редактирования могут изменить содержимое выделенных файлов.
Moreover, when the tree layout of the files list is enabled (see [Files and statistics](../files-and-stats)), they can also be applied to [tracks, segments, and points of interest](../gpx). Более того, когда включено отображение списка файлов в виде дерева (см. [Файлы и статистика](../files-and-stats)), они также могут быть применены к [трекам, звеньям и интересным точкам](../gpx).
Therefore, we will refer to the elements that can be modified by these actions as _file items_. Таким образом, мы будем ссылаться на элементы, которые могут быть изменены этими действиями как _файловые единицы_.
Note that except for the undo and redo actions, the edit actions are also accessible through the context menu (right-click) of the file items. Обратите внимание, что за исключением действий отмены и повтора, действия редактирования также доступны через контекстное меню (щелчок правой кнопкой мыши) файловых единиц.
### <Undo2 size="16" class="inline-block" style="margin-bottom: 2px" /><Redo2 size="16" class="inline-block" style="margin-bottom: 2px" /> Undo and redo ### <Undo2 size="16" class="inline-block" style="margin-bottom: 2px" /><Redo2 size="16" class="inline-block" style="margin-bottom: 2px" /> Отменить и повторить
Using these buttons, you can undo or redo the last actions you performed. С помощью этих кнопок можно отменить или повторить последние выполненные действия.
This applies to all actions of the interface but not to view options, application settings, or map navigation. Это применимо ко всему поведению интерфейса кроме параметров просмотра, настроек приложения или перемещению по карте.
### <Info size="16" class="inline-block" style="margin-bottom: 2px" /> Info... ### <Info size="16" class="inline-block" style="margin-bottom: 2px" />Информация...
Open the information dialog of the currently selected file item, where you can see and edit its name and description. Открывает информационный диалог выбранного в данный момент файла, где вы можете посмотреть и отредактировать его имя и описание.
### <PaintBucket size="16" class="inline-block" style="margin-bottom: 2px" /> Appearance... ### <PaintBucket size="16" class="inline-block" style="margin-bottom: 2px" />Внешнее представление...
Open the appearance dialog, where you can change the color, opacity, and width of the selected file items on the map. Открывает диалог внешнего представления, где можно изменить цвет, прозрачность и ширину выделенных файловых элементов на карте.
### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Hide/unhide ### <EyeOff size="16" class="inline-block" style="margin-bottom: 2px" /> Скрыть/показать
Toggle the visibility of the selected file items on the map. Переключить видимость выбранных файлов на карте.
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New track ### <Plus size="16" class="inline-block" style="margin-bottom: 2px" />Новый маршрут
Create a new track in the selected file. Создать новый маршрут в выбранном файле.
<DocsNote> <DocsNote>
This action is only available when the tree layout of the files list is enabled. Это действие доступно только при включенном отображении списка файлов в виде дерева.
Additionally, the selection must be a single file. Кроме того, должен быть выделен только один файл.
</DocsNote> </DocsNote>
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New segment ### <Plus size="16" class="inline-block" style="margin-bottom: 2px" />Новый отрезок
Create a new segment in the selected track. Создать новый отрезок на выбранном маршруте.
<DocsNote> <DocsNote>
This action is only available when the tree layout of the files list is enabled. Это действие доступно только при включенном отображении списка файлов в виде дерева.
Additionally, the selection must be a single track. Кроме того, должен быть выделен только один маршрут.
</DocsNote> </DocsNote>
### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" /> Select all ### <FileStack size="16" class="inline-block" style="margin-bottom: 2px" />Выбрать всё
Add all file items in the current hierarchy level to the selection. Добавить все файлы текущего уровня иерархии к выделению.
### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" /> Center ### <Maximize size="16" class="inline-block" style="margin-bottom: 2px" />По центру
Center the map on the selected file items. Выровнять карту по центру на выбранных файловых единицах.
### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Copy ### <ClipboardCopy size="16" class="inline-block" style="margin-bottom: 2px" /> Копировать
Copy the selected file items to the clipboard. Скопировать выбранные файловые единицы в буфер обмена.
<DocsNote> <DocsNote>
This action is only available when the tree layout of the files list is enabled. Это действие доступно только при включенном отображении списка файлов в виде дерева.
</DocsNote> </DocsNote>
### <Scissors size="16" class="inline-block" style="margin-bottom: 2px" /> Cut ### <Scissors size="16" class="inline-block" style="margin-bottom: 2px" /> Вырезать
Cut the selected file items to the clipboard. Вырезать выбранные файлы в буфер обмена.
<DocsNote> <DocsNote>
This action is only available when the tree layout of the files list is enabled. Это действие доступно только при включенном отображении списка файлов в виде дерева.
</DocsNote> </DocsNote>
### <ClipboardPaste size="16" class="inline-block" style="margin-bottom: 2px" /> Paste ### <ClipboardPaste size="16" class="inline-block" style="margin-bottom: 2px" />Вставить
Paste the file items from the clipboard to the current hierarchy level if they are compatible with it. Вставить файлы из буфера обмена на текущий уровень иерархии, если они совместимы с ним.
<DocsNote> <DocsNote>
This action is only available when the tree layout of the files list is enabled. Это действие доступно только при включенном отображении списка файлов в виде дерева.
</DocsNote> </DocsNote>
### <Trash2 size="16" class="inline-block" style="margin-bottom: 2px" /> Delete ### <Trash2 size="16" class="inline-block" style="margin-bottom: 2px" />Удалить
Delete the selected file items. Удалить выбранные файловые единицы.
+1 -1
View File
@@ -71,7 +71,7 @@ title: Планирование и редактирование маршрута
Изменить направление маршрута на противоположное. Изменить направление маршрута на противоположное.
### <House size="16" class="inline-block" style="margin-bottom: 2px" /> Back to start ### <House size="16" class="inline-block" style="margin-bottom: 2px" /> Вернуться в начало
Подсоедините последнюю точку маршрута к начальной точке, используя выбранные параметры маршрутизации. Подсоедините последнюю точку маршрута к начальной точке, используя выбранные параметры маршрутизации.
@@ -23,7 +23,7 @@ title: 檔案和統計
Once you have [opened](./menu/file) files, they will be shown as tabs in the file list located at the bottom of the map. Once you have [opened](./menu/file) files, they will be shown as tabs in the file list located at the bottom of the map.
You can reorder them by dragging and dropping the tabs. You can reorder them by dragging and dropping the tabs.
And when many files are open, you can scroll through the list of tabs to navigate between them. 當開啟的檔案較多時,也可以捲動分頁清單,在不同檔案之間切換。
<DocsNote> <DocsNote>
+1 -1
View File
@@ -11,7 +11,7 @@ title: 設定
### <Ruler size="16" class="inline-block" style="margin-bottom: 2px" /> 距離單位 ### <Ruler size="16" class="inline-block" style="margin-bottom: 2px" /> 距離單位
Change the units used to display distances in the interface. 變更介面中顯示距離時所使用的單位。
### <Zap size="16" class="inline-block" style="margin-bottom: 2px" /> Velocity units ### <Zap size="16" class="inline-block" style="margin-bottom: 2px" /> Velocity units
+21 -21
View File
@@ -1,5 +1,5 @@
--- ---
title: View options title: 檢視選項
--- ---
<script lang="ts"> <script lang="ts">
@@ -9,38 +9,38 @@ title: View options
# { title } # { title }
This menu provides options to rearrange the interface and the map view. 此選單提供重新排列介面與地圖檢視方式的選項。
### <ChartArea size="16" class="inline-block" style="margin-bottom: 2px" /> Elevation profile ### <ChartArea size="16" class="inline-block" style="margin-bottom: 2px" /> 高度剖面圖
Hide the elevation profile to make room for the map, or show it to inspect the current selection. 隱藏高度剖面圖以騰出更多地圖空間,或顯示高度剖面圖以查看目前的選取內容。
### <ListTree size="16" class="inline-block" style="margin-bottom: 2px" /> File tree ### <ListTree size="16" class="inline-block" style="margin-bottom: 2px" /> 檔案樹狀結構
Toggle the tree layout for the [file list](../files-and-stats). 切換[檔案清單](../files-and-stats)的樹狀版面配置。
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map. This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](../gpx) contained inside the files through collapsible sections. 此外,檔案樹狀檢視可讓你透過可收合的區段,查看檔案中包含的[軌跡、區段和興趣點](../gpx)。
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Switch to previous basemap ### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> 切換至上一個底圖
Change the basemap to the one previously selected through the [map layer control](../map-controls). 將底圖變更為先前透過[地圖圖層控制項](../map-controls)選取的底圖。
### <Layers2 size="16" class="inline-block" style="margin-bottom: 2px" /> Toggle overlays ### <Layers2 size="16" class="inline-block" style="margin-bottom: 2px" /> 切換疊加圖層
Toggle the visibility of the map overlays selected through the [map layer control](../map-controls). 切換透過[地圖圖層控制項](../map-controls)選取之地圖疊加圖層的顯示狀態。
### <Coins size="16" class="inline-block" style="margin-bottom: 2px" /> Distance markers ### <Coins size="16" class="inline-block" style="margin-bottom: 2px" /> 距離標記
Toggle the visibility of distance markers on the map. 切換地圖上距離標記的顯示狀態。
They are displayed for the current selection, like the [elevation profile](../files-and-stats). 距離標記會針對目前的選取內容顯示,就像[高度剖面圖](../files-and-stats)一樣。
### <Milestone size="16" class="inline-block" style="margin-bottom: 2px" /> Direction arrows ### <Milestone size="16" class="inline-block" style="margin-bottom: 2px" /> 方向箭頭
Toggle the visibility of direction arrows on the map. 切換地圖上方向箭頭的顯示狀態。
### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> Toggle 3D ### <Box size="16" class="inline-block" style="margin-bottom: 2px" /> 切換 3D
Enter or exit the 3D map view. 進入或離開 3D 地圖檢視。
<DocsNote> <DocsNote>
@@ -48,7 +48,7 @@ To control the orientation and tilt of the map, you can also drag the map while
</DocsNote> </DocsNote>
### <Maximize2 size="16" class="inline-block" style="margin-bottom: 2px" /> Full screen ### <Maximize2 size="16" class="inline-block" style="margin-bottom: 2px" /> 全螢幕
Enter or exit full screen mode. 進入或離開全螢幕模式。
You can also press <kbd>F11</kbd> to toggle, or <kbd>Escape</kbd> to exit. 你也可以按下 <kbd>F11</kbd> 來切換,或按下 <kbd>Escape</kbd> 離開。
+4 -4
View File
@@ -1,6 +1,6 @@
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { selection } from '$lib/logic/selection'; import { selection } from '$lib/logic/selection';
import { LngLatBounds } from 'maplibre-gl'; import maplibregl from 'maplibre-gl';
import { ListFileItem, ListWaypointItem } from '$lib/components/file-list/file-list'; import { ListFileItem, ListWaypointItem } from '$lib/components/file-list/file-list';
import { fileStateCollection, GPXFileStateCollectionObserver } from '$lib/logic/file-state'; import { fileStateCollection, GPXFileStateCollectionObserver } from '$lib/logic/file-state';
import { gpxStatistics } from '$lib/logic/statistics'; import { gpxStatistics } from '$lib/logic/statistics';
@@ -10,7 +10,7 @@ import type { Coordinates } from 'gpx';
import { page } from '$app/state'; import { page } from '$app/state';
export class BoundsManager { export class BoundsManager {
private _bounds: LngLatBounds = new LngLatBounds(); private _bounds: maplibregl.LngLatBounds = new maplibregl.LngLatBounds();
private _files: Set<string> = new Set(); private _files: Set<string> = new Set();
private _fileStateCollectionObserver: GPXFileStateCollectionObserver | null = null; private _fileStateCollectionObserver: GPXFileStateCollectionObserver | null = null;
private _unsubscribes: (() => void)[] = []; private _unsubscribes: (() => void)[] = [];
@@ -87,12 +87,12 @@ export class BoundsManager {
} }
this._unsubscribes.forEach((unsubscribe) => unsubscribe()); this._unsubscribes.forEach((unsubscribe) => unsubscribe());
this._unsubscribes = []; this._unsubscribes = [];
this._bounds = new LngLatBounds([180, 90, -180, -90]); this._bounds = new maplibregl.LngLatBounds([180, 90, -180, -90]);
} }
centerMapOnSelection() { centerMapOnSelection() {
let selected = get(selection).getSelected(); let selected = get(selection).getSelected();
let bounds = new LngLatBounds(); let bounds = new maplibregl.LngLatBounds();
if (selected.find((item) => item instanceof ListWaypointItem)) { if (selected.find((item) => item instanceof ListWaypointItem)) {
selection.applyToOrderedSelectedItemsFromFile((fileId, level, items) => { selection.applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
+6 -6
View File
@@ -1,16 +1,16 @@
import { ListItem, ListLevel } from '$lib/components/file-list/file-list'; import { ListItem, ListLevel } from '$lib/components/file-list/file-list';
import { GPXFile, GPXStatistics, GPXStatisticsGroup, type Track } from 'gpx'; import { GPXFile, GPXStatistics, GPXStatisticsGroup, type Track } from 'gpx';
import { LngLatBounds } from 'maplibre-gl'; import maplibregl from 'maplibre-gl';
export class GPXStatisticsTree { export class GPXStatisticsTree {
level: ListLevel; level: ListLevel;
statistics: { statistics: {
[key: string]: GPXStatisticsTree | GPXStatistics; [key: string]: GPXStatisticsTree | GPXStatistics;
} = {}; } = {};
wptBounds: LngLatBounds; wptBounds: maplibregl.LngLatBounds;
constructor(element: GPXFile | Track) { constructor(element: GPXFile | Track) {
this.wptBounds = new LngLatBounds(); this.wptBounds = new maplibregl.LngLatBounds();
if (element instanceof GPXFile) { if (element instanceof GPXFile) {
this.level = ListLevel.FILE; this.level = ListLevel.FILE;
element.children.forEach((child, index) => { element.children.forEach((child, index) => {
@@ -49,11 +49,11 @@ export class GPXStatisticsTree {
return statistics; return statistics;
} }
intersectsBBox(bounds: LngLatBounds): boolean { intersectsBBox(bounds: maplibregl.LngLatBounds): boolean {
for (let key in this.statistics) { for (let key in this.statistics) {
const stats = this.statistics[key]; const stats = this.statistics[key];
if (stats instanceof GPXStatistics) { if (stats instanceof GPXStatistics) {
const bbox = new LngLatBounds( const bbox = new maplibregl.LngLatBounds(
stats.global.bounds.southWest, stats.global.bounds.southWest,
stats.global.bounds.northEast stats.global.bounds.northEast
); );
@@ -67,7 +67,7 @@ export class GPXStatisticsTree {
return false; return false;
} }
intersectsWaypointBBox(bounds: LngLatBounds): boolean { intersectsWaypointBBox(bounds: maplibregl.LngLatBounds): boolean {
return !this.wptBounds.isEmpty() && this.wptBounds.intersects(bounds); return !this.wptBounds.isEmpty() && this.wptBounds.intersects(bounds);
} }
} }
+4 -4
View File
@@ -3,7 +3,7 @@ import { twMerge } from 'tailwind-merge';
import { base } from '$app/paths'; import { base } from '$app/paths';
import { languages } from '$lib/languages'; import { languages } from '$lib/languages';
import { TrackPoint, Waypoint, type Coordinates, crossarcDistance, distance, GPXFile } from 'gpx'; import { TrackPoint, Waypoint, type Coordinates, crossarcDistance, distance, GPXFile } from 'gpx';
import { Map as MapLibreMap, LngLatBounds } from 'maplibre-gl'; import maplibregl from 'maplibre-gl';
import { pointToTile, pointToTileFraction } from '@mapbox/tilebelt'; import { pointToTile, pointToTileFraction } from '@mapbox/tilebelt';
import type { GPXStatisticsTree } from '$lib/logic/statistics-tree'; import type { GPXStatisticsTree } from '$lib/logic/statistics-tree';
import { ListTrackSegmentItem } from '$lib/components/file-list/file-list'; import { ListTrackSegmentItem } from '$lib/components/file-list/file-list';
@@ -58,7 +58,7 @@ export function getClosestTrackSegments(
let segmentBounds = segmentStatistics.global.bounds; let segmentBounds = segmentStatistics.global.bounds;
let northEast = segmentBounds.northEast; let northEast = segmentBounds.northEast;
let southWest = segmentBounds.southWest; let southWest = segmentBounds.southWest;
let bounds = new LngLatBounds(southWest, northEast); let bounds = new maplibregl.LngLatBounds(southWest, northEast);
if (bounds.contains(point)) { if (bounds.contains(point)) {
segmentBoundsDistances.push([0, trackIndex, segmentIndex]); segmentBoundsDistances.push([0, trackIndex, segmentIndex]);
} else { } else {
@@ -106,7 +106,7 @@ export function getElevation(
let coordinates = points.map((point) => let coordinates = points.map((point) =>
point instanceof TrackPoint || point instanceof Waypoint ? point.getCoordinates() : point point instanceof TrackPoint || point instanceof Waypoint ? point.getCoordinates() : point
); );
let bbox = new LngLatBounds(); let bbox = new maplibregl.LngLatBounds();
coordinates.forEach((coord) => bbox.extend(coord)); coordinates.forEach((coord) => bbox.extend(coord));
let tiles = coordinates.map((coord) => pointToTile(coord.lon, coord.lat, ELEVATION_ZOOM)); let tiles = coordinates.map((coord) => pointToTile(coord.lon, coord.lat, ELEVATION_ZOOM));
@@ -193,7 +193,7 @@ export function getElevation(
); );
} }
export function loadSVGIcon(map: MapLibreMap, id: string, svg: string, size: number = 100) { export function loadSVGIcon(map: maplibregl.Map, id: string, svg: string, size: number = 100) {
if (!map.hasImage(id)) { if (!map.hasImage(id)) {
let icon = new Image(size, size); let icon = new Image(size, size);
icon.onload = () => { icon.onload = () => {
+2 -2
View File
@@ -163,7 +163,7 @@
"path": "Sendero", "path": "Sendero",
"pedestrian": "Peatonal", "pedestrian": "Peatonal",
"cycleway": "Ciclovía", "cycleway": "Ciclovía",
"steps": "Escalones", "steps": "Pasos",
"road": "Carretera", "road": "Carretera",
"bridleway": "Ruta ecuestre", "bridleway": "Ruta ecuestre",
"platform": "Plataforma", "platform": "Plataforma",
@@ -230,7 +230,7 @@
"extract": { "extract": {
"tooltip": "Extraer contenidos en elementos separados", "tooltip": "Extraer contenidos en elementos separados",
"button": "Extraer", "button": "Extraer",
"help": "Al extraer los contenidos de los elementos seleccionados creará un elemento separado para cada uno.", "help": "La extracción del contenido de los elementos seleccionados creará un elemento separado para cada uno de sus contenidos.",
"help_invalid_selection": "Su selección debe contener elementos con múltiples trazas para extraerlos." "help_invalid_selection": "Su selección debe contener elementos con múltiples trazas para extraerlos."
}, },
"elevation": { "elevation": {
+1 -1
View File
@@ -20,7 +20,7 @@
"export": "Exporteren...", "export": "Exporteren...",
"export_all": "Alles exporteren...", "export_all": "Alles exporteren...",
"export_options": "Exporteer instellingen", "export_options": "Exporteer instellingen",
"support_message": "Het gereedschap is gratis te gebruiken, maar is niet gratis om te onderhouden. Overweeg om de website te ondersteunen als je hem vaak gebruikt. Bedankt!", "support_message": "De tool is gratis te gebruiken, maar is niet gratis om te onderhouden. Overweeg om de website te ondersteunen als je hem vaak gebruikt. Bedankt!",
"support_button": "Help de website gratis te houden", "support_button": "Help de website gratis te houden",
"download_file": "Bestand downloaden", "download_file": "Bestand downloaden",
"download_files": "Bestanden downloaden", "download_files": "Bestanden downloaden",
+3 -3
View File
@@ -64,7 +64,7 @@
"ctrl": "Ctrl", "ctrl": "Ctrl",
"click": "Clique", "click": "Clique",
"drag": "Arrastar", "drag": "Arrastar",
"right_click_drag": "Right-click drag", "right_click_drag": "Clique direito arraste",
"metadata": { "metadata": {
"button": "Informações...", "button": "Informações...",
"name": "Nome", "name": "Nome",
@@ -181,7 +181,7 @@
}, },
"sac_scale": { "sac_scale": {
"hiking": "Caminhada", "hiking": "Caminhada",
"mountain_hiking": "Mountain hiking", "mountain_hiking": "",
"demanding_mountain_hiking": "Demanding mountain hiking", "demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking", "alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking", "demanding_alpine_hiking": "Demanding alpine hiking",
@@ -239,7 +239,7 @@
"help_no_selection": "Select a file item to request elevation data." "help_no_selection": "Select a file item to request elevation data."
}, },
"waypoint": { "waypoint": {
"tooltip": "Create and edit points of interest", "tooltip": "Criar ponto de interesse",
"icon": "Icon", "icon": "Icon",
"link": "Link", "link": "Link",
"longitude": "Longitude", "longitude": "Longitude",