mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-01-01 15:54:44 +00:00
Compare commits
20 Commits
9cff71fed3
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
595ea8e2d3 | ||
|
|
d3e733aa3e | ||
|
|
a011768d2d | ||
|
|
4b45b5d716 | ||
|
|
ebe9681c12 | ||
|
|
51c85e4cd5 | ||
|
|
2e171dfbee | ||
|
|
a6a3917986 | ||
|
|
21f2448213 | ||
|
|
e7a1d0488b | ||
|
|
22b8e0edb4 | ||
|
|
d062a38e8f | ||
|
|
affa59130f | ||
|
|
3c816567bc | ||
|
|
e92e48ffde | ||
|
|
4ce7777b86 | ||
|
|
bc130ad867 | ||
|
|
867b6a6ac7 | ||
|
|
e585fd084c | ||
|
|
b47bb4a771 |
@@ -1,6 +0,0 @@
|
|||||||
# Ignore files for PNPM, NPM and YARN
|
|
||||||
pnpm-lock.yaml
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
src/lib/components/ui
|
|
||||||
*.mdx
|
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"postinstall": "npm run build",
|
"postinstall": "npm run build",
|
||||||
"lint": "prettier --check . && eslint .",
|
"lint": "prettier --check . --config ../.prettierrc && eslint .",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write . --config ../.prettierrc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,7 +148,9 @@ export class GPXFile extends GPXTreeNode<Track> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
this.wpt = gpx.wpt ? gpx.wpt.map((waypoint) => new Waypoint(waypoint)) : [];
|
this.wpt = gpx.wpt
|
||||||
|
? gpx.wpt.map((waypoint, index) => new Waypoint(waypoint, index))
|
||||||
|
: [];
|
||||||
this.trk = gpx.trk ? gpx.trk.map((track) => new Track(track)) : [];
|
this.trk = gpx.trk ? gpx.trk.map((track) => new Track(track)) : [];
|
||||||
if (gpx.rte && gpx.rte.length > 0) {
|
if (gpx.rte && gpx.rte.length > 0) {
|
||||||
this.trk = this.trk.concat(gpx.rte.map((route) => convertRouteToTrack(route)));
|
this.trk = this.trk.concat(gpx.rte.map((route) => convertRouteToTrack(route)));
|
||||||
@@ -186,9 +188,6 @@ export class GPXFile extends GPXTreeNode<Track> {
|
|||||||
segment._data['segmentIndex'] = segmentIndex;
|
segment._data['segmentIndex'] = segmentIndex;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.wpt.forEach((waypoint, waypointIndex) => {
|
|
||||||
waypoint._data['index'] = waypointIndex;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get children(): Array<Track> {
|
get children(): Array<Track> {
|
||||||
@@ -807,7 +806,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
constructor(segment?: (TrackSegmentType & { _data?: any }) | TrackSegment) {
|
constructor(segment?: (TrackSegmentType & { _data?: any }) | TrackSegment) {
|
||||||
super();
|
super();
|
||||||
if (segment) {
|
if (segment) {
|
||||||
this.trkpt = segment.trkpt.map((point) => new TrackPoint(point));
|
this.trkpt = segment.trkpt.map((point, index) => new TrackPoint(point, index));
|
||||||
if (segment.hasOwnProperty('_data')) {
|
if (segment.hasOwnProperty('_data')) {
|
||||||
this._data = segment._data;
|
this._data = segment._data;
|
||||||
}
|
}
|
||||||
@@ -819,12 +818,10 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
_computeStatistics(): GPXStatistics {
|
_computeStatistics(): GPXStatistics {
|
||||||
let statistics = new GPXStatistics();
|
let statistics = new GPXStatistics();
|
||||||
|
|
||||||
statistics.local.points = this.trkpt.map((point) => point);
|
statistics.local.points = this.trkpt.slice(0);
|
||||||
|
|
||||||
const points = this.trkpt;
|
const points = this.trkpt;
|
||||||
for (let i = 0; i < points.length; i++) {
|
for (let i = 0; i < points.length; i++) {
|
||||||
points[i]._data['index'] = i;
|
|
||||||
|
|
||||||
// distance
|
// distance
|
||||||
let dist = 0;
|
let dist = 0;
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
@@ -1317,7 +1314,7 @@ export class TrackPoint {
|
|||||||
|
|
||||||
_data: { [key: string]: any } = {};
|
_data: { [key: string]: any } = {};
|
||||||
|
|
||||||
constructor(point: (TrackPointType & { _data?: any }) | TrackPoint) {
|
constructor(point: (TrackPointType & { _data?: any }) | TrackPoint, index?: number) {
|
||||||
this.attributes = point.attributes;
|
this.attributes = point.attributes;
|
||||||
this.ele = point.ele;
|
this.ele = point.ele;
|
||||||
this.time = point.time;
|
this.time = point.time;
|
||||||
@@ -1325,6 +1322,9 @@ export class TrackPoint {
|
|||||||
if (point.hasOwnProperty('_data')) {
|
if (point.hasOwnProperty('_data')) {
|
||||||
this._data = point._data;
|
this._data = point._data;
|
||||||
}
|
}
|
||||||
|
if (index !== undefined) {
|
||||||
|
this._data.index = index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCoordinates(): Coordinates {
|
getCoordinates(): Coordinates {
|
||||||
@@ -1468,11 +1468,18 @@ export class TrackPoint {
|
|||||||
|
|
||||||
clone(): TrackPoint {
|
clone(): TrackPoint {
|
||||||
return new TrackPoint({
|
return new TrackPoint({
|
||||||
attributes: cloneJSON(this.attributes),
|
attributes: {
|
||||||
|
lat: this.attributes.lat,
|
||||||
|
lon: this.attributes.lon,
|
||||||
|
},
|
||||||
ele: this.ele,
|
ele: this.ele,
|
||||||
time: this.time ? new Date(this.time.getTime()) : undefined,
|
time: this.time ? new Date(this.time.getTime()) : undefined,
|
||||||
extensions: cloneJSON(this.extensions),
|
extensions: this.extensions ? cloneJSON(this.extensions) : undefined,
|
||||||
_data: cloneJSON(this._data),
|
_data: {
|
||||||
|
index: this._data?.index,
|
||||||
|
anchor: this._data?.anchor,
|
||||||
|
zoom: this._data?.zoom,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1491,7 +1498,7 @@ export class Waypoint {
|
|||||||
type?: string;
|
type?: string;
|
||||||
_data: { [key: string]: any } = {};
|
_data: { [key: string]: any } = {};
|
||||||
|
|
||||||
constructor(waypoint: (WaypointType & { _data?: any }) | Waypoint) {
|
constructor(waypoint: (WaypointType & { _data?: any }) | Waypoint, index?: number) {
|
||||||
this.attributes = waypoint.attributes;
|
this.attributes = waypoint.attributes;
|
||||||
this.ele = waypoint.ele;
|
this.ele = waypoint.ele;
|
||||||
this.time = waypoint.time;
|
this.time = waypoint.time;
|
||||||
@@ -1510,6 +1517,9 @@ export class Waypoint {
|
|||||||
if (waypoint.hasOwnProperty('_data')) {
|
if (waypoint.hasOwnProperty('_data')) {
|
||||||
this._data = waypoint._data;
|
this._data = waypoint._data;
|
||||||
}
|
}
|
||||||
|
if (index !== undefined) {
|
||||||
|
this._data.index = index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCoordinates(): Coordinates {
|
getCoordinates(): Coordinates {
|
||||||
@@ -1557,7 +1567,10 @@ export class Waypoint {
|
|||||||
|
|
||||||
clone(): Waypoint {
|
clone(): Waypoint {
|
||||||
return new Waypoint({
|
return new Waypoint({
|
||||||
attributes: cloneJSON(this.attributes),
|
attributes: {
|
||||||
|
lat: this.attributes.lat,
|
||||||
|
lon: this.attributes.lon,
|
||||||
|
},
|
||||||
ele: this.ele,
|
ele: this.ele,
|
||||||
time: this.time ? new Date(this.time.getTime()) : undefined,
|
time: this.time ? new Date(this.time.getTime()) : undefined,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
|
|||||||
@@ -59,13 +59,13 @@ function ramerDouglasPeuckerRecursive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function crossarcDistance(
|
export function crossarcDistance(
|
||||||
point1: TrackPoint,
|
point1: TrackPoint | Coordinates,
|
||||||
point2: TrackPoint,
|
point2: TrackPoint | Coordinates,
|
||||||
point3: TrackPoint | Coordinates
|
point3: TrackPoint | Coordinates
|
||||||
): number {
|
): number {
|
||||||
return crossarc(
|
return crossarc(
|
||||||
point1.getCoordinates(),
|
point1 instanceof TrackPoint ? point1.getCoordinates() : point1,
|
||||||
point2.getCoordinates(),
|
point2 instanceof TrackPoint ? point2.getCoordinates() : point2,
|
||||||
point3 instanceof TrackPoint ? point3.getCoordinates() : point3
|
point3 instanceof TrackPoint ? point3.getCoordinates() : point3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
3
website/.prettierignore
Normal file
3
website/.prettierignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
src/lib/components/ui
|
||||||
|
src/lib/docs/**/*.mdx
|
||||||
|
**/*.webmanifest
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||||
"style": "default",
|
"style": "default",
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"css": "src/app.css",
|
"css": "src/app.css",
|
||||||
"baseColor": "slate"
|
"baseColor": "slate"
|
||||||
},
|
},
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"components": "$lib/components",
|
"components": "$lib/components",
|
||||||
"utils": "$lib/utils",
|
"utils": "$lib/utils",
|
||||||
"ui": "$lib/components/ui",
|
"ui": "$lib/components/ui",
|
||||||
"hooks": "$lib/hooks",
|
"hooks": "$lib/hooks",
|
||||||
"lib": "$lib"
|
"lib": "$lib"
|
||||||
},
|
},
|
||||||
"typescript": true,
|
"typescript": true,
|
||||||
"registry": "https://shadcn-svelte.com/registry"
|
"registry": "https://shadcn-svelte.com/registry"
|
||||||
}
|
}
|
||||||
|
|||||||
35
website/package-lock.json
generated
35
website/package-lock.json
generated
@@ -14,7 +14,7 @@
|
|||||||
"@mapbox/sphericalmercator": "^2.0.1",
|
"@mapbox/sphericalmercator": "^2.0.1",
|
||||||
"@mapbox/tilebelt": "^2.0.2",
|
"@mapbox/tilebelt": "^2.0.2",
|
||||||
"@types/mapbox__sphericalmercator": "^1.2.3",
|
"@types/mapbox__sphericalmercator": "^1.2.3",
|
||||||
"chart.js": "^4.4.9",
|
"chart.js": "^4.5.1",
|
||||||
"chartjs-plugin-zoom": "^2.2.0",
|
"chartjs-plugin-zoom": "^2.2.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dexie": "^4.0.11",
|
"dexie": "^4.0.11",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"gpx": "file:../gpx",
|
"gpx": "file:../gpx",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"mapbox-gl": "^3.16.0",
|
"mapbox-gl": "^3.17.0",
|
||||||
"mapillary-js": "^4.1.2",
|
"mapillary-js": "^4.1.2",
|
||||||
"png.js": "^0.2.1",
|
"png.js": "^0.2.1",
|
||||||
"sanitize-html": "^2.17.0",
|
"sanitize-html": "^2.17.0",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"@types/sortablejs": "^1.15.8",
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.33.1",
|
"@typescript-eslint/eslint-plugin": "^8.33.1",
|
||||||
"@typescript-eslint/parser": "^8.33.1",
|
"@typescript-eslint/parser": "^8.33.1",
|
||||||
"bits-ui": "^2.12.0",
|
"bits-ui": "^2.14.4",
|
||||||
"eslint": "^9.28.0",
|
"eslint": "^9.28.0",
|
||||||
"eslint-config-prettier": "^10.1.5",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
"eslint-plugin-svelte": "^3.9.1",
|
"eslint-plugin-svelte": "^3.9.1",
|
||||||
@@ -3241,9 +3241,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/bits-ui": {
|
"node_modules/bits-ui": {
|
||||||
"version": "2.12.0",
|
"version": "2.14.4",
|
||||||
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.14.4.tgz",
|
||||||
"integrity": "sha512-8NF4ILNyAJlIxDXpl/akGXGBV5QmZAe+8gTfPttM5P6/+LrijumcSfFXY5cr4QkXwTmLA7H5stYpbgJf2XFJvg==",
|
"integrity": "sha512-W6kenhnbd/YVvur+DKkaVJ6GldE53eLewur5AhUCqslYQ0vjZr8eWlOfwZnMiPB+PF5HMVqf61vXBvmyrAmPWg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -3664,9 +3664,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/chart.js": {
|
"node_modules/chart.js": {
|
||||||
"version": "4.4.9",
|
"version": "4.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||||
"integrity": "sha512-EyZ9wWKgpAU0fLJ43YAEIF8sr5F2W3LqbS40ZJyHIner2lY14ufqv2VMp69MAiZ2rpwxEUxEhIH/0U3xyRynxg==",
|
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kurkle/color": "^0.3.0"
|
"@kurkle/color": "^0.3.0"
|
||||||
@@ -6069,12 +6069,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mapbox-gl": {
|
"node_modules/mapbox-gl": {
|
||||||
"version": "3.16.0",
|
"version": "3.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.17.0.tgz",
|
||||||
"integrity": "sha512-rluV1Zp/0oHf1Y9BV+nePRNnKyTdljko3E19CzO5rBqtQaNUYS0ePCMPRtxOuWRwSdKp3f9NWJkOCjemM8nmjw==",
|
"integrity": "sha512-nCrDKRlr5di6xUksUDslNWwxroJ5yv1hT8pyVFtcpWJOOKsYQxF/wOFTMie8oxMnXeFkrz1Tl1TwA1XN1yX0KA==",
|
||||||
"license": "SEE LICENSE IN LICENSE.txt",
|
"license": "SEE LICENSE IN LICENSE.txt",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"src/style-spec",
|
"src/style-spec",
|
||||||
|
"test/build/vite",
|
||||||
|
"test/build/webpack",
|
||||||
"test/build/typings"
|
"test/build/typings"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -6102,7 +6104,6 @@
|
|||||||
"pbf": "^4.0.1",
|
"pbf": "^4.0.1",
|
||||||
"potpack": "^2.0.0",
|
"potpack": "^2.0.0",
|
||||||
"quickselect": "^3.0.0",
|
"quickselect": "^3.0.0",
|
||||||
"serialize-to-js": "^3.1.2",
|
|
||||||
"supercluster": "^8.0.1",
|
"supercluster": "^8.0.1",
|
||||||
"tinyqueue": "^3.0.0"
|
"tinyqueue": "^3.0.0"
|
||||||
}
|
}
|
||||||
@@ -7634,14 +7635,6 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/serialize-to-js": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-owllqNuDDEimQat7EPG0tH7JjO090xKNzUtYz6X+Sk2BXDnOCilDdNLwjWeFywG9xkJul1ULvtUQa9O4pUaY0w==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/set-cookie-parser": {
|
"node_modules/set-cookie-parser": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz",
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint": "prettier --check . && eslint .",
|
"lint": "prettier --check . --config ../.prettierrc && eslint .",
|
||||||
"format": "prettier --write ."
|
"format": "prettier --write . --config ../.prettierrc"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@lucide/svelte": "^0.544.0",
|
"@lucide/svelte": "^0.544.0",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"@types/sortablejs": "^1.15.8",
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.33.1",
|
"@typescript-eslint/eslint-plugin": "^8.33.1",
|
||||||
"@typescript-eslint/parser": "^8.33.1",
|
"@typescript-eslint/parser": "^8.33.1",
|
||||||
"bits-ui": "^2.12.0",
|
"bits-ui": "^2.14.4",
|
||||||
"eslint": "^9.28.0",
|
"eslint": "^9.28.0",
|
||||||
"eslint-config-prettier": "^10.1.5",
|
"eslint-config-prettier": "^10.1.5",
|
||||||
"eslint-plugin-svelte": "^3.9.1",
|
"eslint-plugin-svelte": "^3.9.1",
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
"@mapbox/sphericalmercator": "^2.0.1",
|
"@mapbox/sphericalmercator": "^2.0.1",
|
||||||
"@mapbox/tilebelt": "^2.0.2",
|
"@mapbox/tilebelt": "^2.0.2",
|
||||||
"@types/mapbox__sphericalmercator": "^1.2.3",
|
"@types/mapbox__sphericalmercator": "^1.2.3",
|
||||||
"chart.js": "^4.4.9",
|
"chart.js": "^4.5.1",
|
||||||
"chartjs-plugin-zoom": "^2.2.0",
|
"chartjs-plugin-zoom": "^2.2.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dexie": "^4.0.11",
|
"dexie": "^4.0.11",
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
"gpx": "file:../gpx",
|
"gpx": "file:../gpx",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"mapbox-gl": "^3.16.0",
|
"mapbox-gl": "^3.17.0",
|
||||||
"mapillary-js": "^4.1.2",
|
"mapillary-js": "^4.1.2",
|
||||||
"png.js": "^0.2.1",
|
"png.js": "^0.2.1",
|
||||||
"sanitize-html": "^2.17.0",
|
"sanitize-html": "^2.17.0",
|
||||||
|
|||||||
@@ -1,126 +1,126 @@
|
|||||||
@import "tailwindcss";
|
@import 'tailwindcss';
|
||||||
@import "tw-animate-css";
|
@import 'tw-animate-css';
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: hsl(0 0% 100%) /* <- Wrap in HSL */;
|
--background: hsl(0 0% 100%) /* <- Wrap in HSL */;
|
||||||
--foreground: hsl(240 10% 3.9%);
|
--foreground: hsl(240 10% 3.9%);
|
||||||
--muted: hsl(240 4.8% 95.9%);
|
--muted: hsl(240 4.8% 95.9%);
|
||||||
--muted-foreground: hsl(240 3.8% 46.1%);
|
--muted-foreground: hsl(240 3.8% 46.1%);
|
||||||
--popover: hsl(0 0% 100%);
|
--popover: hsl(0 0% 100%);
|
||||||
--popover-foreground: hsl(240 10% 3.9%);
|
--popover-foreground: hsl(240 10% 3.9%);
|
||||||
--card: hsl(0 0% 100%);
|
--card: hsl(0 0% 100%);
|
||||||
--card-foreground: hsl(240 10% 3.9%);
|
--card-foreground: hsl(240 10% 3.9%);
|
||||||
--border: hsl(240 5.9% 90%);
|
--border: hsl(240 5.9% 90%);
|
||||||
--input: hsl(240 5.9% 90%);
|
--input: hsl(240 5.9% 90%);
|
||||||
--primary: hsl(240 5.9% 10%);
|
--primary: hsl(240 5.9% 10%);
|
||||||
--primary-foreground: hsl(0 0% 98%);
|
--primary-foreground: hsl(0 0% 98%);
|
||||||
--secondary: hsl(240 4.8% 95.9%);
|
--secondary: hsl(240 4.8% 95.9%);
|
||||||
--secondary-foreground: hsl(240 5.9% 10%);
|
--secondary-foreground: hsl(240 5.9% 10%);
|
||||||
--accent: hsl(240 4.8% 95.9%);
|
--accent: hsl(240 4.8% 95.9%);
|
||||||
--accent-foreground: hsl(240 5.9% 10%);
|
--accent-foreground: hsl(240 5.9% 10%);
|
||||||
--destructive: hsl(0 72.2% 50.6%);
|
--destructive: hsl(0 72.2% 50.6%);
|
||||||
--destructive-foreground: hsl(0 0% 98%);
|
--destructive-foreground: hsl(0 0% 98%);
|
||||||
--ring: hsl(240 10% 3.9%);
|
--ring: hsl(240 10% 3.9%);
|
||||||
--sidebar: hsl(0 0% 98%);
|
--sidebar: hsl(0 0% 98%);
|
||||||
--sidebar-foreground: hsl(240 5.3% 26.1%);
|
--sidebar-foreground: hsl(240 5.3% 26.1%);
|
||||||
--sidebar-primary: hsl(240 5.9% 10%);
|
--sidebar-primary: hsl(240 5.9% 10%);
|
||||||
--sidebar-primary-foreground: hsl(0 0% 98%);
|
--sidebar-primary-foreground: hsl(0 0% 98%);
|
||||||
--sidebar-accent: hsl(240 4.8% 95.9%);
|
--sidebar-accent: hsl(240 4.8% 95.9%);
|
||||||
--sidebar-accent-foreground: hsl(240 5.9% 10%);
|
--sidebar-accent-foreground: hsl(240 5.9% 10%);
|
||||||
--sidebar-border: hsl(220 13% 91%);
|
--sidebar-border: hsl(220 13% 91%);
|
||||||
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
||||||
|
|
||||||
--support: rgb(220 15 130);
|
--support: rgb(220 15 130);
|
||||||
--link: rgb(0 110 180);
|
--link: rgb(0 110 180);
|
||||||
--selection: hsl(240 4.8% 93%);
|
--selection: hsl(240 4.8% 93%);
|
||||||
|
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: hsl(240 10% 3.9%);
|
--background: hsl(240 10% 3.9%);
|
||||||
--foreground: hsl(0 0% 98%);
|
--foreground: hsl(0 0% 98%);
|
||||||
--muted: hsl(240 3.7% 15.9%);
|
--muted: hsl(240 3.7% 15.9%);
|
||||||
--muted-foreground: hsl(240 5% 64.9%);
|
--muted-foreground: hsl(240 5% 64.9%);
|
||||||
--popover: hsl(240 10% 3.9%);
|
--popover: hsl(240 10% 3.9%);
|
||||||
--popover-foreground: hsl(0 0% 98%);
|
--popover-foreground: hsl(0 0% 98%);
|
||||||
--card: hsl(240 10% 3.9%);
|
--card: hsl(240 10% 3.9%);
|
||||||
--card-foreground: hsl(0 0% 98%);
|
--card-foreground: hsl(0 0% 98%);
|
||||||
--border: hsl(240 3.7% 15.9%);
|
--border: hsl(240 3.7% 15.9%);
|
||||||
--input: hsl(240 3.7% 15.9%);
|
--input: hsl(240 3.7% 15.9%);
|
||||||
--primary: hsl(0 0% 98%);
|
--primary: hsl(0 0% 98%);
|
||||||
--primary-foreground: hsl(240 5.9% 10%);
|
--primary-foreground: hsl(240 5.9% 10%);
|
||||||
--secondary: hsl(240 3.7% 15.9%);
|
--secondary: hsl(240 3.7% 15.9%);
|
||||||
--secondary-foreground: hsl(0 0% 98%);
|
--secondary-foreground: hsl(0 0% 98%);
|
||||||
--accent: hsl(240 3.7% 15.9%);
|
--accent: hsl(240 3.7% 15.9%);
|
||||||
--accent-foreground: hsl(0 0% 98%);
|
--accent-foreground: hsl(0 0% 98%);
|
||||||
--destructive: hsl(0 62.8% 30.6%);
|
--destructive: hsl(0 62.8% 30.6%);
|
||||||
--destructive-foreground: hsl(0 0% 98%);
|
--destructive-foreground: hsl(0 0% 98%);
|
||||||
--ring: hsl(240 4.9% 83.9%);
|
--ring: hsl(240 4.9% 83.9%);
|
||||||
--sidebar: hsl(240 5.9% 10%);
|
--sidebar: hsl(240 5.9% 10%);
|
||||||
--sidebar-foreground: hsl(240 4.8% 95.9%);
|
--sidebar-foreground: hsl(240 4.8% 95.9%);
|
||||||
--sidebar-primary: hsl(224.3 76.3% 48%);
|
--sidebar-primary: hsl(224.3 76.3% 48%);
|
||||||
--sidebar-primary-foreground: hsl(0 0% 100%);
|
--sidebar-primary-foreground: hsl(0 0% 100%);
|
||||||
--sidebar-accent: hsl(240 3.7% 15.9%);
|
--sidebar-accent: hsl(240 3.7% 15.9%);
|
||||||
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
|
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
|
||||||
--sidebar-border: hsl(240 3.7% 15.9%);
|
--sidebar-border: hsl(240 3.7% 15.9%);
|
||||||
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
||||||
|
|
||||||
--support: rgb(255 110 190);
|
--support: rgb(255 110 190);
|
||||||
--link: rgb(80 190 255);
|
--link: rgb(80 190 255);
|
||||||
--selection: hsl(240 3.7% 22%);
|
--selection: hsl(240 3.7% 22%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
/* Radius (for rounded-*) */
|
/* Radius (for rounded-*) */
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
--radius-lg: var(--radius);
|
--radius-lg: var(--radius);
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
|
||||||
/* Colors */
|
/* Colors */
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
--color-muted: var(--muted);
|
--color-muted: var(--muted);
|
||||||
--color-muted-foreground: var(--muted-foreground);
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
--color-popover: var(--popover);
|
--color-popover: var(--popover);
|
||||||
--color-popover-foreground: var(--popover-foreground);
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
--color-card: var(--card);
|
--color-card: var(--card);
|
||||||
--color-card-foreground: var(--card-foreground);
|
--color-card-foreground: var(--card-foreground);
|
||||||
--color-border: var(--border);
|
--color-border: var(--border);
|
||||||
--color-input: var(--input);
|
--color-input: var(--input);
|
||||||
--color-primary: var(--primary);
|
--color-primary: var(--primary);
|
||||||
--color-primary-foreground: var(--primary-foreground);
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
--color-secondary: var(--secondary);
|
--color-secondary: var(--secondary);
|
||||||
--color-secondary-foreground: var(--secondary-foreground);
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
--color-accent: var(--accent);
|
--color-accent: var(--accent);
|
||||||
--color-accent-foreground: var(--accent-foreground);
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
--color-destructive: var(--destructive);
|
--color-destructive: var(--destructive);
|
||||||
--color-destructive-foreground: var(--destructive-foreground);
|
--color-destructive-foreground: var(--destructive-foreground);
|
||||||
--color-ring: var(--ring);
|
--color-ring: var(--ring);
|
||||||
--color-radius: var(--radius);
|
--color-radius: var(--radius);
|
||||||
--color-sidebar: var(--sidebar);
|
--color-sidebar: var(--sidebar);
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
--color-sidebar-accent: var(--sidebar-accent);
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
--color-support: var(--support);
|
--color-support: var(--support);
|
||||||
--color-link: var(--link);
|
--color-link: var(--link);
|
||||||
|
|
||||||
--breakpoint-xs: 540px;
|
--breakpoint-xs: 540px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -836,6 +836,7 @@ export const overpassTree: LayerTreeType = {
|
|||||||
shower: true,
|
shower: true,
|
||||||
shelter: true,
|
shelter: true,
|
||||||
barrier: true,
|
barrier: true,
|
||||||
|
cemetery: true,
|
||||||
},
|
},
|
||||||
tourism: {
|
tourism: {
|
||||||
attraction: true,
|
attraction: true,
|
||||||
@@ -919,6 +920,7 @@ export const defaultOverpassQueries: LayerTreeType = {
|
|||||||
shower: false,
|
shower: false,
|
||||||
shelter: false,
|
shelter: false,
|
||||||
barrier: false,
|
barrier: false,
|
||||||
|
cemetery: false,
|
||||||
},
|
},
|
||||||
tourism: {
|
tourism: {
|
||||||
attraction: false,
|
attraction: false,
|
||||||
@@ -1053,6 +1055,7 @@ export const defaultOverpassTree: LayerTreeType = {
|
|||||||
shower: false,
|
shower: false,
|
||||||
shelter: false,
|
shelter: false,
|
||||||
barrier: false,
|
barrier: false,
|
||||||
|
cemetery: false,
|
||||||
},
|
},
|
||||||
tourism: {
|
tourism: {
|
||||||
attraction: false,
|
attraction: false,
|
||||||
@@ -1099,9 +1102,7 @@ type OverpassQueryData = {
|
|||||||
svg: string;
|
svg: string;
|
||||||
color: string;
|
color: string;
|
||||||
};
|
};
|
||||||
tags:
|
tags: Record<string, string | string[]> | Record<string, string | string[]>[];
|
||||||
| Record<string, string | boolean | string[]>
|
|
||||||
| Record<string, string | boolean | string[]>[];
|
|
||||||
symbol?: string;
|
symbol?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1182,6 +1183,20 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
|||||||
},
|
},
|
||||||
symbol: 'Shelter',
|
symbol: 'Shelter',
|
||||||
},
|
},
|
||||||
|
cemetery: {
|
||||||
|
icon: {
|
||||||
|
svg: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 17v-10a6 5 0 1 1 12 0v10"/><path d="M 4 21 a 1 1 0 0 0 1 1 h 14 a 1 1 0 0 0 1-1 v -1 a 2 2 0 0 0-2-2 H6 a 2 2 0 0 0-2 2 z"/></svg>',
|
||||||
|
color: '#000000',
|
||||||
|
},
|
||||||
|
tags: [
|
||||||
|
{
|
||||||
|
landuse: 'cemetery',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amenity: 'grave_yard',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
'fuel-station': {
|
'fuel-station': {
|
||||||
icon: {
|
icon: {
|
||||||
svg: Fuel,
|
svg: Fuel,
|
||||||
@@ -1218,7 +1233,25 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
|||||||
color: '#000000',
|
color: '#000000',
|
||||||
},
|
},
|
||||||
tags: {
|
tags: {
|
||||||
barrier: true,
|
barrier: [
|
||||||
|
'bar',
|
||||||
|
'barrier_board',
|
||||||
|
'block',
|
||||||
|
'chain',
|
||||||
|
'cycle_barrier',
|
||||||
|
'gate',
|
||||||
|
'hampshire_gate',
|
||||||
|
'horse_stile',
|
||||||
|
'kissing_gate',
|
||||||
|
'lift_gate',
|
||||||
|
'motorcycle_barrier',
|
||||||
|
'sliding_beam',
|
||||||
|
'sliding_gate',
|
||||||
|
'stile',
|
||||||
|
'swing_gate',
|
||||||
|
'turnstile',
|
||||||
|
'wicket_gate',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
attraction: {
|
attraction: {
|
||||||
|
|||||||
@@ -538,6 +538,7 @@
|
|||||||
let targetInput =
|
let targetInput =
|
||||||
e &&
|
e &&
|
||||||
e.target &&
|
e.target &&
|
||||||
|
e.target instanceof HTMLElement &&
|
||||||
(e.target.tagName === 'INPUT' ||
|
(e.target.tagName === 'INPUT' ||
|
||||||
e.target.tagName === 'TEXTAREA' ||
|
e.target.tagName === 'TEXTAREA' ||
|
||||||
e.target.tagName === 'SELECT' ||
|
e.target.tagName === 'SELECT' ||
|
||||||
|
|||||||
@@ -14,7 +14,12 @@ import {
|
|||||||
getTemperatureWithUnits,
|
getTemperatureWithUnits,
|
||||||
getVelocityWithUnits,
|
getVelocityWithUnits,
|
||||||
} from '$lib/units';
|
} from '$lib/units';
|
||||||
import Chart from 'chart.js/auto';
|
import Chart, {
|
||||||
|
type ChartEvent,
|
||||||
|
type ChartOptions,
|
||||||
|
type ScriptableLineSegmentContext,
|
||||||
|
type TooltipItem,
|
||||||
|
} from 'chart.js/auto';
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl from 'mapbox-gl';
|
||||||
import { get, type Readable, type Writable } from 'svelte/store';
|
import { get, type Readable, type Writable } from 'svelte/store';
|
||||||
import { map } from '$lib/components/map/map';
|
import { map } from '$lib/components/map/map';
|
||||||
@@ -27,6 +32,20 @@ const { distanceUnits, velocityUnits, temperatureUnits } = settings;
|
|||||||
Chart.defaults.font.family =
|
Chart.defaults.font.family =
|
||||||
'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'; // Tailwind CSS font
|
'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'; // Tailwind CSS font
|
||||||
|
|
||||||
|
interface ElevationProfilePoint {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
time?: Date;
|
||||||
|
slope: {
|
||||||
|
at: number;
|
||||||
|
segment: number;
|
||||||
|
length: number;
|
||||||
|
};
|
||||||
|
extensions: Record<string, any>;
|
||||||
|
coordinates: [number, number];
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class ElevationProfile {
|
export class ElevationProfile {
|
||||||
private _chart: Chart | null = null;
|
private _chart: Chart | null = null;
|
||||||
private _canvas: HTMLCanvasElement;
|
private _canvas: HTMLCanvasElement;
|
||||||
@@ -90,7 +109,7 @@ export class ElevationProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
let options = {
|
let options: ChartOptions<'line'> = {
|
||||||
animation: false,
|
animation: false,
|
||||||
parsing: false,
|
parsing: false,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
@@ -98,8 +117,8 @@ export class ElevationProfile {
|
|||||||
x: {
|
x: {
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function (value: number) {
|
callback: function (value: number | string) {
|
||||||
return `${value.toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`;
|
return `${(value as number).toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`;
|
||||||
},
|
},
|
||||||
align: 'inner',
|
align: 'inner',
|
||||||
maxRotation: 0,
|
maxRotation: 0,
|
||||||
@@ -108,8 +127,8 @@ export class ElevationProfile {
|
|||||||
y: {
|
y: {
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: function (value: number) {
|
callback: function (value: number | string) {
|
||||||
return getElevationWithUnits(value, false);
|
return getElevationWithUnits(value as number, false);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -140,8 +159,8 @@ export class ElevationProfile {
|
|||||||
title: () => {
|
title: () => {
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
label: (context: Chart.TooltipContext) => {
|
label: (context: TooltipItem<'line'>) => {
|
||||||
let point = context.raw;
|
let point = context.raw as ElevationProfilePoint;
|
||||||
if (context.datasetIndex === 0) {
|
if (context.datasetIndex === 0) {
|
||||||
const map_ = get(map);
|
const map_ = get(map);
|
||||||
if (map_ && this._marker) {
|
if (map_ && this._marker) {
|
||||||
@@ -165,10 +184,10 @@ export class ElevationProfile {
|
|||||||
return `${i18n._('quantities.power')}: ${getPowerWithUnits(point.y)}`;
|
return `${i18n._('quantities.power')}: ${getPowerWithUnits(point.y)}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
afterBody: (contexts: Chart.TooltipContext[]) => {
|
afterBody: (contexts: TooltipItem<'line'>[]) => {
|
||||||
let context = contexts.filter((context) => context.datasetIndex === 0);
|
let context = contexts.filter((context) => context.datasetIndex === 0);
|
||||||
if (context.length === 0) return;
|
if (context.length === 0) return;
|
||||||
let point = context[0].raw;
|
let point = context[0].raw as ElevationProfilePoint;
|
||||||
let slope = {
|
let slope = {
|
||||||
at: point.slope.at.toFixed(1),
|
at: point.slope.at.toFixed(1),
|
||||||
segment: point.slope.segment.toFixed(1),
|
segment: point.slope.segment.toFixed(1),
|
||||||
@@ -227,6 +246,7 @@ export class ElevationProfile {
|
|||||||
onPanStart: () => {
|
onPanStart: () => {
|
||||||
this._panning = true;
|
this._panning = true;
|
||||||
this._slicedGPXStatistics.set(undefined);
|
this._slicedGPXStatistics.set(undefined);
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
onPanComplete: () => {
|
onPanComplete: () => {
|
||||||
this._panning = false;
|
this._panning = false;
|
||||||
@@ -238,13 +258,13 @@ export class ElevationProfile {
|
|||||||
},
|
},
|
||||||
mode: 'x',
|
mode: 'x',
|
||||||
onZoomStart: ({ chart, event }: { chart: Chart; event: any }) => {
|
onZoomStart: ({ chart, event }: { chart: Chart; event: any }) => {
|
||||||
|
if (!this._chart) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const maxZoom = this._chart.getInitialScaleBounds()?.x?.max ?? 0;
|
||||||
if (
|
if (
|
||||||
event.deltaY < 0 &&
|
event.deltaY < 0 &&
|
||||||
Math.abs(
|
Math.abs(maxZoom / this._chart.getZoomLevel()) < 0.01
|
||||||
this._chart.getInitialScaleBounds().x.max /
|
|
||||||
this._chart.options.plugins.zoom.limits.x.minRange -
|
|
||||||
this._chart.getZoomLevel()
|
|
||||||
) < 0.01
|
|
||||||
) {
|
) {
|
||||||
// Disable wheel pan if zoomed in to the max, and zooming in
|
// Disable wheel pan if zoomed in to the max, and zooming in
|
||||||
return false;
|
return false;
|
||||||
@@ -262,7 +282,6 @@ export class ElevationProfile {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
stacked: false,
|
|
||||||
onResize: () => {
|
onResize: () => {
|
||||||
this.updateOverlay();
|
this.updateOverlay();
|
||||||
},
|
},
|
||||||
@@ -270,7 +289,7 @@ export class ElevationProfile {
|
|||||||
|
|
||||||
let datasets: string[] = ['speed', 'hr', 'cad', 'atemp', 'power'];
|
let datasets: string[] = ['speed', 'hr', 'cad', 'atemp', 'power'];
|
||||||
datasets.forEach((id) => {
|
datasets.forEach((id) => {
|
||||||
options.scales[`y${id}`] = {
|
options.scales![`y${id}`] = {
|
||||||
type: 'linear',
|
type: 'linear',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
grid: {
|
grid: {
|
||||||
@@ -291,7 +310,7 @@ export class ElevationProfile {
|
|||||||
{
|
{
|
||||||
id: 'toggleMarker',
|
id: 'toggleMarker',
|
||||||
events: ['mouseout'],
|
events: ['mouseout'],
|
||||||
afterEvent: (chart: Chart, args: { event: Chart.ChartEvent }) => {
|
afterEvent: (chart: Chart, args: { event: ChartEvent }) => {
|
||||||
if (args.event.type === 'mouseout') {
|
if (args.event.type === 'mouseout') {
|
||||||
const map_ = get(map);
|
const map_ = get(map);
|
||||||
if (map_ && this._marker) {
|
if (map_ && this._marker) {
|
||||||
@@ -305,7 +324,7 @@ export class ElevationProfile {
|
|||||||
|
|
||||||
let startIndex = 0;
|
let startIndex = 0;
|
||||||
let endIndex = 0;
|
let endIndex = 0;
|
||||||
const getIndex = (evt) => {
|
const getIndex = (evt: PointerEvent) => {
|
||||||
if (!this._chart) {
|
if (!this._chart) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -329,16 +348,16 @@ export class ElevationProfile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let point = points.find((point) => point.element.raw);
|
const point = points.find((point) => (point.element as any).raw);
|
||||||
if (point) {
|
if (point) {
|
||||||
return point.element.raw.index;
|
return (point.element as any).raw.index;
|
||||||
} else {
|
} else {
|
||||||
return points[0].index;
|
return points[0].index;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let dragStarted = false;
|
let dragStarted = false;
|
||||||
const onMouseDown = (evt) => {
|
const onMouseDown = (evt: PointerEvent) => {
|
||||||
if (evt.shiftKey) {
|
if (evt.shiftKey) {
|
||||||
// Panning interaction
|
// Panning interaction
|
||||||
return;
|
return;
|
||||||
@@ -347,7 +366,7 @@ export class ElevationProfile {
|
|||||||
this._canvas.style.cursor = 'col-resize';
|
this._canvas.style.cursor = 'col-resize';
|
||||||
startIndex = getIndex(evt);
|
startIndex = getIndex(evt);
|
||||||
};
|
};
|
||||||
const onMouseMove = (evt) => {
|
const onMouseMove = (evt: PointerEvent) => {
|
||||||
if (dragStarted) {
|
if (dragStarted) {
|
||||||
this._dragging = true;
|
this._dragging = true;
|
||||||
endIndex = getIndex(evt);
|
endIndex = getIndex(evt);
|
||||||
@@ -367,7 +386,7 @@ export class ElevationProfile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onMouseUp = (evt) => {
|
const onMouseUp = (evt: PointerEvent) => {
|
||||||
dragStarted = false;
|
dragStarted = false;
|
||||||
this._dragging = false;
|
this._dragging = false;
|
||||||
this._canvas.style.cursor = '';
|
this._canvas.style.cursor = '';
|
||||||
@@ -409,62 +428,77 @@ export class ElevationProfile {
|
|||||||
segment: {},
|
segment: {},
|
||||||
};
|
};
|
||||||
this._chart.data.datasets[1] = {
|
this._chart.data.datasets[1] = {
|
||||||
data: data.local.points.map((point, index) => {
|
data:
|
||||||
return {
|
data.global.time.total > 0
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
? data.local.points.map((point, index) => {
|
||||||
y: getConvertedVelocity(data.local.speed[index]),
|
return {
|
||||||
index: index,
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
};
|
y: getConvertedVelocity(data.local.speed[index]),
|
||||||
}),
|
index: index,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [],
|
||||||
normalized: true,
|
normalized: true,
|
||||||
yAxisID: 'yspeed',
|
yAxisID: 'yspeed',
|
||||||
};
|
};
|
||||||
this._chart.data.datasets[2] = {
|
this._chart.data.datasets[2] = {
|
||||||
data: data.local.points.map((point, index) => {
|
data:
|
||||||
return {
|
data.global.hr.count > 0
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
? data.local.points.map((point, index) => {
|
||||||
y: point.getHeartRate(),
|
return {
|
||||||
index: index,
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
};
|
y: point.getHeartRate(),
|
||||||
}),
|
index: index,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [],
|
||||||
normalized: true,
|
normalized: true,
|
||||||
yAxisID: 'yhr',
|
yAxisID: 'yhr',
|
||||||
};
|
};
|
||||||
this._chart.data.datasets[3] = {
|
this._chart.data.datasets[3] = {
|
||||||
data: data.local.points.map((point, index) => {
|
data:
|
||||||
return {
|
data.global.cad.count > 0
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
? data.local.points.map((point, index) => {
|
||||||
y: point.getCadence(),
|
return {
|
||||||
index: index,
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
};
|
y: point.getCadence(),
|
||||||
}),
|
index: index,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [],
|
||||||
normalized: true,
|
normalized: true,
|
||||||
yAxisID: 'ycad',
|
yAxisID: 'ycad',
|
||||||
};
|
};
|
||||||
this._chart.data.datasets[4] = {
|
this._chart.data.datasets[4] = {
|
||||||
data: data.local.points.map((point, index) => {
|
data:
|
||||||
return {
|
data.global.atemp.count > 0
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
? data.local.points.map((point, index) => {
|
||||||
y: getConvertedTemperature(point.getTemperature()),
|
return {
|
||||||
index: index,
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
};
|
y: getConvertedTemperature(point.getTemperature()),
|
||||||
}),
|
index: index,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [],
|
||||||
normalized: true,
|
normalized: true,
|
||||||
yAxisID: 'yatemp',
|
yAxisID: 'yatemp',
|
||||||
};
|
};
|
||||||
this._chart.data.datasets[5] = {
|
this._chart.data.datasets[5] = {
|
||||||
data: data.local.points.map((point, index) => {
|
data:
|
||||||
return {
|
data.global.power.count > 0
|
||||||
x: getConvertedDistance(data.local.distance.total[index]),
|
? data.local.points.map((point, index) => {
|
||||||
y: point.getPower(),
|
return {
|
||||||
index: index,
|
x: getConvertedDistance(data.local.distance.total[index]),
|
||||||
};
|
y: point.getPower(),
|
||||||
}),
|
index: index,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [],
|
||||||
normalized: true,
|
normalized: true,
|
||||||
yAxisID: 'ypower',
|
yAxisID: 'ypower',
|
||||||
};
|
};
|
||||||
this._chart.options.scales.x['min'] = 0;
|
this._chart.options.scales!.x!['min'] = 0;
|
||||||
this._chart.options.scales.x['max'] = getConvertedDistance(data.global.distance.total);
|
this._chart.options.scales!.x!['max'] = getConvertedDistance(data.global.distance.total);
|
||||||
|
|
||||||
this.setVisibility();
|
this.setVisibility();
|
||||||
this.setFill();
|
this.setFill();
|
||||||
@@ -513,21 +547,24 @@ export class ElevationProfile {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const elevationFill = get(this._elevationFill);
|
const elevationFill = get(this._elevationFill);
|
||||||
|
const dataset = this._chart.data.datasets[0];
|
||||||
|
let segment: any = {};
|
||||||
if (elevationFill === 'slope') {
|
if (elevationFill === 'slope') {
|
||||||
this._chart.data.datasets[0]['segment'] = {
|
segment = {
|
||||||
backgroundColor: this.slopeFillCallback,
|
backgroundColor: this.slopeFillCallback,
|
||||||
};
|
};
|
||||||
} else if (elevationFill === 'surface') {
|
} else if (elevationFill === 'surface') {
|
||||||
this._chart.data.datasets[0]['segment'] = {
|
segment = {
|
||||||
backgroundColor: this.surfaceFillCallback,
|
backgroundColor: this.surfaceFillCallback,
|
||||||
};
|
};
|
||||||
} else if (elevationFill === 'highway') {
|
} else if (elevationFill === 'highway') {
|
||||||
this._chart.data.datasets[0]['segment'] = {
|
segment = {
|
||||||
backgroundColor: this.highwayFillCallback,
|
backgroundColor: this.highwayFillCallback,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
this._chart.data.datasets[0]['segment'] = {};
|
segment = {};
|
||||||
}
|
}
|
||||||
|
Object.assign(dataset, { segment });
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOverlay() {
|
updateOverlay() {
|
||||||
@@ -575,19 +612,22 @@ export class ElevationProfile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slopeFillCallback(context) {
|
slopeFillCallback(context: ScriptableLineSegmentContext & { p0: { raw: any } }) {
|
||||||
return getSlopeColor(context.p0.raw.slope.segment);
|
const point = context.p0.raw as ElevationProfilePoint;
|
||||||
|
return getSlopeColor(point.slope.segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
surfaceFillCallback(context) {
|
surfaceFillCallback(context: ScriptableLineSegmentContext & { p0: { raw: any } }) {
|
||||||
return getSurfaceColor(context.p0.raw.extensions.surface);
|
const point = context.p0.raw as ElevationProfilePoint;
|
||||||
|
return getSurfaceColor(point.extensions.surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
highwayFillCallback(context) {
|
highwayFillCallback(context: ScriptableLineSegmentContext & { p0: { raw: any } }) {
|
||||||
|
const point = context.p0.raw as ElevationProfilePoint;
|
||||||
return getHighwayColor(
|
return getHighwayColor(
|
||||||
context.p0.raw.extensions.highway,
|
point.extensions.highway,
|
||||||
context.p0.raw.extensions.sac_scale,
|
point.extensions.sac_scale,
|
||||||
context.p0.raw.extensions.mtb_scale
|
point.extensions.mtb_scale
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
class="p-1 has-[>svg]:px-2 h-8 justify-start {className}"
|
size="sm"
|
||||||
|
class="justify-start {className}"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
navigator.clipboard.writeText(
|
navigator.clipboard.writeText(
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { TrackPoint } from 'gpx';
|
import type { TrackPoint } from 'gpx';
|
||||||
|
import { Button } from '$lib/components/ui/button';
|
||||||
import CopyCoordinates from '$lib/components/map/gpx-layer/CopyCoordinates.svelte';
|
import CopyCoordinates from '$lib/components/map/gpx-layer/CopyCoordinates.svelte';
|
||||||
import * as Card from '$lib/components/ui/card';
|
import * as Card from '$lib/components/ui/card';
|
||||||
import WithUnits from '$lib/components/WithUnits.svelte';
|
import WithUnits from '$lib/components/WithUnits.svelte';
|
||||||
import { Compass, Mountain, Timer } from '@lucide/svelte';
|
import { Compass, Earth, Mountain, Timer } from '@lucide/svelte';
|
||||||
import { i18n } from '$lib/i18n.svelte';
|
import { i18n } from '$lib/i18n.svelte';
|
||||||
import type { PopupItem } from '$lib/components/map/map-popup';
|
import type { PopupItem } from '$lib/components/map/map-popup';
|
||||||
|
import { map } from '$lib/components/map/map';
|
||||||
|
|
||||||
let { trackpoint }: { trackpoint: PopupItem<TrackPoint> } = $props();
|
let { trackpoint }: { trackpoint: PopupItem<TrackPoint> } = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -35,5 +37,16 @@
|
|||||||
onCopy={() => trackpoint.hide?.()}
|
onCopy={() => trackpoint.hide?.()}
|
||||||
class="mt-0.5"
|
class="mt-0.5"
|
||||||
/>
|
/>
|
||||||
|
{#if trackpoint.fileId === undefined}
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
href={`https://www.openstreetmap.org/edit?#map=${(($map?.getZoom() ?? 17) + 1).toFixed(0)}/${trackpoint.item.getLatitude().toFixed(5)}/${trackpoint.item.getLongitude().toFixed(5)}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Earth size="14" />
|
||||||
|
{i18n._('menu.edit_osm')}
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { get, type Readable } from 'svelte/store';
|
import { get, type Readable } from 'svelte/store';
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl, { type FilterSpecification } from 'mapbox-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';
|
||||||
import {
|
import {
|
||||||
@@ -153,8 +153,6 @@ export class GPXLayer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadIcons();
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
file._data.style &&
|
file._data.style &&
|
||||||
file._data.style.color &&
|
file._data.style.color &&
|
||||||
@@ -164,6 +162,8 @@ export class GPXLayer {
|
|||||||
this.layerColor = `#${file._data.style.color}`;
|
this.layerColor = `#${file._data.style.color}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.loadIcons();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let source = _map.getSource(this.fileId) as mapboxgl.GeoJSONSource | undefined;
|
let source = _map.getSource(this.fileId) as mapboxgl.GeoJSONSource | undefined;
|
||||||
if (source) {
|
if (source) {
|
||||||
@@ -281,25 +281,23 @@ export class GPXLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let visibleSegments: [number, number][] = [];
|
let visibleTrackSegmentIds: string[] = [];
|
||||||
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
||||||
if (!segment._data.hidden) {
|
if (!segment._data.hidden) {
|
||||||
visibleSegments.push([trackIndex, segmentIndex]);
|
visibleTrackSegmentIds.push(`${trackIndex}-${segmentIndex}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const segmentFilter: FilterSpecification = [
|
||||||
|
'in',
|
||||||
|
['get', 'trackSegmentId'],
|
||||||
|
['literal', visibleTrackSegmentIds],
|
||||||
|
];
|
||||||
|
|
||||||
_map.setFilter(
|
_map.setFilter(this.fileId, segmentFilter, { validate: false });
|
||||||
this.fileId,
|
|
||||||
[
|
if (_map.getLayer(this.fileId + '-direction')) {
|
||||||
'any',
|
_map.setFilter(this.fileId + '-direction', segmentFilter, { validate: false });
|
||||||
...visibleSegments.map(([trackIndex, segmentIndex]) => [
|
}
|
||||||
'all',
|
|
||||||
['==', 'trackIndex', trackIndex],
|
|
||||||
['==', 'segmentIndex', segmentIndex],
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
{ validate: false }
|
|
||||||
);
|
|
||||||
|
|
||||||
let visibleWaypoints: number[] = [];
|
let visibleWaypoints: number[] = [];
|
||||||
file.wpt.forEach((waypoint, waypointIndex) => {
|
file.wpt.forEach((waypoint, waypointIndex) => {
|
||||||
@@ -313,21 +311,6 @@ export class GPXLayer {
|
|||||||
['in', ['get', 'waypointIndex'], ['literal', visibleWaypoints]],
|
['in', ['get', 'waypointIndex'], ['literal', visibleWaypoints]],
|
||||||
{ validate: false }
|
{ validate: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_map.getLayer(this.fileId + '-direction')) {
|
|
||||||
_map.setFilter(
|
|
||||||
this.fileId + '-direction',
|
|
||||||
[
|
|
||||||
'any',
|
|
||||||
...visibleSegments.map(([trackIndex, segmentIndex]) => [
|
|
||||||
'all',
|
|
||||||
['==', 'trackIndex', trackIndex],
|
|
||||||
['==', 'segmentIndex', segmentIndex],
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
{ validate: false }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// No reliable way to check if the map is ready to add sources and layers
|
// No reliable way to check if the map is ready to add sources and layers
|
||||||
return;
|
return;
|
||||||
@@ -686,6 +669,7 @@ export class GPXLayer {
|
|||||||
}
|
}
|
||||||
feature.properties.trackIndex = trackIndex;
|
feature.properties.trackIndex = trackIndex;
|
||||||
feature.properties.segmentIndex = segmentIndex;
|
feature.properties.segmentIndex = segmentIndex;
|
||||||
|
feature.properties.trackSegmentId = `${trackIndex}-${segmentIndex}`;
|
||||||
|
|
||||||
segmentIndex++;
|
segmentIndex++;
|
||||||
if (segmentIndex >= file.trk[trackIndex].trkseg.length) {
|
if (segmentIndex >= file.trk[trackIndex].trkseg.length) {
|
||||||
@@ -718,7 +702,7 @@ export class GPXLayer {
|
|||||||
properties: {
|
properties: {
|
||||||
fileId: this.fileId,
|
fileId: this.fileId,
|
||||||
waypointIndex: index,
|
waypointIndex: index,
|
||||||
icon: `${this.fileId}-waypoint-${getSymbolKey(waypoint.sym) ?? 'default'}`,
|
icon: `waypoint-${getSymbolKey(waypoint.sym) ?? 'default'}-${this.layerColor}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -739,7 +723,7 @@ export class GPXLayer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
symbols.forEach((symbol) => {
|
symbols.forEach((symbol) => {
|
||||||
const iconId = `${this.fileId}-waypoint-${symbol ?? 'default'}`;
|
const iconId = `waypoint-${symbol ?? 'default'}-${this.layerColor}`;
|
||||||
if (!_map.hasImage(iconId)) {
|
if (!_map.hasImage(iconId)) {
|
||||||
let icon = new Image(100, 100);
|
let icon = new Image(100, 100);
|
||||||
icon.onload = () => {
|
icon.onload = () => {
|
||||||
|
|||||||
@@ -285,10 +285,12 @@ function getQuery(query: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getQueryItem(tags: Record<string, string | boolean | string[]>) {
|
function getQueryItem(tags: Record<string, string | string[]>) {
|
||||||
let arrayEntry = Object.values(tags).find((value) => Array.isArray(value));
|
let arrayEntry = Object.entries(tags).find((entry): entry is [string, string[]] =>
|
||||||
|
Array.isArray(entry[1])
|
||||||
|
);
|
||||||
if (arrayEntry !== undefined) {
|
if (arrayEntry !== undefined) {
|
||||||
return arrayEntry
|
return arrayEntry[1]
|
||||||
.map(
|
.map(
|
||||||
(val) =>
|
(val) =>
|
||||||
`nwr${Object.entries(tags)
|
`nwr${Object.entries(tags)
|
||||||
@@ -311,7 +313,7 @@ function belongsToQuery(element: any, query: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function belongsToQueryItem(element: any, tags: Record<string, string | boolean | string[]>) {
|
function belongsToQueryItem(element: any, tags: Record<string, string | string[]>) {
|
||||||
return Object.entries(tags).every(([tag, value]) =>
|
return Object.entries(tags).every(([tag, value]) =>
|
||||||
Array.isArray(value) ? value.includes(element.tags[tag]) : element.tags[tag] === value
|
Array.isArray(value) ? value.includes(element.tags[tag]) : element.tags[tag] === value
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: View options
|
title: Opciones de vista
|
||||||
---
|
---
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ Beste era batez, fitxategiak zuzenean arrastatu eta jaregin ditzakezu zure fitxa
|
|||||||
|
|
||||||
Sortu hautatutako fitxategien kopia bat.
|
Sortu hautatutako fitxategien kopia bat.
|
||||||
|
|
||||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete
|
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Ezabatu
|
||||||
|
|
||||||
Delete the currently selected files.
|
Ezabatu hautatutako fitxategiak.
|
||||||
|
|
||||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Ezabatu guztiak
|
||||||
|
|
||||||
Delete all files.
|
Ezabatu fitxategi guztiak.
|
||||||
|
|
||||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Esportatu...
|
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Esportatu...
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ cÈ inoltre possibile trascinare i file direttamente dal file system del tuo Pc
|
|||||||
|
|
||||||
Crea una copia dei file attualmente selezionati.
|
Crea una copia dei file attualmente selezionati.
|
||||||
|
|
||||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete
|
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" />Elimina
|
||||||
|
|
||||||
Delete the currently selected files.
|
Elimina i file attualmente selezionati.
|
||||||
|
|
||||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" />Cancella tutto
|
||||||
|
|
||||||
Delete all files.
|
Elimina tutti i file.
|
||||||
|
|
||||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Esporta...
|
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Esporta...
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Deze handleiding zal je door alle componenten en gereedschappen van de interface
|
|||||||
<DocsImage src="getting-started/interface" alt="De gpx.studio interface." />
|
<DocsImage src="getting-started/interface" alt="De gpx.studio interface." />
|
||||||
|
|
||||||
Zoals weergegeven in bovenstaande scherm, is de interface verdeeld in vier hoofddelen rond de kaart.
|
Zoals weergegeven in bovenstaande scherm, is de interface verdeeld in vier hoofddelen rond de kaart.
|
||||||
Voordat we in de details van elke sectie duiken, hebben we een snel overzicht van de interface.
|
Voordat we in de details van elke sectie duiken, eerst een snel overzicht van de interface.
|
||||||
|
|
||||||
## Menu
|
## Menu
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ Deze actie is alleen beschikbaar wanneer de verticale indeling van de bestandsli
|
|||||||
|
|
||||||
### <ClipboardPaste size="16" class="inline-block" style="margin-bottom: 2px" /> Plakken
|
### <ClipboardPaste size="16" class="inline-block" style="margin-bottom: 2px" /> Plakken
|
||||||
|
|
||||||
Plak de bestandsitems van het klembord naar het huidige hiërarchie niveau indien compatibel.
|
Plak de bestandsitems van het klembord naar het huidige hiërarchieniveau indien compatibel.
|
||||||
|
|
||||||
<DocsNote>
|
<DocsNote>
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ title: 文件
|
|||||||
|
|
||||||
Delete the currently selected files.
|
Delete the currently selected files.
|
||||||
|
|
||||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> 删除全部
|
||||||
|
|
||||||
Delete all files.
|
删除全部文件。
|
||||||
|
|
||||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> 导出...
|
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> 导出...
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
import { i18n } from '$lib/i18n.svelte';
|
import { i18n } from '$lib/i18n.svelte';
|
||||||
import { freeze, type WritableDraft } from 'immer';
|
import { freeze, type WritableDraft } from 'immer';
|
||||||
import {
|
import {
|
||||||
distance,
|
|
||||||
GPXFile,
|
GPXFile,
|
||||||
parseGPX,
|
parseGPX,
|
||||||
Track,
|
Track,
|
||||||
@@ -30,7 +29,7 @@ import {
|
|||||||
} from 'gpx';
|
} from 'gpx';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import { settings } from '$lib/logic/settings';
|
import { settings } from '$lib/logic/settings';
|
||||||
import { getClosestLinePoint, getElevation } from '$lib/utils';
|
import { getClosestLinePoint, getClosestTrackSegments, getElevation } from '$lib/utils';
|
||||||
import { gpxStatistics } from '$lib/logic/statistics';
|
import { gpxStatistics } from '$lib/logic/statistics';
|
||||||
import { boundsManager } from './bounds';
|
import { boundsManager } from './bounds';
|
||||||
|
|
||||||
@@ -453,34 +452,13 @@ export const fileActions = {
|
|||||||
selection.applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
selection.applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
let file = fileStateCollection.getFile(fileId);
|
let file = fileStateCollection.getFile(fileId);
|
||||||
if (file) {
|
let statistics = fileStateCollection.getStatistics(fileId);
|
||||||
|
if (file && statistics) {
|
||||||
if (file.trk.length > 1) {
|
if (file.trk.length > 1) {
|
||||||
let fileIds = getFileIds(file.trk.length);
|
let fileIds = getFileIds(file.trk.length);
|
||||||
let closest = file.wpt.map((wpt, wptIndex) => {
|
let closest = file.wpt.map((wpt) =>
|
||||||
return {
|
getClosestTrackSegments(file, statistics, wpt.getCoordinates())
|
||||||
wptIndex: wptIndex,
|
);
|
||||||
index: [0],
|
|
||||||
distance: Number.MAX_VALUE,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
file.trk.forEach((track, index) => {
|
|
||||||
track.getSegments().forEach((segment) => {
|
|
||||||
segment.trkpt.forEach((point) => {
|
|
||||||
file.wpt.forEach((wpt, wptIndex) => {
|
|
||||||
let dist = distance(
|
|
||||||
point.getCoordinates(),
|
|
||||||
wpt.getCoordinates()
|
|
||||||
);
|
|
||||||
if (dist < closest[wptIndex].distance) {
|
|
||||||
closest[wptIndex].distance = dist;
|
|
||||||
closest[wptIndex].index = [index];
|
|
||||||
} else if (dist === closest[wptIndex].distance) {
|
|
||||||
closest[wptIndex].index.push(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
file.trk.forEach((track, index) => {
|
file.trk.forEach((track, index) => {
|
||||||
let newFile = file.clone();
|
let newFile = file.clone();
|
||||||
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
||||||
@@ -495,9 +473,11 @@ export const fileActions = {
|
|||||||
newFile.replaceWaypoints(
|
newFile.replaceWaypoints(
|
||||||
0,
|
0,
|
||||||
file.wpt.length - 1,
|
file.wpt.length - 1,
|
||||||
closest
|
file.wpt.filter((wpt, wptIndex) =>
|
||||||
.filter((c) => c.index.includes(index))
|
closest[wptIndex].some(
|
||||||
.map((c) => file.wpt[c.wptIndex])
|
([trackIndex, segmentIndex]) => trackIndex === index
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
newFile._data.id = fileIds[index];
|
newFile._data.id = fileIds[index];
|
||||||
newFile.metadata.name =
|
newFile.metadata.name =
|
||||||
@@ -506,29 +486,9 @@ export const fileActions = {
|
|||||||
});
|
});
|
||||||
} else if (file.trk.length === 1) {
|
} else if (file.trk.length === 1) {
|
||||||
let fileIds = getFileIds(file.trk[0].trkseg.length);
|
let fileIds = getFileIds(file.trk[0].trkseg.length);
|
||||||
let closest = file.wpt.map((wpt, wptIndex) => {
|
let closest = file.wpt.map((wpt) =>
|
||||||
return {
|
getClosestTrackSegments(file, statistics, wpt.getCoordinates())
|
||||||
wptIndex: wptIndex,
|
);
|
||||||
index: [0],
|
|
||||||
distance: Number.MAX_VALUE,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
file.trk[0].trkseg.forEach((segment, index) => {
|
|
||||||
segment.trkpt.forEach((point) => {
|
|
||||||
file.wpt.forEach((wpt, wptIndex) => {
|
|
||||||
let dist = distance(
|
|
||||||
point.getCoordinates(),
|
|
||||||
wpt.getCoordinates()
|
|
||||||
);
|
|
||||||
if (dist < closest[wptIndex].distance) {
|
|
||||||
closest[wptIndex].distance = dist;
|
|
||||||
closest[wptIndex].index = [index];
|
|
||||||
} else if (dist === closest[wptIndex].distance) {
|
|
||||||
closest[wptIndex].index.push(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
file.trk[0].trkseg.forEach((segment, index) => {
|
file.trk[0].trkseg.forEach((segment, index) => {
|
||||||
let newFile = file.clone();
|
let newFile = file.clone();
|
||||||
newFile.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [
|
newFile.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [
|
||||||
@@ -537,9 +497,11 @@ export const fileActions = {
|
|||||||
newFile.replaceWaypoints(
|
newFile.replaceWaypoints(
|
||||||
0,
|
0,
|
||||||
file.wpt.length - 1,
|
file.wpt.length - 1,
|
||||||
closest
|
file.wpt.filter((wpt, wptIndex) =>
|
||||||
.filter((c) => c.index.includes(index))
|
closest[wptIndex].some(
|
||||||
.map((c) => file.wpt[c.wptIndex])
|
([trackIndex, segmentIndex]) => segmentIndex === index
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
newFile._data.id = fileIds[index];
|
newFile._data.id = fileIds[index];
|
||||||
newFile.metadata.name = `${file.trk[0].name ?? file.metadata.name} (${index + 1})`;
|
newFile.metadata.name = `${file.trk[0].name ?? file.metadata.name} (${index + 1})`;
|
||||||
|
|||||||
@@ -22,25 +22,34 @@ export class GPXStatisticsTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getStatisticsFor(item: ListItem): GPXStatistics {
|
getStatisticsFor(item: ListItem): GPXStatistics {
|
||||||
let statistics = new GPXStatistics();
|
let statistics = [];
|
||||||
let id = item.getIdAtLevel(this.level);
|
let id = item.getIdAtLevel(this.level);
|
||||||
if (id === undefined || id === 'waypoints') {
|
if (id === undefined || id === 'waypoints') {
|
||||||
Object.keys(this.statistics).forEach((key) => {
|
Object.keys(this.statistics).forEach((key) => {
|
||||||
if (this.statistics[key] instanceof GPXStatistics) {
|
if (this.statistics[key] instanceof GPXStatistics) {
|
||||||
statistics.mergeWith(this.statistics[key]);
|
statistics.push(this.statistics[key]);
|
||||||
} else {
|
} else {
|
||||||
statistics.mergeWith(this.statistics[key].getStatisticsFor(item));
|
statistics.push(this.statistics[key].getStatisticsFor(item));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let child = this.statistics[id];
|
let child = this.statistics[id];
|
||||||
if (child instanceof GPXStatistics) {
|
if (child instanceof GPXStatistics) {
|
||||||
statistics.mergeWith(child);
|
statistics.push(child);
|
||||||
} else if (child !== undefined) {
|
} else if (child !== undefined) {
|
||||||
statistics.mergeWith(child.getStatisticsFor(item));
|
statistics.push(child.getStatisticsFor(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return statistics;
|
if (statistics.length === 0) {
|
||||||
|
return new GPXStatistics();
|
||||||
|
} else if (statistics.length === 1) {
|
||||||
|
return statistics[0];
|
||||||
|
} else {
|
||||||
|
return statistics.reduce((acc, curr) => {
|
||||||
|
acc.mergeWith(curr);
|
||||||
|
return acc;
|
||||||
|
}, new GPXStatistics());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export type GPXFileWithStatistics = { file: GPXFile; statistics: GPXStatisticsTree };
|
export type GPXFileWithStatistics = { file: GPXFile; statistics: GPXStatisticsTree };
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ import { type ClassValue, clsx } from 'clsx';
|
|||||||
import { twMerge } from 'tailwind-merge';
|
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 } from 'gpx';
|
import { TrackPoint, Waypoint, type Coordinates, crossarcDistance, distance, GPXFile } from 'gpx';
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl from 'mapbox-gl';
|
||||||
import { pointToTile, pointToTileFraction } from '@mapbox/tilebelt';
|
import { pointToTile, pointToTileFraction } from '@mapbox/tilebelt';
|
||||||
import { PUBLIC_MAPBOX_TOKEN } from '$env/static/public';
|
import { PUBLIC_MAPBOX_TOKEN } from '$env/static/public';
|
||||||
import PNGReader from 'png.js';
|
import PNGReader from 'png.js';
|
||||||
|
import type { GPXStatisticsTree } from '$lib/logic/statistics-tree';
|
||||||
|
import { ListTrackSegmentItem } from '$lib/components/file-list/file-list';
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
@@ -47,6 +49,59 @@ export function getClosestLinePoint(
|
|||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getClosestTrackSegments(
|
||||||
|
file: GPXFile,
|
||||||
|
statistics: GPXStatisticsTree,
|
||||||
|
point: Coordinates
|
||||||
|
): [number, number][] {
|
||||||
|
let segmentBoundsDistances: [number, number, number][] = [];
|
||||||
|
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
||||||
|
let segmentStatistics = statistics.getStatisticsFor(
|
||||||
|
new ListTrackSegmentItem(file._data.id, trackIndex, segmentIndex)
|
||||||
|
);
|
||||||
|
let segmentBounds = segmentStatistics.global.bounds;
|
||||||
|
let northEast = segmentBounds.northEast;
|
||||||
|
let southWest = segmentBounds.southWest;
|
||||||
|
let bounds = new mapboxgl.LngLatBounds(southWest, northEast);
|
||||||
|
if (bounds.contains(point)) {
|
||||||
|
segmentBoundsDistances.push([0, trackIndex, segmentIndex]);
|
||||||
|
} else {
|
||||||
|
let northWest: Coordinates = { lat: northEast.lat, lon: southWest.lon };
|
||||||
|
let southEast: Coordinates = { lat: southWest.lat, lon: northEast.lon };
|
||||||
|
let distanceToBounds = Math.min(
|
||||||
|
crossarcDistance(northWest, northEast, point),
|
||||||
|
crossarcDistance(northEast, southEast, point),
|
||||||
|
crossarcDistance(southEast, southWest, point),
|
||||||
|
crossarcDistance(southWest, northWest, point)
|
||||||
|
);
|
||||||
|
segmentBoundsDistances.push([distanceToBounds, trackIndex, segmentIndex]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
segmentBoundsDistances.sort((a, b) => a[0] - b[0]);
|
||||||
|
|
||||||
|
let closest: { distance: number; indices: [number, number][] } = {
|
||||||
|
distance: Number.MAX_VALUE,
|
||||||
|
indices: [],
|
||||||
|
};
|
||||||
|
for (let s = 0; s < segmentBoundsDistances.length; s++) {
|
||||||
|
if (segmentBoundsDistances[s][0] > closest.distance) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const segment = file.getSegment(segmentBoundsDistances[s][1], segmentBoundsDistances[s][2]);
|
||||||
|
segment.trkpt.forEach((pt) => {
|
||||||
|
let dist = distance(pt.getCoordinates(), point);
|
||||||
|
if (dist < closest.distance) {
|
||||||
|
closest.distance = dist;
|
||||||
|
closest.indices = [[segmentBoundsDistances[s][1], segmentBoundsDistances[s][2]]];
|
||||||
|
} else if (dist === closest.distance) {
|
||||||
|
closest.indices.push([segmentBoundsDistances[s][1], segmentBoundsDistances[s][2]]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return closest.indices;
|
||||||
|
}
|
||||||
|
|
||||||
export function getElevation(
|
export function getElevation(
|
||||||
points: (TrackPoint | Waypoint | Coordinates)[],
|
points: (TrackPoint | Waypoint | Coordinates)[],
|
||||||
ELEVATION_ZOOM: number = 13,
|
ELEVATION_ZOOM: number = 13,
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Паказаць",
|
"unhide": "Паказаць",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Адчыніць у",
|
"open_in": "Адчыніць у",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Veure",
|
"unhide": "Veure",
|
||||||
"center": "Centrar",
|
"center": "Centrar",
|
||||||
"open_in": "Obrir amb",
|
"open_in": "Obrir amb",
|
||||||
"copy_coordinates": "Copiar coordenades"
|
"copy_coordinates": "Copiar coordenades",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Aigua",
|
"water": "Aigua",
|
||||||
"shower": "Dutxa",
|
"shower": "Dutxa",
|
||||||
"shelter": "Refugi",
|
"shelter": "Refugi",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cotxes i motos",
|
"motorized": "Cotxes i motos",
|
||||||
"fuel-station": "Gasolinera",
|
"fuel-station": "Gasolinera",
|
||||||
"parking": "Aparcament",
|
"parking": "Aparcament",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Zobrazit skryté",
|
"unhide": "Zobrazit skryté",
|
||||||
"center": "Vycentrovat",
|
"center": "Vycentrovat",
|
||||||
"open_in": "Otevřít v",
|
"open_in": "Otevřít v",
|
||||||
"copy_coordinates": "Zkopírovat souřadnice"
|
"copy_coordinates": "Zkopírovat souřadnice",
|
||||||
|
"edit_osm": "Upravit v OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Voda",
|
"water": "Voda",
|
||||||
"shower": "Sprcha",
|
"shower": "Sprcha",
|
||||||
"shelter": "Přístřeší",
|
"shelter": "Přístřeší",
|
||||||
|
"cemetery": "Hřbitov",
|
||||||
"motorized": "Automobily a motocykly",
|
"motorized": "Automobily a motocykly",
|
||||||
"fuel-station": "Čerpací stanice",
|
"fuel-station": "Čerpací stanice",
|
||||||
"parking": "Parkoviště",
|
"parking": "Parkoviště",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Unhide",
|
"unhide": "Unhide",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Kopier koordinater"
|
"copy_coordinates": "Kopier koordinater",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Einblenden",
|
"unhide": "Einblenden",
|
||||||
"center": "Zentrieren",
|
"center": "Zentrieren",
|
||||||
"open_in": "Öffnen in",
|
"open_in": "Öffnen in",
|
||||||
"copy_coordinates": "Koordinaten kopieren"
|
"copy_coordinates": "Koordinaten kopieren",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Trinkwasser",
|
"water": "Trinkwasser",
|
||||||
"shower": "Dusche",
|
"shower": "Dusche",
|
||||||
"shelter": "Unterstand",
|
"shelter": "Unterstand",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Autos und Motorräder",
|
"motorized": "Autos und Motorräder",
|
||||||
"fuel-station": "Tankstelle",
|
"fuel-station": "Tankstelle",
|
||||||
"parking": "Parken",
|
"parking": "Parken",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Unhide",
|
"unhide": "Unhide",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Unhide",
|
"unhide": "Unhide",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
"switch_basemap": "Cambiar al mapa base anterior",
|
"switch_basemap": "Cambiar al mapa base anterior",
|
||||||
"toggle_overlays": "Alternar capas",
|
"toggle_overlays": "Alternar capas",
|
||||||
"toggle_3d": "Alternar 3D",
|
"toggle_3d": "Alternar 3D",
|
||||||
"settings": "Configuraciones",
|
"settings": "Configuración",
|
||||||
"distance_units": "Unidades de distancia",
|
"distance_units": "Unidades de distancia",
|
||||||
"metric": "Métrico",
|
"metric": "Métrico",
|
||||||
"imperial": "Imperial",
|
"imperial": "Imperial",
|
||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Mostrar",
|
"unhide": "Mostrar",
|
||||||
"center": "Centrar",
|
"center": "Centrar",
|
||||||
"open_in": "Abrir en",
|
"open_in": "Abrir en",
|
||||||
"copy_coordinates": "Copiar coordenadas"
|
"copy_coordinates": "Copiar coordenadas",
|
||||||
|
"edit_osm": "Editar en OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Agua",
|
"water": "Agua",
|
||||||
"shower": "Ducha",
|
"shower": "Ducha",
|
||||||
"shelter": "Refugio",
|
"shelter": "Refugio",
|
||||||
|
"cemetery": "Cementerio",
|
||||||
"motorized": "Coches y motos",
|
"motorized": "Coches y motos",
|
||||||
"fuel-station": "Gasolinera",
|
"fuel-station": "Gasolinera",
|
||||||
"parking": "Aparcamiento",
|
"parking": "Aparcamiento",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"undo": "Desegin",
|
"undo": "Desegin",
|
||||||
"redo": "Berregin",
|
"redo": "Berregin",
|
||||||
"delete": "Ezabatu",
|
"delete": "Ezabatu",
|
||||||
"delete_all": "Delete all",
|
"delete_all": "Ezabatu guztiak",
|
||||||
"select_all": "Hautatu dena",
|
"select_all": "Hautatu dena",
|
||||||
"view": "Ikusi",
|
"view": "Ikusi",
|
||||||
"elevation_profile": "Altuera profila",
|
"elevation_profile": "Altuera profila",
|
||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Erakutsi",
|
"unhide": "Erakutsi",
|
||||||
"center": "Erdiratu",
|
"center": "Erdiratu",
|
||||||
"open_in": "Ireki hemen",
|
"open_in": "Ireki hemen",
|
||||||
"copy_coordinates": "Kopiatu koordenatuak"
|
"copy_coordinates": "Kopiatu koordenatuak",
|
||||||
|
"edit_osm": "Editatu OpenStreeMapen"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Ura",
|
"water": "Ura",
|
||||||
"shower": "Dutxa",
|
"shower": "Dutxa",
|
||||||
"shelter": "Babeslekua",
|
"shelter": "Babeslekua",
|
||||||
|
"cemetery": "Hilerria",
|
||||||
"motorized": "Kotxeak eta motorrak",
|
"motorized": "Kotxeak eta motorrak",
|
||||||
"fuel-station": "Gasolindegia",
|
"fuel-station": "Gasolindegia",
|
||||||
"parking": "Aparkalekua",
|
"parking": "Aparkalekua",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Näytä",
|
"unhide": "Näytä",
|
||||||
"center": "Keskitä",
|
"center": "Keskitä",
|
||||||
"open_in": "Avaa",
|
"open_in": "Avaa",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Afficher",
|
"unhide": "Afficher",
|
||||||
"center": "Centrer",
|
"center": "Centrer",
|
||||||
"open_in": "Ouvrir avec",
|
"open_in": "Ouvrir avec",
|
||||||
"copy_coordinates": "Copier les coordonnées"
|
"copy_coordinates": "Copier les coordonnées",
|
||||||
|
"edit_osm": "Éditer dans OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Cours d'eau",
|
"water": "Cours d'eau",
|
||||||
"shower": "Douche",
|
"shower": "Douche",
|
||||||
"shelter": "Abri",
|
"shelter": "Abri",
|
||||||
|
"cemetery": "Cimetière",
|
||||||
"motorized": "Voitures et motos",
|
"motorized": "Voitures et motos",
|
||||||
"fuel-station": "Station-service",
|
"fuel-station": "Station-service",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Unhide",
|
"unhide": "Unhide",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "גשם",
|
"shower": "גשם",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Felfedés ",
|
"unhide": "Felfedés ",
|
||||||
"center": "Középre ",
|
"center": "Középre ",
|
||||||
"open_in": "Megnyitás itt ",
|
"open_in": "Megnyitás itt ",
|
||||||
"copy_coordinates": "Koordináták másolása"
|
"copy_coordinates": "Koordináták másolása",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Víz",
|
"water": "Víz",
|
||||||
"shower": "Zuhanyozó",
|
"shower": "Zuhanyozó",
|
||||||
"shelter": "Menedék",
|
"shelter": "Menedék",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Autók és Motorok",
|
"motorized": "Autók és Motorok",
|
||||||
"fuel-station": "Benzinkút",
|
"fuel-station": "Benzinkút",
|
||||||
"parking": "Parkoló",
|
"parking": "Parkoló",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Tampilkan",
|
"unhide": "Tampilkan",
|
||||||
"center": "Tengah",
|
"center": "Tengah",
|
||||||
"open_in": "Buka di",
|
"open_in": "Buka di",
|
||||||
"copy_coordinates": "Salin koordinat"
|
"copy_coordinates": "Salin koordinat",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Air",
|
"water": "Air",
|
||||||
"shower": "Mandi",
|
"shower": "Mandi",
|
||||||
"shelter": "Penampungan",
|
"shelter": "Penampungan",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Mobil dan Motor",
|
"motorized": "Mobil dan Motor",
|
||||||
"fuel-station": "Stasiun bahan bakar",
|
"fuel-station": "Stasiun bahan bakar",
|
||||||
"parking": "Parkir",
|
"parking": "Parkir",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"undo": "Annulla",
|
"undo": "Annulla",
|
||||||
"redo": "Ripeti",
|
"redo": "Ripeti",
|
||||||
"delete": "Elimina",
|
"delete": "Elimina",
|
||||||
"delete_all": "Delete all",
|
"delete_all": "Cancella tutto",
|
||||||
"select_all": "Seleziona tutto",
|
"select_all": "Seleziona tutto",
|
||||||
"view": "Visualizza",
|
"view": "Visualizza",
|
||||||
"elevation_profile": "Profilo altimetrico",
|
"elevation_profile": "Profilo altimetrico",
|
||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Mostra",
|
"unhide": "Mostra",
|
||||||
"center": "Centra",
|
"center": "Centra",
|
||||||
"open_in": "Apri con",
|
"open_in": "Apri con",
|
||||||
"copy_coordinates": "Copia le coordinate"
|
"copy_coordinates": "Copia le coordinate",
|
||||||
|
"edit_osm": "Modifica in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Acqua",
|
"water": "Acqua",
|
||||||
"shower": "Doccia",
|
"shower": "Doccia",
|
||||||
"shelter": "Riparo",
|
"shelter": "Riparo",
|
||||||
|
"cemetery": "Cimitero",
|
||||||
"motorized": "Auto e Motocicli",
|
"motorized": "Auto e Motocicli",
|
||||||
"fuel-station": "Stazione di Rifornimento",
|
"fuel-station": "Stazione di Rifornimento",
|
||||||
"parking": "Parcheggio",
|
"parking": "Parcheggio",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "표시",
|
"unhide": "표시",
|
||||||
"center": "중앙",
|
"center": "중앙",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "대피소",
|
"shelter": "대피소",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "주유소",
|
"fuel-station": "주유소",
|
||||||
"parking": "주차장",
|
"parking": "주차장",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Rodyti",
|
"unhide": "Rodyti",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Atverti naudojant",
|
"open_in": "Atverti naudojant",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Vanduo",
|
"water": "Vanduo",
|
||||||
"shower": "Dušas",
|
"shower": "Dušas",
|
||||||
"shelter": "Prieglauda",
|
"shelter": "Prieglauda",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Automobiliai ir motociklai",
|
"motorized": "Automobiliai ir motociklai",
|
||||||
"fuel-station": "Degalinė",
|
"fuel-station": "Degalinė",
|
||||||
"parking": "Automobilių stovėjimo aikštelė",
|
"parking": "Automobilių stovėjimo aikštelė",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Unhide",
|
"unhide": "Unhide",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Maak zichtbaar",
|
"unhide": "Maak zichtbaar",
|
||||||
"center": "Midden",
|
"center": "Midden",
|
||||||
"open_in": "Openen in",
|
"open_in": "Openen in",
|
||||||
"copy_coordinates": "Coördinaten kopiëren"
|
"copy_coordinates": "Coördinaten kopiëren",
|
||||||
|
"edit_osm": "Bewerken in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Douche",
|
"shower": "Douche",
|
||||||
"shelter": "Schuilplaats",
|
"shelter": "Schuilplaats",
|
||||||
|
"cemetery": "Begraafplaats",
|
||||||
"motorized": "Auto's en Motorfietsen",
|
"motorized": "Auto's en Motorfietsen",
|
||||||
"fuel-station": "Tankstation",
|
"fuel-station": "Tankstation",
|
||||||
"parking": "Parkeren",
|
"parking": "Parkeren",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Vis",
|
"unhide": "Vis",
|
||||||
"center": "Sentrer",
|
"center": "Sentrer",
|
||||||
"open_in": "Åpne I",
|
"open_in": "Åpne I",
|
||||||
"copy_coordinates": "Kopier koordinater"
|
"copy_coordinates": "Kopier koordinater",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Vann",
|
"water": "Vann",
|
||||||
"shower": "Dusj",
|
"shower": "Dusj",
|
||||||
"shelter": "Ly",
|
"shelter": "Ly",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Biler og motorsykler",
|
"motorized": "Biler og motorsykler",
|
||||||
"fuel-station": "Bensinstasjon",
|
"fuel-station": "Bensinstasjon",
|
||||||
"parking": "Parkering",
|
"parking": "Parkering",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Pokaż",
|
"unhide": "Pokaż",
|
||||||
"center": "Wyśrodkuj",
|
"center": "Wyśrodkuj",
|
||||||
"open_in": "Otwórz w",
|
"open_in": "Otwórz w",
|
||||||
"copy_coordinates": "Kopiuj współrzędne"
|
"copy_coordinates": "Kopiuj współrzędne",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Woda",
|
"water": "Woda",
|
||||||
"shower": "Prysznic",
|
"shower": "Prysznic",
|
||||||
"shelter": "Schronienie",
|
"shelter": "Schronienie",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Samochody i motocykle",
|
"motorized": "Samochody i motocykle",
|
||||||
"fuel-station": "Stacja paliw",
|
"fuel-station": "Stacja paliw",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Mostrar",
|
"unhide": "Mostrar",
|
||||||
"center": "Centralizar",
|
"center": "Centralizar",
|
||||||
"open_in": "Abrir em",
|
"open_in": "Abrir em",
|
||||||
"copy_coordinates": "Copiar coordenadas"
|
"copy_coordinates": "Copiar coordenadas",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Água",
|
"water": "Água",
|
||||||
"shower": "Chuveiro",
|
"shower": "Chuveiro",
|
||||||
"shelter": "Abrigo",
|
"shelter": "Abrigo",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Carros e Motocicletas",
|
"motorized": "Carros e Motocicletas",
|
||||||
"fuel-station": "Postos de combustível",
|
"fuel-station": "Postos de combustível",
|
||||||
"parking": "Estacionamento",
|
"parking": "Estacionamento",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Mostrar",
|
"unhide": "Mostrar",
|
||||||
"center": "Centro",
|
"center": "Centro",
|
||||||
"open_in": "Abrir em",
|
"open_in": "Abrir em",
|
||||||
"copy_coordinates": "Copiar coordenadas"
|
"copy_coordinates": "Copiar coordenadas",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Água",
|
"water": "Água",
|
||||||
"shower": "Chuveiro",
|
"shower": "Chuveiro",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Carros e Motocicletas",
|
"motorized": "Carros e Motocicletas",
|
||||||
"fuel-station": "Postos de combustível",
|
"fuel-station": "Postos de combustível",
|
||||||
"parking": "Estacionamento",
|
"parking": "Estacionamento",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Dezvăluie",
|
"unhide": "Dezvăluie",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Copiază coordonatele"
|
"copy_coordinates": "Copiază coordonatele",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Отобразить",
|
"unhide": "Отобразить",
|
||||||
"center": "По центру",
|
"center": "По центру",
|
||||||
"open_in": "Открыть в",
|
"open_in": "Открыть в",
|
||||||
"copy_coordinates": "Скопировать координаты"
|
"copy_coordinates": "Скопировать координаты",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Вода",
|
"water": "Вода",
|
||||||
"shower": "Душ",
|
"shower": "Душ",
|
||||||
"shelter": "Укрытие",
|
"shelter": "Укрытие",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Автомобили и мотоциклы",
|
"motorized": "Автомобили и мотоциклы",
|
||||||
"fuel-station": "Заправочная станция",
|
"fuel-station": "Заправочная станция",
|
||||||
"parking": "Парковка",
|
"parking": "Парковка",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Prikaži",
|
"unhide": "Prikaži",
|
||||||
"center": "Centar",
|
"center": "Centar",
|
||||||
"open_in": "Otvorite u",
|
"open_in": "Otvorite u",
|
||||||
"copy_coordinates": "Kopiraj koordinate"
|
"copy_coordinates": "Kopiraj koordinate",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Voda",
|
"water": "Voda",
|
||||||
"shower": "Tuš",
|
"shower": "Tuš",
|
||||||
"shelter": "Sklonište",
|
"shelter": "Sklonište",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Automobili i motocikli",
|
"motorized": "Automobili i motocikli",
|
||||||
"fuel-station": "Benzinska stanica",
|
"fuel-station": "Benzinska stanica",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Visa",
|
"unhide": "Visa",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Öppna i",
|
"open_in": "Öppna i",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Dusch",
|
"shower": "Dusch",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Bensinstation",
|
"fuel-station": "Bensinstation",
|
||||||
"parking": "Parkering",
|
"parking": "Parkering",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Unhide",
|
"unhide": "Unhide",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Göster",
|
"unhide": "Göster",
|
||||||
"center": "Merkez",
|
"center": "Merkez",
|
||||||
"open_in": "Uygulamada Aç",
|
"open_in": "Uygulamada Aç",
|
||||||
"copy_coordinates": "Koordinatları kopyala"
|
"copy_coordinates": "Koordinatları kopyala",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Su",
|
"water": "Su",
|
||||||
"shower": "Duş",
|
"shower": "Duş",
|
||||||
"shelter": "Barınak",
|
"shelter": "Barınak",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Araba ve Motosiklet",
|
"motorized": "Araba ve Motosiklet",
|
||||||
"fuel-station": "Benzin istasyonu",
|
"fuel-station": "Benzin istasyonu",
|
||||||
"parking": "Otopark",
|
"parking": "Otopark",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Показати",
|
"unhide": "Показати",
|
||||||
"center": "Центр",
|
"center": "Центр",
|
||||||
"open_in": "Відкрити в",
|
"open_in": "Відкрити в",
|
||||||
"copy_coordinates": "Копіювати координати"
|
"copy_coordinates": "Копіювати координати",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Вода",
|
"water": "Вода",
|
||||||
"shower": "Душ",
|
"shower": "Душ",
|
||||||
"shelter": "Укриття",
|
"shelter": "Укриття",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Автомобілі та Мотоцикли",
|
"motorized": "Автомобілі та Мотоцикли",
|
||||||
"fuel-station": "Паливна станція",
|
"fuel-station": "Паливна станція",
|
||||||
"parking": "Парковка",
|
"parking": "Парковка",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Unhide",
|
"unhide": "Unhide",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Nước",
|
"water": "Nước",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "Unhide",
|
"unhide": "Unhide",
|
||||||
"center": "Center",
|
"center": "Center",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"copy_coordinates": "Copy coordinates"
|
"copy_coordinates": "Copy coordinates",
|
||||||
|
"edit_osm": "Edit in OpenStreetMap"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "Water",
|
"water": "Water",
|
||||||
"shower": "Shower",
|
"shower": "Shower",
|
||||||
"shelter": "Shelter",
|
"shelter": "Shelter",
|
||||||
|
"cemetery": "Cemetery",
|
||||||
"motorized": "Cars and Motorcycles",
|
"motorized": "Cars and Motorcycles",
|
||||||
"fuel-station": "Fuel Station",
|
"fuel-station": "Fuel Station",
|
||||||
"parking": "Parking",
|
"parking": "Parking",
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"undo": "撤销",
|
"undo": "撤销",
|
||||||
"redo": "恢复",
|
"redo": "恢复",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
"delete_all": "",
|
"delete_all": "全部删除",
|
||||||
"select_all": "全选",
|
"select_all": "全选",
|
||||||
"view": "显示",
|
"view": "显示",
|
||||||
"elevation_profile": "海拔剖面图",
|
"elevation_profile": "海拔剖面图",
|
||||||
@@ -79,7 +79,8 @@
|
|||||||
"unhide": "显示",
|
"unhide": "显示",
|
||||||
"center": "居中",
|
"center": "居中",
|
||||||
"open_in": "打开于",
|
"open_in": "打开于",
|
||||||
"copy_coordinates": "复制坐标"
|
"copy_coordinates": "复制坐标",
|
||||||
|
"edit_osm": "在 OpenStreetMap 中编辑"
|
||||||
},
|
},
|
||||||
"toolbar": {
|
"toolbar": {
|
||||||
"routing": {
|
"routing": {
|
||||||
@@ -352,6 +353,7 @@
|
|||||||
"water": "饮用水",
|
"water": "饮用水",
|
||||||
"shower": "淋浴",
|
"shower": "淋浴",
|
||||||
"shelter": "庇护所",
|
"shelter": "庇护所",
|
||||||
|
"cemetery": "墓地",
|
||||||
"motorized": "汽车和摩托车",
|
"motorized": "汽车和摩托车",
|
||||||
"fuel-station": "加油站",
|
"fuel-station": "加油站",
|
||||||
"parking": "停车场",
|
"parking": "停车场",
|
||||||
|
|||||||
Reference in New Issue
Block a user