1 Commits

Author SHA1 Message Date
vcoppe
d18f77bd57 drop file tabs to desktop 2024-09-30 18:17:20 +02:00
110 changed files with 2236 additions and 3724 deletions

View File

@@ -133,21 +133,22 @@ export class GPXFile extends GPXTreeNode<Track> {
} }
if (gpx.hasOwnProperty('_data')) { if (gpx.hasOwnProperty('_data')) {
this._data = gpx._data; this._data = gpx._data;
}
if (!this._data.hasOwnProperty('style')) { if (!this._data.hasOwnProperty('style')) {
let style = this.getStyle(); let style = this.getStyle();
let fileStyle = {}; let fileStyle = {};
if (style.color.length === 1) { if (style.color.length === 1) {
fileStyle['color'] = style.color[0]; fileStyle['color'] = style.color[0];
} }
if (style.weight.length === 1) { if (style.weight.length === 1) {
fileStyle['weight'] = style.weight[0]; fileStyle['weight'] = style.weight[0];
} }
if (style.opacity.length === 1) { if (style.opacity.length === 1) {
fileStyle['opacity'] = style.opacity[0]; fileStyle['opacity'] = style.opacity[0];
} }
if (Object.keys(fileStyle).length > 0) { if (Object.keys(fileStyle).length > 0) {
this.setStyle(fileStyle); this.setStyle(fileStyle);
}
} }
} }
} else { } else {
@@ -774,16 +775,12 @@ export class TrackSegment extends GPXTreeLeaf {
} }
} }
if (i > 0 && points[i - 1].extensions && points[i - 1].extensions["gpxtpx:TrackPointExtension"] && points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]) { if (i > 0 && points[i - 1].extensions && points[i - 1].extensions["gpxtpx:TrackPointExtension"] && points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] && points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface) {
Object.entries(points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]).forEach(([key, value]) => { let surface = points[i - 1].extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface;
if (statistics.global.extensions[key] === undefined) { if (statistics.global.surface[surface] === undefined) {
statistics.global.extensions[key] = {}; statistics.global.surface[surface] = 0;
} }
if (statistics.global.extensions[key][value] === undefined) { statistics.global.surface[surface] += dist;
statistics.global.extensions[key][value] = 0;
}
statistics.global.extensions[key][value] += dist;
});
} }
} }
@@ -893,7 +890,7 @@ export class TrackSegment extends GPXTreeLeaf {
} }
// Producers // Producers
replaceTrackPoints(start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date, removeGaps?: boolean) { replaceTrackPoints(start: number, end: number, points: TrackPoint[], speed?: number, startTime?: Date) {
let og = getOriginal(this); // Read as much as possible from the original object because it is faster let og = getOriginal(this); // Read as much as possible from the original object because it is faster
let trkpt = og.trkpt.slice(); let trkpt = og.trkpt.slice();
@@ -912,21 +909,6 @@ export class TrackSegment extends GPXTreeLeaf {
} else if (last !== undefined && points[0].time < last.time) { } else if (last !== undefined && points[0].time < last.time) {
// Adapt timestamps of the new points because they are too early // Adapt timestamps of the new points because they are too early
points = withShiftedAndCompressedTimestamps(points, speed, 1, last); points = withShiftedAndCompressedTimestamps(points, speed, 1, last);
} else if (last !== undefined && removeGaps) {
// Remove gaps between the new points and the previous point
if (last.getLatitude() === points[0].getLatitude() && last.getLongitude() === points[0].getLongitude()) {
// Same point, make the new points start at its timestamp and remove the first point
if (points[0].time > last.time) {
points = withShiftedAndCompressedTimestamps(points, speed, 1, last).slice(1);
}
} else {
// Different points, make the new points start one second after the previous point
if (points[0].time.getTime() - last.time.getTime() > 1000) {
let artificialLast = points[0].clone();
artificialLast.time = new Date(last.time.getTime() + 1000);
points = withShiftedAndCompressedTimestamps(points, speed, 1, artificialLast);
}
}
} }
} }
if (end < trkpt.length - 1) { if (end < trkpt.length - 1) {
@@ -1078,10 +1060,11 @@ export class TrackPoint {
return this.extensions && this.extensions["gpxpx:PowerExtension"] && this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] ? this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] : undefined; return this.extensions && this.extensions["gpxpx:PowerExtension"] && this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] ? this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] : undefined;
} }
setExtensions(extensions: Record<string, string>) { getSurface(): string {
if (Object.keys(extensions).length === 0) { return this.extensions && this.extensions["gpxtpx:TrackPointExtension"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface ? this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface : undefined;
return; }
}
setSurface(surface: string): void {
if (!this.extensions) { if (!this.extensions) {
this.extensions = {}; this.extensions = {};
} }
@@ -1091,13 +1074,7 @@ export class TrackPoint {
if (!this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]) { if (!this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]) {
this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] = {}; this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] = {};
} }
Object.entries(extensions).forEach(([key, value]) => { this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]["surface"] = surface;
this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"][key] = value;
});
}
getExtensions(): Record<string, string> {
return this.extensions && this.extensions["gpxtpx:TrackPointExtension"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] ? this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] : {};
} }
toTrackPointType(exclude: string[] = []): TrackPointType { toTrackPointType(exclude: string[] = []): TrackPointType {
@@ -1127,11 +1104,8 @@ export class TrackPoint {
if (this.extensions["gpxpx:PowerExtension"] && this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] && !exclude.includes('power')) { if (this.extensions["gpxpx:PowerExtension"] && this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] && !exclude.includes('power')) {
trkpt.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] = this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"]; trkpt.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] = this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"];
} }
if (this.extensions["gpxtpx:TrackPointExtension"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] && !exclude.includes('extensions')) { if (this.extensions["gpxtpx:TrackPointExtension"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] && this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface && !exclude.includes('surface')) {
trkpt.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] = {}; trkpt.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"] = { surface: this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"].surface };
Object.entries(this.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"]).forEach(([key, value]) => {
trkpt.extensions["gpxtpx:TrackPointExtension"]["gpxtpx:Extensions"][key] = value;
});
} }
} }
return trkpt; return trkpt;
@@ -1281,7 +1255,7 @@ export class GPXStatistics {
avg: number, avg: number,
count: number, count: number,
}, },
extensions: Record<string, Record<string, number>>, surface: Record<string, number>,
}; };
local: { local: {
points: TrackPoint[], points: TrackPoint[],
@@ -1352,7 +1326,7 @@ export class GPXStatistics {
avg: 0, avg: 0,
count: 0, count: 0,
}, },
extensions: {}, surface: {},
}; };
this.local = { this.local = {
points: [], points: [],
@@ -1422,16 +1396,11 @@ export class GPXStatistics {
this.global.cad.count += other.global.cad.count; this.global.cad.count += other.global.cad.count;
this.global.power.avg = (this.global.power.count * this.global.power.avg + other.global.power.count * other.global.power.avg) / Math.max(1, this.global.power.count + other.global.power.count); this.global.power.avg = (this.global.power.count * this.global.power.avg + other.global.power.count * other.global.power.avg) / Math.max(1, this.global.power.count + other.global.power.count);
this.global.power.count += other.global.power.count; this.global.power.count += other.global.power.count;
Object.keys(other.global.extensions).forEach((extension) => { Object.keys(other.global.surface).forEach((surface) => {
if (this.global.extensions[extension] === undefined) { if (this.global.surface[surface] === undefined) {
this.global.extensions[extension] = {}; this.global.surface[surface] = 0;
} }
Object.keys(other.global.extensions[extension]).forEach((value) => { this.global.surface[surface] += other.global.surface[surface];
if (this.global.extensions[extension][value] === undefined) {
this.global.extensions[extension][value] = 0;
}
this.global.extensions[extension][value] += other.global.extensions[extension][value];
});
}); });
} }

View File

@@ -61,7 +61,7 @@ export function parseGPX(gpxData: string): GPXFile {
return new GPXFile(parsed); return new GPXFile(parsed);
} }
export function buildGPX(file: GPXFile, exclude: string[]): string { export function buildGPX(file: GPXFile, exclude: string[] = []): string {
const gpx = file.toGPXFileType(exclude); const gpx = file.toGPXFileType(exclude);
const builder = new XMLBuilder({ const builder = new XMLBuilder({

View File

@@ -92,7 +92,9 @@ export type TrackPointExtension = {
'gpxtpx:atemp'?: number; 'gpxtpx:atemp'?: number;
'gpxtpx:hr'?: number; 'gpxtpx:hr'?: number;
'gpxtpx:cad'?: number; 'gpxtpx:cad'?: number;
'gpxtpx:Extensions'?: Record<string, string>; 'gpxtpx:Extensions'?: {
surface?: string;
};
} }
export type PowerExtension = { export type PowerExtension = {

View File

@@ -45,9 +45,24 @@ export async function handle({ event, resolve }) {
`; `;
} }
const stringsHTML = page === 'app' ? stringsToHTML(strings) : '';
const response = await resolve(event, { const response = await resolve(event, {
transformPageChunk: ({ html }) => html.replace('<html>', htmlTag).replace('<head>', headTag), transformPageChunk: ({ html }) => html.replace('<html>', htmlTag).replace('<head>', headTag).replace('</body>', `<div class="fixed -z-10 text-transparent">${stringsHTML}</div></body>`)
}); });
return response; return response;
} }
function stringsToHTML(dictionary, strings = new Set(), root = true) {
Object.values(dictionary).forEach((value) => {
if (typeof value === 'object') {
stringsToHTML(value, strings, false);
} else {
strings.add(value);
}
});
if (root) {
return Array.from(strings).map((string) => `<p>${string}</p>`).join('');
}
}

View File

@@ -1,161 +0,0 @@
export const surfaceColors: { [key: string]: string } = {
"missing": "#d1d1d1",
"paved": "#8c8c8c",
"unpaved": "#6b443a",
"asphalt": "#8c8c8c",
"concrete": "#8c8c8c",
"cobblestone": "#ffd991",
"paving_stones": "#8c8c8c",
"sett": "#ffd991",
"metal": "#8c8c8c",
"wood": "#6b443a",
"compacted": "#ffffa8",
"fine_gravel": "#ffffa8",
"gravel": "#ffffa8",
"pebblestone": "#ffffa8",
"rock": "#ffd991",
"dirt": "#ffffa8",
"ground": "#6b443a",
"earth": "#6b443a",
"mud": "#6b443a",
"sand": "#ffffc4",
"grass": "#61b55c",
"grass_paver": "#61b55c",
"clay": "#6b443a",
"stone": "#ffd991",
};
export function getSurfaceColor(surface: string): string {
return surfaceColors[surface] ? surfaceColors[surface] : surfaceColors.missing;
}
export const highwayColors: { [key: string]: string } = {
"missing": "#d1d1d1",
"motorway": "#ff4d33",
"motorway_link": "#ff4d33",
"trunk": "#ff5e4d",
"trunk_link": "#ff947f",
"primary": "#ff6e5c",
"primary_link": "#ff6e5c",
"secondary": "#ff8d7b",
"secondary_link": "#ff8d7b",
"tertiary": "#ffd75f",
"tertiary_link": "#ffd75f",
"unclassified": "#f1f2a5",
"road": "#f1f2a5",
"residential": "#73b2ff",
"living_street": "#73b2ff",
"service": "#9c9cd9",
"track": "#a8e381",
"footway": "#a8e381",
"path": "#a8e381",
"pedestrian": "#a8e381",
"cycleway": "#9de2ff",
"construction": "#e09a4a",
"bridleway": "#946f43",
"raceway": "#ff0000",
"rest_area": "#9c9cd9",
"services": "#9c9cd9",
"corridor": "#474747",
"elevator": "#474747",
"steps": "#474747",
"bus_stop": "#8545a3",
"busway": "#8545a3",
"via_ferrata": "#474747"
};
export const sacScaleColors: { [key: string]: string } = {
"hiking": "#007700",
"mountain_hiking": "#1843ad",
"demanding_mountain_hiking": "#ffff00",
"alpine_hiking": "#ff9233",
"demanding_alpine_hiking": "#ff0000",
"difficult_alpine_hiking": "#000000",
};
export const mtbScaleColors: { [key: string]: string } = {
"0-": "#007700",
"0": "#007700",
"0+": "#007700",
"1-": "#1843ad",
"1": "#1843ad",
"1+": "#1843ad",
"2-": "#ffff00",
"2": "#ffff00",
"2+": "#ffff00",
"3": "#ff0000",
"4": "#00ff00",
"5": "#000000",
"6": "#b105eb",
};
function createPattern(backgroundColor: string, sacScaleColor: string | undefined, mtbScaleColor: string | undefined, size: number = 16, lineWidth: number = 4) {
let canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
let ctx = canvas.getContext('2d');
if (ctx) {
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, size, size);
ctx.lineWidth = lineWidth;
const halfSize = size / 2;
const halfLineWidth = lineWidth / 2;
if (sacScaleColor) {
ctx.strokeStyle = sacScaleColor;
ctx.beginPath();
ctx.moveTo(halfSize - halfLineWidth, - halfLineWidth);
ctx.lineTo(size + halfLineWidth, halfSize + halfLineWidth);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(- halfLineWidth, halfSize - halfLineWidth);
ctx.lineTo(halfSize + halfLineWidth, size + halfLineWidth);
ctx.stroke();
}
if (mtbScaleColor) {
ctx.strokeStyle = mtbScaleColor;
ctx.beginPath();
ctx.moveTo(halfSize - halfLineWidth, size + halfLineWidth);
ctx.lineTo(size + halfLineWidth, halfSize - halfLineWidth);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(- halfLineWidth, halfSize + halfLineWidth);
ctx.lineTo(halfSize + halfLineWidth, - halfLineWidth);
ctx.stroke();
}
}
return ctx?.createPattern(canvas, 'repeat') || backgroundColor;
}
const patterns: Record<string, string | CanvasPattern> = {};
export function getHighwayColor(highway: string, sacScale: string | undefined, mtbScale: string | undefined) {
let backgroundColor = highwayColors[highway] ? highwayColors[highway] : highwayColors.missing;
let sacScaleColor = sacScale ? sacScaleColors[sacScale] : undefined;
let mtbScaleColor = mtbScale ? mtbScaleColors[mtbScale] : undefined;
if (sacScale || mtbScale) {
let patternId = `${backgroundColor}-${[sacScale, mtbScale].filter(x => x).join('-')}`;
if (!patterns[patternId]) {
patterns[patternId] = createPattern(backgroundColor, sacScaleColor, mtbScaleColor);
}
return patterns[patternId];
}
return backgroundColor;
}
const maxSlope = 20;
export function getSlopeColor(slope: number): string {
if (slope > maxSlope) {
slope = maxSlope;
} else if (slope < -maxSlope) {
slope = -maxSlope;
}
let v = slope / maxSlope;
v = 1 / (1 + Math.exp(-6 * v));
v = v - 0.5;
let hue = ((0.5 - v) * 120).toString(10);
let lightness = 90 - Math.abs(v) * 70;
return `hsl(${hue},70%,${lightness}%)`;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@@ -1,11 +1,11 @@
import { TramFront, Utensils, ShoppingBasket, Droplet, ShowerHead, Fuel, CircleParking, Fence, FerrisWheel, Bed, Mountain, Pickaxe, Store, TrainFront, Bus, Ship, Croissant, House, Tent, Wrench, Binoculars } from 'lucide-static'; import { TramFront, Utensils, ShoppingBasket, Droplet, ShowerHead, Fuel, CircleParking, Fence, FerrisWheel, Bed, Mountain, Pickaxe, Store, TrainFront, Bus, Ship, Croissant, House, Tent, Wrench, Binoculars } from 'lucide-static';
import { type StyleSpecification } from 'mapbox-gl'; import { type Style } from 'mapbox-gl';
import ignFrTopo from './custom/ign-fr-topo.json'; import ignFrTopo from './custom/ign-fr-topo.json';
import ignFrPlan from './custom/ign-fr-plan.json'; import ignFrPlan from './custom/ign-fr-plan.json';
import ignFrSatellite from './custom/ign-fr-satellite.json'; import ignFrSatellite from './custom/ign-fr-satellite.json';
import bikerouterGravel from './custom/bikerouter-gravel.json'; import bikerouterGravel from './custom/bikerouter-gravel.json';
export const basemaps: { [key: string]: string | StyleSpecification; } = { export const basemaps: { [key: string]: string | Style; } = {
mapboxOutdoors: 'mapbox://styles/mapbox/outdoors-v12', mapboxOutdoors: 'mapbox://styles/mapbox/outdoors-v12',
mapboxSatellite: 'mapbox://styles/mapbox/satellite-streets-v12', mapboxSatellite: 'mapbox://styles/mapbox/satellite-streets-v12',
openStreetMap: { openStreetMap: {
@@ -158,7 +158,7 @@ export const basemaps: { [key: string]: string | StyleSpecification; } = {
tiles: ['https://www.ign.es/wmts/mapa-raster?layer=MTN&style=default&tilematrixset=GoogleMapsCompatible&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image/jpeg&TileMatrix={z}&TileCol={x}&TileRow={y}'], tiles: ['https://www.ign.es/wmts/mapa-raster?layer=MTN&style=default&tilematrixset=GoogleMapsCompatible&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image/jpeg&TileMatrix={z}&TileCol={x}&TileRow={y}'],
tileSize: 256, tileSize: 256,
maxzoom: 20, maxzoom: 20,
attribution: '&copy; <a href="https://www.ign.es" target="_blank">IGN</a>' attribution: 'IGN-F/Géoportail'
} }
}, },
layers: [{ layers: [{
@@ -167,23 +167,6 @@ export const basemaps: { [key: string]: string | StyleSpecification; } = {
source: 'ignEs', source: 'ignEs',
}], }],
}, },
ignEsSatellite: {
version: 8,
sources: {
ignEsSatellite: {
type: 'raster',
tiles: ['https://www.ign.es/wmts/pnoa-ma?layer=OI.OrthoimageCoverage&style=default&tilematrixset=GoogleMapsCompatible&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image/jpeg&TileMatrix={z}&TileCol={x}&TileRow={y}'],
tileSize: 256,
maxzoom: 20,
attribution: '&copy; <a href="https://www.ign.es" target="_blank">IGN</a>'
}
},
layers: [{
id: 'ignEsSatellite',
type: 'raster',
source: 'ignEsSatellite',
}],
},
ordnanceSurvey: "https://api.os.uk/maps/vector/v1/vts/resources/styles?srs=3857&key=piCT8WysfuC3xLSUW7sGLfrAAJoYDvQz", ordnanceSurvey: "https://api.os.uk/maps/vector/v1/vts/resources/styles?srs=3857&key=piCT8WysfuC3xLSUW7sGLfrAAJoYDvQz",
norwayTopo: { norwayTopo: {
version: 8, version: 8,
@@ -303,7 +286,7 @@ export const basemaps: { [key: string]: string | StyleSpecification; } = {
}, },
}; };
export const overlays: { [key: string]: string | StyleSpecification; } = { export const overlays: { [key: string]: string | Style; } = {
cyclOSMlite: { cyclOSMlite: {
version: 8, version: 8,
sources: { sources: {
@@ -653,7 +636,6 @@ export const basemapTree: LayerTreeType = {
}, },
spain: { spain: {
ignEs: true, ignEs: true,
ignEsSatellite: true,
}, },
sweden: { sweden: {
swedenTopo: true, swedenTopo: true,
@@ -758,7 +740,7 @@ export const overpassTree: LayerTreeType = {
export const defaultBasemap = 'mapboxOutdoors'; export const defaultBasemap = 'mapboxOutdoors';
// Default overlays used (none) // Default overlays used (none)
export const defaultOverlays: LayerTreeType = { export const defaultOverlays = {
overlays: { overlays: {
world: { world: {
waymarked_trails: { waymarked_trails: {
@@ -873,7 +855,6 @@ export const defaultBasemapTree: LayerTreeType = {
}, },
spain: { spain: {
ignEs: false, ignEs: false,
ignEsSatellite: false,
}, },
sweden: { sweden: {
swedenTopo: false, swedenTopo: false,

View File

@@ -0,0 +1,31 @@
export const surfaceColors: { [key: string]: string } = {
'missing': '#d1d1d1',
'paved': '#8c8c8c',
'unpaved': '#6b443a',
'asphalt': '#8c8c8c',
'concrete': '#8c8c8c',
'chipseal': '#8c8c8c',
'cobblestone': '#ffd991',
'unhewn_cobblestone': '#ffd991',
'paving_stones': '#8c8c8c',
'stepping_stones': '#c7b2db',
'sett': '#ffd991',
'metal': '#8c8c8c',
'wood': '#6b443a',
'compacted': '#ffffa8',
'fine_gravel': '#ffffa8',
'gravel': '#ffffa8',
'pebblestone': '#ffffa8',
'rock': '#ffd991',
'dirt': '#ffffa8',
'ground': '#6b443a',
'earth': '#6b443a',
'snow': '#bdfffc',
'ice': '#bdfffc',
'salt': '#b6c0f2',
'mud': '#6b443a',
'sand': '#ffffc4',
'woodchips': '#6b443a',
'grass': '#61b55c',
'grass_paver': '#61b55c'
}

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Tooltip from '$lib/components/ui/tooltip/index.js'; import * as Tooltip from '$lib/components/ui/tooltip/index.js';
import type { Builder } from 'bits-ui';
export let variant: export let variant:
| 'default' | 'default'
@@ -13,12 +12,11 @@
| undefined = 'default'; | undefined = 'default';
export let label: string; export let label: string;
export let side: 'top' | 'right' | 'bottom' | 'left' = 'top'; export let side: 'top' | 'right' | 'bottom' | 'left' = 'top';
export let builders: Builder[] = [];
</script> </script>
<Tooltip.Root> <Tooltip.Root>
<Tooltip.Trigger asChild let:builder> <Tooltip.Trigger asChild let:builder>
<Button builders={[...builders, builder]} {variant} {...$$restProps} on:click> <Button builders={[builder]} {variant} {...$$restProps}>
<slot /> <slot />
</Button> </Button>
</Tooltip.Trigger> </Tooltip.Trigger>

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import ButtonWithTooltip from '$lib/components/ButtonWithTooltip.svelte';
import * as Popover from '$lib/components/ui/popover';
import * as ToggleGroup from '$lib/components/ui/toggle-group'; import * as ToggleGroup from '$lib/components/ui/toggle-group';
import Tooltip from '$lib/components/Tooltip.svelte';
import Chart from 'chart.js/auto'; import Chart from 'chart.js/auto';
import mapboxgl from 'mapbox-gl'; import mapboxgl from 'mapbox-gl';
import { map } from '$lib/stores'; import { map } from '$lib/stores';
@@ -13,15 +12,12 @@
Orbit, Orbit,
SquareActivity, SquareActivity,
Thermometer, Thermometer,
Zap, Zap
Circle,
Check,
ChartNoAxesColumn,
Construction
} from 'lucide-svelte'; } from 'lucide-svelte';
import { getSlopeColor, getSurfaceColor, getHighwayColor } from '$lib/assets/colors'; import { surfaceColors } from '$lib/assets/surfaces';
import { _ } from 'svelte-i18n'; import { _, locale } from 'svelte-i18n';
import { import {
getCadenceUnits,
getCadenceWithUnits, getCadenceWithUnits,
getConvertedDistance, getConvertedDistance,
getConvertedElevation, getConvertedElevation,
@@ -30,26 +26,45 @@
getDistanceUnits, getDistanceUnits,
getDistanceWithUnits, getDistanceWithUnits,
getElevationWithUnits, getElevationWithUnits,
getHeartRateUnits,
getHeartRateWithUnits, getHeartRateWithUnits,
getPowerUnits,
getPowerWithUnits, getPowerWithUnits,
getTemperatureUnits,
getTemperatureWithUnits, getTemperatureWithUnits,
getVelocityWithUnits getVelocityUnits,
getVelocityWithUnits,
secondsToHHMMSS
} from '$lib/units'; } from '$lib/units';
import type { Writable } from 'svelte/store'; import type { Writable } from 'svelte/store';
import { DateFormatter } from '@internationalized/date';
import type { GPXStatistics } from 'gpx'; import type { GPXStatistics } from 'gpx';
import { settings } from '$lib/db'; import { settings } from '$lib/db';
import { mode } from 'mode-watcher'; import { mode } from 'mode-watcher';
import { df } from '$lib/utils';
export let gpxStatistics: Writable<GPXStatistics>; export let gpxStatistics: Writable<GPXStatistics>;
export let slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>; export let slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined>;
export let panelSize: number;
export let additionalDatasets: string[]; export let additionalDatasets: string[];
export let elevationFill: 'slope' | 'surface' | 'highway' | undefined; export let elevationFill: 'slope' | 'surface' | undefined;
export let showControls: boolean = true; export let showControls: boolean = true;
const { distanceUnits, velocityUnits, temperatureUnits } = settings; const { distanceUnits, velocityUnits, temperatureUnits } = settings;
let df: DateFormatter;
$: if ($locale) {
df = new DateFormatter($locale, {
dateStyle: 'medium',
timeStyle: 'medium'
});
}
let canvas: HTMLCanvasElement; let canvas: HTMLCanvasElement;
let showAdditionalScales = true;
let updateShowAdditionalScales = () => {
showAdditionalScales = canvas.width / window.devicePixelRatio >= 600;
};
let overlay: HTMLCanvasElement; let overlay: HTMLCanvasElement;
let chart: Chart; let chart: Chart;
@@ -68,11 +83,12 @@
x: { x: {
type: 'linear', type: 'linear',
ticks: { ticks: {
callback: function (value: number) { callback: function (value: number, index: number, ticks: { value: number }[]) {
if (index === ticks.length - 1) {
return `${value.toFixed(1).replace(/\.0+$/, '')}`;
}
return `${value.toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`; return `${value.toFixed(1).replace(/\.0+$/, '')} ${getDistanceUnits()}`;
}, }
align: 'inner',
maxRotation: 0
} }
}, },
y: { y: {
@@ -143,10 +159,7 @@
segment: point.slope.segment.toFixed(1), segment: point.slope.segment.toFixed(1),
length: getDistanceWithUnits(point.slope.length) length: getDistanceWithUnits(point.slope.length)
}; };
let surface = point.extensions.surface ? point.extensions.surface : 'unknown'; let surface = point.surface ? point.surface : 'unknown';
let highway = point.extensions.highway ? point.extensions.highway : 'unknown';
let sacScale = point.extensions.sac_scale;
let mtbScale = point.extensions.mtb_scale;
let labels = [ let labels = [
` ${$_('quantities.distance')}: ${getDistanceWithUnits(point.x, false)}`, ` ${$_('quantities.distance')}: ${getDistanceWithUnits(point.x, false)}`,
@@ -159,17 +172,6 @@
); );
} }
if (elevationFill === 'highway') {
labels.push(
` ${$_('quantities.highway')}: ${$_(`toolbar.routing.highway.${highway}`)}${
sacScale ? ` (${$_(`toolbar.routing.sac_scale.${sacScale}`)})` : ''
}`
);
if (mtbScale) {
labels.push(` ${$_('toolbar.routing.mtb_scale')}: ${mtbScale}`);
}
}
if (point.time) { if (point.time) {
labels.push(` ${$_('quantities.time')}: ${df.format(point.time)}`); labels.push(` ${$_('quantities.time')}: ${df.format(point.time)}`);
} }
@@ -224,21 +226,72 @@
stacked: false, stacked: false,
onResize: function () { onResize: function () {
updateOverlay(); updateOverlay();
updateShowAdditionalScales();
} }
}; };
let datasets: string[] = ['speed', 'hr', 'cad', 'atemp', 'power']; let datasets: {
datasets.forEach((id) => { [key: string]: {
id: string;
getLabel: () => string;
getUnits: () => string;
};
} = {
speed: {
id: 'speed',
getLabel: () => ($velocityUnits === 'speed' ? $_('quantities.speed') : $_('quantities.pace')),
getUnits: () => getVelocityUnits()
},
hr: {
id: 'hr',
getLabel: () => $_('quantities.heartrate'),
getUnits: () => getHeartRateUnits()
},
cad: {
id: 'cad',
getLabel: () => $_('quantities.cadence'),
getUnits: () => getCadenceUnits()
},
atemp: {
id: 'atemp',
getLabel: () => $_('quantities.temperature'),
getUnits: () => getTemperatureUnits()
},
power: {
id: 'power',
getLabel: () => $_('quantities.power'),
getUnits: () => getPowerUnits()
}
};
for (let [id, dataset] of Object.entries(datasets)) {
options.scales[`y${id}`] = { options.scales[`y${id}`] = {
type: 'linear', type: 'linear',
position: 'right', position: 'right',
title: {
display: true,
text: dataset.getLabel() + ' (' + dataset.getUnits() + ')',
padding: {
top: 6,
bottom: 0
}
},
grid: { grid: {
display: false display: false
}, },
reverse: () => id === 'speed' && $velocityUnits === 'pace', reverse: () => id === 'speed' && $velocityUnits === 'pace',
display: false display: false
}; };
}); }
options.scales.yspeed['ticks'] = {
callback: function (value: number) {
if ($velocityUnits === 'speed') {
return value;
} else {
return secondsToHHMMSS(value);
}
}
};
onMount(async () => { onMount(async () => {
Chart.register((await import('chartjs-plugin-zoom')).default); // dynamic import to avoid SSR and 'window is not defined' error Chart.register((await import('chartjs-plugin-zoom')).default); // dynamic import to avoid SSR and 'window is not defined' error
@@ -271,6 +324,8 @@
element element
}); });
updateShowAdditionalScales();
let startIndex = 0; let startIndex = 0;
let endIndex = 0; let endIndex = 0;
function getIndex(evt) { function getIndex(evt) {
@@ -359,7 +414,7 @@
segment: data.local.slope.segment[index], segment: data.local.slope.segment[index],
length: data.local.slope.length[index] length: data.local.slope.length[index]
}, },
extensions: point.getExtensions(), surface: point.getSurface(),
coordinates: point.getCoordinates(), coordinates: point.getCoordinates(),
index: index index: index
}; };
@@ -369,6 +424,7 @@
order: 1 order: 1
}; };
chart.data.datasets[1] = { chart.data.datasets[1] = {
label: datasets.speed.getLabel(),
data: data.local.points.map((point, index) => { data: data.local.points.map((point, index) => {
return { return {
x: getConvertedDistance(data.local.distance.total[index]), x: getConvertedDistance(data.local.distance.total[index]),
@@ -377,10 +433,11 @@
}; };
}), }),
normalized: true, normalized: true,
yAxisID: 'yspeed', yAxisID: `y${datasets.speed.id}`,
hidden: true hidden: true
}; };
chart.data.datasets[2] = { chart.data.datasets[2] = {
label: datasets.hr.getLabel(),
data: data.local.points.map((point, index) => { data: data.local.points.map((point, index) => {
return { return {
x: getConvertedDistance(data.local.distance.total[index]), x: getConvertedDistance(data.local.distance.total[index]),
@@ -389,10 +446,11 @@
}; };
}), }),
normalized: true, normalized: true,
yAxisID: 'yhr', yAxisID: `y${datasets.hr.id}`,
hidden: true hidden: true
}; };
chart.data.datasets[3] = { chart.data.datasets[3] = {
label: datasets.cad.getLabel(),
data: data.local.points.map((point, index) => { data: data.local.points.map((point, index) => {
return { return {
x: getConvertedDistance(data.local.distance.total[index]), x: getConvertedDistance(data.local.distance.total[index]),
@@ -401,10 +459,11 @@
}; };
}), }),
normalized: true, normalized: true,
yAxisID: 'ycad', yAxisID: `y${datasets.cad.id}`,
hidden: true hidden: true
}; };
chart.data.datasets[4] = { chart.data.datasets[4] = {
label: datasets.atemp.getLabel(),
data: data.local.points.map((point, index) => { data: data.local.points.map((point, index) => {
return { return {
x: getConvertedDistance(data.local.distance.total[index]), x: getConvertedDistance(data.local.distance.total[index]),
@@ -413,10 +472,11 @@
}; };
}), }),
normalized: true, normalized: true,
yAxisID: 'yatemp', yAxisID: `y${datasets.atemp.id}`,
hidden: true hidden: true
}; };
chart.data.datasets[5] = { chart.data.datasets[5] = {
label: datasets.power.getLabel(),
data: data.local.points.map((point, index) => { data: data.local.points.map((point, index) => {
return { return {
x: getConvertedDistance(data.local.distance.total[index]), x: getConvertedDistance(data.local.distance.total[index]),
@@ -425,29 +485,43 @@
}; };
}), }),
normalized: true, normalized: true,
yAxisID: 'ypower', yAxisID: `y${datasets.power.id}`,
hidden: true hidden: true
}; };
chart.options.scales.x['min'] = 0; chart.options.scales.x['min'] = 0;
chart.options.scales.x['max'] = getConvertedDistance(data.global.distance.total); chart.options.scales.x['max'] = getConvertedDistance(data.global.distance.total);
// update units
for (let [id, dataset] of Object.entries(datasets)) {
chart.options.scales[`y${id}`].title.text =
dataset.getLabel() + ' (' + dataset.getUnits() + ')';
}
chart.update(); chart.update();
} }
let maxSlope = 20;
function slopeFillCallback(context) { function slopeFillCallback(context) {
return getSlopeColor(context.p0.raw.slope.segment); let slope = context.p0.raw.slope.segment;
if (slope > maxSlope) {
slope = maxSlope;
} else if (slope < -maxSlope) {
slope = -maxSlope;
}
let v = slope / maxSlope;
v = 1 / (1 + Math.exp(-6 * v));
v = v - 0.5;
let hue = ((0.5 - v) * 120).toString(10);
let lightness = 90 - Math.abs(v) * 70;
return ['hsl(', hue, ',70%,', lightness, '%)'].join('');
} }
function surfaceFillCallback(context) { function surfaceFillCallback(context) {
return getSurfaceColor(context.p0.raw.extensions.surface); let surface = context.p0.raw.surface;
} return surfaceColors[surface] ? surfaceColors[surface] : surfaceColors.missing;
function highwayFillCallback(context) {
return getHighwayColor(
context.p0.raw.extensions.highway,
context.p0.raw.extensions.sac_scale,
context.p0.raw.extensions.mtb_scale
);
} }
$: if (chart) { $: if (chart) {
@@ -459,10 +533,6 @@
chart.data.datasets[0]['segment'] = { chart.data.datasets[0]['segment'] = {
backgroundColor: surfaceFillCallback backgroundColor: surfaceFillCallback
}; };
} else if (elevationFill === 'highway') {
chart.data.datasets[0]['segment'] = {
backgroundColor: highwayFillCallback
};
} else { } else {
chart.data.datasets[0]['segment'] = {}; chart.data.datasets[0]['segment'] = {};
} }
@@ -482,6 +552,12 @@
chart.data.datasets[4].hidden = !includeTemperature; chart.data.datasets[4].hidden = !includeTemperature;
chart.data.datasets[5].hidden = !includePower; chart.data.datasets[5].hidden = !includePower;
} }
chart.options.scales[`y${datasets.speed.id}`].display = includeSpeed && showAdditionalScales;
chart.options.scales[`y${datasets.hr.id}`].display = includeHeartRate && showAdditionalScales;
chart.options.scales[`y${datasets.cad.id}`].display = includeCadence && showAdditionalScales;
chart.options.scales[`y${datasets.atemp.id}`].display =
includeTemperature && showAdditionalScales;
chart.options.scales[`y${datasets.power.id}`].display = includePower && showAdditionalScales;
chart.update(); chart.update();
} }
@@ -492,8 +568,6 @@
overlay.width = canvas.width / window.devicePixelRatio; overlay.width = canvas.width / window.devicePixelRatio;
overlay.height = canvas.height / window.devicePixelRatio; overlay.height = canvas.height / window.devicePixelRatio;
overlay.style.width = `${overlay.width}px`;
overlay.style.height = `${overlay.height}px`;
if ($slicedGPXStatistics) { if ($slicedGPXStatistics) {
let startIndex = $slicedGPXStatistics[1]; let startIndex = $slicedGPXStatistics[1];
@@ -517,7 +591,7 @@
startPixel, startPixel,
chart.chartArea.top, chart.chartArea.top,
endPixel - startPixel, endPixel - startPixel,
chart.chartArea.height chart.chartArea.bottom - chart.chartArea.top
); );
} }
} else if (overlay) { } else if (overlay) {
@@ -537,135 +611,75 @@
}); });
</script> </script>
<div class="h-full grow min-w-0 relative py-2"> <div class="h-full grow min-w-0 flex flex-row gap-4 items-center {$$props.class ?? ''}">
<canvas bind:this={overlay} class="w-full h-full absolute pointer-events-none"></canvas> <div class="grow h-full min-w-0 relative">
<canvas bind:this={canvas} class="w-full h-full absolute"></canvas> <canvas bind:this={overlay} class=" w-full h-full absolute pointer-events-none"></canvas>
<canvas bind:this={canvas} class="w-full h-full"></canvas>
</div>
{#if showControls} {#if showControls}
<div class="absolute bottom-10 right-1.5"> <div class="h-full flex flex-col justify-center" style="width: {panelSize > 158 ? 22 : 42}px">
<Popover.Root> <ToggleGroup.Root
<Popover.Trigger asChild let:builder> class="{panelSize > 158
<ButtonWithTooltip ? 'flex-col'
label={$_('chart.settings')} : 'flex-row'} flex-wrap gap-0 min-h-0 content-center border rounded-t-md"
builders={[builder]} type="single"
variant="outline" bind:value={elevationFill}
class="w-7 h-7 p-0 flex justify-center opacity-70 hover:opacity-100 transition-opacity duration-300 hover:bg-background" >
<ToggleGroup.Item class="p-0 w-5 h-5" value="slope" aria-label={$_('chart.show_slope')}>
<Tooltip side="left" label={$_('chart.show_slope')}>
<TriangleRight size="15" />
</Tooltip>
</ToggleGroup.Item>
<ToggleGroup.Item class="p-0 w-5 h-5" value="surface" aria-label={$_('chart.show_surface')}>
<Tooltip side="left" label={$_('chart.show_surface')}>
<BrickWall size="15" />
</Tooltip>
</ToggleGroup.Item>
</ToggleGroup.Root>
<ToggleGroup.Root
class="{panelSize > 158
? 'flex-col'
: 'flex-row'} flex-wrap gap-0 min-h-0 content-center border rounded-b-md -mt-[1px]"
type="multiple"
bind:value={additionalDatasets}
>
<ToggleGroup.Item
class="p-0 w-5 h-5"
value="speed"
aria-label={$velocityUnits === 'speed' ? $_('chart.show_speed') : $_('chart.show_pace')}
>
<Tooltip
side="left"
label={$velocityUnits === 'speed' ? $_('chart.show_speed') : $_('chart.show_pace')}
> >
<ChartNoAxesColumn size="18" /> <Zap size="15" />
</ButtonWithTooltip> </Tooltip>
</Popover.Trigger> </ToggleGroup.Item>
<Popover.Content class="w-fit p-0 flex flex-col divide-y" side="top" sideOffset={-32}> <ToggleGroup.Item class="p-0 w-5 h-5" value="hr" aria-label={$_('chart.show_heartrate')}>
<ToggleGroup.Root <Tooltip side="left" label={$_('chart.show_heartrate')}>
class="flex flex-col items-start gap-0 p-1" <HeartPulse size="15" />
type="single" </Tooltip>
bind:value={elevationFill} </ToggleGroup.Item>
> <ToggleGroup.Item class="p-0 w-5 h-5" value="cad" aria-label={$_('chart.show_cadence')}>
<ToggleGroup.Item <Tooltip side="left" label={$_('chart.show_cadence')}>
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground" <Orbit size="15" />
value="slope" </Tooltip>
> </ToggleGroup.Item>
<div class="w-6 flex justify-center items-center"> <ToggleGroup.Item
{#if elevationFill === 'slope'} class="p-0 w-5 h-5"
<Circle class="h-1.5 w-1.5 fill-current text-current" /> value="atemp"
{/if} aria-label={$_('chart.show_temperature')}
</div> >
<TriangleRight size="15" class="mr-1" /> <Tooltip side="left" label={$_('chart.show_temperature')}>
{$_('quantities.slope')} <Thermometer size="15" />
</ToggleGroup.Item> </Tooltip>
<ToggleGroup.Item </ToggleGroup.Item>
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground" <ToggleGroup.Item class="p-0 w-5 h-5" value="power" aria-label={$_('chart.show_power')}>
value="surface" <Tooltip side="left" label={$_('chart.show_power')}>
variant="outline" <SquareActivity size="15" />
> </Tooltip>
<div class="w-6 flex justify-center items-center"> </ToggleGroup.Item>
{#if elevationFill === 'surface'} </ToggleGroup.Root>
<Circle class="h-1.5 w-1.5 fill-current text-current" />
{/if}
</div>
<BrickWall size="15" class="mr-1" />
{$_('quantities.surface')}
</ToggleGroup.Item>
<ToggleGroup.Item
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="highway"
variant="outline"
>
<div class="w-6 flex justify-center items-center">
{#if elevationFill === 'highway'}
<Circle class="h-1.5 w-1.5 fill-current text-current" />
{/if}
</div>
<Construction size="15" class="mr-1" />
{$_('quantities.highway')}
</ToggleGroup.Item>
</ToggleGroup.Root>
<ToggleGroup.Root
class="flex flex-col items-start gap-0 p-1"
type="multiple"
bind:value={additionalDatasets}
>
<ToggleGroup.Item
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="speed"
>
<div class="w-6 flex justify-center items-center">
{#if additionalDatasets.includes('speed')}
<Check size="14" />
{/if}
</div>
<Zap size="15" class="mr-1" />
{$velocityUnits === 'speed' ? $_('quantities.speed') : $_('quantities.pace')}
</ToggleGroup.Item>
<ToggleGroup.Item
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="hr"
>
<div class="w-6 flex justify-center items-center">
{#if additionalDatasets.includes('hr')}
<Check size="14" />
{/if}
</div>
<HeartPulse size="15" class="mr-1" />
{$_('quantities.heartrate')}
</ToggleGroup.Item>
<ToggleGroup.Item
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="cad"
>
<div class="w-6 flex justify-center items-center">
{#if additionalDatasets.includes('cad')}
<Check size="14" />
{/if}
</div>
<Orbit size="15" class="mr-1" />
{$_('quantities.cadence')}
</ToggleGroup.Item>
<ToggleGroup.Item
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="atemp"
>
<div class="w-6 flex justify-center items-center">
{#if additionalDatasets.includes('atemp')}
<Check size="14" />
{/if}
</div>
<Thermometer size="15" class="mr-1" />
{$_('quantities.temperature')}
</ToggleGroup.Item>
<ToggleGroup.Item
class="p-0 pr-1.5 h-6 w-full rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="power"
>
<div class="w-6 flex justify-center items-center">
{#if additionalDatasets.includes('power')}
<Check size="14" />
{/if}
</div>
<SquareActivity size="15" class="mr-1" />
{$_('quantities.power')}
</ToggleGroup.Item>
</ToggleGroup.Root>
</Popover.Content>
</Popover.Root>
</div> </div>
{/if} {/if}
</div> </div>

View File

@@ -16,7 +16,7 @@
import { import {
Download, Download,
Zap, Zap,
Earth, BrickWall,
HeartPulse, HeartPulse,
Orbit, Orbit,
Thermometer, Thermometer,
@@ -31,19 +31,19 @@
let open = false; let open = false;
let exportOptions: Record<string, boolean> = { let exportOptions: Record<string, boolean> = {
time: true, time: true,
surface: true,
hr: true, hr: true,
cad: true, cad: true,
atemp: true, atemp: true,
power: true, power: true
extensions: true
}; };
let hide: Record<string, boolean> = { let hide: Record<string, boolean> = {
time: false, time: false,
surface: false,
hr: false, hr: false,
cad: false, cad: false,
atemp: false, atemp: false,
power: false, power: false
extensions: false
}; };
$: if ($exportState !== ExportState.NONE) { $: if ($exportState !== ExportState.NONE) {
@@ -63,11 +63,11 @@
} }
hide.time = statistics.global.time.total === 0; hide.time = statistics.global.time.total === 0;
hide.surface = !Object.keys(statistics.global.surface).some((key) => key !== 'unknown');
hide.hr = statistics.global.hr.count === 0; hide.hr = statistics.global.hr.count === 0;
hide.cad = statistics.global.cad.count === 0; hide.cad = statistics.global.cad.count === 0;
hide.atemp = statistics.global.atemp.count === 0; hide.atemp = statistics.global.atemp.count === 0;
hide.power = statistics.global.power.count === 0; hide.power = statistics.global.power.count === 0;
hide.extensions = Object.keys(statistics.global.extensions).length === 0;
} }
$: exclude = Object.keys(exportOptions).filter((key) => !exportOptions[key]); $: exclude = Object.keys(exportOptions).filter((key) => !exportOptions[key]);
@@ -144,11 +144,11 @@
{$_('quantities.time')} {$_('quantities.time')}
</Label> </Label>
</div> </div>
<div class="flex flex-row items-center gap-1.5 {hide.extensions ? 'hidden' : ''}"> <div class="flex flex-row items-center gap-1.5 {hide.surface ? 'hidden' : ''}">
<Checkbox id="export-extensions" bind:checked={exportOptions.extensions} /> <Checkbox id="export-surface" bind:checked={exportOptions.surface} />
<Label for="export-extensions" class="flex flex-row items-center gap-1"> <Label for="export-surface" class="flex flex-row items-center gap-1">
<Earth size="16" /> <BrickWall size="16" />
{$_('quantities.osm_extensions')} {$_('quantities.surface')}
</Label> </Label>
</div> </div>
<div class="flex flex-row items-center gap-1.5 {hide.hr ? 'hidden' : ''}"> <div class="flex flex-row items-center gap-1.5 {hide.hr ? 'hidden' : ''}">

View File

@@ -28,7 +28,7 @@
<Card.Root <Card.Root
class="h-full {orientation === 'vertical' class="h-full {orientation === 'vertical'
? 'min-w-40 sm:min-w-44 text-sm sm:text-base' ? 'min-w-44 sm:min-w-52 text-sm sm:text-base'
: 'w-full'} border-none shadow-none" : 'w-full'} border-none shadow-none"
> >
<Card.Content <Card.Content
@@ -38,15 +38,15 @@
> >
<Tooltip label={$_('quantities.distance')}> <Tooltip label={$_('quantities.distance')}>
<span class="flex flex-row items-center"> <span class="flex flex-row items-center">
<Ruler size="16" class="mr-1" /> <Ruler size="18" class="mr-1" />
<WithUnits value={statistics.global.distance.total} type="distance" /> <WithUnits value={statistics.global.distance.total} type="distance" />
</span> </span>
</Tooltip> </Tooltip>
<Tooltip label={$_('quantities.elevation_gain_loss')}> <Tooltip label={$_('quantities.elevation_gain_loss')}>
<span class="flex flex-row items-center"> <span class="flex flex-row items-center">
<MoveUpRight size="16" class="mr-1" /> <MoveUpRight size="18" class="mr-1" />
<WithUnits value={statistics.global.elevation.gain} type="elevation" /> <WithUnits value={statistics.global.elevation.gain} type="elevation" />
<MoveDownRight size="16" class="mx-1" /> <MoveDownRight size="18" class="mx-1" />
<WithUnits value={statistics.global.elevation.loss} type="elevation" /> <WithUnits value={statistics.global.elevation.loss} type="elevation" />
</span> </span>
</Tooltip> </Tooltip>
@@ -58,7 +58,7 @@
)} / {$_('quantities.total')})" )} / {$_('quantities.total')})"
> >
<span class="flex flex-row items-center"> <span class="flex flex-row items-center">
<Zap size="16" class="mr-1" /> <Zap size="18" class="mr-1" />
<WithUnits value={statistics.global.speed.moving} type="speed" showUnits={false} /> <WithUnits value={statistics.global.speed.moving} type="speed" showUnits={false} />
<span class="mx-1">/</span> <span class="mx-1">/</span>
<WithUnits value={statistics.global.speed.total} type="speed" /> <WithUnits value={statistics.global.speed.total} type="speed" />
@@ -71,7 +71,7 @@
label="{$_('quantities.time')} ({$_('quantities.moving')} / {$_('quantities.total')})" label="{$_('quantities.time')} ({$_('quantities.moving')} / {$_('quantities.total')})"
> >
<span class="flex flex-row items-center"> <span class="flex flex-row items-center">
<Timer size="16" class="mr-1" /> <Timer size="18" class="mr-1" />
<WithUnits value={statistics.global.time.moving} type="time" /> <WithUnits value={statistics.global.time.moving} type="time" />
<span class="mx-1">/</span> <span class="mx-1">/</span>
<WithUnits value={statistics.global.time.total} type="time" /> <WithUnits value={statistics.global.time.total} type="time" />

View File

@@ -50,20 +50,6 @@
language = 'en'; language = 'en';
} }
const loadJson = mapboxgl.Style.prototype._load;
mapboxgl.Style.prototype._load = function (json, validate) {
if (
json['sources'] &&
json['sources']['mapbox-satellite'] &&
json['sources']['mapbox-satellite']['data'] &&
json['sources']['mapbox-satellite']['data']['data']
) {
// Temporary fix for https://github.com/gpxstudio/gpx.studio/issues/129
delete json['sources']['mapbox-satellite']['data']['data'];
}
loadJson.call(this, json, validate);
};
let newMap = new mapboxgl.Map({ let newMap = new mapboxgl.Map({
container: 'map', container: 'map',
style: { style: {
@@ -348,7 +334,7 @@
div :global(.mapboxgl-popup) { div :global(.mapboxgl-popup) {
@apply w-fit; @apply w-fit;
@apply z-50; @apply z-20;
} }
div :global(.mapboxgl-popup-content) { div :global(.mapboxgl-popup-content) {

View File

@@ -1,25 +0,0 @@
<svelte:options accessors />
<script lang="ts">
import { TrackPoint, Waypoint } from 'gpx';
import type { Writable } from 'svelte/store';
import WaypointPopup from '$lib/components/gpx-layer/WaypointPopup.svelte';
import TrackpointPopup from '$lib/components/gpx-layer/TrackpointPopup.svelte';
import OverpassPopup from '$lib/components/layer-control/OverpassPopup.svelte';
import type { PopupItem } from './MapPopup';
export let item: Writable<PopupItem | null>;
export let container: HTMLDivElement | null = null;
</script>
<div bind:this={container}>
{#if $item}
{#if $item.item instanceof Waypoint}
<WaypointPopup waypoint={$item} />
{:else if $item.item instanceof TrackPoint}
<TrackpointPopup trackpoint={$item} />
{:else}
<OverpassPopup poi={$item} />
{/if}
{/if}
</div>

View File

@@ -1,78 +0,0 @@
import { TrackPoint, Waypoint } from "gpx";
import mapboxgl from "mapbox-gl";
import { tick } from "svelte";
import { get, writable, type Writable } from "svelte/store";
import MapPopupComponent from "./MapPopup.svelte";
export type PopupItem<T = Waypoint | TrackPoint | any> = {
item: T;
fileId?: string;
};
export class MapPopup {
map: mapboxgl.Map;
popup: mapboxgl.Popup;
item: Writable<PopupItem | null> = writable(null);
maybeHideBinded = this.maybeHide.bind(this);
constructor(map: mapboxgl.Map, options?: mapboxgl.PopupOptions) {
this.map = map;
this.popup = new mapboxgl.Popup(options);
let component = new MapPopupComponent({
target: document.body,
props: {
item: this.item
}
});
tick().then(() => this.popup.setDOMContent(component.container));
}
setItem(item: PopupItem | null) {
this.item.set(item);
if (item === null) {
this.hide();
} else {
tick().then(() => this.show());
}
}
show() {
const i = get(this.item);
if (i === null) {
this.hide();
return;
}
this.popup.setLngLat(this.getCoordinates()).addTo(this.map);
this.map.on('mousemove', this.maybeHideBinded);
}
maybeHide(e: mapboxgl.MapMouseEvent) {
const i = get(this.item);
if (i === null) {
this.hide();
return;
}
if (this.map.project(this.getCoordinates()).dist(this.map.project(e.lngLat)) > 60) {
this.hide();
}
}
hide() {
this.popup.remove();
this.map.off('mousemove', this.maybeHideBinded);
}
remove() {
this.popup.remove();
}
getCoordinates() {
const i = get(this.item);
if (i === null) {
return new mapboxgl.LngLat(0, 0);
}
return (i.item instanceof Waypoint || i.item instanceof TrackPoint) ? i.item.getCoordinates() : new mapboxgl.LngLat(i.item.lon, i.item.lat);
}
}

View File

@@ -260,7 +260,9 @@
options.elevation.power ? 'power' : null options.elevation.power ? 'power' : null
].filter((dataset) => dataset !== null)} ].filter((dataset) => dataset !== null)}
elevationFill={options.elevation.fill} elevationFill={options.elevation.fill}
panelSize={options.elevation.height}
showControls={options.elevation.controls} showControls={options.elevation.controls}
class="py-2"
/> />
{/if} {/if}
</div> </div>

View File

@@ -10,7 +10,7 @@ export type EmbeddingOptions = {
show: boolean; show: boolean;
height: number; height: number;
controls: boolean; controls: boolean;
fill: 'slope' | 'surface' | 'highway' | undefined; fill: 'slope' | 'surface' | undefined;
speed: boolean; speed: boolean;
hr: boolean; hr: boolean;
cad: boolean; cad: boolean;

View File

@@ -142,7 +142,7 @@
let value = selected?.value; let value = selected?.value;
if (value === 'none') { if (value === 'none') {
options.elevation.fill = undefined; options.elevation.fill = undefined;
} else if (value === 'slope' || value === 'surface' || value === 'highway') { } else if (value === 'slope' || value === 'surface') {
options.elevation.fill = value; options.elevation.fill = value;
} }
}} }}
@@ -153,7 +153,6 @@
<Select.Content> <Select.Content>
<Select.Item value="slope">{$_('quantities.slope')}</Select.Item> <Select.Item value="slope">{$_('quantities.slope')}</Select.Item>
<Select.Item value="surface">{$_('quantities.surface')}</Select.Item> <Select.Item value="surface">{$_('quantities.surface')}</Select.Item>
<Select.Item value="highway">{$_('quantities.highway')}</Select.Item>
<Select.Item value="none">{$_('embedding.none')}</Select.Item> <Select.Item value="none">{$_('embedding.none')}</Select.Item>
</Select.Content> </Select.Content>
</Select.Root> </Select.Root>
@@ -166,35 +165,35 @@
<Checkbox id="show-speed" bind:checked={options.elevation.speed} /> <Checkbox id="show-speed" bind:checked={options.elevation.speed} />
<Label for="show-speed" class="flex flex-row items-center gap-1"> <Label for="show-speed" class="flex flex-row items-center gap-1">
<Zap size="16" /> <Zap size="16" />
{$_('quantities.speed')} {$_('chart.show_speed')}
</Label> </Label>
</div> </div>
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-2">
<Checkbox id="show-hr" bind:checked={options.elevation.hr} /> <Checkbox id="show-hr" bind:checked={options.elevation.hr} />
<Label for="show-hr" class="flex flex-row items-center gap-1"> <Label for="show-hr" class="flex flex-row items-center gap-1">
<HeartPulse size="16" /> <HeartPulse size="16" />
{$_('quantities.heartrate')} {$_('chart.show_heartrate')}
</Label> </Label>
</div> </div>
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-2">
<Checkbox id="show-cad" bind:checked={options.elevation.cad} /> <Checkbox id="show-cad" bind:checked={options.elevation.cad} />
<Label for="show-cad" class="flex flex-row items-center gap-1"> <Label for="show-cad" class="flex flex-row items-center gap-1">
<Orbit size="16" /> <Orbit size="16" />
{$_('quantities.cadence')} {$_('chart.show_cadence')}
</Label> </Label>
</div> </div>
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-2">
<Checkbox id="show-temp" bind:checked={options.elevation.temp} /> <Checkbox id="show-temp" bind:checked={options.elevation.temp} />
<Label for="show-temp" class="flex flex-row items-center gap-1"> <Label for="show-temp" class="flex flex-row items-center gap-1">
<Thermometer size="16" /> <Thermometer size="16" />
{$_('quantities.temperature')} {$_('chart.show_temperature')}
</Label> </Label>
</div> </div>
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-2">
<Checkbox id="show-power" bind:checked={options.elevation.power} /> <Checkbox id="show-power" bind:checked={options.elevation.power} />
<Label for="show-power" class="flex flex-row items-center gap-1"> <Label for="show-power" class="flex flex-row items-center gap-1">
<SquareActivity size="16" /> <SquareActivity size="16" />
{$_('quantities.power')} {$_('chart.show_power')}
</Label> </Label>
</div> </div>
</div> </div>

View File

@@ -1,374 +1,388 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
let dragging: Writable<ListLevel | null> = writable(null); let dragging: Writable<ListLevel | null> = writable(null);
let updating = false; let updating = false;
</script> </script>
<script lang="ts"> <script lang="ts">
import { GPXFile, Track, Waypoint, type AnyGPXTreeElement, type GPXTreeElement } from 'gpx'; import {
import { afterUpdate, getContext, onDestroy, onMount } from 'svelte'; buildGPX,
import Sortable from 'sortablejs/Sortable'; GPXFile,
import { getFileIds, settings, type GPXFileWithStatistics } from '$lib/db'; Track,
import { get, writable, type Readable, type Writable } from 'svelte/store'; Waypoint,
import FileListNodeStore from './FileListNodeStore.svelte'; type AnyGPXTreeElement,
import FileListNode from './FileListNode.svelte'; type GPXTreeElement
import { } from 'gpx';
ListFileItem, import { afterUpdate, getContext, onDestroy, onMount } from 'svelte';
ListLevel, import Sortable from 'sortablejs/Sortable';
ListRootItem, import { getFile, getFileIds, settings, type GPXFileWithStatistics } from '$lib/db';
ListWaypointsItem, import { get, writable, type Readable, type Writable } from 'svelte/store';
allowedMoves, import FileListNodeStore from './FileListNodeStore.svelte';
moveItems, import FileListNode from './FileListNode.svelte';
type ListItem import {
} from './FileList'; ListFileItem,
import { selection } from './Selection'; ListLevel,
import { isMac } from '$lib/utils'; ListRootItem,
import { _ } from 'svelte-i18n'; ListWaypointsItem,
allowedMoves,
moveItems,
type ListItem
} from './FileList';
import { selection } from './Selection';
import { isMac } from '$lib/utils';
import { _ } from 'svelte-i18n';
export let node: export let node:
| Map<string, Readable<GPXFileWithStatistics | undefined>> | Map<string, Readable<GPXFileWithStatistics | undefined>>
| GPXTreeElement<AnyGPXTreeElement> | GPXTreeElement<AnyGPXTreeElement>
| Waypoint; | Waypoint;
export let item: ListItem; export let item: ListItem;
export let waypointRoot: boolean = false; export let waypointRoot: boolean = false;
let container: HTMLElement; let container: HTMLElement;
let elements: { [id: string]: HTMLElement } = {}; let elements: { [id: string]: HTMLElement } = {};
let sortableLevel: ListLevel = let sortableLevel: ListLevel =
node instanceof Map node instanceof Map
? ListLevel.FILE ? ListLevel.FILE
: node instanceof GPXFile : node instanceof GPXFile
? waypointRoot ? waypointRoot
? ListLevel.WAYPOINTS ? ListLevel.WAYPOINTS
: item instanceof ListWaypointsItem : item instanceof ListWaypointsItem
? ListLevel.WAYPOINT ? ListLevel.WAYPOINT
: ListLevel.TRACK : ListLevel.TRACK
: node instanceof Track : node instanceof Track
? ListLevel.SEGMENT ? ListLevel.SEGMENT
: ListLevel.WAYPOINT; : ListLevel.WAYPOINT;
let sortable: Sortable; let sortable: Sortable;
let orientation = getContext<'vertical' | 'horizontal'>('orientation'); let orientation = getContext<'vertical' | 'horizontal'>('orientation');
let destroyed = false; let destroyed = false;
let lastUpdateStart = 0; let lastUpdateStart = 0;
function updateToSelection(e) { function updateToSelection(e) {
if (destroyed) { if (destroyed) {
return; return;
} }
lastUpdateStart = Date.now(); lastUpdateStart = Date.now();
setTimeout(() => { setTimeout(() => {
if (Date.now() - lastUpdateStart >= 40) { if (Date.now() - lastUpdateStart >= 40) {
if (updating) { if (updating) {
return; return;
} }
updating = true; updating = true;
// Sortable updates selection // Sortable updates selection
let changed = getChangedIds(); let changed = getChangedIds();
if (changed.length > 0) { if (changed.length > 0) {
selection.update(($selection) => { selection.update(($selection) => {
$selection.clear(); $selection.clear();
Object.entries(elements).forEach(([id, element]) => { Object.entries(elements).forEach(([id, element]) => {
$selection.set( $selection.set(
item.extend(getRealId(id)), item.extend(getRealId(id)),
element.classList.contains('sortable-selected') element.classList.contains('sortable-selected')
); );
}); });
if ( if (
e.originalEvent && e.originalEvent &&
!( !(e.originalEvent.ctrlKey || e.originalEvent.metaKey || e.originalEvent.shiftKey) &&
e.originalEvent.ctrlKey || ($selection.size > 1 || !$selection.has(item.extend(getRealId(changed[0]))))
e.originalEvent.metaKey || ) {
e.originalEvent.shiftKey // Fix bug that sometimes causes a single select to be treated as a multi-select
) && $selection.clear();
($selection.size > 1 || $selection.set(item.extend(getRealId(changed[0])), true);
!$selection.has(item.extend(getRealId(changed[0])))) }
) {
// Fix bug that sometimes causes a single select to be treated as a multi-select
$selection.clear();
$selection.set(item.extend(getRealId(changed[0])), true);
}
return $selection; return $selection;
}); });
} }
updating = false; updating = false;
} }
}, 50); }, 50);
} }
function updateFromSelection() { function updateFromSelection() {
if (destroyed || updating) { if (destroyed || updating) {
return; return;
} }
updating = true; updating = true;
// Selection updates sortable // Selection updates sortable
let changed = getChangedIds(); let changed = getChangedIds();
for (let id of changed) { for (let id of changed) {
let element = elements[id]; let element = elements[id];
if (element) { if (element) {
if ($selection.has(item.extend(id))) { if ($selection.has(item.extend(id))) {
Sortable.utils.select(element); Sortable.utils.select(element);
element.scrollIntoView({ element.scrollIntoView({
behavior: 'smooth', behavior: 'smooth',
block: 'nearest' block: 'nearest'
}); });
} else { } else {
Sortable.utils.deselect(element); Sortable.utils.deselect(element);
} }
} }
} }
updating = false; updating = false;
} }
$: if ($selection) { $: if ($selection) {
updateFromSelection(); updateFromSelection();
} }
const { fileOrder } = settings; const { fileOrder } = settings;
function syncFileOrder() { function syncFileOrder() {
if (!sortable || sortableLevel !== ListLevel.FILE) { if (!sortable || sortableLevel !== ListLevel.FILE) {
return; return;
} }
const currentOrder = sortable.toArray(); const currentOrder = sortable.toArray();
if (currentOrder.length !== $fileOrder.length) { if (currentOrder.length !== $fileOrder.length) {
sortable.sort($fileOrder); sortable.sort($fileOrder);
} else { } else {
for (let i = 0; i < currentOrder.length; i++) { for (let i = 0; i < currentOrder.length; i++) {
if (currentOrder[i] !== $fileOrder[i]) { if (currentOrder[i] !== $fileOrder[i]) {
sortable.sort($fileOrder); sortable.sort($fileOrder);
break; break;
} }
} }
} }
} }
$: if ($fileOrder) { $: if ($fileOrder) {
syncFileOrder(); syncFileOrder();
} }
function createSortable() { function createSortable() {
sortable = Sortable.create(container, { sortable = Sortable.create(container, {
group: { group: {
name: sortableLevel, name: sortableLevel,
pull: allowedMoves[sortableLevel], pull: allowedMoves[sortableLevel],
put: true put: true
}, },
direction: orientation, direction: orientation,
forceAutoScrollFallback: true, forceAutoScrollFallback: true,
multiDrag: true, multiDrag: true,
multiDragKey: isMac() ? 'Meta' : 'Ctrl', multiDragKey: isMac() ? 'Meta' : 'Ctrl',
avoidImplicitDeselect: true, avoidImplicitDeselect: true,
onSelect: updateToSelection, onSelect: updateToSelection,
onDeselect: updateToSelection, onDeselect: updateToSelection,
onStart: () => { onStart: () => {
dragging.set(sortableLevel); dragging.set(sortableLevel);
}, },
onEnd: () => { onEnd: () => {
dragging.set(null); dragging.set(null);
}, },
onSort: (e) => { onSort: (e) => {
if (sortableLevel === ListLevel.FILE) { if (sortableLevel === ListLevel.FILE) {
let newFileOrder = sortable.toArray(); let newFileOrder = sortable.toArray();
if (newFileOrder.length !== get(fileOrder).length) { if (newFileOrder.length !== get(fileOrder).length) {
fileOrder.set(newFileOrder); fileOrder.set(newFileOrder);
} else { } else {
for (let i = 0; i < newFileOrder.length; i++) { for (let i = 0; i < newFileOrder.length; i++) {
if (newFileOrder[i] !== get(fileOrder)[i]) { if (newFileOrder[i] !== get(fileOrder)[i]) {
fileOrder.set(newFileOrder); fileOrder.set(newFileOrder);
break; break;
} }
} }
} }
} }
let fromItem = Sortable.get(e.from)._item; let fromItem = Sortable.get(e.from)._item;
let toItem = Sortable.get(e.to)._item; let toItem = Sortable.get(e.to)._item;
if (item === toItem && !(fromItem instanceof ListRootItem)) { if (item === toItem && !(fromItem instanceof ListRootItem)) {
// Event is triggered on source and destination list, only handle it once // Event is triggered on source and destination list, only handle it once
let fromItems = []; let fromItems = [];
let toItems = []; let toItems = [];
if (Sortable.get(e.from)._waypointRoot) { if (Sortable.get(e.from)._waypointRoot) {
fromItems = [fromItem.extend('waypoints')]; fromItems = [fromItem.extend('waypoints')];
} else { } else {
let oldIndices: number[] = let oldIndices: number[] =
e.oldIndicies.length > 0 e.oldIndicies.length > 0 ? e.oldIndicies.map((i) => i.index) : [e.oldIndex];
? e.oldIndicies.map((i) => i.index) oldIndices = oldIndices.filter((i) => i >= 0);
: [e.oldIndex]; oldIndices.sort((a, b) => a - b);
oldIndices = oldIndices.filter((i) => i >= 0);
oldIndices.sort((a, b) => a - b);
fromItems = oldIndices.map((i) => fromItem.extend(i)); fromItems = oldIndices.map((i) => fromItem.extend(i));
} }
if (Sortable.get(e.from)._waypointRoot && Sortable.get(e.to)._waypointRoot) { if (Sortable.get(e.from)._waypointRoot && Sortable.get(e.to)._waypointRoot) {
toItems = [toItem.extend('waypoints')]; toItems = [toItem.extend('waypoints')];
} else { } else {
if (Sortable.get(e.to)._waypointRoot) { if (Sortable.get(e.to)._waypointRoot) {
toItem = toItem.extend('waypoints'); toItem = toItem.extend('waypoints');
} }
let newIndices: number[] = let newIndices: number[] =
e.newIndicies.length > 0 e.newIndicies.length > 0 ? e.newIndicies.map((i) => i.index) : [e.newIndex];
? e.newIndicies.map((i) => i.index) newIndices = newIndices.filter((i) => i >= 0);
: [e.newIndex]; newIndices.sort((a, b) => a - b);
newIndices = newIndices.filter((i) => i >= 0);
newIndices.sort((a, b) => a - b);
if (toItem instanceof ListRootItem) { if (toItem instanceof ListRootItem) {
let newFileIds = getFileIds(newIndices.length); let newFileIds = getFileIds(newIndices.length);
toItems = newIndices.map((i, index) => { toItems = newIndices.map((i, index) => {
$fileOrder.splice(i, 0, newFileIds[index]); $fileOrder.splice(i, 0, newFileIds[index]);
return item.extend(newFileIds[index]); return item.extend(newFileIds[index]);
}); });
} else { } else {
toItems = newIndices.map((i) => toItem.extend(i)); toItems = newIndices.map((i) => toItem.extend(i));
} }
} }
moveItems(fromItem, toItem, fromItems, toItems); moveItems(fromItem, toItem, fromItems, toItems);
} }
} },
}); setData: function (dataTransfer: DataTransfer, dragEl: HTMLElement) {
Object.defineProperty(sortable, '_item', { if (sortableLevel === ListLevel.FILE) {
value: item, const fileId = dragEl.getAttribute('data-id');
writable: true const file = fileId ? getFile(fileId) : null;
});
Object.defineProperty(sortable, '_waypointRoot', { if (file) {
value: waypointRoot, const data = buildGPX(file);
writable: true dataTransfer.setData(
}); 'DownloadURL',
} `application/gpx+xml:${file.metadata.name}.gpx:data:text/octet-stream;charset=utf-8,${encodeURIComponent(data)}`
);
dataTransfer.dropEffect = 'copy';
dataTransfer.effectAllowed = 'copy';
}
}
}
});
Object.defineProperty(sortable, '_item', {
value: item,
writable: true
});
onMount(() => { Object.defineProperty(sortable, '_waypointRoot', {
createSortable(); value: waypointRoot,
destroyed = false; writable: true
}); });
}
afterUpdate(() => { onMount(() => {
elements = {}; createSortable();
container.childNodes.forEach((element) => { destroyed = false;
if (element instanceof HTMLElement) { });
let attr = element.getAttribute('data-id');
if (attr) {
if (node instanceof Map && !node.has(attr)) {
element.remove();
} else {
elements[attr] = element;
}
}
}
});
syncFileOrder(); afterUpdate(() => {
updateFromSelection(); elements = {};
}); container.childNodes.forEach((element) => {
if (element instanceof HTMLElement) {
let attr = element.getAttribute('data-id');
if (attr) {
if (node instanceof Map && !node.has(attr)) {
element.remove();
} else {
elements[attr] = element;
}
}
}
});
onDestroy(() => { syncFileOrder();
destroyed = true; updateFromSelection();
}); });
function getChangedIds() { onDestroy(() => {
let changed: (string | number)[] = []; destroyed = true;
Object.entries(elements).forEach(([id, element]) => { });
let realId = getRealId(id);
let realItem = item.extend(realId);
let inSelection = get(selection).has(realItem);
let isSelected = element.classList.contains('sortable-selected');
if (inSelection !== isSelected) {
changed.push(realId);
}
});
return changed;
}
function getRealId(id: string | number) { function getChangedIds() {
return sortableLevel === ListLevel.FILE || sortableLevel === ListLevel.WAYPOINTS let changed: (string | number)[] = [];
? id Object.entries(elements).forEach(([id, element]) => {
: parseInt(id); let realId = getRealId(id);
} let realItem = item.extend(realId);
let inSelection = get(selection).has(realItem);
let isSelected = element.classList.contains('sortable-selected');
if (inSelection !== isSelected) {
changed.push(realId);
}
});
return changed;
}
$: canDrop = $dragging !== null && allowedMoves[$dragging].includes(sortableLevel); function getRealId(id: string | number) {
return sortableLevel === ListLevel.FILE || sortableLevel === ListLevel.WAYPOINTS
? id
: parseInt(id);
}
$: canDrop = $dragging !== null && allowedMoves[$dragging].includes(sortableLevel);
</script> </script>
<div <div
bind:this={container} bind:this={container}
class="sortable {orientation} flex {orientation === 'vertical' class="sortable {orientation} flex {orientation === 'vertical'
? 'flex-col' ? 'flex-col'
: 'flex-row gap-1'} {canDrop ? 'min-h-5' : ''}" : 'flex-row gap-1'} {canDrop ? 'min-h-5' : ''}"
> >
{#if node instanceof Map} {#if node instanceof Map}
{#each node as [fileId, file] (fileId)} {#each node as [fileId, file] (fileId)}
<div data-id={fileId}> <div data-id={fileId}>
<FileListNodeStore {file} /> <FileListNodeStore {file} />
</div> </div>
{/each} {/each}
{:else if node instanceof GPXFile} {:else if node instanceof GPXFile}
{#if item instanceof ListWaypointsItem} {#if item instanceof ListWaypointsItem}
{#each node.wpt as wpt, i (wpt)} {#each node.wpt as wpt, i (wpt)}
<div data-id={i} class="ml-1"> <div data-id={i} class="ml-1">
<FileListNode node={wpt} item={item.extend(i)} /> <FileListNode node={wpt} item={item.extend(i)} />
</div> </div>
{/each} {/each}
{:else if waypointRoot} {:else if waypointRoot}
{#if node.wpt.length > 0} {#if node.wpt.length > 0}
<div data-id="waypoints"> <div data-id="waypoints">
<FileListNode {node} item={item.extend('waypoints')} /> <FileListNode {node} item={item.extend('waypoints')} />
</div> </div>
{/if} {/if}
{:else} {:else}
{#each node.children as child, i (child)} {#each node.children as child, i (child)}
<div data-id={i}> <div data-id={i}>
<FileListNode node={child} item={item.extend(i)} /> <FileListNode node={child} item={item.extend(i)} />
</div> </div>
{/each} {/each}
{/if} {/if}
{:else if node instanceof Track} {:else if node instanceof Track}
{#each node.children as child, i (child)} {#each node.children as child, i (child)}
<div data-id={i} class="ml-1"> <div data-id={i} class="ml-1">
<FileListNode node={child} item={item.extend(i)} /> <FileListNode node={child} item={item.extend(i)} />
</div> </div>
{/each} {/each}
{/if} {/if}
</div> </div>
{#if node instanceof GPXFile && item instanceof ListFileItem} {#if node instanceof GPXFile && item instanceof ListFileItem}
{#if !waypointRoot} {#if !waypointRoot}
<svelte:self {node} {item} waypointRoot={true} /> <svelte:self {node} {item} waypointRoot={true} />
{/if} {/if}
{/if} {/if}
<style lang="postcss"> <style lang="postcss">
.sortable > div { .sortable > div {
@apply rounded-md; @apply rounded-md;
@apply h-fit; @apply h-fit;
@apply leading-none; @apply leading-none;
} }
.vertical :global(button) { .vertical :global(button) {
@apply hover:bg-muted; @apply hover:bg-muted;
} }
.vertical :global(.sortable-selected button) { .vertical :global(.sortable-selected button) {
@apply hover:bg-accent; @apply hover:bg-accent;
} }
.vertical :global(.sortable-selected) { .vertical :global(.sortable-selected) {
@apply bg-accent; @apply bg-accent;
} }
.horizontal :global(button) { .horizontal :global(button) {
@apply bg-accent; @apply bg-accent;
@apply hover:bg-muted; @apply hover:bg-muted;
} }
.horizontal :global(.sortable-selected button) { .horizontal :global(.sortable-selected button) {
@apply bg-background; @apply bg-background;
} }
</style> </style>

View File

@@ -49,11 +49,17 @@
gpxLayers, gpxLayers,
map map
} from '$lib/stores'; } from '$lib/stores';
import { GPXTreeElement, Track, type AnyGPXTreeElement, Waypoint, GPXFile } from 'gpx'; import {
GPXTreeElement,
Track,
TrackSegment,
type AnyGPXTreeElement,
Waypoint,
GPXFile
} from 'gpx';
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
import MetadataDialog from './MetadataDialog.svelte'; import MetadataDialog from './MetadataDialog.svelte';
import StyleDialog from './StyleDialog.svelte'; import StyleDialog from './StyleDialog.svelte';
import { waypointPopup } from '$lib/components/gpx-layer/GPXLayerPopup';
export let node: GPXTreeElement<AnyGPXTreeElement> | Waypoint[] | Waypoint; export let node: GPXTreeElement<AnyGPXTreeElement> | Waypoint[] | Waypoint;
export let item: ListItem; export let item: ListItem;
@@ -173,7 +179,7 @@
if (layer && file) { if (layer && file) {
let waypoint = file.wpt[item.getWaypointIndex()]; let waypoint = file.wpt[item.getWaypointIndex()];
if (waypoint) { if (waypoint) {
waypointPopup?.setItem({ item: waypoint, fileId: item.getFileId() }); layer.showWaypointPopup(waypoint);
} }
} }
} }
@@ -182,7 +188,7 @@
if (item instanceof ListWaypointItem) { if (item instanceof ListWaypointItem) {
let layer = gpxLayers.get(item.getFileId()); let layer = gpxLayers.get(item.getFileId());
if (layer) { if (layer) {
waypointPopup?.setItem(null); layer.hideWaypointPopup();
} }
} }
}} }}

View File

@@ -5,8 +5,6 @@ import { get } from "svelte/store";
const { distanceMarkers, distanceUnits } = settings; const { distanceMarkers, distanceUnits } = settings;
const stops = [[100, 0], [50, 7], [25, 8, 10], [10, 10], [5, 11], [1, 13]];
export class DistanceMarkers { export class DistanceMarkers {
map: mapboxgl.Map; map: mapboxgl.Map;
updateBinded: () => void = this.update.bind(this); updateBinded: () => void = this.update.bind(this);
@@ -33,36 +31,30 @@ export class DistanceMarkers {
data: this.getDistanceMarkersGeoJSON() data: this.getDistanceMarkersGeoJSON()
}); });
} }
stops.forEach(([d, minzoom, maxzoom]) => { if (!this.map.getLayer('distance-markers')) {
if (!this.map.getLayer(`distance-markers-${d}`)) { this.map.addLayer({
this.map.addLayer({ id: 'distance-markers',
id: `distance-markers-${d}`, type: 'symbol',
type: 'symbol', source: 'distance-markers',
source: 'distance-markers', layout: {
filter: d === 5 ? ['any', ['==', ['get', 'level'], 5], ['==', ['get', 'level'], 25]] : ['==', ['get', 'level'], d], 'text-field': ['get', 'distance'],
minzoom: minzoom, 'text-size': 14,
maxzoom: maxzoom ?? 24, 'text-font': ['Open Sans Bold'],
layout: { 'text-padding': 20,
'text-field': ['get', 'distance'], },
'text-size': 14, paint: {
'text-font': ['Open Sans Bold'], 'text-color': 'black',
}, 'text-halo-width': 2,
paint: { 'text-halo-color': 'white',
'text-color': 'black', }
'text-halo-width': 2, });
'text-halo-color': 'white', } else {
} this.map.moveLayer('distance-markers');
}); }
} else {
this.map.moveLayer(`distance-markers-${d}`);
}
});
} else { } else {
stops.forEach(([d]) => { if (this.map.getLayer('distance-markers')) {
if (this.map.getLayer(`distance-markers-${d}`)) { this.map.removeLayer('distance-markers');
this.map.removeLayer(`distance-markers-${d}`); }
}
});
} }
} catch (e) { // No reliable way to check if the map is ready to add sources and layers } catch (e) { // No reliable way to check if the map is ready to add sources and layers
return; return;
@@ -81,7 +73,6 @@ export class DistanceMarkers {
for (let i = 0; i < statistics.local.distance.total.length; i++) { for (let i = 0; i < statistics.local.distance.total.length; i++) {
if (statistics.local.distance.total[i] >= currentTargetDistance * (get(distanceUnits) === 'metric' ? 1 : 1.60934)) { if (statistics.local.distance.total[i] >= currentTargetDistance * (get(distanceUnits) === 'metric' ? 1 : 1.60934)) {
let distance = currentTargetDistance.toFixed(0); let distance = currentTargetDistance.toFixed(0);
let [level, minzoom] = stops.find(([d]) => currentTargetDistance % d === 0) ?? [0, 0];
features.push({ features.push({
type: 'Feature', type: 'Feature',
geometry: { geometry: {
@@ -90,8 +81,6 @@ export class DistanceMarkers {
}, },
properties: { properties: {
distance, distance,
level,
minzoom,
} }
} as GeoJSON.Feature); } as GeoJSON.Feature);
currentTargetDistance += 1; currentTargetDistance += 1;

View File

@@ -2,10 +2,11 @@ import { currentTool, map, Tool } from "$lib/stores";
import { settings, type GPXFileWithStatistics, dbUtils } from "$lib/db"; import { settings, type GPXFileWithStatistics, dbUtils } from "$lib/db";
import { get, type Readable } from "svelte/store"; import { get, type Readable } from "svelte/store";
import mapboxgl from "mapbox-gl"; import mapboxgl from "mapbox-gl";
import { waypointPopup, deleteWaypoint, trackpointPopup } from "./GPXLayerPopup"; import { currentPopupWaypoint, deleteWaypoint, waypointPopup } from "./WaypointPopup";
import { addSelectItem, selectItem, selection } from "$lib/components/file-list/Selection"; import { addSelectItem, selectItem, selection } from "$lib/components/file-list/Selection";
import { ListTrackSegmentItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList"; import { ListTrackSegmentItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList";
import { getClosestLinePoint, getElevation, resetCursor, setGrabbingCursor, setPointerCursor, setScissorsCursor } from "$lib/utils"; import type { Waypoint } from "gpx";
import { getElevation, resetCursor, setGrabbingCursor, setPointerCursor, setScissorsCursor } from "$lib/utils";
import { selectedWaypoint } from "$lib/components/toolbar/tools/Waypoint.svelte"; import { selectedWaypoint } from "$lib/components/toolbar/tools/Waypoint.svelte";
import { MapPin, Square } from "lucide-static"; import { MapPin, Square } from "lucide-static";
import { getSymbolKey, symbols } from "$lib/assets/symbols"; import { getSymbolKey, symbols } from "$lib/assets/symbols";
@@ -42,31 +43,6 @@ function decrementColor(color: string) {
} }
} }
const inspectKey = 'Shift';
let inspectKeyDown: KeyDown | null = null;
class KeyDown {
key: string;
down: boolean = false;
constructor(key: string) {
this.key = key;
document.addEventListener('keydown', this.onKeyDown);
document.addEventListener('keyup', this.onKeyUp);
}
onKeyDown = (e: KeyboardEvent) => {
if (e.key === this.key) {
this.down = true;
}
}
onKeyUp = (e: KeyboardEvent) => {
if (e.key === this.key) {
this.down = false;
}
}
isDown() {
return this.down;
}
}
function getMarkerForSymbol(symbol: string | undefined, layerColor: string) { function getMarkerForSymbol(symbol: string | undefined, layerColor: string) {
let symbolSvg = symbol ? symbols[symbol]?.iconSvg : undefined; let symbolSvg = symbol ? symbols[symbol]?.iconSvg : undefined;
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
@@ -104,9 +80,9 @@ export class GPXLayer {
updateBinded: () => void = this.update.bind(this); updateBinded: () => void = this.update.bind(this);
layerOnMouseEnterBinded: (e: any) => void = this.layerOnMouseEnter.bind(this); layerOnMouseEnterBinded: (e: any) => void = this.layerOnMouseEnter.bind(this);
layerOnMouseLeaveBinded: () => void = this.layerOnMouseLeave.bind(this); layerOnMouseLeaveBinded: () => void = this.layerOnMouseLeave.bind(this);
layerOnMouseMoveBinded: (e: any) => void = this.layerOnMouseMove.bind(this);
layerOnClickBinded: (e: any) => void = this.layerOnClick.bind(this); layerOnClickBinded: (e: any) => void = this.layerOnClick.bind(this);
layerOnContextMenuBinded: (e: any) => void = this.layerOnContextMenu.bind(this); layerOnContextMenuBinded: (e: any) => void = this.layerOnContextMenu.bind(this);
maybeHideWaypointPopupBinded: (e: any) => void = this.maybeHideWaypointPopup.bind(this);
constructor(map: mapboxgl.Map, fileId: string, file: Readable<GPXFileWithStatistics | undefined>) { constructor(map: mapboxgl.Map, fileId: string, file: Readable<GPXFileWithStatistics | undefined>) {
this.map = map; this.map = map;
@@ -137,10 +113,6 @@ export class GPXLayer {
this.draggable = get(currentTool) === Tool.WAYPOINT; this.draggable = get(currentTool) === Tool.WAYPOINT;
this.map.on('style.import.load', this.updateBinded); this.map.on('style.import.load', this.updateBinded);
if (inspectKeyDown === null) {
inspectKeyDown = new KeyDown(inspectKey);
}
} }
update() { update() {
@@ -185,7 +157,6 @@ export class GPXLayer {
this.map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded); this.map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded);
this.map.on('mouseenter', this.fileId, this.layerOnMouseEnterBinded); this.map.on('mouseenter', this.fileId, this.layerOnMouseEnterBinded);
this.map.on('mouseleave', this.fileId, this.layerOnMouseLeaveBinded); this.map.on('mouseleave', this.fileId, this.layerOnMouseLeaveBinded);
this.map.on('mousemove', this.fileId, this.layerOnMouseMoveBinded);
} }
if (get(directionMarkers)) { if (get(directionMarkers)) {
@@ -253,11 +224,11 @@ export class GPXLayer {
}).setLngLat(waypoint.getCoordinates()); }).setLngLat(waypoint.getCoordinates());
Object.defineProperty(marker, '_waypoint', { value: waypoint, writable: true }); Object.defineProperty(marker, '_waypoint', { value: waypoint, writable: true });
let dragEndTimestamp = 0; let dragEndTimestamp = 0;
marker.getElement().addEventListener('mousemove', (e) => { marker.getElement().addEventListener('mouseover', (e) => {
if (marker._isDragging) { if (marker._isDragging) {
return; return;
} }
waypointPopup?.setItem({ item: marker._waypoint, fileId: this.fileId }); this.showWaypointPopup(marker._waypoint);
e.stopPropagation(); e.stopPropagation();
}); });
marker.getElement().addEventListener('click', (e) => { marker.getElement().addEventListener('click', (e) => {
@@ -280,14 +251,14 @@ export class GPXLayer {
} else if (get(currentTool) === Tool.WAYPOINT) { } else if (get(currentTool) === Tool.WAYPOINT) {
selectedWaypoint.set([marker._waypoint, this.fileId]); selectedWaypoint.set([marker._waypoint, this.fileId]);
} else { } else {
waypointPopup?.setItem({ item: marker._waypoint, fileId: this.fileId }); this.showWaypointPopup(marker._waypoint);
} }
e.stopPropagation(); e.stopPropagation();
}); });
marker.on('dragstart', () => { marker.on('dragstart', () => {
setGrabbingCursor(); setGrabbingCursor();
marker.getElement().style.cursor = 'grabbing'; marker.getElement().style.cursor = 'grabbing';
waypointPopup?.hide(); this.hideWaypointPopup();
}); });
marker.on('dragend', (e) => { marker.on('dragend', (e) => {
resetCursor(); resetCursor();
@@ -336,7 +307,6 @@ export class GPXLayer {
this.map.off('contextmenu', this.fileId, this.layerOnContextMenuBinded); this.map.off('contextmenu', this.fileId, this.layerOnContextMenuBinded);
this.map.off('mouseenter', this.fileId, this.layerOnMouseEnterBinded); this.map.off('mouseenter', this.fileId, this.layerOnMouseEnterBinded);
this.map.off('mouseleave', this.fileId, this.layerOnMouseLeaveBinded); this.map.off('mouseleave', this.fileId, this.layerOnMouseLeaveBinded);
this.map.off('mousemove', this.fileId, this.layerOnMouseMoveBinded);
this.map.off('style.import.load', this.updateBinded); this.map.off('style.import.load', this.updateBinded);
if (this.map.getLayer(this.fileId + '-direction')) { if (this.map.getLayer(this.fileId + '-direction')) {
@@ -383,19 +353,6 @@ export class GPXLayer {
resetCursor(); resetCursor();
} }
layerOnMouseMove(e: any) {
if (inspectKeyDown?.isDown()) {
let trackIndex = e.features[0].properties.trackIndex;
let segmentIndex = e.features[0].properties.segmentIndex;
const file = get(this.file)?.file;
if (file) {
const closest = getClosestLinePoint(file.trk[trackIndex].trkseg[segmentIndex].trkpt, { lat: e.lngLat.lat, lon: e.lngLat.lng });
trackpointPopup?.setItem({ item: closest, fileId: this.fileId });
}
}
}
layerOnClick(e: any) { layerOnClick(e: any) {
if (get(currentTool) === Tool.ROUTING && get(selection).hasAnyChildren(new ListRootItem(), true, ['waypoints'])) { if (get(currentTool) === Tool.ROUTING && get(selection).hasAnyChildren(new ListRootItem(), true, ['waypoints'])) {
return; return;
@@ -434,6 +391,43 @@ export class GPXLayer {
} }
} }
showWaypointPopup(waypoint: Waypoint) {
if (get(currentPopupWaypoint) !== null) {
this.hideWaypointPopup();
}
let marker = this.markers[waypoint._data.index];
if (marker) {
currentPopupWaypoint.set([waypoint, this.fileId]);
marker.setPopup(waypointPopup);
marker.togglePopup();
this.map.on('mousemove', this.maybeHideWaypointPopupBinded);
}
}
maybeHideWaypointPopup(e: any) {
let waypoint = get(currentPopupWaypoint)?.[0];
if (waypoint) {
let marker = this.markers[waypoint._data.index];
if (marker) {
if (this.map.project(marker.getLngLat()).dist(this.map.project(e.lngLat)) > 100) {
this.hideWaypointPopup();
}
} else {
this.hideWaypointPopup();
}
}
}
hideWaypointPopup() {
let waypoint = get(currentPopupWaypoint)?.[0];
if (waypoint) {
let marker = this.markers[waypoint._data.index];
marker?.getPopup()?.remove();
currentPopupWaypoint.set(null);
this.map.off('mousemove', this.maybeHideWaypointPopupBinded);
}
}
getGeoJSON(): GeoJSON.FeatureCollection { getGeoJSON(): GeoJSON.FeatureCollection {
let file = get(this.file)?.file; let file = get(this.file)?.file;
if (!file) { if (!file) {

View File

@@ -1,44 +0,0 @@
import { dbUtils } from "$lib/db";
import { MapPopup } from "$lib/components/MapPopup";
export let waypointPopup: MapPopup | null = null;
export let trackpointPopup: MapPopup | null = null;
export function createPopups(map: mapboxgl.Map) {
removePopups();
waypointPopup = new MapPopup(map, {
closeButton: false,
focusAfterOpen: false,
maxWidth: undefined,
offset: {
'top': [0, 0],
'top-left': [0, 0],
'top-right': [0, 0],
'bottom': [0, -30],
'bottom-left': [0, -30],
'bottom-right': [0, -30],
'left': [10, -15],
'right': [-10, -15],
},
});
trackpointPopup = new MapPopup(map, {
closeButton: false,
focusAfterOpen: false,
maxWidth: undefined,
});
}
export function removePopups() {
if (waypointPopup !== null) {
waypointPopup.remove();
waypointPopup = null;
}
if (trackpointPopup !== null) {
trackpointPopup.remove();
trackpointPopup = null;
}
}
export function deleteWaypoint(fileId: string, waypointIndex: number) {
dbUtils.applyToFile(fileId, (file) => file.replaceWaypoints(waypointIndex, waypointIndex, []));
}

View File

@@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import { map, gpxLayers } from '$lib/stores'; import { map, gpxLayers } from '$lib/stores';
import { GPXLayer } from './GPXLayer'; import { GPXLayer } from './GPXLayer';
import WaypointPopup from './WaypointPopup.svelte';
import { fileObservers } from '$lib/db'; import { fileObservers } from '$lib/db';
import { DistanceMarkers } from './DistanceMarkers'; import { DistanceMarkers } from './DistanceMarkers';
import { StartEndMarkers } from './StartEndMarkers'; import { StartEndMarkers } from './StartEndMarkers';
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { createPopups, removePopups } from './GPXLayerPopup';
let distanceMarkers: DistanceMarkers | undefined = undefined; let distanceMarkers: DistanceMarkers | undefined = undefined;
let startEndMarkers: StartEndMarkers | undefined = undefined; let startEndMarkers: StartEndMarkers | undefined = undefined;
@@ -35,7 +35,6 @@
if (startEndMarkers) { if (startEndMarkers) {
startEndMarkers.remove(); startEndMarkers.remove();
} }
createPopups($map);
distanceMarkers = new DistanceMarkers($map); distanceMarkers = new DistanceMarkers($map);
startEndMarkers = new StartEndMarkers($map); startEndMarkers = new StartEndMarkers($map);
} }
@@ -43,14 +42,17 @@
onDestroy(() => { onDestroy(() => {
gpxLayers.forEach((layer) => layer.remove()); gpxLayers.forEach((layer) => layer.remove());
gpxLayers.clear(); gpxLayers.clear();
removePopups();
if (distanceMarkers) { if (distanceMarkers) {
distanceMarkers.remove(); distanceMarkers.remove();
distanceMarkers = undefined; distanceMarkers = undefined;
} }
if (startEndMarkers) { if (startEndMarkers) {
startEndMarkers.remove(); startEndMarkers.remove();
startEndMarkers = undefined; startEndMarkers = undefined;
} }
}); });
</script> </script>
<WaypointPopup />

View File

@@ -1,36 +0,0 @@
<script lang="ts">
import type { TrackPoint } from 'gpx';
import type { PopupItem } from '$lib/components/MapPopup';
import * as Card from '$lib/components/ui/card';
import WithUnits from '$lib/components/WithUnits.svelte';
import { Compass, Mountain, Timer } from 'lucide-svelte';
import { df } from '$lib/utils';
export let trackpoint: PopupItem<TrackPoint>;
</script>
<Card.Root class="border-none shadow-md text-base p-2">
<Card.Header class="p-0">
<Card.Title class="text-md"></Card.Title>
</Card.Header>
<Card.Content class="flex flex-col p-0 text-xs gap-1">
<div class="flex flex-row items-center gap-1">
<Compass size="14" />
{trackpoint.item.getLatitude().toFixed(6)}&deg; {trackpoint.item
.getLongitude()
.toFixed(6)}&deg;
</div>
{#if trackpoint.item.ele !== undefined}
<div class="flex flex-row items-center gap-1">
<Mountain size="14" />
<WithUnits value={trackpoint.item.ele} type="elevation" />
</div>
{/if}
{#if trackpoint.item.time}
<div class="flex flex-row items-center gap-1">
<Timer size="14" />
{df.format(trackpoint.item.time)}
</div>
{/if}
</Card.Content>
</Card.Root>

View File

@@ -2,20 +2,23 @@
import * as Card from '$lib/components/ui/card'; import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import Shortcut from '$lib/components/Shortcut.svelte'; import Shortcut from '$lib/components/Shortcut.svelte';
import { deleteWaypoint } from './GPXLayerPopup'; import { waypointPopup, currentPopupWaypoint, deleteWaypoint } from './WaypointPopup';
import WithUnits from '$lib/components/WithUnits.svelte'; import WithUnits from '$lib/components/WithUnits.svelte';
import { Dot, ExternalLink, Trash2 } from 'lucide-svelte'; import { Dot, ExternalLink, Trash2 } from 'lucide-svelte';
import { onMount } from 'svelte';
import { Tool, currentTool } from '$lib/stores'; import { Tool, currentTool } from '$lib/stores';
import { getSymbolKey, symbols } from '$lib/assets/symbols'; import { getSymbolKey, symbols } from '$lib/assets/symbols';
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
import sanitizeHtml from 'sanitize-html'; import sanitizeHtml from 'sanitize-html';
import type { Waypoint } from 'gpx';
import type { PopupItem } from '$lib/components/MapPopup';
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
export let waypoint: PopupItem<Waypoint>; let popupElement: HTMLDivElement;
$: symbolKey = waypoint ? getSymbolKey(waypoint.item.sym) : undefined; onMount(() => {
waypointPopup.setDOMContent(popupElement);
popupElement.classList.remove('hidden');
});
$: symbolKey = $currentPopupWaypoint ? getSymbolKey($currentPopupWaypoint[0].sym) : undefined;
function sanitize(text: string | undefined): string { function sanitize(text: string | undefined): string {
if (text === undefined) { if (text === undefined) {
@@ -31,63 +34,68 @@
} }
</script> </script>
<Card.Root class="border-none shadow-md text-base p-2 max-w-[50dvw]"> <div bind:this={popupElement} class="hidden">
<Card.Header class="p-0"> {#if $currentPopupWaypoint}
<Card.Title class="text-md"> <Card.Root class="border-none shadow-md text-base max-w-80 p-2">
{#if waypoint.item.link && waypoint.item.link.attributes && waypoint.item.link.attributes.href} <Card.Header class="p-0">
<a href={waypoint.item.link.attributes.href} target="_blank"> <Card.Title class="text-md">
{waypoint.item.name ?? waypoint.item.link.attributes.href} {#if $currentPopupWaypoint[0].link && $currentPopupWaypoint[0].link.attributes && $currentPopupWaypoint[0].link.attributes.href}
<ExternalLink size="12" class="inline-block mb-1.5" /> <a href={$currentPopupWaypoint[0].link.attributes.href} target="_blank">
</a> {$currentPopupWaypoint[0].name ?? $currentPopupWaypoint[0].link.attributes.href}
{:else} <ExternalLink size="12" class="inline-block mb-1.5" />
{waypoint.item.name ?? $_('gpx.waypoint')} </a>
{/if}
</Card.Title>
</Card.Header>
<Card.Content class="flex flex-col text-sm p-0">
<div class="flex flex-row items-center text-muted-foreground text-xs whitespace-nowrap">
{#if symbolKey}
<span>
{#if symbols[symbolKey].icon}
<svelte:component
this={symbols[symbolKey].icon}
size="12"
class="inline-block mb-0.5"
/>
{:else} {:else}
<span class="w-4 inline-block" /> {$currentPopupWaypoint[0].name ?? $_('gpx.waypoint')}
{/if} {/if}
{$_(`gpx.symbol.${symbolKey}`)} </Card.Title>
</span> </Card.Header>
<Dot size="16" /> <Card.Content class="flex flex-col p-0 text-sm">
{/if} <div class="flex flex-row items-center text-muted-foreground text-xs whitespace-nowrap">
{waypoint.item.getLatitude().toFixed(6)}&deg; {waypoint.item.getLongitude().toFixed(6)}&deg; {#if symbolKey}
{#if waypoint.item.ele !== undefined} <span>
<Dot size="16" /> {#if symbols[symbolKey].icon}
<WithUnits value={waypoint.item.ele} type="elevation" /> <svelte:component
{/if} this={symbols[symbolKey].icon}
</div> size="12"
<ScrollArea class="flex flex-col" viewportClasses="max-h-[30dvh]"> class="inline-block mb-0.5"
{#if waypoint.item.desc} />
<span class="whitespace-pre-wrap">{@html sanitize(waypoint.item.desc)}</span> {:else}
{/if} <span class="w-4 inline-block" />
{#if waypoint.item.cmt && waypoint.item.cmt !== waypoint.item.desc} {/if}
<span class="whitespace-pre-wrap">{@html sanitize(waypoint.item.cmt)}</span> {$_(`gpx.symbol.${symbolKey}`)}
{/if} </span>
</ScrollArea> <Dot size="16" />
{#if $currentTool === Tool.WAYPOINT} {/if}
<Button {$currentPopupWaypoint[0].getLatitude().toFixed(6)}&deg; {$currentPopupWaypoint[0]
class="mt-2 w-full px-2 py-1 h-8 justify-start" .getLongitude()
variant="outline" .toFixed(6)}&deg;
on:click={() => deleteWaypoint(waypoint.fileId, waypoint.item._data.index)} {#if $currentPopupWaypoint[0].ele !== undefined}
> <Dot size="16" />
<Trash2 size="16" class="mr-1" /> <WithUnits value={$currentPopupWaypoint[0].ele} type="elevation" />
{$_('menu.delete')} {/if}
<Shortcut shift={true} click={true} /> </div>
</Button> {#if $currentPopupWaypoint[0].desc}
{/if} <span class="whitespace-pre-wrap">{@html sanitize($currentPopupWaypoint[0].desc)}</span>
</Card.Content> {/if}
</Card.Root> {#if $currentPopupWaypoint[0].cmt && $currentPopupWaypoint[0].cmt !== $currentPopupWaypoint[0].desc}
<span class="whitespace-pre-wrap">{@html sanitize($currentPopupWaypoint[0].cmt)}</span>
{/if}
{#if $currentTool === Tool.WAYPOINT}
<Button
class="mt-2 w-full px-2 py-1 h-8 justify-start"
variant="outline"
on:click={() =>
deleteWaypoint($currentPopupWaypoint[1], $currentPopupWaypoint[0]._data.index)}
>
<Trash2 size="16" class="mr-1" />
{$_('menu.delete')}
<Shortcut shift={true} click={true} />
</Button>
{/if}
</Card.Content>
</Card.Root>
{/if}
</div>
<style lang="postcss"> <style lang="postcss">
div :global(a) { div :global(a) {

View File

@@ -0,0 +1,25 @@
import { dbUtils } from "$lib/db";
import type { Waypoint } from "gpx";
import mapboxgl from "mapbox-gl";
import { writable } from "svelte/store";
export const currentPopupWaypoint = writable<[Waypoint, string] | null>(null);
export const waypointPopup = new mapboxgl.Popup({
closeButton: false,
maxWidth: undefined,
offset: {
'top': [0, 0],
'top-left': [0, 0],
'top-right': [0, 0],
'bottom': [0, -30],
'bottom-left': [0, -30],
'bottom-right': [0, -30],
'left': [0, 0],
'right': [0, 0]
},
});
export function deleteWaypoint(fileId: string, waypointIndex: number) {
dbUtils.applyToFile(fileId, (file) => file.replaceWaypoints(waypointIndex, waypointIndex, []));
}

View File

@@ -1,422 +1,435 @@
<script lang="ts"> <script lang="ts">
import * as Card from '$lib/components/ui/card'; import * as Card from '$lib/components/ui/card';
import { Input } from '$lib/components/ui/input'; import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label'; import { Label } from '$lib/components/ui/label';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import { Separator } from '$lib/components/ui/separator'; import { Separator } from '$lib/components/ui/separator';
import * as RadioGroup from '$lib/components/ui/radio-group'; import * as RadioGroup from '$lib/components/ui/radio-group';
import { import {
CirclePlus, CirclePlus,
CircleX, CircleX,
Minus, Minus,
Pencil, Pencil,
Plus, Plus,
Save, Save,
Trash2, Trash2,
Move, Move,
Map, Map,
Layers2 Layers2
} from 'lucide-svelte'; } from 'lucide-svelte';
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
import { settings } from '$lib/db'; import { settings } from '$lib/db';
import { defaultBasemap, type CustomLayer } from '$lib/assets/layers'; import { defaultBasemap, type CustomLayer } from '$lib/assets/layers';
import { map } from '$lib/stores'; import { map } from '$lib/stores';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import Sortable from 'sortablejs/Sortable'; import Sortable from 'sortablejs/Sortable';
import { customBasemapUpdate } from './utils'; import { customBasemapUpdate } from './utils';
const { const {
customLayers, customLayers,
selectedBasemapTree, selectedBasemapTree,
selectedOverlayTree, selectedOverlayTree,
currentBasemap, currentBasemap,
previousBasemap, previousBasemap,
currentOverlays, currentOverlays,
previousOverlays, previousOverlays,
customBasemapOrder, customBasemapOrder,
customOverlayOrder customOverlayOrder
} = settings; } = settings;
let name: string = ''; let name: string = '';
let tileUrls: string[] = ['']; let tileUrls: string[] = [''];
let maxZoom: number = 20; let maxZoom: number = 20;
let layerType: 'basemap' | 'overlay' = 'basemap'; let layerType: 'basemap' | 'overlay' = 'basemap';
let resourceType: 'raster' | 'vector' = 'raster'; let resourceType: 'raster' | 'vector' = 'raster';
let basemapContainer: HTMLElement; let basemapContainer: HTMLElement;
let overlayContainer: HTMLElement; let overlayContainer: HTMLElement;
let basemapSortable: Sortable; let basemapSortable: Sortable;
let overlaySortable: Sortable; let overlaySortable: Sortable;
onMount(() => { onMount(() => {
if ($customBasemapOrder.length === 0) { if ($customBasemapOrder.length === 0) {
$customBasemapOrder = Object.keys($customLayers).filter( $customBasemapOrder = Object.keys($customLayers).filter(
(id) => $customLayers[id].layerType === 'basemap' (id) => $customLayers[id].layerType === 'basemap'
); );
} }
if ($customOverlayOrder.length === 0) { if ($customOverlayOrder.length === 0) {
$customOverlayOrder = Object.keys($customLayers).filter( $customOverlayOrder = Object.keys($customLayers).filter(
(id) => $customLayers[id].layerType === 'overlay' (id) => $customLayers[id].layerType === 'overlay'
); );
} }
basemapSortable = Sortable.create(basemapContainer, { basemapSortable = Sortable.create(basemapContainer, {
onSort: (e) => { onSort: (e) => {
$customBasemapOrder = basemapSortable.toArray(); $customBasemapOrder = basemapSortable.toArray();
$selectedBasemapTree.basemaps['custom'] = $customBasemapOrder.reduce((acc, id) => { $selectedBasemapTree.basemaps['custom'] = $customBasemapOrder.reduce((acc, id) => {
acc[id] = true; acc[id] = true;
return acc; return acc;
}, {}); }, {});
} }
}); });
overlaySortable = Sortable.create(overlayContainer, { overlaySortable = Sortable.create(overlayContainer, {
onSort: (e) => { onSort: (e) => {
$customOverlayOrder = overlaySortable.toArray(); $customOverlayOrder = overlaySortable.toArray();
$selectedOverlayTree.overlays['custom'] = $customOverlayOrder.reduce((acc, id) => { $selectedOverlayTree.overlays['custom'] = $customOverlayOrder.reduce((acc, id) => {
acc[id] = true; acc[id] = true;
return acc; return acc;
}, {}); }, {});
} }
}); });
basemapSortable.sort($customBasemapOrder); basemapSortable.sort($customBasemapOrder);
overlaySortable.sort($customOverlayOrder); overlaySortable.sort($customOverlayOrder);
}); });
onDestroy(() => { onDestroy(() => {
basemapSortable.destroy(); basemapSortable.destroy();
overlaySortable.destroy(); overlaySortable.destroy();
}); });
$: if (tileUrls[0].length > 0) { $: if (tileUrls[0].length > 0) {
if ( if (
tileUrls[0].includes('.json') || tileUrls[0].includes('.json') ||
(tileUrls[0].includes('api.mapbox.com/styles') && !tileUrls[0].includes('tiles')) (tileUrls[0].includes('api.mapbox.com/styles') && !tileUrls[0].includes('tiles'))
) { ) {
resourceType = 'vector'; resourceType = 'vector';
} else { } else {
resourceType = 'raster'; resourceType = 'raster';
} }
} }
function createLayer() { function createLayer() {
if (selectedLayerId && $customLayers[selectedLayerId].layerType !== layerType) { if (selectedLayerId && $customLayers[selectedLayerId].layerType !== layerType) {
deleteLayer(selectedLayerId); deleteLayer(selectedLayerId);
} }
if (typeof maxZoom === 'string') { if (typeof maxZoom === 'string') {
maxZoom = parseInt(maxZoom); maxZoom = parseInt(maxZoom);
} }
let is512 = tileUrls.some((url) => url.includes('512'));
let layerId = selectedLayerId ?? getLayerId(); let layerId = selectedLayerId ?? getLayerId();
let layer: CustomLayer = { let layer: CustomLayer = {
id: layerId, id: layerId,
name: name, name: name,
tileUrls: tileUrls.map((url) => decodeURI(url.trim())), tileUrls: tileUrls.map((url) => decodeURI(url.trim())),
maxZoom: maxZoom, maxZoom: maxZoom,
layerType: layerType, layerType: layerType,
resourceType: resourceType, resourceType: resourceType,
value: '' value: ''
}; };
if (resourceType === 'vector') { if (resourceType === 'vector') {
layer.value = layer.tileUrls[0]; layer.value = layer.tileUrls[0];
} else { } else {
layer.value = { layer.value = {
version: 8, version: 8,
sources: { sources: {
[layerId]: { [layerId]: {
type: 'raster', type: 'raster',
tiles: layer.tileUrls, tiles: layer.tileUrls,
tileSize: is512 ? 512 : 256, tileSize: 256,
maxzoom: maxZoom maxzoom: maxZoom
} }
}, },
layers: [ layers: [
{ {
id: layerId, id: layerId,
type: 'raster', type: 'raster',
source: layerId source: layerId
} }
] ]
}; };
} }
$customLayers[layerId] = layer; $customLayers[layerId] = layer;
addLayer(layerId); addLayer(layerId);
selectedLayerId = undefined; selectedLayerId = undefined;
setDataFromSelectedLayer(); setDataFromSelectedLayer();
} }
function getLayerId() { function getLayerId() {
for (let id = 0; ; id++) { for (let id = 0; ; id++) {
if (!$customLayers.hasOwnProperty(`custom-${id}`)) { if (!$customLayers.hasOwnProperty(`custom-${id}`)) {
return `custom-${id}`; return `custom-${id}`;
} }
} }
} }
function addLayer(layerId: string) { function addLayer(layerId: string) {
if (layerType === 'basemap') { if (layerType === 'basemap') {
selectedBasemapTree.update(($tree) => { selectedBasemapTree.update(($tree) => {
if (!$tree.basemaps.hasOwnProperty('custom')) { if (!$tree.basemaps.hasOwnProperty('custom')) {
$tree.basemaps['custom'] = {}; $tree.basemaps['custom'] = {};
} }
$tree.basemaps['custom'][layerId] = true; $tree.basemaps['custom'][layerId] = true;
return $tree; return $tree;
}); });
if ($currentBasemap === layerId) { if ($currentBasemap === layerId) {
$customBasemapUpdate++; $customBasemapUpdate++;
} else { } else {
$currentBasemap = layerId; $currentBasemap = layerId;
} }
if (!$customBasemapOrder.includes(layerId)) { if (!$customBasemapOrder.includes(layerId)) {
$customBasemapOrder = [...$customBasemapOrder, layerId]; $customBasemapOrder = [...$customBasemapOrder, layerId];
} }
} else { } else {
selectedOverlayTree.update(($tree) => { selectedOverlayTree.update(($tree) => {
if (!$tree.overlays.hasOwnProperty('custom')) { if (!$tree.overlays.hasOwnProperty('custom')) {
$tree.overlays['custom'] = {}; $tree.overlays['custom'] = {};
} }
$tree.overlays['custom'][layerId] = true; $tree.overlays['custom'][layerId] = true;
return $tree; return $tree;
}); });
if ( if (
$currentOverlays.overlays['custom'] && $currentOverlays.overlays['custom'] &&
$currentOverlays.overlays['custom'][layerId] && $currentOverlays.overlays['custom'][layerId] &&
$map $map
) { ) {
try { try {
$map.removeImport(layerId); $map.removeImport(layerId);
} catch (e) { } catch (e) {
// No reliable way to check if the map is ready to remove sources and layers // No reliable way to check if the map is ready to remove sources and layers
} }
} }
if (!$currentOverlays.overlays.hasOwnProperty('custom')) { if (!$currentOverlays.overlays.hasOwnProperty('custom')) {
$currentOverlays.overlays['custom'] = {}; $currentOverlays.overlays['custom'] = {};
} }
$currentOverlays.overlays['custom'][layerId] = true; $currentOverlays.overlays['custom'][layerId] = true;
if (!$customOverlayOrder.includes(layerId)) { if (!$customOverlayOrder.includes(layerId)) {
$customOverlayOrder = [...$customOverlayOrder, layerId]; $customOverlayOrder = [...$customOverlayOrder, layerId];
} }
} }
} }
function tryDeleteLayer(node: any, id: string): any { function tryDeleteLayer(node: any, id: string): any {
if (node.hasOwnProperty(id)) { if (node.hasOwnProperty(id)) {
delete node[id]; delete node[id];
} }
return node; return node;
} }
function deleteLayer(layerId: string) { function deleteLayer(layerId: string) {
let layer = $customLayers[layerId]; let layer = $customLayers[layerId];
if (layer.layerType === 'basemap') { if (layer.layerType === 'basemap') {
if (layerId === $currentBasemap) { if (layerId === $currentBasemap) {
$currentBasemap = defaultBasemap; $currentBasemap = defaultBasemap;
} }
if (layerId === $previousBasemap) { if (layerId === $previousBasemap) {
$previousBasemap = defaultBasemap; $previousBasemap = defaultBasemap;
} }
$selectedBasemapTree.basemaps['custom'] = tryDeleteLayer( $selectedBasemapTree.basemaps['custom'] = tryDeleteLayer(
$selectedBasemapTree.basemaps['custom'], $selectedBasemapTree.basemaps['custom'],
layerId layerId
); );
if (Object.keys($selectedBasemapTree.basemaps['custom']).length === 0) { if (Object.keys($selectedBasemapTree.basemaps['custom']).length === 0) {
$selectedBasemapTree.basemaps = tryDeleteLayer($selectedBasemapTree.basemaps, 'custom'); $selectedBasemapTree.basemaps = tryDeleteLayer(
} $selectedBasemapTree.basemaps,
$customBasemapOrder = $customBasemapOrder.filter((id) => id !== layerId); 'custom'
} else { );
$currentOverlays.overlays['custom'][layerId] = false; }
if ($previousOverlays.overlays['custom']) { $customBasemapOrder = $customBasemapOrder.filter((id) => id !== layerId);
$previousOverlays.overlays['custom'] = tryDeleteLayer( } else {
$previousOverlays.overlays['custom'], $currentOverlays.overlays['custom'][layerId] = false;
layerId if ($previousOverlays.overlays['custom']) {
); $previousOverlays.overlays['custom'] = tryDeleteLayer(
} $previousOverlays.overlays['custom'],
layerId
);
}
$selectedOverlayTree.overlays['custom'] = tryDeleteLayer( $selectedOverlayTree.overlays['custom'] = tryDeleteLayer(
$selectedOverlayTree.overlays['custom'], $selectedOverlayTree.overlays['custom'],
layerId layerId
); );
if (Object.keys($selectedOverlayTree.overlays['custom']).length === 0) { if (Object.keys($selectedOverlayTree.overlays['custom']).length === 0) {
$selectedOverlayTree.overlays = tryDeleteLayer($selectedOverlayTree.overlays, 'custom'); $selectedOverlayTree.overlays = tryDeleteLayer(
} $selectedOverlayTree.overlays,
$customOverlayOrder = $customOverlayOrder.filter((id) => id !== layerId); 'custom'
);
}
$customOverlayOrder = $customOverlayOrder.filter((id) => id !== layerId);
if ( if (
$currentOverlays.overlays['custom'] && $currentOverlays.overlays['custom'] &&
$currentOverlays.overlays['custom'][layerId] && $currentOverlays.overlays['custom'][layerId] &&
$map $map
) { ) {
try { try {
$map.removeImport(layerId); $map.removeImport(layerId);
} catch (e) { } catch (e) {
// No reliable way to check if the map is ready to remove sources and layers // No reliable way to check if the map is ready to remove sources and layers
} }
} }
} }
$customLayers = tryDeleteLayer($customLayers, layerId); $customLayers = tryDeleteLayer($customLayers, layerId);
} }
let selectedLayerId: string | undefined = undefined; let selectedLayerId: string | undefined = undefined;
function setDataFromSelectedLayer() { function setDataFromSelectedLayer() {
if (selectedLayerId) { if (selectedLayerId) {
const layer = $customLayers[selectedLayerId]; const layer = $customLayers[selectedLayerId];
name = layer.name; name = layer.name;
tileUrls = layer.tileUrls; tileUrls = layer.tileUrls;
maxZoom = layer.maxZoom; maxZoom = layer.maxZoom;
layerType = layer.layerType; layerType = layer.layerType;
resourceType = layer.resourceType; resourceType = layer.resourceType;
} else { } else {
name = ''; name = '';
tileUrls = ['']; tileUrls = [''];
maxZoom = 20; maxZoom = 20;
layerType = 'basemap'; layerType = 'basemap';
resourceType = 'raster'; resourceType = 'raster';
} }
} }
$: selectedLayerId, setDataFromSelectedLayer(); $: selectedLayerId, setDataFromSelectedLayer();
</script> </script>
<div class="flex flex-col"> <div class="flex flex-col">
{#if $customBasemapOrder.length > 0} {#if $customBasemapOrder.length > 0}
<div class="flex flex-row items-center gap-1 font-semibold mb-2"> <div class="flex flex-row items-center gap-1 font-semibold mb-2">
<Map size="16" /> <Map size="16" />
{$_('layers.label.basemaps')} {$_('layers.label.basemaps')}
<div class="grow"> <div class="grow">
<Separator /> <Separator />
</div> </div>
</div> </div>
{/if} {/if}
<div <div
bind:this={basemapContainer} bind:this={basemapContainer}
class="ml-1.5 flex flex-col gap-1 {$customBasemapOrder.length > 0 ? 'mb-2' : ''}" class="ml-1.5 flex flex-col gap-1 {$customBasemapOrder.length > 0 ? 'mb-2' : ''}"
> >
{#each $customBasemapOrder as id (id)} {#each $customBasemapOrder as id (id)}
<div class="flex flex-row items-center gap-2" data-id={id}> <div class="flex flex-row items-center gap-2" data-id={id}>
<Move size="12" /> <Move size="12" />
<span class="grow">{$customLayers[id].name}</span> <span class="grow">{$customLayers[id].name}</span>
<Button variant="outline" on:click={() => (selectedLayerId = id)} class="p-1 h-7"> <Button variant="outline" on:click={() => (selectedLayerId = id)} class="p-1 h-7">
<Pencil size="16" /> <Pencil size="16" />
</Button> </Button>
<Button variant="outline" on:click={() => deleteLayer(id)} class="p-1 h-7"> <Button variant="outline" on:click={() => deleteLayer(id)} class="p-1 h-7">
<Trash2 size="16" /> <Trash2 size="16" />
</Button> </Button>
</div> </div>
{/each} {/each}
</div> </div>
{#if $customOverlayOrder.length > 0} {#if $customOverlayOrder.length > 0}
<div class="flex flex-row items-center gap-1 font-semibold mb-2"> <div class="flex flex-row items-center gap-1 font-semibold mb-2">
<Layers2 size="16" /> <Layers2 size="16" />
{$_('layers.label.overlays')} {$_('layers.label.overlays')}
<div class="grow"> <div class="grow">
<Separator /> <Separator />
</div> </div>
</div> </div>
{/if} {/if}
<div <div
bind:this={overlayContainer} bind:this={overlayContainer}
class="ml-1.5 flex flex-col gap-1 {$customOverlayOrder.length > 0 ? 'mb-2' : ''}" class="ml-1.5 flex flex-col gap-1 {$customOverlayOrder.length > 0 ? 'mb-2' : ''}"
> >
{#each $customOverlayOrder as id (id)} {#each $customOverlayOrder as id (id)}
<div class="flex flex-row items-center gap-2" data-id={id}> <div class="flex flex-row items-center gap-2" data-id={id}>
<Move size="12" /> <Move size="12" />
<span class="grow">{$customLayers[id].name}</span> <span class="grow">{$customLayers[id].name}</span>
<Button variant="outline" on:click={() => (selectedLayerId = id)} class="p-1 h-7"> <Button variant="outline" on:click={() => (selectedLayerId = id)} class="p-1 h-7">
<Pencil size="16" /> <Pencil size="16" />
</Button> </Button>
<Button variant="outline" on:click={() => deleteLayer(id)} class="p-1 h-7"> <Button variant="outline" on:click={() => deleteLayer(id)} class="p-1 h-7">
<Trash2 size="16" /> <Trash2 size="16" />
</Button> </Button>
</div> </div>
{/each} {/each}
</div> </div>
<Card.Root> <Card.Root>
<Card.Header class="p-3"> <Card.Header class="p-3">
<Card.Title class="text-base"> <Card.Title class="text-base">
{#if selectedLayerId} {#if selectedLayerId}
{$_('layers.custom_layers.edit')} {$_('layers.custom_layers.edit')}
{:else} {:else}
{$_('layers.custom_layers.new')} {$_('layers.custom_layers.new')}
{/if} {/if}
</Card.Title> </Card.Title>
</Card.Header> </Card.Header>
<Card.Content class="p-3 pt-0"> <Card.Content class="p-3 pt-0">
<fieldset class="flex flex-col gap-2"> <fieldset class="flex flex-col gap-2">
<Label for="name">{$_('menu.metadata.name')}</Label> <Label for="name">{$_('menu.metadata.name')}</Label>
<Input bind:value={name} id="name" class="h-8" /> <Input bind:value={name} id="name" class="h-8" />
<Label for="url">{$_('layers.custom_layers.urls')}</Label> <Label for="url">{$_('layers.custom_layers.urls')}</Label>
{#each tileUrls as url, i} {#each tileUrls as url, i}
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-2">
<Input <Input
bind:value={tileUrls[i]} bind:value={tileUrls[i]}
id="url" id="url"
class="h-8" class="h-8"
placeholder={$_('layers.custom_layers.url_placeholder')} placeholder={$_('layers.custom_layers.url_placeholder')}
/> />
{#if tileUrls.length > 1} {#if tileUrls.length > 1}
<Button <Button
on:click={() => (tileUrls = tileUrls.filter((_, index) => index !== i))} on:click={() =>
variant="outline" (tileUrls = tileUrls.filter((_, index) => index !== i))}
class="p-1 h-8" variant="outline"
> class="p-1 h-8"
<Minus size="16" /> >
</Button> <Minus size="16" />
{/if} </Button>
{#if i === tileUrls.length - 1} {/if}
<Button {#if i === tileUrls.length - 1}
on:click={() => (tileUrls = [...tileUrls, ''])} <Button
variant="outline" on:click={() => (tileUrls = [...tileUrls, ''])}
class="p-1 h-8" variant="outline"
> class="p-1 h-8"
<Plus size="16" /> >
</Button> <Plus size="16" />
{/if} </Button>
</div> {/if}
{/each} </div>
{#if resourceType === 'raster'} {/each}
<Label for="maxZoom">{$_('layers.custom_layers.max_zoom')}</Label> {#if resourceType === 'raster'}
<Input type="number" bind:value={maxZoom} id="maxZoom" min={0} max={22} class="h-8" /> <Label for="maxZoom">{$_('layers.custom_layers.max_zoom')}</Label>
{/if} <Input
<Label>{$_('layers.custom_layers.layer_type')}</Label> type="number"
<RadioGroup.Root bind:value={layerType} class="flex flex-row"> bind:value={maxZoom}
<div class="flex items-center space-x-2"> id="maxZoom"
<RadioGroup.Item value="basemap" id="basemap" /> min={0}
<Label for="basemap">{$_('layers.custom_layers.basemap')}</Label> max={22}
</div> class="h-8"
<div class="flex items-center space-x-2"> />
<RadioGroup.Item value="overlay" id="overlay" /> {/if}
<Label for="overlay">{$_('layers.custom_layers.overlay')}</Label> <Label>{$_('layers.custom_layers.layer_type')}</Label>
</div> <RadioGroup.Root bind:value={layerType} class="flex flex-row">
</RadioGroup.Root> <div class="flex items-center space-x-2">
{#if selectedLayerId} <RadioGroup.Item value="basemap" id="basemap" />
<div class="mt-2 flex flex-row gap-2"> <Label for="basemap">{$_('layers.custom_layers.basemap')}</Label>
<Button variant="outline" on:click={createLayer} class="grow"> </div>
<Save size="16" class="mr-1" /> <div class="flex items-center space-x-2">
{$_('layers.custom_layers.update')} <RadioGroup.Item value="overlay" id="overlay" />
</Button> <Label for="overlay">{$_('layers.custom_layers.overlay')}</Label>
<Button variant="outline" on:click={() => (selectedLayerId = undefined)}> </div>
<CircleX size="16" /> </RadioGroup.Root>
</Button> {#if selectedLayerId}
</div> <div class="mt-2 flex flex-row gap-2">
{:else} <Button variant="outline" on:click={createLayer} class="grow">
<Button variant="outline" class="mt-2" on:click={createLayer}> <Save size="16" class="mr-1" />
<CirclePlus size="16" class="mr-1" /> {$_('layers.custom_layers.update')}
{$_('layers.custom_layers.create')} </Button>
</Button> <Button variant="outline" on:click={() => (selectedLayerId = undefined)}>
{/if} <CircleX size="16" />
</fieldset> </Button>
</Card.Content> </div>
</Card.Root> {:else}
<Button variant="outline" class="mt-2" on:click={createLayer}>
<CirclePlus size="16" class="mr-1" />
{$_('layers.custom_layers.create')}
</Button>
{/if}
</fieldset>
</Card.Content>
</Card.Root>
</div> </div>

View File

@@ -13,6 +13,7 @@
import { get, writable } from 'svelte/store'; import { get, writable } from 'svelte/store';
import { customBasemapUpdate, getLayers } from './utils'; import { customBasemapUpdate, getLayers } from './utils';
import { OverpassLayer } from './OverpassLayer'; import { OverpassLayer } from './OverpassLayer';
import OverpassPopup from './OverpassPopup.svelte';
let container: HTMLDivElement; let container: HTMLDivElement;
let overpassLayer: OverpassLayer; let overpassLayer: OverpassLayer;
@@ -33,7 +34,7 @@
if ($map) { if ($map) {
let basemap = basemaps.hasOwnProperty($currentBasemap) let basemap = basemaps.hasOwnProperty($currentBasemap)
? basemaps[$currentBasemap] ? basemaps[$currentBasemap]
: ($customLayers[$currentBasemap]?.value ?? basemaps[defaultBasemap]); : $customLayers[$currentBasemap]?.value ?? basemaps[defaultBasemap];
$map.removeImport('basemap'); $map.removeImport('basemap');
if (typeof basemap === 'string') { if (typeof basemap === 'string') {
$map.addImport({ id: 'basemap', url: basemap }, 'overlays'); $map.addImport({ id: 'basemap', url: basemap }, 'overlays');
@@ -84,21 +85,18 @@
} }
function updateOverlays() { function updateOverlays() {
if ($map && $currentOverlays && $opacities) { if ($map && $currentOverlays) {
let overlayLayers = getLayers($currentOverlays); let overlayLayers = getLayers($currentOverlays);
try { try {
let activeOverlays = $map.getStyle().imports.reduce((acc, i) => { let activeOverlays = $map
if (!['basemap', 'overlays', 'glyphs-and-sprite'].includes(i.id)) { .getStyle()
acc[i.id] = i; .imports.filter((i) => i.id !== 'basemap' && i.id !== 'overlays');
} let toRemove = activeOverlays.filter((i) => !overlayLayers[i.id]);
return acc; toRemove.forEach((i) => {
}, {}); $map.removeImport(i.id);
let toRemove = Object.keys(activeOverlays).filter((id) => !overlayLayers[id]);
toRemove.forEach((id) => {
$map.removeImport(id);
}); });
let toAdd = Object.entries(overlayLayers) let toAdd = Object.entries(overlayLayers)
.filter(([id, selected]) => selected && !activeOverlays.hasOwnProperty(id)) .filter(([id, selected]) => selected && !activeOverlays.some((j) => j.id === id))
.map(([id]) => id); .map(([id]) => id);
toAdd.forEach((id) => { toAdd.forEach((id) => {
addOverlay(id); addOverlay(id);
@@ -109,7 +107,7 @@
} }
} }
$: if ($map && $currentOverlays && $opacities) { $: if ($map && $currentOverlays) {
updateOverlays(); updateOverlays();
} }
@@ -132,9 +130,7 @@
}); });
currentBasemap.subscribe((value) => { currentBasemap.subscribe((value) => {
// Updates coming from the database, or from the user swapping basemaps // Updates coming from the database, or from the user swapping basemaps
if (value !== get(selectedBasemap)) { selectedBasemap.set(value);
selectedBasemap.set(value);
}
}); });
let open = false; let open = false;
@@ -213,6 +209,8 @@
</div> </div>
</CustomControl> </CustomControl>
<OverpassPopup />
<svelte:window <svelte:window
on:click={(e) => { on:click={(e) => {
if (open && !cancelEvents && !container.contains(e.target)) { if (open && !cancelEvents && !container.contains(e.target)) {

View File

@@ -157,16 +157,15 @@
max={1} max={1}
step={0.1} step={0.1}
disabled={$selectedOverlay === undefined} disabled={$selectedOverlay === undefined}
onValueChange={(value) => { onValueChange={() => {
if ($selectedOverlay) { if ($selectedOverlay) {
if ($map && isSelected($currentOverlays, $selectedOverlay.value)) { $opacities[$selectedOverlay.value] = $overlayOpacity[0];
try { if ($map) {
$map.removeImport($selectedOverlay.value); if ($map.getLayer($selectedOverlay.value)) {
} catch (e) { $map.removeLayer($selectedOverlay.value);
// No reliable way to check if the map is ready to remove sources and layers $currentOverlays = $currentOverlays;
} }
} }
$opacities[$selectedOverlay.value] = value[0];
} }
}} }}
/> />

View File

@@ -1,10 +1,10 @@
import SphericalMercator from "@mapbox/sphericalmercator"; import SphericalMercator from "@mapbox/sphericalmercator";
import { getLayers } from "./utils"; import { getLayers } from "./utils";
import mapboxgl from "mapbox-gl";
import { get, writable } from "svelte/store"; import { get, writable } from "svelte/store";
import { liveQuery } from "dexie"; import { liveQuery } from "dexie";
import { db, settings } from "$lib/db"; import { db, settings } from "$lib/db";
import { overpassQueryData } from "$lib/assets/layers"; import { overpassQueryData } from "$lib/assets/layers";
import { MapPopup } from "$lib/components/MapPopup";
const { const {
currentOverpassQueries currentOverpassQueries
@@ -14,6 +14,14 @@ const mercator = new SphericalMercator({
size: 256, size: 256,
}); });
export const overpassPopupPOI = writable<Record<string, any> | null>(null);
export const overpassPopup = new mapboxgl.Popup({
closeButton: false,
maxWidth: undefined,
offset: 15,
});
let data = writable<GeoJSON.FeatureCollection>({ type: 'FeatureCollection', features: [] }); let data = writable<GeoJSON.FeatureCollection>({ type: 'FeatureCollection', features: [] });
liveQuery(() => db.overpassdata.toArray()).subscribe((pois) => { liveQuery(() => db.overpassdata.toArray()).subscribe((pois) => {
@@ -26,7 +34,6 @@ export class OverpassLayer {
queryZoom = 12; queryZoom = 12;
expirationTime = 7 * 24 * 3600 * 1000; expirationTime = 7 * 24 * 3600 * 1000;
map: mapboxgl.Map; map: mapboxgl.Map;
popup: MapPopup;
currentQueries: Set<string> = new Set(); currentQueries: Set<string> = new Set();
nextQueries: Map<string, { x: number, y: number, queries: string[] }> = new Map(); nextQueries: Map<string, { x: number, y: number, queries: string[] }> = new Map();
@@ -35,15 +42,10 @@ export class OverpassLayer {
queryIfNeededBinded = this.queryIfNeeded.bind(this); queryIfNeededBinded = this.queryIfNeeded.bind(this);
updateBinded = this.update.bind(this); updateBinded = this.update.bind(this);
onHoverBinded = this.onHover.bind(this); onHoverBinded = this.onHover.bind(this);
maybeHidePopupBinded = this.maybeHidePopup.bind(this);
constructor(map: mapboxgl.Map) { constructor(map: mapboxgl.Map) {
this.map = map; this.map = map;
this.popup = new MapPopup(map, {
closeButton: false,
focusAfterOpen: false,
maxWidth: undefined,
offset: 15,
});
} }
add() { add() {
@@ -123,12 +125,27 @@ export class OverpassLayer {
} }
onHover(e: any) { onHover(e: any) {
this.popup.setItem({ overpassPopupPOI.set({
item: { ...e.features[0].properties,
...e.features[0].properties, sym: overpassQueryData[e.features[0].properties.query].symbol ?? ''
sym: overpassQueryData[e.features[0].properties.query].symbol ?? ''
}
}); });
overpassPopup.setLngLat(e.features[0].geometry.coordinates);
overpassPopup.addTo(this.map);
this.map.on('mousemove', this.maybeHidePopupBinded);
}
maybeHidePopup(e: any) {
let poi = get(overpassPopupPOI);
if (poi && this.map.project([poi.lon, poi.lat]).dist(this.map.project(e.lngLat)) > 100) {
this.hideWaypointPopup();
}
}
hideWaypointPopup() {
overpassPopupPOI.set(null);
overpassPopup.remove();
this.map.off('mousemove', this.maybeHidePopupBinded);
} }
query(bbox: [number, number, number, number]) { query(bbox: [number, number, number, number]) {

View File

@@ -1,95 +1,102 @@
<script lang="ts"> <script lang="ts">
import * as Card from '$lib/components/ui/card'; import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import { overpassPopup, overpassPopupPOI } from './OverpassLayer';
import { selection } from '$lib/components/file-list/Selection'; import { selection } from '$lib/components/file-list/Selection';
import { PencilLine, MapPin } from 'lucide-svelte'; import { PencilLine, MapPin } from 'lucide-svelte';
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
import { dbUtils } from '$lib/db'; import { dbUtils } from '$lib/db';
import type { PopupItem } from '$lib/components/MapPopup';
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
export let poi: PopupItem<any>; let popupElement: HTMLDivElement;
onMount(() => {
overpassPopup.setDOMContent(popupElement);
popupElement.classList.remove('hidden');
});
let tags = {}; let tags = {};
let name = ''; let name = '';
$: if (poi) { $: if ($overpassPopupPOI) {
tags = JSON.parse(poi.item.tags); tags = JSON.parse($overpassPopupPOI.tags);
if (tags.name !== undefined && tags.name !== '') { if (tags.name !== undefined && tags.name !== '') {
name = tags.name; name = tags.name;
} else { } else {
name = $_(`layers.label.${poi.item.query}`); name = $_(`layers.label.${$overpassPopupPOI.query}`);
} }
} }
</script> </script>
<Card.Root class="border-none shadow-md text-base p-2 max-w-[50dvw]"> <div bind:this={popupElement} class="hidden">
<Card.Header class="p-0"> {#if $overpassPopupPOI}
<Card.Title class="text-md"> <Card.Root class="border-none shadow-md text-base p-2 max-w-[50dvw]">
<div class="flex flex-row gap-3"> <Card.Header class="p-0">
<div class="flex flex-col"> <Card.Title class="text-md">
{name} <div class="flex flex-row gap-3">
<div class="text-muted-foreground text-sm font-normal"> <div class="flex flex-col">
{poi.item.lat.toFixed(6)}&deg; {poi.item.lon.toFixed(6)}&deg; {name}
<div class="text-muted-foreground text-sm font-normal">
{$overpassPopupPOI.lat.toFixed(6)}&deg; {$overpassPopupPOI.lon.toFixed(6)}&deg;
</div>
</div>
<Button
class="ml-auto p-1.5 h-8"
variant="outline"
href="https://www.openstreetmap.org/edit?editor=id&node={$overpassPopupPOI.id}"
target="_blank"
>
<PencilLine size="16" />
</Button>
</div> </div>
</div> </Card.Title>
<Button </Card.Header>
class="ml-auto p-1.5 h-8"
variant="outline"
href="https://www.openstreetmap.org/edit?editor=id&node={poi.item.id}"
target="_blank"
>
<PencilLine size="16" />
</Button>
</div>
</Card.Title>
</Card.Header>
<Card.Content class="flex flex-col p-0 text-sm mt-1 whitespace-normal break-all">
<ScrollArea class="flex flex-col" viewportClasses="max-h-[30dvh]">
{#if tags.image || tags['image:0']} {#if tags.image || tags['image:0']}
<div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto"> <div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto">
<!-- svelte-ignore a11y-missing-attribute --> <!-- svelte-ignore a11y-missing-attribute -->
<img src={tags.image ?? tags['image:0']} /> <img src={tags.image ?? tags['image:0']} />
</div> </div>
{/if} {/if}
<div class="grid grid-cols-[auto_auto] gap-x-3"> <Card.Content class="flex flex-col p-0 text-sm mt-1 whitespace-normal break-all">
{#each Object.entries(tags) as [key, value]} <div class="grid grid-cols-[auto_auto] gap-x-3">
{#if key !== 'name' && !key.includes('image')} {#each Object.entries(tags) as [key, value]}
<span class="font-mono">{key}</span> {#if key !== 'name' && !key.includes('image')}
{#if key === 'website' || key.startsWith('website:') || key === 'contact:website' || key === 'contact:facebook' || key === 'contact:instagram' || key === 'contact:twitter'} <span class="font-mono">{key}</span>
<a href={value} target="_blank" class="text-link underline">{value}</a> {#if key === 'website' || key === 'contact:website' || key === 'contact:facebook' || key === 'contact:instagram' || key === 'contact:twitter'}
{:else if key === 'phone' || key === 'contact:phone'} <a href={value} target="_blank" class="text-link underline">{value}</a>
<a href={'tel:' + value} class="text-link underline">{value}</a> {:else if key === 'phone' || key === 'contact:phone'}
{:else if key === 'email' || key === 'contact:email'} <a href={'tel:' + value} class="text-link underline">{value}</a>
<a href={'mailto:' + value} class="text-link underline">{value}</a> {:else if key === 'email' || key === 'contact:email'}
{:else} <a href={'mailto:' + value} class="text-link underline">{value}</a>
<span>{value}</span> {:else}
<span>{value}</span>
{/if}
{/if} {/if}
{/if} {/each}
{/each} </div>
</div> <Button
</ScrollArea> class="mt-2"
<Button variant="outline"
class="mt-2" disabled={$selection.size === 0}
variant="outline" on:click={() => {
disabled={$selection.size === 0} let desc = Object.entries(tags)
on:click={() => { .map(([key, value]) => `${key}: ${value}`)
let desc = Object.entries(tags) .join('\n');
.map(([key, value]) => `${key}: ${value}`) dbUtils.addOrUpdateWaypoint({
.join('\n'); attributes: {
dbUtils.addOrUpdateWaypoint({ lat: $overpassPopupPOI.lat,
attributes: { lon: $overpassPopupPOI.lon
lat: poi.item.lat, },
lon: poi.item.lon name: name,
}, desc: desc,
name: name, cmt: desc,
desc: desc, sym: $overpassPopupPOI.sym
cmt: desc, });
sym: poi.item.sym }}
}); >
}} <MapPin size="16" class="mr-1" />
> {$_('toolbar.waypoint.add')}
<MapPin size="16" class="mr-1" /> </Button>
{$_('toolbar.waypoint.add')} </Card.Content>
</Button> </Card.Root>
</Card.Content> {/if}
</Card.Root> </div>

View File

@@ -1,17 +1,16 @@
import mapboxgl, { type LayerSpecification, type VectorSourceSpecification } from "mapbox-gl"; import mapboxgl from "mapbox-gl";
import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module'; import { Viewer } from 'mapillary-js/dist/mapillary.module';
import 'mapillary-js/dist/mapillary.css'; import 'mapillary-js/dist/mapillary.css';
import { resetCursor, setPointerCursor } from "$lib/utils"; import { resetCursor, setPointerCursor } from "$lib/utils";
import type { Writable } from "svelte/store";
const mapillarySource: VectorSourceSpecification = { const mapillarySource = {
type: 'vector', type: 'vector',
tiles: ['https://tiles.mapillary.com/maps/vtp/mly1_computed_public/2/{z}/{x}/{y}?access_token=MLY|4381405525255083|3204871ec181638c3c31320490f03011'], tiles: ['https://tiles.mapillary.com/maps/vtp/mly1_computed_public/2/{z}/{x}/{y}?access_token=MLY|4381405525255083|3204871ec181638c3c31320490f03011'],
minzoom: 6, minzoom: 6,
maxzoom: 14, maxzoom: 14,
}; };
const mapillarySequenceLayer: LayerSpecification = { const mapillarySequenceLayer = {
id: 'mapillary-sequence', id: 'mapillary-sequence',
type: 'line', type: 'line',
source: 'mapillary', source: 'mapillary',
@@ -27,7 +26,7 @@ const mapillarySequenceLayer: LayerSpecification = {
}, },
}; };
const mapillaryImageLayer: LayerSpecification = { const mapillaryImageLayer = {
id: 'mapillary-image', id: 'mapillary-image',
type: 'circle', type: 'circle',
source: 'mapillary', source: 'mapillary',
@@ -41,56 +40,35 @@ const mapillaryImageLayer: LayerSpecification = {
export class MapillaryLayer { export class MapillaryLayer {
map: mapboxgl.Map; map: mapboxgl.Map;
marker: mapboxgl.Marker; popup: mapboxgl.Popup;
viewer: Viewer; viewer: Viewer;
active = false;
popupOpen: Writable<boolean>;
addBinded = this.add.bind(this); addBinded = this.add.bind(this);
onMouseEnterBinded = this.onMouseEnter.bind(this); onMouseEnterBinded = this.onMouseEnter.bind(this);
onMouseLeaveBinded = this.onMouseLeave.bind(this); onMouseLeaveBinded = this.onMouseLeave.bind(this);
constructor(map: mapboxgl.Map, container: HTMLElement, popupOpen: Writable<boolean>) { constructor(map: mapboxgl.Map, container: HTMLElement) {
this.map = map; this.map = map;
this.viewer = new Viewer({ this.viewer = new Viewer({
accessToken: 'MLY|4381405525255083|3204871ec181638c3c31320490f03011', accessToken: 'MLY|4381405525255083|3204871ec181638c3c31320490f03011',
container, container,
}); });
container.classList.remove('hidden');
const element = document.createElement('div'); this.popup = new mapboxgl.Popup({
element.className = 'mapboxgl-user-location mapboxgl-user-location-show-heading'; closeButton: false,
const dot = document.createElement('div'); maxWidth: container.style.width,
dot.className = 'mapboxgl-user-location-dot'; }).setDOMContent(container);
const heading = document.createElement('div');
heading.className = 'mapboxgl-user-location-heading';
element.appendChild(dot);
element.appendChild(heading);
this.marker = new mapboxgl.Marker({
rotationAlignment: 'map',
element
});
this.viewer.on('position', async () => { this.viewer.on('position', async () => {
if (this.active) { if (this.popup.isOpen()) {
popupOpen.set(true);
let latLng = await this.viewer.getPosition(); let latLng = await this.viewer.getPosition();
this.marker.setLngLat(latLng).addTo(this.map); this.popup.setLngLat(latLng);
if (!this.map.getBounds()?.contains(latLng)) { if (!this.map.getBounds().contains(latLng)) {
this.map.panTo(latLng); this.map.panTo(latLng);
} }
} }
}); });
this.viewer.on('bearing', (e: ViewerBearingEvent) => {
if (this.active) {
this.marker.setRotation(e.bearing);
}
});
this.popupOpen = popupOpen;
} }
add() { add() {
@@ -123,19 +101,15 @@ export class MapillaryLayer {
this.map.removeSource('mapillary'); this.map.removeSource('mapillary');
} }
this.marker.remove(); this.popup.remove();
this.popupOpen.set(false);
} }
closePopup() { closePopup() {
this.active = false; this.popup.remove();
this.marker.remove();
this.popupOpen.set(false);
} }
onMouseEnter(e: mapboxgl.MapMouseEvent) { onMouseEnter(e: mapboxgl.MapLayerMouseEvent) {
this.active = true; this.popup.addTo(this.map).setLngLat(e.lngLat);
this.viewer.resize(); this.viewer.resize();
this.viewer.moveTo(e.features[0].properties.id); this.viewer.moveTo(e.features[0].properties.id);

View File

@@ -8,18 +8,16 @@
import { map, streetViewEnabled } from '$lib/stores'; import { map, streetViewEnabled } from '$lib/stores';
import { settings } from '$lib/db'; import { settings } from '$lib/db';
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
import { writable } from 'svelte/store';
const { streetViewSource } = settings; const { streetViewSource } = settings;
let googleRedirect: GoogleRedirect; let googleRedirect: GoogleRedirect;
let mapillaryLayer: MapillaryLayer; let mapillaryLayer: MapillaryLayer;
let mapillaryOpen = writable(false);
let container: HTMLElement; let container: HTMLElement;
$: if ($map) { $: if ($map) {
googleRedirect = new GoogleRedirect($map); googleRedirect = new GoogleRedirect($map);
mapillaryLayer = new MapillaryLayer($map, container, mapillaryOpen); mapillaryLayer = new MapillaryLayer($map, container);
} }
$: if (mapillaryLayer) { $: if (mapillaryLayer) {
@@ -55,9 +53,7 @@
<div <div
bind:this={container} bind:this={container}
class="{$mapillaryOpen class="hidden relative w-[50vw] h-[40vh] rounded-md border-background border-2"
? ''
: 'hidden'} !absolute bottom-[44px] right-2.5 z-10 w-[40%] h-[40%] bg-background rounded-md overflow-hidden border-background border-2"
> >
<!-- svelte-ignore a11y-click-events-have-key-events --> <!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->

View File

@@ -11,18 +11,15 @@
import { selection } from '$lib/components/file-list/Selection'; import { selection } from '$lib/components/file-list/Selection';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import { Label } from '$lib/components/ui/label/index.js'; import { Label } from '$lib/components/ui/label/index.js';
import { Checkbox } from '$lib/components/ui/checkbox';
import * as RadioGroup from '$lib/components/ui/radio-group'; import * as RadioGroup from '$lib/components/ui/radio-group';
import { _, locale } from 'svelte-i18n'; import { _, locale } from 'svelte-i18n';
import { dbUtils, getFile } from '$lib/db'; import { dbUtils, getFile } from '$lib/db';
import { Group } from 'lucide-svelte'; import { Group } from 'lucide-svelte';
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
import Shortcut from '$lib/components/Shortcut.svelte'; import Shortcut from '$lib/components/Shortcut.svelte';
import { gpxStatistics } from '$lib/stores';
let canMergeTraces = false; let canMergeTraces = false;
let canMergeContents = false; let canMergeContents = false;
let removeGaps = false;
$: if ($selection.size > 1) { $: if ($selection.size > 1) {
canMergeTraces = true; canMergeTraces = true;
@@ -59,31 +56,22 @@
<div class="flex flex-col gap-3 w-full max-w-80 {$$props.class ?? ''}"> <div class="flex flex-col gap-3 w-full max-w-80 {$$props.class ?? ''}">
<RadioGroup.Root bind:value={mergeType}> <RadioGroup.Root bind:value={mergeType}>
<Label class="flex flex-row items-center gap-1.5 leading-5"> <Label class="flex flex-row items-center gap-2 leading-5">
<RadioGroup.Item value={MergeType.TRACES} /> <RadioGroup.Item value={MergeType.TRACES} />
{$_('toolbar.merge.merge_traces')} {$_('toolbar.merge.merge_traces')}
</Label> </Label>
<Label class="flex flex-row items-center gap-1.5 leading-5"> <Label class="flex flex-row items-center gap-2 leading-5">
<RadioGroup.Item value={MergeType.CONTENTS} /> <RadioGroup.Item value={MergeType.CONTENTS} />
{$_('toolbar.merge.merge_contents')} {$_('toolbar.merge.merge_contents')}
</Label> </Label>
</RadioGroup.Root> </RadioGroup.Root>
{#if mergeType === MergeType.TRACES && $gpxStatistics.global.time.total > 0}
<div class="flex flex-row items-center gap-1.5">
<Checkbox id="remove-gaps" bind:checked={removeGaps} />
<Label for="remove-gaps">{$_('toolbar.merge.remove_gaps')}</Label>
</div>
{/if}
<Button <Button
variant="outline" variant="outline"
class="whitespace-normal h-fit" class="whitespace-normal h-fit"
disabled={(mergeType === MergeType.TRACES && !canMergeTraces) || disabled={(mergeType === MergeType.TRACES && !canMergeTraces) ||
(mergeType === MergeType.CONTENTS && !canMergeContents)} (mergeType === MergeType.CONTENTS && !canMergeContents)}
on:click={() => { on:click={() => {
dbUtils.mergeSelection( dbUtils.mergeSelection(mergeType === MergeType.TRACES);
mergeType === MergeType.TRACES,
mergeType === MergeType.TRACES && $gpxStatistics.global.time.total > 0 && removeGaps
);
}} }}
> >
<Group size="16" class="mr-1 shrink-0" /> <Group size="16" class="mr-1 shrink-0" />

View File

@@ -3,7 +3,6 @@
import { Switch } from '$lib/components/ui/switch'; import { Switch } from '$lib/components/ui/switch';
import { Label } from '$lib/components/ui/label/index.js'; import { Label } from '$lib/components/ui/label/index.js';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import * as RadioGroup from '$lib/components/ui/radio-group';
import Help from '$lib/components/Help.svelte'; import Help from '$lib/components/Help.svelte';
import ButtonWithTooltip from '$lib/components/ButtonWithTooltip.svelte'; import ButtonWithTooltip from '$lib/components/ButtonWithTooltip.svelte';
import Tooltip from '$lib/components/Tooltip.svelte'; import Tooltip from '$lib/components/Tooltip.svelte';
@@ -20,22 +19,16 @@
RouteOff, RouteOff,
Repeat, Repeat,
SquareArrowUpLeft, SquareArrowUpLeft,
SquareArrowOutDownRight, SquareArrowOutDownRight
Timer
} from 'lucide-svelte'; } from 'lucide-svelte';
import { import { map, newGPXFile, routingControls, selectFileWhenLoaded } from '$lib/stores';
gpxStatistics,
map,
newGPXFile,
routingControls,
selectFileWhenLoaded
} from '$lib/stores';
import { dbUtils, getFile, getFileIds, settings } from '$lib/db'; import { dbUtils, getFile, getFileIds, settings } from '$lib/db';
import { brouterProfiles, routingProfileSelectItem } from './Routing'; import { brouterProfiles, routingProfileSelectItem } from './Routing';
import { _, locale } from 'svelte-i18n'; import { _, locale } from 'svelte-i18n';
import { RoutingControls } from './RoutingControls'; import { RoutingControls } from './RoutingControls';
import mapboxgl from 'mapbox-gl';
import { fileObservers } from '$lib/db'; import { fileObservers } from '$lib/db';
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
import { getOrderedSelection, selection } from '$lib/components/file-list/Selection'; import { getOrderedSelection, selection } from '$lib/components/file-list/Selection';
@@ -47,7 +40,6 @@
type ListItem type ListItem
} from '$lib/components/file-list/FileList'; } from '$lib/components/file-list/FileList';
import { flyAndScale, getURLForLanguage, resetCursor, setCrosshairCursor } from '$lib/utils'; import { flyAndScale, getURLForLanguage, resetCursor, setCrosshairCursor } from '$lib/utils';
import { TimestampsMode } from '$lib/types';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import { TrackPoint } from 'gpx'; import { TrackPoint } from 'gpx';
@@ -57,7 +49,7 @@
export let popupElement: HTMLElement | undefined = undefined; export let popupElement: HTMLElement | undefined = undefined;
let selectedItem: ListItem | null = null; let selectedItem: ListItem | null = null;
const { privateRoads, routing, timestampsMode } = settings; const { privateRoads, routing } = settings;
$: if ($map && popup && popupElement) { $: if ($map && popup && popupElement) {
// remove controls for deleted files // remove controls for deleted files
@@ -169,7 +161,7 @@
</Select.Root> </Select.Root>
</Label> </Label>
<Label class="flex flex-row justify-between items-center gap-2"> <Label class="flex flex-row justify-between items-center gap-2">
<span class="flex flex-row items-center gap-1"> <span class="flex flex-row gap-1">
<TriangleAlert size="16" /> <TriangleAlert size="16" />
{$_('toolbar.routing.allow_private')} {$_('toolbar.routing.allow_private')}
</span> </span>
@@ -177,28 +169,6 @@
</Label> </Label>
</div> </div>
{/if} {/if}
{#if $gpxStatistics.global.time.total > 0}
<RadioGroup.Root bind:value={$timestampsMode}>
<div class="flex flex-row items-center gap-2">
<RadioGroup.Item
value={TimestampsMode.PRESERVE_AVERAGE_SPEED}
id={TimestampsMode.PRESERVE_AVERAGE_SPEED}
/>
<Label for={TimestampsMode.PRESERVE_AVERAGE_SPEED}>
{$_('toolbar.routing.preserve_average_speed')}
</Label>
</div>
<div class="flex flex-row items-center gap-2">
<RadioGroup.Item
value={TimestampsMode.PRESERVE_TIMESTAMPS}
id={TimestampsMode.PRESERVE_TIMESTAMPS}
/>
<Label for={TimestampsMode.PRESERVE_TIMESTAMPS}>
{$_('toolbar.routing.preserve_timestamps')}
</Label>
</div>
</RadioGroup.Root>
{/if}
</div> </div>
<div class="flex flex-row flex-wrap justify-center gap-1"> <div class="flex flex-row flex-wrap justify-center gap-1">
<ButtonWithTooltip <ButtonWithTooltip

View File

@@ -3,6 +3,7 @@ import { TrackPoint, distance } from "gpx";
import { derived, get, writable } from "svelte/store"; import { derived, get, writable } from "svelte/store";
import { settings } from "$lib/db"; import { settings } from "$lib/db";
import { _, isLoading, locale } from "svelte-i18n"; import { _, isLoading, locale } from "svelte-i18n";
import { map } from "$lib/stores";
import { getElevation } from "$lib/utils"; import { getElevation } from "$lib/utils";
const { routing, routingProfile, privateRoads } = settings; const { routing, routingProfile, privateRoads } = settings;
@@ -65,7 +66,7 @@ async function getRoute(points: Coordinates[], brouterProfile: string, privateRo
const latIdx = messages[0].indexOf("Latitude"); const latIdx = messages[0].indexOf("Latitude");
const tagIdx = messages[0].indexOf("WayTags"); const tagIdx = messages[0].indexOf("WayTags");
let messageIdx = 1; let messageIdx = 1;
let tags = messageIdx < messages.length ? getTags(messages[messageIdx][tagIdx]) : {}; let surface = messageIdx < messages.length ? getSurface(messages[messageIdx][tagIdx]) : undefined;
for (let i = 0; i < coordinates.length; i++) { for (let i = 0; i < coordinates.length; i++) {
let coord = coordinates[i]; let coord = coordinates[i];
@@ -82,26 +83,25 @@ async function getRoute(points: Coordinates[], brouterProfile: string, privateRo
coordinates[i][1] == Number(messages[messageIdx][latIdx]) / 1000000) { coordinates[i][1] == Number(messages[messageIdx][latIdx]) / 1000000) {
messageIdx++; messageIdx++;
if (messageIdx == messages.length) tags = {}; if (messageIdx == messages.length) surface = undefined;
else tags = getTags(messages[messageIdx][tagIdx]); else surface = getSurface(messages[messageIdx][tagIdx]);
} }
route[route.length - 1].setExtensions(tags); if (surface) {
route[route.length - 1].setSurface(surface);
}
} }
return route; return route;
} }
function getTags(message: string): { [key: string]: string } { function getSurface(message: string): string | undefined {
const fields = message.split(" "); const fields = message.split(" ");
let tags: { [key: string]: string } = {}; for (let i = 0; i < fields.length; i++) if (fields[i].startsWith("surface=")) {
for (let i = 0; i < fields.length; i++) { return fields[i].substring(8);
let [key, value] = fields[i].split("=");
key = key.replace(/:/g, '_');
tags[key] = value;
} }
return tags; return undefined;
} };
function getIntermediatePoints(points: Coordinates[]): Promise<TrackPoint[]> { function getIntermediatePoints(points: Coordinates[]): Promise<TrackPoint[]> {
let route: TrackPoint[] = []; let route: TrackPoint[] = [];

View File

@@ -2,16 +2,15 @@ import { distance, type Coordinates, TrackPoint, TrackSegment, Track, projectedP
import { get, writable, type Readable } from "svelte/store"; import { get, writable, type Readable } from "svelte/store";
import mapboxgl from "mapbox-gl"; import mapboxgl from "mapbox-gl";
import { route } from "./Routing"; import { route } from "./Routing";
import { toast } from "svelte-sonner"; import { toast } from "svelte-sonner";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { dbUtils, settings, type GPXFileWithStatistics } from "$lib/db"; import { dbUtils, type GPXFileWithStatistics } from "$lib/db";
import { getOrderedSelection, selection } from "$lib/components/file-list/Selection"; import { getOrderedSelection, selection } from "$lib/components/file-list/Selection";
import { ListFileItem, ListTrackItem, ListTrackSegmentItem } from "$lib/components/file-list/FileList"; import { ListFileItem, ListTrackItem, ListTrackSegmentItem } from "$lib/components/file-list/FileList";
import { currentTool, streetViewEnabled, Tool } from "$lib/stores"; import { currentTool, streetViewEnabled, Tool } from "$lib/stores";
import { getClosestLinePoint, resetCursor, setGrabbingCursor } from "$lib/utils"; import { getClosestLinePoint, resetCursor, setGrabbingCursor } from "$lib/utils";
import { TimestampsMode } from "$lib/types";
const { streetViewSource, timestampsMode } = settings;
export const canChangeStart = writable(false); export const canChangeStart = writable(false);
@@ -459,7 +458,7 @@ export class RoutingControls {
} }
async appendAnchor(e: mapboxgl.MapMouseEvent) { // Add a new anchor to the end of the last segment async appendAnchor(e: mapboxgl.MapMouseEvent) { // Add a new anchor to the end of the last segment
if (get(streetViewEnabled) && get(streetViewSource) === 'google') { if (get(streetViewEnabled)) {
return; return;
} }
@@ -575,11 +574,9 @@ export class RoutingControls {
} }
if (anchors[0].point._data.index === 0) { // First anchor is the first point of the segment if (anchors[0].point._data.index === 0) { // First anchor is the first point of the segment
response[0].time = anchors[0].point.time;
anchors[0].point = response[0]; // replace the first anchor anchors[0].point = response[0]; // replace the first anchor
anchors[0].point._data.index = 0; anchors[0].point._data.index = 0;
} else if (anchors[0].point._data.index === segment.trkpt.length - 1 && distance(anchors[0].point.getCoordinates(), response[0].getCoordinates()) < 1) { // First anchor is the last point of the segment, and the new point is close enough } else if (anchors[0].point._data.index === segment.trkpt.length - 1 && distance(anchors[0].point.getCoordinates(), response[0].getCoordinates()) < 1) { // First anchor is the last point of the segment, and the new point is close enough
response[0].time = anchors[0].point.time;
anchors[0].point = response[0]; // replace the first anchor anchors[0].point = response[0]; // replace the first anchor
anchors[0].point._data.index = segment.trkpt.length - 1; anchors[0].point._data.index = segment.trkpt.length - 1;
} else { } else {
@@ -588,7 +585,6 @@ export class RoutingControls {
} }
if (anchors[anchors.length - 1].point._data.index === segment.trkpt.length - 1) { // Last anchor is the last point of the segment if (anchors[anchors.length - 1].point._data.index === segment.trkpt.length - 1) { // Last anchor is the last point of the segment
response[response.length - 1].time = anchors[anchors.length - 1].point.time;
anchors[anchors.length - 1].point = response[response.length - 1]; // replace the last anchor anchors[anchors.length - 1].point = response[response.length - 1]; // replace the last anchor
anchors[anchors.length - 1].point._data.index = segment.trkpt.length - 1; anchors[anchors.length - 1].point._data.index = segment.trkpt.length - 1;
} else { } else {
@@ -612,40 +608,20 @@ export class RoutingControls {
let startTime = anchors[0].point.time; let startTime = anchors[0].point.time;
if (stats.global.speed.moving > 0) { if (stats.global.speed.moving > 0) {
let replacingTime = 0;
if (get(timestampsMode) === TimestampsMode.PRESERVE_TIMESTAMPS) {
this.extendResponseToContiguousAdaptedTimePoints(segment, anchors, response);
response.forEach((point) => point.time = undefined);
startTime = anchors[0].point.time;
replacingTime = stats.local.time.total[anchors[anchors.length - 1].point._data.index] - stats.local.time.total[anchors[0].point._data.index];
if (replacingTime > 0) {
for (let i = 1; i < anchors.length - 1; i++) {
anchors[i].point._data['adapted_time'] = true;
}
}
}
let replacingDistance = 0; let replacingDistance = 0;
for (let i = 1; i < response.length; i++) { for (let i = 1; i < response.length; i++) {
replacingDistance += distance(response[i - 1].getCoordinates(), response[i].getCoordinates()) / 1000; replacingDistance += distance(response[i - 1].getCoordinates(), response[i].getCoordinates()) / 1000;
} }
let replacedDistance = stats.local.distance.moving[anchors[anchors.length - 1].point._data.index] - stats.local.distance.moving[anchors[0].point._data.index];
if (get(timestampsMode) === TimestampsMode.PRESERVE_AVERAGE_SPEED || replacingTime === 0) { let newDistance = stats.global.distance.moving + replacingDistance - replacedDistance;
let replacedDistance = stats.local.distance.moving[anchors[anchors.length - 1].point._data.index] - stats.local.distance.moving[anchors[0].point._data.index]; let newTime = newDistance / stats.global.speed.moving * 3600;
let newDistance = stats.global.distance.moving + replacingDistance - replacedDistance; let remainingTime = stats.global.time.moving - (stats.local.time.moving[anchors[anchors.length - 1].point._data.index] - stats.local.time.moving[anchors[0].point._data.index]);
let newTime = newDistance / stats.global.speed.moving * 3600; let replacingTime = newTime - remainingTime;
let remainingTime = stats.global.time.moving - (stats.local.time.moving[anchors[anchors.length - 1].point._data.index] - stats.local.time.moving[anchors[0].point._data.index]); if (replacingTime <= 0) { // Fallback to simple time difference
replacingTime = newTime - remainingTime; replacingTime = stats.local.time.total[anchors[anchors.length - 1].point._data.index] - stats.local.time.total[anchors[0].point._data.index];
if (replacingTime <= 0) { // Fallback to simple time difference
replacingTime = stats.local.time.total[anchors[anchors.length - 1].point._data.index] - stats.local.time.total[anchors[0].point._data.index];
}
} }
speed = replacingDistance / replacingTime * 3600; speed = replacingDistance / replacingTime * 3600;
@@ -661,41 +637,6 @@ export class RoutingControls {
return true; return true;
} }
extendResponseToContiguousAdaptedTimePoints(segment: TrackSegment, anchors: Anchor[], response: TrackPoint[]) {
while (anchors[0].point._data.adapted_time) {
let previousAnchor = null;
for (let i = 0; i < this.anchors.length; i++) {
if (this.anchors[i].point._data.index < anchors[0].point._data.index) {
previousAnchor = this.anchors[i];
} else {
break;
}
}
if (previousAnchor === null) {
break;
} else {
response.splice(0, 0, ...segment.trkpt.slice(previousAnchor.point._data.index, anchors[0].point._data.index).map((point) => point.clone()));
anchors.splice(0, 0, previousAnchor);
}
}
while (anchors[anchors.length - 1].point._data.adapted_time) {
let nextAnchor = null;
for (let i = this.anchors.length - 1; i >= 0; i--) {
if (this.anchors[i].point._data.index > anchors[anchors.length - 1].point._data.index) {
nextAnchor = this.anchors[i];
} else {
break;
}
}
if (nextAnchor === null) {
break;
} else {
response.push(...segment.trkpt.slice(anchors[anchors.length - 1].point._data.index + 1, nextAnchor.point._data.index + 1).map((point) => point.clone()));
anchors.push(nextAnchor);
}
}
}
destroy() { destroy() {
this.remove(); this.remove();
this.unsubscribes.forEach((unsubscribe) => unsubscribe()); this.unsubscribes.forEach((unsubscribe) => unsubscribe());

View File

@@ -1,33 +1,31 @@
<script lang="ts"> <script lang="ts">
import { ScrollArea as ScrollAreaPrimitive } from 'bits-ui'; import { ScrollArea as ScrollAreaPrimitive } from "bits-ui";
import { Scrollbar } from './index.js'; import { Scrollbar } from "./index.js";
import { cn } from '$lib/utils.js'; import { cn } from "$lib/utils.js";
type $$Props = ScrollAreaPrimitive.Props & { type $$Props = ScrollAreaPrimitive.Props & {
orientation?: 'vertical' | 'horizontal' | 'both'; orientation?: "vertical" | "horizontal" | "both";
scrollbarXClasses?: string; scrollbarXClasses?: string;
scrollbarYClasses?: string; scrollbarYClasses?: string;
viewportClasses?: string;
}; };
let className: $$Props['class'] = undefined; let className: $$Props["class"] = undefined;
export { className as class }; export { className as class };
export let orientation = 'vertical'; export let orientation = "vertical";
export let scrollbarXClasses: string = ''; export let scrollbarXClasses: string = "";
export let scrollbarYClasses: string = ''; export let scrollbarYClasses: string = "";
export let viewportClasses: string = '';
</script> </script>
<ScrollAreaPrimitive.Root {...$$restProps} class={cn('relative overflow-hidden', className)}> <ScrollAreaPrimitive.Root {...$$restProps} class={cn("relative overflow-hidden", className)}>
<ScrollAreaPrimitive.Viewport class={cn('h-full w-full rounded-[inherit]', viewportClasses)}> <ScrollAreaPrimitive.Viewport class="h-full w-full rounded-[inherit]">
<ScrollAreaPrimitive.Content> <ScrollAreaPrimitive.Content>
<slot /> <slot />
</ScrollAreaPrimitive.Content> </ScrollAreaPrimitive.Content>
</ScrollAreaPrimitive.Viewport> </ScrollAreaPrimitive.Viewport>
{#if orientation === 'vertical' || orientation === 'both'} {#if orientation === "vertical" || orientation === "both"}
<Scrollbar orientation="vertical" class={scrollbarYClasses} /> <Scrollbar orientation="vertical" class={scrollbarYClasses} />
{/if} {/if}
{#if orientation === 'horizontal' || orientation === 'both'} {#if orientation === "horizontal" || orientation === "both"}
<Scrollbar orientation="horizontal" class={scrollbarXClasses} /> <Scrollbar orientation="horizontal" class={scrollbarXClasses} />
{/if} {/if}
<ScrollAreaPrimitive.Corner /> <ScrollAreaPrimitive.Corner />

View File

@@ -9,8 +9,8 @@ import { ListFileItem, ListItem, ListTrackItem, ListLevel, ListTrackSegmentItem,
import { updateAnchorPoints } from '$lib/components/toolbar/tools/routing/Simplify'; import { updateAnchorPoints } from '$lib/components/toolbar/tools/routing/Simplify';
import { SplitType } from '$lib/components/toolbar/tools/scissors/Scissors.svelte'; import { SplitType } from '$lib/components/toolbar/tools/scissors/Scissors.svelte';
import { getClosestLinePoint, getElevation } from '$lib/utils'; import { getClosestLinePoint, getElevation } from '$lib/utils';
import { TimestampsMode } from '$lib/types';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import type mapboxgl from 'mapbox-gl';
enableMapSet(); enableMapSet();
enablePatches(); enablePatches();
@@ -91,7 +91,6 @@ export const settings = {
routing: dexieSettingStore('routing', true), routing: dexieSettingStore('routing', true),
routingProfile: dexieSettingStore('routingProfile', 'bike'), routingProfile: dexieSettingStore('routingProfile', 'bike'),
privateRoads: dexieSettingStore('privateRoads', false), privateRoads: dexieSettingStore('privateRoads', false),
timestampsMode: dexieSettingStore('timestampsMode', TimestampsMode.PRESERVE_AVERAGE_SPEED),
currentBasemap: dexieSettingStore('currentBasemap', defaultBasemap), currentBasemap: dexieSettingStore('currentBasemap', defaultBasemap),
previousBasemap: dexieSettingStore('previousBasemap', defaultBasemap), previousBasemap: dexieSettingStore('previousBasemap', defaultBasemap),
selectedBasemapTree: dexieSettingStore('selectedBasemapTree', defaultBasemapTree), selectedBasemapTree: dexieSettingStore('selectedBasemapTree', defaultBasemapTree),
@@ -575,7 +574,7 @@ export const dbUtils = {
}); });
}); });
}, },
mergeSelection: (mergeTraces: boolean, removeGaps: boolean) => { mergeSelection: (mergeTraces: boolean) => {
applyGlobal((draft) => { applyGlobal((draft) => {
let first = true; let first = true;
let target: ListItem = new ListRootItem(); let target: ListItem = new ListRootItem();
@@ -650,7 +649,7 @@ export const dbUtils = {
let s = new TrackSegment(); let s = new TrackSegment();
toMerge.trk.map((track) => { toMerge.trk.map((track) => {
track.trkseg.forEach((segment) => { track.trkseg.forEach((segment) => {
s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime, removeGaps); s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime);
}); });
}); });
toMerge.trk = [toMerge.trk[0]]; toMerge.trk = [toMerge.trk[0]];
@@ -659,7 +658,7 @@ export const dbUtils = {
if (toMerge.trkseg.length > 0) { if (toMerge.trkseg.length > 0) {
let s = new TrackSegment(); let s = new TrackSegment();
toMerge.trkseg.forEach((segment) => { toMerge.trkseg.forEach((segment) => {
s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime, removeGaps); s.replaceTrackPoints(s.trkpt.length, s.trkpt.length, segment.trkpt.slice(), speed, startTime);
}); });
toMerge.trkseg = [s]; toMerge.trkseg = [s];
} }

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Soubory a statistiky
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Pomocí tlačítek vpravo od výškového profilu můžete volitelně obarvit výškový profil podle:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -9,29 +9,29 @@ title: Erste Schritte
# { title } # { title }
Willkommen bei der offiziellen Anleitung für **gpx.studio**! Willkommen bei der offiziellen Anleitung für **gpx.studio**!
Diese Anleitung führt Sie durch alle Komponenten und Werkzeuge der Benutzeroberfläche und hilft Ihnen dabei ein erfahrener Anwender zu werden. This guide will walk you through all the components and tools of the interface, helping you become a proficient user of the application.
<DocsImage src="getting-started/interface" alt="Die gpx.studio Oberfläche." /> <DocsImage src="getting-started/interface" alt="Die gpx.studio Oberfläche." />
Wie im obigen Screenshot dargestellt, ist die Benutzeroberfläche in vier Bereiche um die Karte herum aufgeteilt. Wie im obigen Screenshot dargestellt, ist die Oberfläche in vier Bereiche um die Karte herum aufgeteilt.
Bevor wir in die Details der einzelnen Bereiche einsteigen, lassen Sie uns einen Überblick der Benutzeroberfläche verschaffen. Bevor wir in die Details der einzelnen Bereiche einsteigen, lassen Sie uns einen Überblick der Oberfläche verschaffen.
## Menü ## Menü
Am oberen Rand der Benutzeroberfläche findest du das [Hauptmenü](./menu). At the top of the interface, you will find the [main menu](./menu).
Hier können Sie auf allgemeine Aktionen zugreifen, wie zum Beispiel das Öffnen, Schließen und Exportieren von Dateien, Rückgängig machen und Wiederholen sowie Anpassen der Einstellungen der Anwendung. This is where you can access common actions such as opening, closing, and exporting files, undoing and redoing actions, and adjusting the application settings.
## Files and statistics ## Files and statistics
Am unteren Rand der Benutzeroberfläche finden Sie die Liste der aktuell geöffneten Dateien der Anwendung. At the bottom of the interface, you will find the list of files currently open in the application.
Sie können auf eine Datei klicken, um sie auszuwählen und ihre Statistiken unter der Liste anzuzeigen. You can click on a file to select it and display its statistics below the list.
Im [zugehörigen Abschnitt](./files-and-stats) werden wir erklären, wie wir mehrere Dateien auswählen und zu einem vertikalen Layout für eine erweiterte Dateiverwaltung wechseln. In the [dedicated section](./files-and-stats), we will explain how to select multiple files and switch to a vertical layout for advanced file management.
## Werkzeugleiste ## Werkzeugleiste
Am linken Rand der Benutzeroberfläche finden Sie die [Werkzeugleiste](./toolbar), die alle Werkzeuge enthält, mit denen Sie Ihre Dateien bearbeiten können. On the left side of the interface, you will find the [toolbar](./toolbar), which contains all the tools you can use to edit your files.
## Kartensteuerungen ## Kartensteuerungen
Schließlich findest du am rechten Rand der Benutzeroberfläche die [Kartensteuerungen](./map-controls). Finally, on the right side of the interface, you will find the [map controls](./map-controls).
Mit diesen Steuerelementen können Sie auf der Karte navigieren, vergrößern und zwischen verschiedenen Kartenstilen wechseln. These controls allow you to navigate the map, zoom in and out, and switch between different map styles.

View File

@@ -11,7 +11,7 @@ title: Kartensteuerungen
# { title } # { title }
The map controls are located on the right side of the interface. The map controls are located on the right side of the interface.
Mit diesen Steuerelementen können Sie auf der Karte navigieren, vergrößern und zwischen verschiedenen Kartenstilen wechseln. These controls allow you to navigate the map, zoom in and out, and switch between different map styles.
### <Diff size="16" class="inline-block" style="margin-bottom: 2px" /> Map navigation ### <Diff size="16" class="inline-block" style="margin-bottom: 2px" /> Map navigation

View File

@@ -8,10 +8,10 @@ title: Menü
# { title } # { title }
Das Hauptmenü, welches sich oben in der Benutzeroberfläche befindet, bietet Zugriff auf Aktionen, Optionen, und Einstellungen, die wiederum in mehrere Kategorien aufgeteilt sind, wird in den folgenden Abschnitten beschrieben. The main menu, located at the top of the interface, provides access to actions, options, and settings divided into several categories, explained separately in the following sections.
<DocsNote> <DocsNote>
Die meisten Aktionen des Menüs können auch mit den Tastaturkürzeln ausgeführt werden, die im Menü angezeigt werden. Most of the menu actions can also be performed using the keyboard shortcuts displayed in the menu.
</DocsNote> </DocsNote>

View File

@@ -18,7 +18,7 @@ title: Werkzeugleiste
# { title } # { title }
Die Symbolleiste befindet sich auf der linken Seite der Karte und ist das Herzstück der Anwendung, da es Zugriff auf die wichtigsten Funktionen von **gpx.studio** bietet. The toolbar is located on the left side of the map and is the heart of the application, as it provides access to the main features of **gpx.studio**.
Jedes Werkzeug wird durch ein Symbol dargestellt und kann durch einen Klick aktiviert werden. Jedes Werkzeug wird durch ein Symbol dargestellt und kann durch einen Klick aktiviert werden.
<div class="flex flex-row justify-center text-foreground"> <div class="flex flex-row justify-center text-foreground">
@@ -29,4 +29,4 @@ Jedes Werkzeug wird durch ein Symbol dargestellt und kann durch einen Klick akti
As with [edit actions](./menu/edit), most tools can be applied to multiple files at once and to [inner tracks and segments](./gpx). As with [edit actions](./menu/edit), most tools can be applied to multiple files at once and to [inner tracks and segments](./gpx).
Die folgenden Abschnitten beschreiben jedes Werkzeug im Detail. In den folgenden Abschnitten werden die einzelnen Tools detailliert beschrieben.

View File

@@ -10,17 +10,17 @@ title: Extrahieren
# <Ungroup size="24" class="inline-block" style="margin-bottom: 5px" /> { title } # <Ungroup size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
Dieses Werkzeug ermöglicht Ihnen [Tracks (oder Segmente)](../gpx) von Dateien (oder Tracks) zu extrahieren, die mehreren von ihnen enthalten. Dieses Tool ermöglicht Ihnen, [tracks (or segments)](../gpx) von Dateien (oder Tracks) zu extrahieren, die mehreren von denen enthalten.
<div class="flex flex-row justify-center"> <div class="flex flex-row justify-center">
<Extract class="text-foreground p-3 border rounded-md shadow-lg" /> <Extract class="text-foreground p-3 border rounded-md shadow-lg" />
</div> </div>
Wenn Sie das Werkzeug auf eine Datei mit mehreren enthaltenen Tracks anwenden, wird für jeden der darin enthaltenen Tracks eine neue Datei erstellt. Wenn Sie das Tool auf eine Datei mit mehreren enthaltenen Tracks anwenden, wird für jeden der darin enthaltenen Tracks eine neue Datei erstellt.
Similarly, applying the tool to a track containing multiple segments will create (in the same file) a new track for each of the segments it contains. Similarly, applying the tool to a track containing multiple segments will create (in the same file) a new track for each of the segments it contains.
<DocsNote> <DocsNote>
Beim Extrahieren der Tracks aus einer Datei mit <a href="../gpx">points-of-interest</a>, wird das Werkzeug automatisch jeden Punkt zuordnen, an dem es am nächsten ist. Beim Extrahieren der Tracks aus einer Datei mit <a href="../gpx">points-of-interest</a>, wird das Tool automatisch jeden Punkt zuordnen, an dem es am nächsten ist.
</DocsNote> </DocsNote>

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,9 +73,9 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Archivos y estadísticas
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ Puede usar el ratón para acercar o alejar el perfil de elevación y moverse hac
### Datos adicionales ### Datos adicionales
Usando el botón <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> en la parte inferior derecha del perfil de elevación, opcionalmente puede colorear el perfil de elevación por: Usando los botones situados a la derecha del perfil de elevación, puede colorearlo por:
- Información de la **pendiente** calculada a partir de los datos de elevación, o - **pendiente** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> información calculada a partir de los datos de elevación, o
- Datos de **superficie** o **categoría** provenientes de la <a href="https://www.openstreetmap.org/" target="_blank">superficie</a> y etiquetas de <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">autopista</a> de <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">OpenStreetMap</a>. - **superficie** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> datos obtenidos de etiquetas de <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">superficie</a> de <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>.
Solo disponible para archivos creados con **gpx.studio**. Solo disponible para archivos creados con **gpx.studio**.
Si su selección lo incluye, también puede visualizar datos de **velocidad**, **ritmo cardíaco**, **cadencia**, **temperatura** y **potencia** en el perfil de elevación. Si su selección lo incluye, puede visualizar datos de **velocidad** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **ritmo cardíaco** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadencia** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperatura** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" /> y **potencia** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> en el perfil de elevación.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Fichiers et statistiques
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ Vous pouvez également utiliser la molette de la souris pour zoomer ou dézoomer
### Données supplémentaires ### Données supplémentaires
En utilisant le bouton <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> en bas à droite du profil d'altitude, vous pouvez optionnellement colorier le profil altimétrique en fonction : En utilisant les boutons à droite du profil d'altitude, vous pouvez optionnellement colorier le profil d'altitude en fonction de :
- des données de **pente** calculées à partir des données d'altitude, ou - des données de **pente** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> calculées à partir des données d'altitude, ou
- des données de **revêtement** ou de **catégorie** venant des tags <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> et <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> d'<a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>. - des données de **revêtement** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> venant des tags de <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> d'<a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>.
Ceci est uniquement disponible pour les fichiers créés avec **gpx.studio**. Ceci est uniquement disponible pour les fichiers créés avec **gpx.studio**.
Si votre sélection possède ces données, vous pouvez également visualiser les données de : **vitesse** , **fréquence cardiaque** , **cadence** , **température** et **puissance** sur le profil altimétrique. Si votre sélection possède ces données, vous pouvez également visualiser les données de : **vitesse** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **fréquence cardiaque** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **température** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" /> et **puissance** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> sur le profil altimétrique.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -1,5 +1,5 @@
--- ---
title: GYIK title: FAQ
--- ---
<script> <script>
@@ -10,7 +10,7 @@ title: GYIK
### Do I need to donate to use the website? ### Do I need to donate to use the website?
Nem. No.
The website is free to use and always will be (as long as it is financially sustainable). The website is free to use and always will be (as long as it is financially sustainable).
However, donations are appreciated and help keep the website running. However, donations are appreciated and help keep the website running.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -69,14 +69,14 @@ When hovering over the elevation profile, a tooltip will show statistics at the
To get the statistics for a specific section of the elevation profile, you can drag a selection rectangle on the profile. To get the statistics for a specific section of the elevation profile, you can drag a selection rectangle on the profile.
Click on the profile to reset the selection. Click on the profile to reset the selection.
Az egérgörgővel is méretezheti a magassági profilt. Balra és jobbra mozoghat a profil húzásával, miközben lenyomva tartja a <kbd>Shift</kbd> billentyűt. Az egérgörgővel is méretezhetii a magassági profilt. Balra és jobbra mozoghat a profil húzásával, miközben lenyomva tartja a <kbd>Shift</kbd> billentyűt.
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -7,6 +7,6 @@
Minden alkalommal, amikor GPS-pontokat ad hozzá vagy mozgat, szervereink kiszámítják a legjobb útvonalat az úthálózaton.<a href="https://mapbox.com" target="_blank">Mapbox</a> API-jait használjuk a gyönyörű térképek megjelenítésére, a magassági adatok lekérésére és a helyek keresésére. Minden alkalommal, amikor GPS-pontokat ad hozzá vagy mozgat, szervereink kiszámítják a legjobb útvonalat az úthálózaton.<a href="https://mapbox.com" target="_blank">Mapbox</a> API-jait használjuk a gyönyörű térképek megjelenítésére, a magassági adatok lekérésére és a helyek keresésére.
Sajnos ez magas költségű. Sajnos ez magas költségű.
Ha tetszik ez az alkalmazás, kérjük, fontoljon meg egy kis adományt, hogy a webhely továbbra is ingyenes és hirdetésmentes legyen. Ha tetszik ezt az alkalmazás, kérjük, fontoljon meg egy kis adományt, hogy a webhely továbbra is ingyenes és hirdetésmentes legyen.
Nagyon szépen köszönöm a támogatást! ❤️ Nagyon szépen köszönöm a támogatást! ❤️

View File

@@ -1,5 +1,5 @@
--- ---
title: Integráció title: Integration
--- ---
<script> <script>

View File

@@ -12,6 +12,6 @@ The main menu, located at the top of the interface, provides access to actions,
<DocsNote> <DocsNote>
A menüben található műveletek többsége a menüben megjelenített billentyűparancsok segítségével is végrehajtható. Most of the menu actions can also be performed using the keyboard shortcuts displayed in the menu.
</DocsNote> </DocsNote>

View File

@@ -29,4 +29,4 @@ Each tool is represented by an icon and can be activated by clicking on it.
As with [edit actions](./menu/edit), most tools can be applied to multiple files at once and to [inner tracks and segments](./gpx). As with [edit actions](./menu/edit), most tools can be applied to multiple files at once and to [inner tracks and segments](./gpx).
A következő szakaszok részletesen ismertetik az egyes eszközöket. The next sections describe each tool in detail.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Dati aggiuntivi ### Dati aggiuntivi
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Utilizzando i pulsanti a destra del profilo di elevazione, è possibile colorare opzionalmente il profilo di elevazione mediante:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> informazioni calcolate dai dati di elevazione, o
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - .
È disponibile solo per i file creati con **gpx.studio**. È disponibile solo per i file creati con **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. Se la tua selezione lo prevede puoi visualizzare anche i dati di: **velocità** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **frequenza cardiaca** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadenza** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperatura** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, e **potenza** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> sul profilo altimetrico.

View File

@@ -1,5 +1,5 @@
--- ---
title: Azioni sui File title: Azione sui File
--- ---
<script lang="ts"> <script lang="ts">
@@ -17,7 +17,7 @@ Crea un nuovo file vuoto.
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Apri... ### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Apri...
Apre i file dal tuo computer. Open files from your computer.
<DocsNote> <DocsNote>
@@ -33,7 +33,7 @@ Crea una copia dei file attualmente selezionati.
Close the currently selected files. Close the currently selected files.
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" />Chiudi tutto ### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Close all
Chiudi tutti i file. Chiudi tutti i file.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Puslapis nerastas
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Bestanden en statistieken
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ Je kunt het muiswiel ook gebruiken om in en uit te zoomen op het hoogte profiel,
### Aanvullende gegevens ### Aanvullende gegevens
Met behulp van de <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> knoppen aan de rechterkant van het hoogteprofiel kunt u het hoogteprofiel optioneel kleuren door: Met behulp van de knoppen aan de rechterkant van het hoogteprofiel kunt u het hoogteprofiel optioneel kleuren door:
- **helling** informatie berekend uit de hoogtegegevens, of - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> informatie berekend op basis van de hoogtegegevens, of
- **oppervlakte** of **categorie** gegevens afkomstig van <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>zijn <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">oppervlak</a> en <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">snelweg</a> tags. - **oppervlak** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data afkomstig van <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
Dit is alleen beschikbaar voor bestanden gemaakt met **gpx.studio**. Dit is alleen beschikbaar voor bestanden gemaakt met **gpx.studio**.
Als je selectie het bevat, kun je ook de volgende gegevens zichtbaar maken op het hoogteprofiel: **snelheid**, **hartritme**, **tempo**, **temperatuur**, en **vermogen**. Als je selectie het bevat, kun je ook visualiseren: **snelheid** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **hartsnelheid** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, en **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data op het hoogteprofiel.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Arquivos e estatísticas
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ Você também pode usar a roda do mouse para aumentar e diminuir o zoom no perfi
### Dados adicionais ### Dados adicionais
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Usando os botões à direita do perfil de elevação, você pode opcionalmente colorir o perfil de elevação por:
- **slope** information computed from the elevation data, or - **inclinação** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> informação computada a partir dos dados de elevação, ou
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **superfície** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> dados vindos dos marcadores de <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">superfície</a> do <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>.
Isso só está disponível para arquivos criados com **gpx.studio**. Isso só está disponível para arquivos criados com **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. Se sua seleção incluir, você também pode visualizar: **velocidade** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **frequência cardíaca** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadência** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperatura** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />e **energia** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> dados sobre o perfil de elevação.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Файлы и статистика
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ title: Файлы и статистика
### Дополнительные данные ### Дополнительные данные
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: С помощью кнопок справа от профиля высоты можно изменить цвет профиля высоты:
- **slope** information computed from the elevation data, or - **угол наклона** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> информация, вычисленная на основе данных о высоте, или
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **поверхность** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> данные, полученные из тегов <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a> по ключу <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a>.
Это доступно только для файлов, созданных с помощью **gpx.studio**. Это доступно только для файлов, созданных с помощью **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. Если ваш выбор включает это, вы также можете визуализировать: **скорость** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **частоту сердечных сокращений** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **каденцию** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **температуру** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, и **мощность** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> данные на профиле высоты.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -73,10 +73,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -3,7 +3,7 @@ title: Files and statistics
--- ---
<script> <script>
import { ChartNoAxesColumn } from 'lucide-svelte'; import { TriangleRight, BrickWall, Zap, HeartPulse, Orbit, Thermometer, SquareActivity } from 'lucide-svelte';
import DocsNote from '$lib/components/docs/DocsNote.svelte'; import DocsNote from '$lib/components/docs/DocsNote.svelte';
</script> </script>
@@ -74,10 +74,10 @@ You can also use the mouse wheel to zoom in and out on the elevation profile, an
### Additional data ### Additional data
Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-bottom: 2px"/></kbd> button at the bottom-right of the elevation profile, you can optionally color the elevation profile by: Using the buttons on the right of the elevation profile, you can optionally color the elevation profile by:
- **slope** information computed from the elevation data, or - **slope** <TriangleRight size="16" class="inline-block" style="margin-bottom: 2px" /> information computed from the elevation data, or
- **surface** or **category** data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> and <a href="https://wiki.openstreetmap.org/wiki/Key:highway" target="_blank">highway</a> tags. - **surface** <BrickWall size="16" class="inline-block" style="margin-bottom: 2px" /> data coming from <a href="https://www.openstreetmap.org/" target="_blank">OpenStreetMap</a>'s <a href="https://wiki.openstreetmap.org/wiki/Key:surface" target="_blank">surface</a> tags.
This is only available for files created with **gpx.studio**. This is only available for files created with **gpx.studio**.
If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile. If your selection includes it, you can also visualize: **speed** <Zap size="16" class="inline-block" style="margin-bottom: 2px" />, **heart rate** <HeartPulse size="16" class="inline-block" style="margin-bottom: 2px" />, **cadence** <Orbit size="16" class="inline-block" style="margin-bottom: 2px" />, **temperature** <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" />, and **power** <SquareActivity size="16" class="inline-block" style="margin-bottom: 2px" /> data on the elevation profile.

View File

@@ -1,4 +0,0 @@
export enum TimestampsMode {
PRESERVE_TIMESTAMPS = 'preserve_timestamps',
PRESERVE_AVERAGE_SPEED = 'preserve_average_speed',
};

View File

@@ -12,7 +12,6 @@ import mapboxgl from "mapbox-gl";
import tilebelt from "@mapbox/tilebelt"; import tilebelt 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 { DateFormatter } from "@internationalized/date";
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)); return twMerge(clsx(inputs));
@@ -217,15 +216,3 @@ export function getURLForLanguage(lang: string | null | undefined, path: string)
} }
} }
} }
function getDateFormatter(locale: string) {
return new Intl.DateTimeFormat(locale, {
dateStyle: 'medium',
timeStyle: 'medium'
});
}
export let df: DateFormatter = getDateFormatter('en');
locale.subscribe((l) => {
df = getDateFormatter(l ?? 'en');
});

View File

@@ -119,8 +119,11 @@
"unpaved": "Unpaved", "unpaved": "Unpaved",
"asphalt": "Asphalt", "asphalt": "Asphalt",
"concrete": "Concrete", "concrete": "Concrete",
"chipseal": "Chipseal",
"cobblestone": "Cobblestone", "cobblestone": "Cobblestone",
"unhewn_cobblestone": "Unhewn cobblestone",
"paving_stones": "Paving stones", "paving_stones": "Paving stones",
"stepping_stones": "Stepping stones",
"sett": "Sett", "sett": "Sett",
"metal": "Metal", "metal": "Metal",
"wood": "Wood", "wood": "Wood",
@@ -132,59 +135,15 @@
"dirt": "Dirt", "dirt": "Dirt",
"ground": "Ground", "ground": "Ground",
"earth": "Earth", "earth": "Earth",
"snow": "Snow",
"ice": "Ice",
"salt": "Salt",
"mud": "Mud", "mud": "Mud",
"sand": "Sand", "sand": "Sand",
"woodchips": "Woodchips",
"grass": "Grass", "grass": "Grass",
"grass_paver": "Grass paver", "grass_paver": "Grass paver"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "The start point is too far from the nearest road", "from": "The start point is too far from the nearest road",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
@@ -214,7 +173,6 @@
"merge_traces": "Connect the traces", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Merge selection", "merge_selection": "Merge selection",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Merge items together", "tooltip": "Merge items together",
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.", "help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Distance", "distance": "Distance",
@@ -402,11 +366,9 @@
"power": "Power", "power": "Power",
"slope": "Slope", "slope": "Slope",
"surface": "Surface", "surface": "Surface",
"highway": "Category",
"time": "Time", "time": "Time",
"moving": "Moving", "moving": "Moving",
"total": "Total", "total": "Total"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "No pavimentat", "unpaved": "No pavimentat",
"asphalt": "Asfalt", "asphalt": "Asfalt",
"concrete": "Formigó", "concrete": "Formigó",
"chipseal": "Camí asfaltat",
"cobblestone": "Llambordes", "cobblestone": "Llambordes",
"unhewn_cobblestone": "Llambordes grosses",
"paving_stones": "Camí de pedres", "paving_stones": "Camí de pedres",
"stepping_stones": "Empedrat",
"sett": "Establir", "sett": "Establir",
"metal": "Metall", "metal": "Metall",
"wood": "Fusta", "wood": "Fusta",
@@ -132,59 +135,15 @@
"dirt": "Terra", "dirt": "Terra",
"ground": "Terreny", "ground": "Terreny",
"earth": "Planeta Terra", "earth": "Planeta Terra",
"snow": "Neu",
"ice": "Gel",
"salt": "Sal",
"mud": "Fang", "mud": "Fang",
"sand": "Sorra", "sand": "Sorra",
"woodchips": "Estella forestal",
"grass": "Herba", "grass": "Herba",
"grass_paver": "Gespa", "grass_paver": "Gespa"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "El punt d'inici és massa lluny de la via més propera", "from": "El punt d'inici és massa lluny de la via més propera",
"via": "El punt és massa lluny de la via més propera", "via": "El punt és massa lluny de la via més propera",
@@ -214,7 +173,6 @@
"merge_traces": "Connectar les traçades", "merge_traces": "Connectar les traçades",
"merge_contents": "Unir els continguts i mantenir les traçades desconnectades", "merge_contents": "Unir els continguts i mantenir les traçades desconnectades",
"merge_selection": "Combinar la selecció", "merge_selection": "Combinar la selecció",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Unir els elements", "tooltip": "Unir els elements",
"help_merge_traces": "Unir les traçades crearà una única traçada contínua.", "help_merge_traces": "Unir les traçades crearà una única traçada contínua.",
"help_cannot_merge_traces": "Has de sel·leccionar diverses traçades per tal d'unir-les.", "help_cannot_merge_traces": "Has de sel·leccionar diverses traçades per tal d'unir-les.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Distancia", "distance": "Distancia",
@@ -402,11 +366,9 @@
"power": "Power", "power": "Power",
"slope": "Pendent", "slope": "Pendent",
"surface": "Superfície", "surface": "Superfície",
"highway": "Category",
"time": "Time", "time": "Time",
"moving": "Moving", "moving": "Moving",
"total": "Total", "total": "Total"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Nezpevněné", "unpaved": "Nezpevněné",
"asphalt": "Asfalt", "asphalt": "Asfalt",
"concrete": "Beton", "concrete": "Beton",
"chipseal": "Chipseal",
"cobblestone": "Dlažební kostky", "cobblestone": "Dlažební kostky",
"unhewn_cobblestone": "Hrubá dlažba",
"paving_stones": "Dlažba", "paving_stones": "Dlažba",
"stepping_stones": "Nášlapné kameny",
"sett": "Dlažební kostky", "sett": "Dlažební kostky",
"metal": "Kov", "metal": "Kov",
"wood": "Dřevo", "wood": "Dřevo",
@@ -132,59 +135,15 @@
"dirt": "Hlína", "dirt": "Hlína",
"ground": "Pozemek", "ground": "Pozemek",
"earth": "Země", "earth": "Země",
"snow": "Sníh",
"ice": "Led",
"salt": "Sůl",
"mud": "Bláto", "mud": "Bláto",
"sand": "Písek", "sand": "Písek",
"woodchips": "Dřevěné štěpky",
"grass": "Tráva", "grass": "Tráva",
"grass_paver": "Zatravňovací dlažba", "grass_paver": "Zatravňovací dlažba"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "Počáteční bod je příliš daleko od nejbližší cesty", "from": "Počáteční bod je příliš daleko od nejbližší cesty",
"via": "Průchozí bod je příliš daleko od nejbližší cesty", "via": "Průchozí bod je příliš daleko od nejbližší cesty",
@@ -214,7 +173,6 @@
"merge_traces": "Připojit trasy", "merge_traces": "Připojit trasy",
"merge_contents": "Sloučit obsah a zachovat trasy oddělené", "merge_contents": "Sloučit obsah a zachovat trasy oddělené",
"merge_selection": "Sloučit výběr", "merge_selection": "Sloučit výběr",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Sloučit položky", "tooltip": "Sloučit položky",
"help_merge_traces": "Připojení vybraných tras vytvoří jednu spojitou trasu.", "help_merge_traces": "Připojení vybraných tras vytvoří jednu spojitou trasu.",
"help_cannot_merge_traces": "Váš výběr musí obsahovat několik tras, které je chcete připojit.", "help_cannot_merge_traces": "Váš výběr musí obsahovat několik tras, které je chcete připojit.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topografická", "ignFrTopo": "IGN Topografická",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satelitní", "ignFrSatellite": "IGN Satelitní",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topografická", "swedenTopo": "Lantmäteriet Topografická",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Zobrazit sklon svahu",
"show_surface": "Zobrazit údaje o povrchu",
"show_speed": "Zobrazit údaje o rychlosti",
"show_pace": "Zobrazit údaje o tempu",
"show_heartrate": "Zobrazit údaje o tepové frekvenci",
"show_cadence": "Zobrazit údaje o kadenci",
"show_temperature": "Zobrazit údaje o teplotě",
"show_power": "Zobrazit údaje o energii"
}, },
"quantities": { "quantities": {
"distance": "Vzdálenost", "distance": "Vzdálenost",
@@ -402,11 +366,9 @@
"power": "Energie", "power": "Energie",
"slope": "Sklon", "slope": "Sklon",
"surface": "Povrch", "surface": "Povrch",
"highway": "Category",
"time": "Čas", "time": "Čas",
"moving": "V pohybu", "moving": "V pohybu",
"total": "Celkem", "total": "Celkem"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Unpaved", "unpaved": "Unpaved",
"asphalt": "Asphalt", "asphalt": "Asphalt",
"concrete": "Concrete", "concrete": "Concrete",
"chipseal": "Chipseal",
"cobblestone": "Cobblestone", "cobblestone": "Cobblestone",
"unhewn_cobblestone": "Unhewn cobblestone",
"paving_stones": "Paving stones", "paving_stones": "Paving stones",
"stepping_stones": "Stepping stones",
"sett": "Sett", "sett": "Sett",
"metal": "Metal", "metal": "Metal",
"wood": "Wood", "wood": "Wood",
@@ -132,59 +135,15 @@
"dirt": "Dirt", "dirt": "Dirt",
"ground": "Ground", "ground": "Ground",
"earth": "Earth", "earth": "Earth",
"snow": "Snow",
"ice": "Ice",
"salt": "Salt",
"mud": "Mud", "mud": "Mud",
"sand": "Sand", "sand": "Sand",
"woodchips": "Woodchips",
"grass": "Grass", "grass": "Grass",
"grass_paver": "Grass paver", "grass_paver": "Grass paver"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "The start point is too far from the nearest road", "from": "The start point is too far from the nearest road",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
@@ -214,7 +173,6 @@
"merge_traces": "Connect the traces", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Merge selection", "merge_selection": "Merge selection",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Merge items together", "tooltip": "Merge items together",
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.", "help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Distance", "distance": "Distance",
@@ -402,11 +366,9 @@
"power": "Power", "power": "Power",
"slope": "Slope", "slope": "Slope",
"surface": "Surface", "surface": "Surface",
"highway": "Category",
"time": "Time", "time": "Time",
"moving": "Moving", "moving": "Moving",
"total": "Total", "total": "Total"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -1,7 +1,7 @@
{ {
"metadata": { "metadata": {
"home_title": "der online GPX Datei Editor", "home_title": "der online GPX Datei Editor",
"app_title": "App", "app_title": "app",
"embed_title": "der online GPX Datei Editor", "embed_title": "der online GPX Datei Editor",
"help_title": "hilfe", "help_title": "hilfe",
"404_title": "Seite nicht gefunden", "404_title": "Seite nicht gefunden",
@@ -33,8 +33,8 @@
"select_all": "Alle auswählen", "select_all": "Alle auswählen",
"view": "Ansicht", "view": "Ansicht",
"elevation_profile": "Höhenprofil", "elevation_profile": "Höhenprofil",
"vertical_file_view": "Vertikale Dateiliste", "vertical_file_view": "Vertical file list",
"switch_basemap": "Zur vorherigen Basemap wechseln", "switch_basemap": "Switch to previous basemap",
"toggle_overlays": "Toggle overlays", "toggle_overlays": "Toggle overlays",
"toggle_3d": "3D umschalten", "toggle_3d": "3D umschalten",
"settings": "Einstellungen", "settings": "Einstellungen",
@@ -49,21 +49,21 @@
"language": "Sprache", "language": "Sprache",
"mode": "Design", "mode": "Design",
"system": "System", "system": "System",
"light": "Hell", "light": "Light",
"dark": "Dunkel", "dark": "Dark",
"street_view_source": "Street View Quelle", "street_view_source": "Street view source",
"mapillary": "Mapillary", "mapillary": "Mapillary",
"google": "Google", "google": "Google",
"toggle_street_view": "Street View", "toggle_street_view": "Street view",
"layers": "Map layers...", "layers": "Map layers...",
"distance_markers": "Entfernungsmarkierungen", "distance_markers": "Entfernungsmarkierungen",
"direction_markers": "Direction arrows", "direction_markers": "Direction arrows",
"help": "Hilfe", "help": "Hilfe",
"more": "Mehr...", "more": "More...",
"donate": "Spenden", "donate": "Spenden",
"ctrl": "Strg", "ctrl": "Strg",
"click": "Klick", "click": "Klick",
"drag": "Ziehen", "drag": "Drag",
"metadata": { "metadata": {
"button": "Info...", "button": "Info...",
"name": "Bezeichnung", "name": "Bezeichnung",
@@ -77,124 +77,83 @@
"width": "Breite" "width": "Breite"
}, },
"hide": "Verbergen", "hide": "Verbergen",
"unhide": "Einblenden", "unhide": "Unhide",
"center": "Center", "center": "Center",
"open_in": "Open in" "open_in": "Open in"
}, },
"toolbar": { "toolbar": {
"routing": { "routing": {
"tooltip": "Route planen oder bearbeiten", "tooltip": "Plan or edit a route",
"activity": "Aktivität", "activity": "Aktivität",
"use_routing": "Routenführung", "use_routing": "Routenführung",
"use_routing_tooltip": "Connect anchor points via road network, or in a straight line if disabled", "use_routing_tooltip": "Connect anchor points via road network, or in a straight line if disabled",
"allow_private": "Private Straßen erlauben", "allow_private": "Allow private roads",
"reverse": { "reverse": {
"button": "Umkehren", "button": "Umkehren",
"tooltip": "Die Richtung der Route umkehren" "tooltip": "Reverse the direction of the route"
}, },
"route_back_to_start": { "route_back_to_start": {
"button": "Zurück zum Start", "button": "Back to start",
"tooltip": "Den letzten Punkt der Route mit dem Startpunkt verbinden" "tooltip": "Connect the last point of the route with the starting point"
}, },
"round_trip": { "round_trip": {
"button": "Rundfahrt", "button": "Round trip",
"tooltip": "Kehre mit derselben Route zum Startpunkt zurück" "tooltip": "Return to the starting point by the same route"
}, },
"start_loop_here": "Schleife hier starten", "start_loop_here": "Schleife hier starten",
"help_no_file": "Select a trace to use the routing tool, or click on the map to start creating a new route.", "help_no_file": "Select a trace to use the routing tool, or click on the map to start creating a new route.",
"help": "Click on the map to add a new anchor point, or drag existing ones to change the route.", "help": "Click on the map to add a new anchor point, or drag existing ones to change the route.",
"activities": { "activities": {
"bike": "Fahrrad", "bike": "Rad",
"racing_bike": "Rennrad", "racing_bike": "Road bike",
"gravel_bike": "Gravel Bike", "gravel_bike": "Gravel Bike",
"mountain_bike": "Mountainbike", "mountain_bike": "Mountainbike",
"foot": "Laufen/Wandern", "foot": "Laufen/Wandern",
"motorcycle": "Motorrad", "motorcycle": "Moped",
"water": "Wasser", "water": "Water",
"railway": "Railway" "railway": "Railway"
}, },
"surface": { "surface": {
"unknown": "Unbekannt", "unknown": "Unknown",
"paved": "Gepflastert", "paved": "Gepflastert",
"unpaved": "Unbefestigt", "unpaved": "Unbefestigt",
"asphalt": "Asphalt", "asphalt": "Asphalt",
"concrete": "Beton", "concrete": "Beton",
"chipseal": "Chipseal",
"cobblestone": "Kopfsteinpflaster", "cobblestone": "Kopfsteinpflaster",
"paving_stones": "Verbundsteine", "unhewn_cobblestone": "Unhewn cobblestone",
"sett": "Behauenes Steinpflaster", "paving_stones": "Paving stones",
"stepping_stones": "Stepping stones",
"sett": "Sett",
"metal": "Metall", "metal": "Metall",
"wood": "Holz", "wood": "Holz",
"compacted": "verdichtet", "compacted": "Compacted gravel",
"fine_gravel": "Feiner Schotter", "fine_gravel": "Fine gravel",
"gravel": "Schotter", "gravel": "Schotter",
"pebblestone": "Kieselsteine", "pebblestone": "Kieselsteine",
"rock": "Fels", "rock": "Fels",
"dirt": "Dreck", "dirt": "Dreck",
"ground": "Boden", "ground": "Boden",
"earth": "Erde", "earth": "Erde",
"snow": "Schnee",
"ice": "Eis",
"salt": "Salz",
"mud": "Matsch", "mud": "Matsch",
"sand": "Sand", "sand": "Sand",
"woodchips": "Holzhäcksel",
"grass": "Gras", "grass": "Gras",
"grass_paver": "Grass paver", "grass_paver": "Grass paver"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "Der Startpunkt ist zu weit von der nächsten Straße entfernt", "from": "The start point is too far from the nearest road",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
"to": "Der Endpunkt ist zu weit von der nächsten Straße entfernt", "to": "The end point is too far from the nearest road",
"timeout": "Route calculation took too long, try adding points closer together" "timeout": "Route calculation took too long, try adding points closer together"
} }
}, },
"scissors": { "scissors": {
"tooltip": "Schneiden oder Teilen", "tooltip": "Crop or split",
"crop": "Schneiden", "crop": "Crop",
"split_as": "Split the trace into", "split_as": "Split the trace into",
"help_invalid_selection": "Select a trace to crop or split.", "help_invalid_selection": "Select a trace to crop or split.",
"help": "Use the slider to crop the trace, or split it by clicking on one of the split markers or on the trace itself." "help": "Use the slider to crop the trace, or split it by clicking on one of the split markers or on the trace itself."
@@ -202,11 +161,11 @@
"time": { "time": {
"tooltip": "Manage time data", "tooltip": "Manage time data",
"start": "Start", "start": "Start",
"end": "Ziel", "end": "End",
"total_time": "Zeit in Bewegung", "total_time": "Zeit in Bewegung",
"pick_date": "Datum auswählen", "pick_date": "Pick a date",
"artificial": "Erstelle realistische Zeitdaten", "artificial": "Create realistic time data",
"update": "Aktualisiere Zeitdaten", "update": "Update time data",
"help": "Use the form to set new time data.", "help": "Use the form to set new time data.",
"help_invalid_selection": "Select a single trace to manage its time data." "help_invalid_selection": "Select a single trace to manage its time data."
}, },
@@ -214,8 +173,7 @@
"merge_traces": "Connect the traces", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Auswahl zusammenführen", "merge_selection": "Auswahl zusammenführen",
"remove_gaps": "Remove time gaps between traces", "tooltip": "Merge items together",
"tooltip": "Elemente zusammenführen",
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.", "help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
"help_merge_contents": "Merging the contents of the selected items will group all the contents inside the first item.", "help_merge_contents": "Merging the contents of the selected items will group all the contents inside the first item.",
@@ -237,8 +195,8 @@
"tooltip": "Create and edit points of interest", "tooltip": "Create and edit points of interest",
"icon": "Icon", "icon": "Icon",
"link": "Link", "link": "Link",
"longitude": "Längengrad", "longitude": "Longitude",
"latitude": "Breitengrad", "latitude": "Latitude",
"create": "Create point of interest", "create": "Create point of interest",
"add": "Add point of interest to file", "add": "Add point of interest to file",
"help": "Fill in the form to create a new point of interest, or click on an existing one to edit it. Click on the map to fill the coordinates, or drag points of interest to move them.", "help": "Fill in the form to create a new point of interest, or click on an existing one to edit it. Click on the map to fill the coordinates, or drag points of interest to move them.",
@@ -246,15 +204,15 @@
}, },
"reduce": { "reduce": {
"tooltip": "Reduce the number of GPS points", "tooltip": "Reduce the number of GPS points",
"tolerance": "Toleranz", "tolerance": "Tolerance",
"number_of_points": "Anzahl der GPS-Punkte", "number_of_points": "Number of GPS points",
"button": "Minify", "button": "Minify",
"help": "Use the slider to choose the number of GPS points to keep.", "help": "Use the slider to choose the number of GPS points to keep.",
"help_no_selection": "Select a trace to reduce the number of its GPS points." "help_no_selection": "Select a trace to reduce the number of its GPS points."
}, },
"clean": { "clean": {
"tooltip": "Clean GPS points and points of interest with a rectangle selection", "tooltip": "Clean GPS points and points of interest with a rectangle selection",
"delete_trackpoints": "GPS-Punkte löschen", "delete_trackpoints": "Delete GPS points",
"delete_waypoints": "Delete points of interest", "delete_waypoints": "Delete points of interest",
"delete_inside": "Delete inside selection", "delete_inside": "Delete inside selection",
"delete_outside": "Delete outside selection", "delete_outside": "Delete outside selection",
@@ -285,19 +243,19 @@
"basemaps": "Basiskarte", "basemaps": "Basiskarte",
"overlays": "Ebenen", "overlays": "Ebenen",
"custom": "Custom", "custom": "Custom",
"world": "Welt", "world": "World",
"countries": "Länder", "countries": "Countries",
"belgium": "Belgien", "belgium": "Belgium",
"bulgaria": "Bulgarien", "bulgaria": "Bulgarien",
"finland": "Finnland", "finland": "Finland",
"france": "Frankreich", "france": "France",
"new_zealand": "Neuseeland", "new_zealand": "New Zealand",
"norway": "Norwegen", "norway": "Norway",
"spain": "Spanien", "spain": "Spain",
"sweden": "Schweden", "sweden": "Sweden",
"switzerland": "Schweiz", "switzerland": "Switzerland",
"united_kingdom": "Großbritannien", "united_kingdom": "United Kingdom",
"united_states": "USA", "united_states": "United States",
"mapboxOutdoors": "Mapbox Outdoors", "mapboxOutdoors": "Mapbox Outdoors",
"mapboxSatellite": "Mapbox Satellit", "mapboxSatellite": "Mapbox Satellit",
"openStreetMap": "OpenStreetMap", "openStreetMap": "OpenStreetMap",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -337,50 +294,50 @@
"ignSlope": "IGN Slope", "ignSlope": "IGN Slope",
"ignSkiTouring": "IGN Ski Touring", "ignSkiTouring": "IGN Ski Touring",
"waymarked_trails": "Waymarked Trails", "waymarked_trails": "Waymarked Trails",
"waymarkedTrailsHiking": "Wandern", "waymarkedTrailsHiking": "Hiking",
"waymarkedTrailsCycling": "Radfahren", "waymarkedTrailsCycling": "Cycling",
"waymarkedTrailsMTB": "MTB", "waymarkedTrailsMTB": "MTB",
"waymarkedTrailsSkating": "Skating", "waymarkedTrailsSkating": "Skating",
"waymarkedTrailsHorseRiding": "Horse Riding", "waymarkedTrailsHorseRiding": "Horse Riding",
"waymarkedTrailsWinter": "Winter", "waymarkedTrailsWinter": "Winter",
"points_of_interest": "Points of interest", "points_of_interest": "Points of interest",
"food": "Food", "food": "Food",
"bakery": "Bäckerei", "bakery": "Bakery",
"food-store": "Food Store", "food-store": "Food Store",
"eat-and-drink": "Eat and Drink", "eat-and-drink": "Eat and Drink",
"amenities": "Amenities", "amenities": "Amenities",
"toilets": "Toiletten", "toilets": "Toilets",
"water": "Trinkwasser", "water": "Water",
"shower": "Dusche", "shower": "Shower",
"shelter": "Unterstand", "shelter": "Shelter",
"motorized": "Autos und Motorräder", "motorized": "Cars and Motorcycles",
"fuel-station": "Tankstelle", "fuel-station": "Fuel Station",
"parking": "Parking", "parking": "Parking",
"garage": "Garage", "garage": "Garage",
"barrier": "Barriere", "barrier": "Barrier",
"tourism": "Tourismus", "tourism": "Tourism",
"attraction": "Attraction", "attraction": "Attraction",
"viewpoint": "Aussichtspunkt", "viewpoint": "Viewpoint",
"hotel": "Hotel", "hotel": "Hotel",
"campsite": "Campingplatz", "campsite": "Campsite",
"hut": "Hütte", "hut": "Hut",
"picnic": "Picknickplatz", "picnic": "Picnic Area",
"summit": "Gipfel", "summit": "Summit",
"pass": "Gebirgspass", "pass": "Pass",
"climbing": "Climbing", "climbing": "Climbing",
"bicycle": "Fahrrad", "bicycle": "Bicycle",
"bicycle-parking": "Bicycle Parking", "bicycle-parking": "Bicycle Parking",
"bicycle-rental": "Fahrradverleih", "bicycle-rental": "Bicycle Rental",
"bicycle-shop": "Fahrradgeschäft", "bicycle-shop": "Fahrradgeschäft",
"public-transport": "Öffentliche Verkehrsmittel", "public-transport": "Öffentliche Verkehrsmittel",
"railway-station": "Bahnhof", "railway-station": "Bahnhof",
"tram-stop": "Straßenbahnhaltestelle", "tram-stop": "Straßenbahnhaltestelle",
"bus-stop": "Bushaltestelle", "bus-stop": "Bushaltestelle",
"ferry": "Fähre" "ferry": "Ferry"
}, },
"color": { "color": {
"blue": "Blau", "blue": "Blau",
"bluered": "Blau Rot", "bluered": "Blue Red",
"gray": "Grau", "gray": "Grau",
"hot": "Heiß", "hot": "Heiß",
"purple": "Lila", "purple": "Lila",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Distanz", "distance": "Distanz",
@@ -397,16 +361,14 @@
"temperature": "Temperatur", "temperature": "Temperatur",
"speed": "Geschwindigkeit", "speed": "Geschwindigkeit",
"pace": "Tempo", "pace": "Tempo",
"heartrate": "Herzfrequenz", "heartrate": "Heart rate",
"cadence": "Trittfrequenz", "cadence": "Trittfrequenz",
"power": "Leistung", "power": "Leistung",
"slope": "Gefälle", "slope": "Gefälle",
"surface": "Oberfläche", "surface": "Oberfläche",
"highway": "Category",
"time": "Zeit", "time": "Zeit",
"moving": "Bewegung", "moving": "Moving",
"total": "Gesamt", "total": "Gesamt"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",
@@ -427,8 +389,8 @@
"power": "W" "power": "W"
}, },
"gpx": { "gpx": {
"file": "Datei", "file": "File",
"files": "Dateien", "files": "Files",
"track": "Strecke", "track": "Strecke",
"tracks": "Strecken", "tracks": "Strecken",
"segment": "Abschnitt", "segment": "Abschnitt",
@@ -439,56 +401,56 @@
"alert": "Alert", "alert": "Alert",
"anchor": "Anchor", "anchor": "Anchor",
"bank": "Bank", "bank": "Bank",
"beach": "Strand", "beach": "Beach",
"bike_trail": "Bike Trail", "bike_trail": "Bike Trail",
"binoculars": "Binoculars", "binoculars": "Binoculars",
"bridge": "Brücke", "bridge": "Bridge",
"building": "Gebäude", "building": "Building",
"campground": "Campingplatz", "campground": "Campsite",
"car": "Auto", "car": "Car",
"car_repair": "Garage", "car_repair": "Garage",
"convenience_store": "Convenience Store", "convenience_store": "Convenience Store",
"crossing": "Crossing", "crossing": "Crossing",
"department_store": "Department Store", "department_store": "Department Store",
"drinking_water": "Trinkwasser", "drinking_water": "Water",
"exit": "Exit", "exit": "Exit",
"lodge": "Hut", "lodge": "Hut",
"lodging": "Accommodation", "lodging": "Accommodation",
"forest": "Wald", "forest": "Forest",
"gas_station": "Tankstelle", "gas_station": "Fuel Station",
"ground_transportation": "Ground Transportation", "ground_transportation": "Ground Transportation",
"hotel": "Hotel", "hotel": "Hotel",
"house": "House", "house": "House",
"information": "Information", "information": "Information",
"park": "Park", "park": "Park",
"parking_area": "Parkplatz", "parking_area": "Parking",
"pharmacy": "Apotheke", "pharmacy": "Pharmacy",
"picnic_area": "Picknickplatz", "picnic_area": "Picnic Area",
"restaurant": "Restaurant", "restaurant": "Restaurant",
"restricted_area": "Restricted Area", "restricted_area": "Restricted Area",
"restroom": "Toiletten", "restroom": "Toilets",
"road": "Straße", "road": "Road",
"scenic_area": "Scenic Area", "scenic_area": "Scenic Area",
"shelter": "Unterstand", "shelter": "Shelter",
"shopping_center": "Einkaufszentrum", "shopping_center": "Shopping Center",
"shower": "Dusche", "shower": "Shower",
"summit": "Gipfel", "summit": "Summit",
"telephone": "Telefon", "telephone": "Telephone",
"tunnel": "Tunnel", "tunnel": "Tunnel",
"water_source": "Water Source" "water_source": "Water Source"
} }
}, },
"homepage": { "homepage": {
"website": "Webseite", "website": "Website",
"home": "Home", "home": "Home",
"app": "App", "app": "App",
"contact": "Kontakt", "contact": "Contact",
"reddit": "Reddit", "reddit": "Reddit",
"x": "X", "x": "X",
"facebook": "Facebook", "facebook": "Facebook",
"github": "GitHub", "github": "GitHub",
"crowdin": "Crowdin", "crowdin": "Crowdin",
"email": "E-Mail", "email": "Email",
"contribute": "Contribute", "contribute": "Contribute",
"supported_by": "supported by", "supported_by": "supported by",
"support_button": "Support gpx.studio on Ko-fi", "support_button": "Support gpx.studio on Ko-fi",
@@ -504,19 +466,19 @@
"identity_description": "The website is free to use, without ads, and the source code is publicly available on GitHub. This is only possible thanks to the incredible support of the community." "identity_description": "The website is free to use, without ads, and the source code is publicly available on GitHub. This is only possible thanks to the incredible support of the community."
}, },
"docs": { "docs": {
"translate": "Verbessere die Übersetzung auf Crowdin", "translate": "Improve the translation on Crowdin",
"answer_not_found": "Did not find what you were looking for?", "answer_not_found": "Did not find what you were looking for?",
"ask_on_reddit": "Frage die Community auf Reddit", "ask_on_reddit": "Ask the community on Reddit",
"search": { "search": {
"search": "Suche", "search": "Search",
"clear": "Clear", "clear": "Clear",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"recent": "Letzte Suchanfragen", "recent": "Recent searches",
"no_recent": "Keine kürzlichen Suchanfragen", "no_recent": "No recent searches",
"save": "Diese Suchanfrage speichern", "save": "Save this search",
"remove": "Diese Suchanfrage aus dem Verlauf entfernen", "remove": "Remove this search from history",
"favorites": "Favoriten", "favorites": "Favorites",
"remove_favorite": "Diese Suchanfrage aus Favoriten entfernen", "remove_favorite": "Remove this search from favorites",
"to_select": "to select", "to_select": "to select",
"to_navigate": "to navigate", "to_navigate": "to navigate",
"to_close": "to close", "to_close": "to close",
@@ -544,7 +506,7 @@
"preview": "Vorschau", "preview": "Vorschau",
"code": "Integration code" "code": "Integration code"
}, },
"webgl2_required": "WebGL 2 ist erforderlich, um die Karte anzuzeigen.", "webgl2_required": "WebGL 2 is required to display the map.",
"enable_webgl2": "Erfahren Sie, wie Sie WebGL 2 in Ihrem Browser aktivieren", "enable_webgl2": "Learn how to enable WebGL 2 in your browser",
"page_not_found": "Seite nicht gefunden" "page_not_found": "page not found"
} }

View File

@@ -119,8 +119,11 @@
"unpaved": "Μη ασφαλτοστρωμένο", "unpaved": "Μη ασφαλτοστρωμένο",
"asphalt": "Άσφαλτος", "asphalt": "Άσφαλτος",
"concrete": "Τσιμέντο", "concrete": "Τσιμέντο",
"chipseal": "Chipseal",
"cobblestone": "Cobblestone", "cobblestone": "Cobblestone",
"unhewn_cobblestone": "Unhewn cobblestone",
"paving_stones": "Paving stones", "paving_stones": "Paving stones",
"stepping_stones": "Stepping stones",
"sett": "Sett", "sett": "Sett",
"metal": "Μέταλλο", "metal": "Μέταλλο",
"wood": "Ξύλο", "wood": "Ξύλο",
@@ -132,59 +135,15 @@
"dirt": "Βρωμιά", "dirt": "Βρωμιά",
"ground": "Χώμα", "ground": "Χώμα",
"earth": "Γη", "earth": "Γη",
"snow": "Χιόνι",
"ice": "Πάγος",
"salt": "Αλάτι",
"mud": "Λάσπη", "mud": "Λάσπη",
"sand": "Άμμος", "sand": "Άμμος",
"woodchips": "Woodchips",
"grass": "Γρασίδι", "grass": "Γρασίδι",
"grass_paver": "Grass paver", "grass_paver": "Grass paver"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "The start point is too far from the nearest road", "from": "The start point is too far from the nearest road",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
@@ -214,7 +173,6 @@
"merge_traces": "Connect the traces", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Merge selection", "merge_selection": "Merge selection",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Merge items together", "tooltip": "Merge items together",
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.", "help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Απόσταση", "distance": "Απόσταση",
@@ -402,11 +366,9 @@
"power": "Power", "power": "Power",
"slope": "Πλαγιά", "slope": "Πλαγιά",
"surface": "Επιφάνεια", "surface": "Επιφάνεια",
"highway": "Category",
"time": "Time", "time": "Time",
"moving": "Moving", "moving": "Moving",
"total": "Σύνολο", "total": "Σύνολο"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "μέτρα", "meters": "μέτρα",

View File

@@ -88,8 +88,6 @@
"use_routing": "Routing", "use_routing": "Routing",
"use_routing_tooltip": "Connect anchor points via road network, or in a straight line if disabled", "use_routing_tooltip": "Connect anchor points via road network, or in a straight line if disabled",
"allow_private": "Allow private roads", "allow_private": "Allow private roads",
"preserve_average_speed": "Maintain average speed",
"preserve_timestamps": "Adjust timestamps to fill gaps",
"reverse": { "reverse": {
"button": "Reverse", "button": "Reverse",
"tooltip": "Reverse the direction of the route" "tooltip": "Reverse the direction of the route"
@@ -121,8 +119,11 @@
"unpaved": "Unpaved", "unpaved": "Unpaved",
"asphalt": "Asphalt", "asphalt": "Asphalt",
"concrete": "Concrete", "concrete": "Concrete",
"chipseal": "Chipseal",
"cobblestone": "Cobblestone", "cobblestone": "Cobblestone",
"unhewn_cobblestone": "Unhewn cobblestone",
"paving_stones": "Paving stones", "paving_stones": "Paving stones",
"stepping_stones": "Stepping stones",
"sett": "Sett", "sett": "Sett",
"metal": "Metal", "metal": "Metal",
"wood": "Wood", "wood": "Wood",
@@ -134,59 +135,15 @@
"dirt": "Dirt", "dirt": "Dirt",
"ground": "Ground", "ground": "Ground",
"earth": "Earth", "earth": "Earth",
"snow": "Snow",
"ice": "Ice",
"salt": "Salt",
"mud": "Mud", "mud": "Mud",
"sand": "Sand", "sand": "Sand",
"woodchips": "Woodchips",
"grass": "Grass", "grass": "Grass",
"grass_paver": "Grass paver", "grass_paver": "Grass paver"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "The start point is too far from the nearest road", "from": "The start point is too far from the nearest road",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
@@ -216,7 +173,6 @@
"merge_traces": "Connect the traces", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Merge selection", "merge_selection": "Merge selection",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Merge items together", "tooltip": "Merge items together",
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.", "help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
@@ -316,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -390,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Distance", "distance": "Distance",
@@ -404,11 +366,9 @@
"power": "Power", "power": "Power",
"slope": "Slope", "slope": "Slope",
"surface": "Surface", "surface": "Surface",
"highway": "Category",
"time": "Time", "time": "Time",
"moving": "Moving", "moving": "Moving",
"total": "Total", "total": "Total"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Sin pavimento", "unpaved": "Sin pavimento",
"asphalt": "Asfalto", "asphalt": "Asfalto",
"concrete": "Hormigón", "concrete": "Hormigón",
"chipseal": "Pavimento delgado",
"cobblestone": "Adoquinado", "cobblestone": "Adoquinado",
"unhewn_cobblestone": "Adoquín sin labrar",
"paving_stones": "Adoquines", "paving_stones": "Adoquines",
"stepping_stones": "Peldaños",
"sett": "Adoquín", "sett": "Adoquín",
"metal": "Metal", "metal": "Metal",
"wood": "Madera", "wood": "Madera",
@@ -132,59 +135,15 @@
"dirt": "Tierra", "dirt": "Tierra",
"ground": "Tierra", "ground": "Tierra",
"earth": "Tierra natural", "earth": "Tierra natural",
"snow": "Nevado",
"ice": "Helado",
"salt": "Salado",
"mud": "Barro", "mud": "Barro",
"sand": "Arena", "sand": "Arena",
"woodchips": "Virutas de madera",
"grass": "Hierba", "grass": "Hierba",
"grass_paver": "Pavimento con césped", "grass_paver": "Pavimento con césped"
"clay": "Arcilla",
"stone": "Piedra"
}, },
"highway": {
"unknown": "Desconocido",
"motorway": "Autopista",
"motorway_link": "Enlace de autopista",
"trunk": "Carretera principal",
"trunk_link": "Enlace de carretera principal",
"primary": "Carretera principal",
"primary_link": "Enlace de carretera principal",
"secondary": "Carretera secundaria",
"secondary_link": "Enlace de carretera secundaria",
"tertiary": "Carretera terciaria",
"tertiary_link": "Enlace de carretera terciaria",
"unclassified": "Carretera menor",
"residential": "Vía residencial",
"living_street": "Calle residencial",
"service": "Vía de servicio",
"track": "Pista",
"footway": "Passarela",
"path": "Ruta",
"pedestrian": "Peatón",
"cycleway": "Ciclovia",
"steps": "Pasos",
"road": "Carretera",
"bridleway": "Ruta ecuestre",
"platform": "Plataforma",
"raceway": "Circuito de carreras",
"rest_area": "Área de descanso",
"abandoned": "Abandonado",
"services": "Servicios",
"corridor": "Pasillo",
"bus_stop": "Parada de autobús",
"busway": "Carril bus",
"elevator": "Ascensor",
"via_ferrata": "Vía ferrata",
"proposed": "Carretera por construir",
"construction": "Carretera en construcción"
},
"sac_scale": {
"hiking": "Senderismo",
"mountain_hiking": "Senderismo de montaña",
"demanding_mountain_hiking": "Senderismo de montaña exigente",
"alpine_hiking": "Senderismo alpino",
"demanding_alpine_hiking": "Senderismo alpino exigente",
"difficult_alpine_hiking": "Senderismo alpino difícil"
},
"mtb_scale": "Escala MTB",
"error": { "error": {
"from": "El punto de inicio está demasiado lejos de la carretera más cercana", "from": "El punto de inicio está demasiado lejos de la carretera más cercana",
"via": "El punto de paso está demasiado lejos de la carretera más cercana", "via": "El punto de paso está demasiado lejos de la carretera más cercana",
@@ -214,7 +173,6 @@
"merge_traces": "Conectar los trazados", "merge_traces": "Conectar los trazados",
"merge_contents": "Combinar los contenidos y mantener los trazados desconectados", "merge_contents": "Combinar los contenidos y mantener los trazados desconectados",
"merge_selection": "Combinar selección", "merge_selection": "Combinar selección",
"remove_gaps": "Eliminar intervalos vacíos de tiempo entre trazas",
"tooltip": "Combinar elementos", "tooltip": "Combinar elementos",
"help_merge_traces": "Conectar los trazados seleccionados creará un único trazado continuo.", "help_merge_traces": "Conectar los trazados seleccionados creará un único trazado continuo.",
"help_cannot_merge_traces": "Su selección debe contener varios trazados para conectarlos.", "help_cannot_merge_traces": "Su selección debe contener varios trazados para conectarlos.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satélite", "ignFrSatellite": "IGN Satélite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satélite",
"ordnanceSurvey": "Encuesta Ordnance", "ordnanceSurvey": "Encuesta Ordnance",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Configuración del perfil de elevación" "show_slope": "Mostrar datos de pendiente",
"show_surface": "Mostrar datos de superficie",
"show_speed": "Mostrar datos de velocidad",
"show_pace": "Mostrar datos de ritmo",
"show_heartrate": "Mostrar datos de ritmo cardíaco",
"show_cadence": "Mostrar datos de cadencia",
"show_temperature": "Mostrar datos de temperatura",
"show_power": "Mostrar datos de potencia"
}, },
"quantities": { "quantities": {
"distance": "Distancia", "distance": "Distancia",
@@ -402,11 +366,9 @@
"power": "Potencia", "power": "Potencia",
"slope": "Pendiente", "slope": "Pendiente",
"surface": "Superficie", "surface": "Superficie",
"highway": "Categoría",
"time": "Tiempo", "time": "Tiempo",
"moving": "Movimiento", "moving": "Movimiento",
"total": "Total", "total": "Total"
"osm_extensions": "Datos OpenStreetMap"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Päällystämätön", "unpaved": "Päällystämätön",
"asphalt": "Asfaltti", "asphalt": "Asfaltti",
"concrete": "Betoni", "concrete": "Betoni",
"chipseal": "Öljysora",
"cobblestone": "Mukulakivi", "cobblestone": "Mukulakivi",
"unhewn_cobblestone": "Luonnonmukainen mukulakivi",
"paving_stones": "Katukivetys", "paving_stones": "Katukivetys",
"stepping_stones": "Astinkivet",
"sett": "Kivetys", "sett": "Kivetys",
"metal": "Metalli", "metal": "Metalli",
"wood": "Puu", "wood": "Puu",
@@ -132,59 +135,15 @@
"dirt": "Maa", "dirt": "Maa",
"ground": "Maa", "ground": "Maa",
"earth": "Maa", "earth": "Maa",
"snow": "Lumi",
"ice": "Jää",
"salt": "Suola",
"mud": "Muta", "mud": "Muta",
"sand": "Hiekka", "sand": "Hiekka",
"woodchips": "Hake",
"grass": "Nurmi", "grass": "Nurmi",
"grass_paver": "Nurmikivi", "grass_paver": "Nurmikivi"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "Aloituspiste on liian kaukana lähimmästä tiestä", "from": "Aloituspiste on liian kaukana lähimmästä tiestä",
"via": "Reittipiste on liian kaukana lähimmästä tiestä", "via": "Reittipiste on liian kaukana lähimmästä tiestä",
@@ -214,7 +173,6 @@
"merge_traces": "Yhdistä reitit", "merge_traces": "Yhdistä reitit",
"merge_contents": "Yhdistä reitit samaan tiedostoon", "merge_contents": "Yhdistä reitit samaan tiedostoon",
"merge_selection": "Yhdistä valitut", "merge_selection": "Yhdistä valitut",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Yhdistä tiedostot", "tooltip": "Yhdistä tiedostot",
"help_merge_traces": "Yhdistäminen luo valituista reiteistä yhden yhtenäisen reitin.", "help_merge_traces": "Yhdistäminen luo valituista reiteistä yhden yhtenäisen reitin.",
"help_cannot_merge_traces": "Valitse useampia reittejä yhdistääksesi ne.", "help_cannot_merge_traces": "Valitse useampia reittejä yhdistääksesi ne.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Maastokartta", "ignFrTopo": "IGN Maastokartta",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Ilmakuva", "ignFrSatellite": "IGN Ilmakuva",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Maastokartta", "swedenTopo": "Lantmäteriet Maastokartta",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Näytä syketiedot",
"show_cadence": "Show cadence data",
"show_temperature": "Näytä lämpötilatiedot",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Distance", "distance": "Distance",
@@ -402,11 +366,9 @@
"power": "Power", "power": "Power",
"slope": "Kaltevuus", "slope": "Kaltevuus",
"surface": "Surface", "surface": "Surface",
"highway": "Category",
"time": "Time", "time": "Time",
"moving": "Moving", "moving": "Moving",
"total": "Total", "total": "Total"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Sans revêtement", "unpaved": "Sans revêtement",
"asphalt": "Asphalte", "asphalt": "Asphalte",
"concrete": "Béton", "concrete": "Béton",
"chipseal": "Enduit superficiel routier",
"cobblestone": "Pavés", "cobblestone": "Pavés",
"unhewn_cobblestone": "Pavé non taillé",
"paving_stones": "Pavage en pierres", "paving_stones": "Pavage en pierres",
"stepping_stones": "Pierres de gué",
"sett": "Pavés", "sett": "Pavés",
"metal": "Métal", "metal": "Métal",
"wood": "Bois", "wood": "Bois",
@@ -132,59 +135,15 @@
"dirt": "Sol en érosion", "dirt": "Sol en érosion",
"ground": "Sol", "ground": "Sol",
"earth": "Terre", "earth": "Terre",
"snow": "Neige",
"ice": "Glace",
"salt": "Sel",
"mud": "Boue", "mud": "Boue",
"sand": "Sable", "sand": "Sable",
"woodchips": "Plaquette forestière",
"grass": "Herbe", "grass": "Herbe",
"grass_paver": "Mélange d'herbe et de pavés", "grass_paver": "Mélange d'herbe et de pavés"
"clay": "Argile",
"stone": "Pierre"
}, },
"highway": {
"unknown": "Inconnu",
"motorway": "Autoroute",
"motorway_link": "Bretelle d'autoroute",
"trunk": "Route principale",
"trunk_link": "Liaison de route principale",
"primary": "Route principale",
"primary_link": "Liaison de route principale",
"secondary": "Route secondaire",
"secondary_link": "Liaison de route secondaire",
"tertiary": "Route tertiaire",
"tertiary_link": "Liaison de route tertiaire",
"unclassified": "Route mineure",
"residential": "Route résidentielle",
"living_street": "Rue résidentielle",
"service": "Route de service",
"track": "Chemin",
"footway": "Chemin piéton",
"path": "Chemin",
"pedestrian": "Piéton",
"cycleway": "Voie cyclable",
"steps": "Marches",
"road": "Route",
"bridleway": "Chemin d'équitation",
"platform": "Quai",
"raceway": "Circuit de course",
"rest_area": "Aire de repos",
"abandoned": "Abandonné",
"services": "Services",
"corridor": "Couloir",
"bus_stop": "Arrêt de bus",
"busway": "Réservé bus",
"elevator": "Ascenseur",
"via_ferrata": "Via ferrata",
"proposed": "Route à construire",
"construction": "Route en construction"
},
"sac_scale": {
"hiking": "Randonnée",
"mountain_hiking": "Randonnée de montagne",
"demanding_mountain_hiking": "Randonnée de montagne exigeante",
"alpine_hiking": "Randonnée alpine",
"demanding_alpine_hiking": "Randonnée alpine exigeante",
"difficult_alpine_hiking": "Randonnée alpine difficile"
},
"mtb_scale": "Échelle VTT",
"error": { "error": {
"from": "Le point de départ est trop éloigné de la route la plus proche", "from": "Le point de départ est trop éloigné de la route la plus proche",
"via": "Le point de passage est trop éloigné de la route la plus proche", "via": "Le point de passage est trop éloigné de la route la plus proche",
@@ -214,7 +173,6 @@
"merge_traces": "Connecter les traces", "merge_traces": "Connecter les traces",
"merge_contents": "Fusionner les contenus et garder les traces déconnectées", "merge_contents": "Fusionner les contenus et garder les traces déconnectées",
"merge_selection": "Fusionner la sélection", "merge_selection": "Fusionner la sélection",
"remove_gaps": "Supprimer les écarts de temps entre les traces",
"tooltip": "Fusionner les éléments", "tooltip": "Fusionner les éléments",
"help_merge_traces": "Connecter les traces sélectionnées créera une seule trace continue.", "help_merge_traces": "Connecter les traces sélectionnées créera une seule trace continue.",
"help_cannot_merge_traces": "Votre sélection doit contenir plusieurs traces pour pouvoir les connecter.", "help_cannot_merge_traces": "Votre sélection doit contenir plusieurs traces pour pouvoir les connecter.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Réglages du profil altimétrique" "show_slope": "Afficher les données de pente",
"show_surface": "Afficher les données de surface",
"show_speed": "Afficher les données de vitesse",
"show_pace": "Afficher les données d'allure",
"show_heartrate": "Afficher les données de fréquence cardiaque",
"show_cadence": "Afficher les données de cadence",
"show_temperature": "Afficher les données de température",
"show_power": "Afficher les données de puissance"
}, },
"quantities": { "quantities": {
"distance": "Distance", "distance": "Distance",
@@ -401,12 +365,10 @@
"cadence": "Cadence", "cadence": "Cadence",
"power": "Puissance", "power": "Puissance",
"slope": "Pente", "slope": "Pente",
"surface": "Revêtement", "surface": "Surface",
"highway": "Catégorie",
"time": "Temps", "time": "Temps",
"moving": "En mouvement", "moving": "En mouvement",
"total": "Total", "total": "Total"
"osm_extensions": "Données d'OpenStreetMap"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Unpaved", "unpaved": "Unpaved",
"asphalt": "Asphalt", "asphalt": "Asphalt",
"concrete": "Concrete", "concrete": "Concrete",
"chipseal": "Chipseal",
"cobblestone": "Cobblestone", "cobblestone": "Cobblestone",
"unhewn_cobblestone": "Unhewn cobblestone",
"paving_stones": "Paving stones", "paving_stones": "Paving stones",
"stepping_stones": "Stepping stones",
"sett": "Sett", "sett": "Sett",
"metal": "Metal", "metal": "Metal",
"wood": "Wood", "wood": "Wood",
@@ -132,59 +135,15 @@
"dirt": "Dirt", "dirt": "Dirt",
"ground": "Ground", "ground": "Ground",
"earth": "Earth", "earth": "Earth",
"snow": "Snow",
"ice": "Ice",
"salt": "Salt",
"mud": "Mud", "mud": "Mud",
"sand": "Sand", "sand": "Sand",
"woodchips": "Woodchips",
"grass": "Grass", "grass": "Grass",
"grass_paver": "Grass paver", "grass_paver": "Grass paver"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "The start point is too far from the nearest road", "from": "The start point is too far from the nearest road",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
@@ -214,7 +173,6 @@
"merge_traces": "Connect the traces", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Merge selection", "merge_selection": "Merge selection",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Merge items together", "tooltip": "Merge items together",
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.", "help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Distance", "distance": "Distance",
@@ -402,11 +366,9 @@
"power": "Power", "power": "Power",
"slope": "Slope", "slope": "Slope",
"surface": "Surface", "surface": "Surface",
"highway": "Category",
"time": "Time", "time": "Time",
"moving": "Moving", "moving": "Moving",
"total": "Total", "total": "Total"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -5,7 +5,7 @@
"embed_title": "az online GPX szerkesztő", "embed_title": "az online GPX szerkesztő",
"help_title": "súgó", "help_title": "súgó",
"404_title": "az oldal nem található", "404_title": "az oldal nem található",
"description": "GPX fájlok online megtekintése, szerkesztése és létrehozása fejlett útvonal tervezési képességekkel és fájlfeldolgozó eszközökkel, gyönyörű térképekkel és részletes adatmegjelenítésekkel." "description": "GPX-fájlok online megtekintése, szerkesztése és létrehozása fejlett útvonaltervezési képességekkel és fájlfeldolgozó eszközökkel, gyönyörű térképekkel és részletes adatmegjelenítésekkel."
}, },
"menu": { "menu": {
"new": "Új", "new": "Új",
@@ -33,7 +33,7 @@
"select_all": "Összes kijelölése", "select_all": "Összes kijelölése",
"view": "Nézet", "view": "Nézet",
"elevation_profile": "Magassági profil", "elevation_profile": "Magassági profil",
"vertical_file_view": "Függőleges fájl lista", "vertical_file_view": "Függoleges fájl lista",
"switch_basemap": "Váltás az előző alaptérképre", "switch_basemap": "Váltás az előző alaptérképre",
"toggle_overlays": "Átfedés váltása", "toggle_overlays": "Átfedés váltása",
"toggle_3d": "3D nézet bekapcsolása", "toggle_3d": "3D nézet bekapcsolása",
@@ -54,7 +54,7 @@
"street_view_source": "Utcakép nézet forrása", "street_view_source": "Utcakép nézet forrása",
"mapillary": "Mapillary", "mapillary": "Mapillary",
"google": "Google", "google": "Google",
"toggle_street_view": "Utcakép nézet", "toggle_street_view": "Street view",
"layers": "Térképrétegek...", "layers": "Térképrétegek...",
"distance_markers": "Távolsági km szakaszok mutatása", "distance_markers": "Távolsági km szakaszok mutatása",
"direction_markers": "Menetirány mutatása", "direction_markers": "Menetirány mutatása",
@@ -86,11 +86,11 @@
"tooltip": "Útvonal tervezése vagy szerkesztése", "tooltip": "Útvonal tervezése vagy szerkesztése",
"activity": "Tevékenység", "activity": "Tevékenység",
"use_routing": "Útvonal választása", "use_routing": "Útvonal választása",
"use_routing_tooltip": "Útvonal követés kikapcsolva egyenes vonal rajzolása", "use_routing_tooltip": "Útvonal követés kikapcsolava egyenes vonal rajzolása",
"allow_private": "Magánutak engedélyezése", "allow_private": "Magánutak engedélyezése",
"reverse": { "reverse": {
"button": "Út menetirány váltás", "button": "Út menetirány váltás",
"tooltip": "Útvonal irányának megfordítása" "tooltip": "Útvonal irányát megfordítása"
}, },
"route_back_to_start": { "route_back_to_start": {
"button": "Vissza a starthoz", "button": "Vissza a starthoz",
@@ -119,8 +119,11 @@
"unpaved": "Földes", "unpaved": "Földes",
"asphalt": "Aszfalt", "asphalt": "Aszfalt",
"concrete": "Beton", "concrete": "Beton",
"chipseal": "Törmelékes",
"cobblestone": "Macskaköves", "cobblestone": "Macskaköves",
"unhewn_cobblestone": "Vágatlan nagy kavics",
"paving_stones": "Térkő", "paving_stones": "Térkő",
"stepping_stones": "Szikla lépcső",
"sett": "Kőlap", "sett": "Kőlap",
"metal": "Fém", "metal": "Fém",
"wood": "Fa", "wood": "Fa",
@@ -132,59 +135,15 @@
"dirt": "Piszok", "dirt": "Piszok",
"ground": "Föld", "ground": "Föld",
"earth": "Föld", "earth": "Föld",
"snow": "Hó",
"ice": "Jég",
"salt": "Só",
"mud": "Sár", "mud": "Sár",
"sand": "Homok", "sand": "Homok",
"woodchips": "Faforgács",
"grass": "Fű", "grass": "Fű",
"grass_paver": "Gyephézagos térkő", "grass_paver": "Gyephézagos térkő"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "A kiindulási pont túl messze van a legközelebbi úttól", "from": "A kiindulási pont túl messze van a legközelebbi úttól",
"via": "A köztes pont túl messze van a legközelebbi úttól", "via": "A köztes pont túl messze van a legközelebbi úttól",
@@ -200,21 +159,20 @@
"help": "Használja a csúszkát az útvonal kivágásához, vagy ossza fel az egyik jelölőre vagy magára az útvonalra kattintva." "help": "Használja a csúszkát az útvonal kivágásához, vagy ossza fel az egyik jelölőre vagy magára az útvonalra kattintva."
}, },
"time": { "time": {
"tooltip": "Idő beállítása", "tooltip": "Manage time data",
"start": "Rajt", "start": "Rajt",
"end": "Befejezés", "end": "End",
"total_time": "Mozgási idő", "total_time": "Mozgási idő",
"pick_date": "Válassz dátumot", "pick_date": "Pick a date",
"artificial": "Valósághű időadatok hozzáadása", "artificial": "Create realistic time data",
"update": "Használd a formot új idő beállításához.", "update": "Update time data",
"help": "Használd a form-ot az új idő beállításához.", "help": "Use the form to set new time data.",
"help_invalid_selection": "Válasszon ki egyetlen nyomvonalat az időadatainak kezeléséhez." "help_invalid_selection": "Select a single trace to manage its time data."
}, },
"merge": { "merge": {
"merge_traces": "Kösd össze az útvonalakat", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Egyesítés kijelölés", "merge_selection": "Egyesítés kijelőlés",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Merge items together", "tooltip": "Merge items together",
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.", "help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
@@ -226,10 +184,10 @@
"tooltip": "Extract contents to separate items", "tooltip": "Extract contents to separate items",
"button": "Extract", "button": "Extract",
"help": "Extracting the contents of the selected items will create a separate item for each of their contents.", "help": "Extracting the contents of the selected items will create a separate item for each of their contents.",
"help_invalid_selection": "Több nyomvonalat kell tartalmazzon a kijelölés a kinyeréshez." "help_invalid_selection": "Your selection must contain items with multiple traces to extract them."
}, },
"elevation": { "elevation": {
"button": "Magassági információk lekérése", "button": "Request elevation data",
"help": "Requesting elevation data will erase the existing elevation data, if any, and replace it with data from Mapbox.", "help": "Requesting elevation data will erase the existing elevation data, if any, and replace it with data from Mapbox.",
"help_no_selection": "Select a file item to request elevation data." "help_no_selection": "Select a file item to request elevation data."
}, },
@@ -238,23 +196,23 @@
"icon": "Icon", "icon": "Icon",
"link": "Link", "link": "Link",
"longitude": "Longitude", "longitude": "Longitude",
"latitude": "Szélesség", "latitude": "Latitude",
"create": "POI fájlba mentése", "create": "Create point of interest",
"add": "Add point of interest to file", "add": "Add point of interest to file",
"help": "Töltsd ki az űrlapot egy új POI létrehozásához, vagy kattints egy meglévőre a szerkesztéshez. Kattints a térképre a koordináták megadásához, vagy áthelyezéshez egérrel húzd el a POI-kat.", "help": "Fill in the form to create a new point of interest, or click on an existing one to edit it. Click on the map to fill the coordinates, or drag points of interest to move them.",
"help_no_selection": "Select a file to create or edit points of interest." "help_no_selection": "Select a file to create or edit points of interest."
}, },
"reduce": { "reduce": {
"tooltip": "Reduce the number of GPS points", "tooltip": "Reduce the number of GPS points",
"tolerance": "Tűréshatár", "tolerance": "Tolerance",
"number_of_points": "GPS pontok száma", "number_of_points": "Number of GPS points",
"button": "Minify", "button": "Minify",
"help": "Use the slider to choose the number of GPS points to keep.", "help": "Use the slider to choose the number of GPS points to keep.",
"help_no_selection": "Select a trace to reduce the number of its GPS points." "help_no_selection": "Select a trace to reduce the number of its GPS points."
}, },
"clean": { "clean": {
"tooltip": "Clean GPS points and points of interest with a rectangle selection", "tooltip": "Clean GPS points and points of interest with a rectangle selection",
"delete_trackpoints": "GPS pontok törlése", "delete_trackpoints": "Delete GPS points",
"delete_waypoints": "Delete points of interest", "delete_waypoints": "Delete points of interest",
"delete_inside": "Delete inside selection", "delete_inside": "Delete inside selection",
"delete_outside": "Delete outside selection", "delete_outside": "Delete outside selection",
@@ -264,14 +222,14 @@
} }
}, },
"layers": { "layers": {
"settings": "Réteg beállítások", "settings": "Layer settings",
"settings_help": "Select the map layers you want to show in the interface, add custom ones, and adjust their settings.", "settings_help": "Select the map layers you want to show in the interface, add custom ones, and adjust their settings.",
"selection": "Layer selection", "selection": "Layer selection",
"custom_layers": { "custom_layers": {
"title": "Custom layers", "title": "Custom layers",
"new": "New custom layer", "new": "New custom layer",
"edit": "Edit custom layer", "edit": "Edit custom layer",
"urls": "Webcím(ek)", "urls": "URL(s)",
"url_placeholder": "WMTS, WMS vagy Mapbox stílus JSON", "url_placeholder": "WMTS, WMS vagy Mapbox stílus JSON",
"max_zoom": "Max nagyítás", "max_zoom": "Max nagyítás",
"layer_type": "Réteg típus", "layer_type": "Réteg típus",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Műhold", "ignFrSatellite": "IGN Műhold",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Hadifelmérés", "ordnanceSurvey": "Hadifelmérés",
"norwayTopo": "Norvégia topográfiai térképe 4", "norwayTopo": "Norvégia topográfiai térképe 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -364,8 +321,8 @@
"hotel": "Hotel", "hotel": "Hotel",
"campsite": "Kemping", "campsite": "Kemping",
"hut": "Hut", "hut": "Hut",
"picnic": "Piknikező hely", "picnic": "Picnic Area",
"summit": "Csúcs", "summit": "Summit",
"pass": "Pass", "pass": "Pass",
"climbing": "Climbing", "climbing": "Climbing",
"bicycle": "Kerékpár", "bicycle": "Kerékpár",
@@ -376,7 +333,7 @@
"railway-station": "Vasútállomás", "railway-station": "Vasútállomás",
"tram-stop": "Villamos megálló", "tram-stop": "Villamos megálló",
"bus-stop": "Buszmegálló", "bus-stop": "Buszmegálló",
"ferry": "Komp" "ferry": "Ferry"
}, },
"color": { "color": {
"blue": "Kék", "blue": "Kék",
@@ -384,11 +341,18 @@
"gray": "Szürke", "gray": "Szürke",
"hot": "Legérdekesebb", "hot": "Legérdekesebb",
"purple": "Lila", "purple": "Lila",
"orange": "Narancssárga" "orange": "Orange"
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Távolság", "distance": "Távolság",
@@ -402,11 +366,9 @@
"power": "Erő", "power": "Erő",
"slope": "Erőkifejtési szintkép színekkel", "slope": "Erőkifejtési szintkép színekkel",
"surface": "Szintkép", "surface": "Szintkép",
"highway": "Category",
"time": "Idő", "time": "Idő",
"moving": "Moving", "moving": "Moving",
"total": "Összes", "total": "Összes"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",
@@ -421,7 +383,7 @@
"minutes_per_kilometer": "min/km", "minutes_per_kilometer": "min/km",
"minutes_per_mile": "min/mi", "minutes_per_mile": "min/mi",
"minutes_per_nautical_mile": "min/nm", "minutes_per_nautical_mile": "min/nm",
"knots": "csomó", "knots": "kn",
"heartrate": "bpm", "heartrate": "bpm",
"cadence": "rpm", "cadence": "rpm",
"power": "W" "power": "W"
@@ -537,14 +499,14 @@
"manual_camera": "Manual camera", "manual_camera": "Manual camera",
"manual_camera_description": "You can move the map below to adjust the camera position.", "manual_camera_description": "You can move the map below to adjust the camera position.",
"latitude": "Latitude", "latitude": "Latitude",
"longitude": "Hosszúság", "longitude": "Longitude",
"zoom": "Zoom", "zoom": "Zoom",
"pitch": "Dőlésszög", "pitch": "Pitch",
"bearing": "Irányszög", "bearing": "Bearing",
"preview": "Előnézet", "preview": "Előnézet",
"code": "Integration code" "code": "Integration code"
}, },
"webgl2_required": "A térkép megjelenítéséhez WebGL 2 szükséges. ", "webgl2_required": "WebGL 2 is required to display the map.",
"enable_webgl2": "Learn how to enable WebGL 2 in your browser", "enable_webgl2": "Learn how to enable WebGL 2 in your browser",
"page_not_found": "page not found" "page_not_found": "page not found"
} }

View File

@@ -119,8 +119,11 @@
"unpaved": "Non asfaltato", "unpaved": "Non asfaltato",
"asphalt": "Asfalto", "asphalt": "Asfalto",
"concrete": "Calcestruzzo", "concrete": "Calcestruzzo",
"chipseal": "Chipseal",
"cobblestone": "Acciottolato", "cobblestone": "Acciottolato",
"unhewn_cobblestone": "Ciottolame",
"paving_stones": "Paving stones", "paving_stones": "Paving stones",
"stepping_stones": "Stepping stones",
"sett": "Pavé", "sett": "Pavé",
"metal": "Metallo", "metal": "Metallo",
"wood": "Legno", "wood": "Legno",
@@ -132,59 +135,15 @@
"dirt": "Terra", "dirt": "Terra",
"ground": "Terreno", "ground": "Terreno",
"earth": "Terra", "earth": "Terra",
"snow": "Neve",
"ice": "Ghiaccio",
"salt": "Sale",
"mud": "Fango", "mud": "Fango",
"sand": "Sabbia", "sand": "Sabbia",
"woodchips": "Cippato",
"grass": "Erba", "grass": "Erba",
"grass_paver": "Pavimentazione erbosa", "grass_paver": "Pavimentazione erbosa"
"clay": "Argilla",
"stone": "Pietra"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "Il punto di partenza è troppo lontano dalla strada più vicina", "from": "Il punto di partenza è troppo lontano dalla strada più vicina",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
@@ -214,13 +173,12 @@
"merge_traces": "Collega le tracce", "merge_traces": "Collega le tracce",
"merge_contents": "Unisci i contenuti e mantieni le tracce disconnesse", "merge_contents": "Unisci i contenuti e mantieni le tracce disconnesse",
"merge_selection": "Selezione unione", "merge_selection": "Selezione unione",
"remove_gaps": "Rimuovere le differenze di tempo tra le tracce",
"tooltip": "Unisci gli elementi insieme", "tooltip": "Unisci gli elementi insieme",
"help_merge_traces": "Collegando le tracce selezionate si creerà una singola traccia continua.", "help_merge_traces": "Collegando le tracce selezionate si creerà una singola traccia continua.",
"help_cannot_merge_traces": "La tua selezione deve contenere diverse tracce per collegarle.", "help_cannot_merge_traces": "La tua selezione deve contenere diverse tracce per collegarle.",
"help_merge_contents": "Unendo il contenuto degli elementi selezionati si raggrupperà tutti i contenuti all'interno del primo elemento.", "help_merge_contents": "Unendo il contenuto degli elementi selezionati si raggrupperà tutti i contenuti all'interno del primo elemento.",
"help_cannot_merge_contents": "La selezione deve contenere diversi elementi per unire il loro contenuto.", "help_cannot_merge_contents": "La selezione deve contenere diversi elementi per unire il loro contenuto.",
"selection_tip": "Suggerimento: usa {KEYBOARD_SHORTCUT} per aggiungere elementi alla selezione." "selection_tip": "Tip: use {KEYBOARD_SHORTCUT} to add items to the selection."
}, },
"extract": { "extract": {
"tooltip": "Estrae i contenuti per separare gli elementi", "tooltip": "Estrae i contenuti per separare gli elementi",
@@ -230,8 +188,8 @@
}, },
"elevation": { "elevation": {
"button": "Richiedi dati elevazione", "button": "Richiedi dati elevazione",
"help": "Richiedere dati di altitudine cancellerà i dati di altitudine attuali, se presenti, e li rimpiazzerà con quelli provenienti da Mapbox.", "help": "Requesting elevation data will erase the existing elevation data, if any, and replace it with data from Mapbox.",
"help_no_selection": "Seleziona un file per richiedere i dati di altitudine." "help_no_selection": "Select a file item to request elevation data."
}, },
"waypoint": { "waypoint": {
"tooltip": "Crea e modifica punti di interesse", "tooltip": "Crea e modifica punti di interesse",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "Satellitare IGN", "ignFrSatellite": "Satellitare IGN",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Sondaggio Ordnance", "ordnanceSurvey": "Sondaggio Ordnance",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Mostra dati pendenza",
"show_surface": "Mostra dati di superficie",
"show_speed": "Mostra dati velocità",
"show_pace": "Mostra i dati del ritmo",
"show_heartrate": "Mostra i dati della frequenza cardiaca",
"show_cadence": "Mostra i dati di cadenza",
"show_temperature": "Mostra dati di temperatura",
"show_power": "Mostra dati di potenza"
}, },
"quantities": { "quantities": {
"distance": "Distanza", "distance": "Distanza",
@@ -402,11 +366,9 @@
"power": "Potenza", "power": "Potenza",
"slope": "Pendenza", "slope": "Pendenza",
"surface": "Superficie", "surface": "Superficie",
"highway": "Category",
"time": "Dati temporali", "time": "Dati temporali",
"moving": "Movimento", "moving": "Movimento",
"total": "Totale", "total": "Totale"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "비포장 도로", "unpaved": "비포장 도로",
"asphalt": "아스팔트", "asphalt": "아스팔트",
"concrete": "콘크리트", "concrete": "콘크리트",
"chipseal": "칩씰",
"cobblestone": "조약돌", "cobblestone": "조약돌",
"unhewn_cobblestone": "거친 조약돌",
"paving_stones": "포장석", "paving_stones": "포장석",
"stepping_stones": "디딤돌",
"sett": "정형 자갈", "sett": "정형 자갈",
"metal": "금속", "metal": "금속",
"wood": "목재", "wood": "목재",
@@ -132,59 +135,15 @@
"dirt": "흙길", "dirt": "흙길",
"ground": "지면", "ground": "지면",
"earth": "자연 지면", "earth": "자연 지면",
"snow": "눈",
"ice": "얼음",
"salt": "소금",
"mud": "진흙", "mud": "진흙",
"sand": "모래", "sand": "모래",
"woodchips": "목재 칩",
"grass": "잔디", "grass": "잔디",
"grass_paver": "잔디 포장", "grass_paver": "잔디 포장"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "The start point is too far from the nearest road", "from": "The start point is too far from the nearest road",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
@@ -214,7 +173,6 @@
"merge_traces": "Connect the traces", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Merge selection", "merge_selection": "Merge selection",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Merge items together", "tooltip": "Merge items together",
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.", "help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN 위성", "ignFrSatellite": "IGN 위성",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "경사 정보 표시",
"show_surface": "지면 정보 표시",
"show_speed": "속도 정보 표시",
"show_pace": "페이스 정보 표시",
"show_heartrate": "심박수 정보 표시",
"show_cadence": "케이던스 정보 표시",
"show_temperature": "온도 정보 표시",
"show_power": "파워 정보 표시"
}, },
"quantities": { "quantities": {
"distance": "거리", "distance": "거리",
@@ -402,11 +366,9 @@
"power": "파워", "power": "파워",
"slope": "경사", "slope": "경사",
"surface": "표면", "surface": "표면",
"highway": "Category",
"time": "시간", "time": "시간",
"moving": "이동 중", "moving": "이동 중",
"total": "전체", "total": "전체"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Negrįstas", "unpaved": "Negrįstas",
"asphalt": "Asfaltas", "asphalt": "Asfaltas",
"concrete": "Betonas", "concrete": "Betonas",
"chipseal": "Žvyrkelis",
"cobblestone": "Grįstas akmenimis", "cobblestone": "Grįstas akmenimis",
"unhewn_cobblestone": "Grįstas nešlifuotais akmenimis",
"paving_stones": "Plytelės", "paving_stones": "Plytelės",
"stepping_stones": "Plytelės su tarpais",
"sett": "Trinkelės", "sett": "Trinkelės",
"metal": "Metalo", "metal": "Metalo",
"wood": "Medžio", "wood": "Medžio",
@@ -132,59 +135,15 @@
"dirt": "Purvas", "dirt": "Purvas",
"ground": "Žemė", "ground": "Žemė",
"earth": "Žemė", "earth": "Žemė",
"snow": "Sniegas",
"ice": "Ledas",
"salt": "Druska",
"mud": "Purvas", "mud": "Purvas",
"sand": "Smėlis", "sand": "Smėlis",
"woodchips": "Medžio drožlės",
"grass": "Žolė", "grass": "Žolė",
"grass_paver": "Žolės grindinys", "grass_paver": "Žolės grindinys"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "Pradžios taškas yra per toli nuo artimiausio kelio", "from": "Pradžios taškas yra per toli nuo artimiausio kelio",
"via": "Tarpinis taškas yra per toli nuo artimiausio kelio", "via": "Tarpinis taškas yra per toli nuo artimiausio kelio",
@@ -214,7 +173,6 @@
"merge_traces": "Sujungti trasas", "merge_traces": "Sujungti trasas",
"merge_contents": "Sujungti turinį ir laikyti trasas atskirai", "merge_contents": "Sujungti turinį ir laikyti trasas atskirai",
"merge_selection": "Sujungti pasirinkimą", "merge_selection": "Sujungti pasirinkimą",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Sujungti elementus", "tooltip": "Sujungti elementus",
"help_merge_traces": "Sujungus pasirinktas trasas bus sukurta viena ištisinė trasa.", "help_merge_traces": "Sujungus pasirinktas trasas bus sukurta viena ištisinė trasa.",
"help_cannot_merge_traces": "Turi pasirinkti kelias trasas, kad galėtumėte jas sujungti.", "help_cannot_merge_traces": "Turi pasirinkti kelias trasas, kad galėtumėte jas sujungti.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Rodyti nuolydžio duomenis",
"show_surface": "Rodyti paviršiaus duomenis",
"show_speed": "Rodyti greičio duomenis",
"show_pace": "Rodyti tempo duomenis",
"show_heartrate": "Rodyti pulso duomenis",
"show_cadence": "Rodyti tempo duomenis",
"show_temperature": "Rodyti temperatūros duomenis",
"show_power": "Rodyti jėgos duomenis"
}, },
"quantities": { "quantities": {
"distance": "Atstumas", "distance": "Atstumas",
@@ -402,11 +366,9 @@
"power": "Jėga", "power": "Jėga",
"slope": "Nuolydis", "slope": "Nuolydis",
"surface": "Paviršius", "surface": "Paviršius",
"highway": "Category",
"time": "Laikas", "time": "Laikas",
"moving": "Judėjimas", "moving": "Judėjimas",
"total": "Bendras", "total": "Bendras"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Unpaved", "unpaved": "Unpaved",
"asphalt": "Asphalt", "asphalt": "Asphalt",
"concrete": "Concrete", "concrete": "Concrete",
"chipseal": "Chipseal",
"cobblestone": "Cobblestone", "cobblestone": "Cobblestone",
"unhewn_cobblestone": "Unhewn cobblestone",
"paving_stones": "Paving stones", "paving_stones": "Paving stones",
"stepping_stones": "Stepping stones",
"sett": "Sett", "sett": "Sett",
"metal": "Metal", "metal": "Metal",
"wood": "Wood", "wood": "Wood",
@@ -132,59 +135,15 @@
"dirt": "Dirt", "dirt": "Dirt",
"ground": "Ground", "ground": "Ground",
"earth": "Earth", "earth": "Earth",
"snow": "Snow",
"ice": "Ice",
"salt": "Salt",
"mud": "Mud", "mud": "Mud",
"sand": "Sand", "sand": "Sand",
"woodchips": "Woodchips",
"grass": "Grass", "grass": "Grass",
"grass_paver": "Grass paver", "grass_paver": "Grass paver"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "The start point is too far from the nearest road", "from": "The start point is too far from the nearest road",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
@@ -214,7 +173,6 @@
"merge_traces": "Connect the traces", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Merge selection", "merge_selection": "Merge selection",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Merge items together", "tooltip": "Merge items together",
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.", "help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Distance", "distance": "Distance",
@@ -402,11 +366,9 @@
"power": "Power", "power": "Power",
"slope": "Slope", "slope": "Slope",
"surface": "Surface", "surface": "Surface",
"highway": "Category",
"time": "Time", "time": "Time",
"moving": "Moving", "moving": "Moving",
"total": "Total", "total": "Total"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Onverhard", "unpaved": "Onverhard",
"asphalt": "Asfalt", "asphalt": "Asfalt",
"concrete": "Beton", "concrete": "Beton",
"chipseal": "Chipseal",
"cobblestone": "Klinkers", "cobblestone": "Klinkers",
"unhewn_cobblestone": "Ruwe kasseien",
"paving_stones": "Straatstenen", "paving_stones": "Straatstenen",
"stepping_stones": "Stapstenen",
"sett": "Instellen", "sett": "Instellen",
"metal": "Metaal", "metal": "Metaal",
"wood": "Hout", "wood": "Hout",
@@ -132,59 +135,15 @@
"dirt": "Onverhard", "dirt": "Onverhard",
"ground": "Ondergrond", "ground": "Ondergrond",
"earth": "Aarde", "earth": "Aarde",
"snow": "Sneeuw",
"ice": "IJs",
"salt": "Zout",
"mud": "Modder", "mud": "Modder",
"sand": "Zand", "sand": "Zand",
"woodchips": "Houtsnippers",
"grass": "Gras", "grass": "Gras",
"grass_paver": "Grastegel", "grass_paver": "Grastegel"
"clay": "Klei",
"stone": "Steen"
}, },
"highway": {
"unknown": "Onbekend",
"motorway": "Snelweg",
"motorway_link": "Snelweg aansluiting",
"trunk": "Primaire weg",
"trunk_link": "Aansluiting primaire weg",
"primary": "Primaire weg",
"primary_link": "Aansluiting primaire weg",
"secondary": "Secundaire weg",
"secondary_link": "Aansluiting secundaire weg",
"tertiary": "Tertiaire weg",
"tertiary_link": "Aansluiting tertiaire weg",
"unclassified": "Kleine weg",
"residential": "Bebouwde kom",
"living_street": "Woonstraat",
"service": "Ventweg",
"track": "Track",
"footway": "Voetpad",
"path": "Pad",
"pedestrian": "Voetganger",
"cycleway": "Fietspad",
"steps": "Trappen",
"road": "Weg",
"bridleway": "Ruiterpad",
"platform": "Platform",
"raceway": "Race circuit",
"rest_area": "Pauzeplek",
"abandoned": "Verlaten",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bushalte",
"busway": "Busbaan",
"elevator": "Lift",
"via_ferrata": "Via ferrata",
"proposed": "Geplande weg",
"construction": "Weg in aanleg"
},
"sac_scale": {
"hiking": "Hiken",
"mountain_hiking": "Bergwandelen",
"demanding_mountain_hiking": "Veeleisend bergwandelen",
"alpine_hiking": "Alpine wandelen",
"demanding_alpine_hiking": "Veeleisend alpine wandelen",
"difficult_alpine_hiking": "Moeilijke alpiene wandeling"
},
"mtb_scale": "MTB schaal",
"error": { "error": {
"from": "Het startpunt ligt te ver van de dichtstbijzijnde weg", "from": "Het startpunt ligt te ver van de dichtstbijzijnde weg",
"via": "Het via punt ligt te ver van de dichtstbijzijnde weg", "via": "Het via punt ligt te ver van de dichtstbijzijnde weg",
@@ -214,7 +173,6 @@
"merge_traces": "Verbind de routes", "merge_traces": "Verbind de routes",
"merge_contents": "Voeg de inhoud samen en houd de sporen gescheiden", "merge_contents": "Voeg de inhoud samen en houd de sporen gescheiden",
"merge_selection": "Voeg selectie samen", "merge_selection": "Voeg selectie samen",
"remove_gaps": "Verwijder tijdsprongen tussen sporen",
"tooltip": "Items samenvoegen", "tooltip": "Items samenvoegen",
"help_merge_traces": "Het verbinden van de geselecteerde sporen creëert één continu spoor.", "help_merge_traces": "Het verbinden van de geselecteerde sporen creëert één continu spoor.",
"help_cannot_merge_traces": "Uw selectie moet meerdere sporen bevatten om ze te verbinden.", "help_cannot_merge_traces": "Uw selectie moet meerdere sporen bevatten om ze te verbinden.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satelliet", "ignFrSatellite": "IGN Satelliet",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satelliet",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Instellingen hoogteprofiel" "show_slope": "Toon richtingsgegevens",
"show_surface": "Toon oppervlakte gegevens",
"show_speed": "Toon snelheidsgegevens",
"show_pace": "Toon oppervlakte gegevens",
"show_heartrate": "Toon hartslag gegevens",
"show_cadence": "Toon kadans gegevens",
"show_temperature": "Toon temperatuurgegevens",
"show_power": "Toon energie gegevens"
}, },
"quantities": { "quantities": {
"distance": "Afstand", "distance": "Afstand",
@@ -402,11 +366,9 @@
"power": "Kracht", "power": "Kracht",
"slope": "Helling", "slope": "Helling",
"surface": "Oppervlak", "surface": "Oppervlak",
"highway": "Categorie",
"time": "Tijd", "time": "Tijd",
"moving": "Beweging", "moving": "Beweging",
"total": "Totaal", "total": "Totaal"
"osm_extensions": "OpenStreetMap gegevens"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Uasfaltert", "unpaved": "Uasfaltert",
"asphalt": "Asfalt", "asphalt": "Asfalt",
"concrete": "Betong", "concrete": "Betong",
"chipseal": "Grov asfalt",
"cobblestone": "Brostein", "cobblestone": "Brostein",
"unhewn_cobblestone": "Unhewn cobblestone",
"paving_stones": "Paving stones", "paving_stones": "Paving stones",
"stepping_stones": "Stepping stones",
"sett": "Sett", "sett": "Sett",
"metal": "Metall", "metal": "Metall",
"wood": "Treverk", "wood": "Treverk",
@@ -132,59 +135,15 @@
"dirt": "Jord", "dirt": "Jord",
"ground": "Bakke", "ground": "Bakke",
"earth": "Jord", "earth": "Jord",
"snow": "Snø",
"ice": "Is",
"salt": "Salt",
"mud": "Gjørme", "mud": "Gjørme",
"sand": "Sand", "sand": "Sand",
"woodchips": "Treflis",
"grass": "Gress", "grass": "Gress",
"grass_paver": "Grass paver", "grass_paver": "Grass paver"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "The start point is too far from the nearest road", "from": "The start point is too far from the nearest road",
"via": "The via point is too far from the nearest road", "via": "The via point is too far from the nearest road",
@@ -214,7 +173,6 @@
"merge_traces": "Connect the traces", "merge_traces": "Connect the traces",
"merge_contents": "Merge the contents and keep the traces disconnected", "merge_contents": "Merge the contents and keep the traces disconnected",
"merge_selection": "Slå sammen valgte", "merge_selection": "Slå sammen valgte",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Slå sammen elementer", "tooltip": "Slå sammen elementer",
"help_merge_traces": "Ved å koble sammen de valgte sporene vil det opprettes ett enkelt kontinuerlig spor.", "help_merge_traces": "Ved å koble sammen de valgte sporene vil det opprettes ett enkelt kontinuerlig spor.",
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.", "help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Show slope data",
"show_surface": "Show surface data",
"show_speed": "Show speed data",
"show_pace": "Show pace data",
"show_heartrate": "Show heart rate data",
"show_cadence": "Show cadence data",
"show_temperature": "Show temperature data",
"show_power": "Show power data"
}, },
"quantities": { "quantities": {
"distance": "Avstand", "distance": "Avstand",
@@ -402,11 +366,9 @@
"power": "Power", "power": "Power",
"slope": "Skråning", "slope": "Skråning",
"surface": "Overflate", "surface": "Overflate",
"highway": "Category",
"time": "Time", "time": "Time",
"moving": "Moving", "moving": "Moving",
"total": "Totalt", "total": "Totalt"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Nieutwardzona", "unpaved": "Nieutwardzona",
"asphalt": "Asfalt", "asphalt": "Asfalt",
"concrete": "Beton", "concrete": "Beton",
"chipseal": "Nawierzchnia utrwalana powierzchniowo",
"cobblestone": "Bruk", "cobblestone": "Bruk",
"unhewn_cobblestone": "Bruk",
"paving_stones": "Płyta", "paving_stones": "Płyta",
"stepping_stones": "Bruk",
"sett": "Bruk", "sett": "Bruk",
"metal": "Metal", "metal": "Metal",
"wood": "Drewno", "wood": "Drewno",
@@ -132,59 +135,15 @@
"dirt": "Ziemia", "dirt": "Ziemia",
"ground": "Teren", "ground": "Teren",
"earth": "Ziemia", "earth": "Ziemia",
"snow": "Śnieg",
"ice": "Lód",
"salt": "Sól",
"mud": "Błoto", "mud": "Błoto",
"sand": "Piasek", "sand": "Piasek",
"woodchips": "Zrębki",
"grass": "Trawa", "grass": "Trawa",
"grass_paver": "Płyta ażurowa", "grass_paver": "Płyta ażurowa"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "Punkt początkowy jest zbyt daleko od najbliższej drogi", "from": "Punkt początkowy jest zbyt daleko od najbliższej drogi",
"via": "Punkt przelotowy jest zbyt daleko od najbliższej drogi", "via": "Punkt przelotowy jest zbyt daleko od najbliższej drogi",
@@ -214,7 +173,6 @@
"merge_traces": "Połącz ślady", "merge_traces": "Połącz ślady",
"merge_contents": "Scal zawartość, ale utrzymaj rozłączone ślady", "merge_contents": "Scal zawartość, ale utrzymaj rozłączone ślady",
"merge_selection": "Scal zaznaczenie", "merge_selection": "Scal zaznaczenie",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Scal elementy razem", "tooltip": "Scal elementy razem",
"help_merge_traces": "Połączenie zaznaczonych śladów stworzy pojedynczy ciągły ślad.", "help_merge_traces": "Połączenie zaznaczonych śladów stworzy pojedynczy ciągły ślad.",
"help_cannot_merge_traces": "Wybór musi zawierać kilka śladów, aby je połączyć.", "help_cannot_merge_traces": "Wybór musi zawierać kilka śladów, aby je połączyć.",
@@ -314,8 +272,7 @@
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satellite", "ignFrSatellite": "IGN Satellite",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satellite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Pokaż dane nachylenia",
"show_surface": "Pokaż dane nawierzchni",
"show_speed": "Pokaż dane prędkości",
"show_pace": "Pokaż dane tempa",
"show_heartrate": "Pokaż dane tętna",
"show_cadence": "Pokaż dane rytmu",
"show_temperature": "Pokaż dane temperatury",
"show_power": "Pokaż dane mocy"
}, },
"quantities": { "quantities": {
"distance": "Dystans", "distance": "Dystans",
@@ -402,11 +366,9 @@
"power": "Moc", "power": "Moc",
"slope": "Nachylenie", "slope": "Nachylenie",
"surface": "Powierzchnia", "surface": "Powierzchnia",
"highway": "Category",
"time": "Czas", "time": "Czas",
"moving": "W ruchu", "moving": "W ruchu",
"total": "Łącznie", "total": "Łącznie"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

View File

@@ -119,8 +119,11 @@
"unpaved": "Não pavimentado", "unpaved": "Não pavimentado",
"asphalt": "Asfalto", "asphalt": "Asfalto",
"concrete": "Cimento", "concrete": "Cimento",
"chipseal": "Pedriscos",
"cobblestone": "Empedrado", "cobblestone": "Empedrado",
"unhewn_cobblestone": "Paralelepípedo bruto",
"paving_stones": "Paralelepípedo", "paving_stones": "Paralelepípedo",
"stepping_stones": "Caminho de pedras",
"sett": "Pedra portuguesa", "sett": "Pedra portuguesa",
"metal": "Metal", "metal": "Metal",
"wood": "Madeira", "wood": "Madeira",
@@ -132,59 +135,15 @@
"dirt": "Terra", "dirt": "Terra",
"ground": "Chão", "ground": "Chão",
"earth": "Terra", "earth": "Terra",
"snow": "Neve",
"ice": "Gelo",
"salt": "Sal",
"mud": "Lama", "mud": "Lama",
"sand": "Areia", "sand": "Areia",
"woodchips": "Cavacos de madeira",
"grass": "Grama", "grass": "Grama",
"grass_paver": "Pavimentação com grama", "grass_paver": "Pavimentação com grama"
"clay": "Clay",
"stone": "Stone"
}, },
"highway": {
"unknown": "Unknown",
"motorway": "Highway",
"motorway_link": "Highway link",
"trunk": "Primary road",
"trunk_link": "Primary road link",
"primary": "Primary road",
"primary_link": "Primary road link",
"secondary": "Secondary road",
"secondary_link": "Secondary road link",
"tertiary": "Tertiary road",
"tertiary_link": "Tertiary road link",
"unclassified": "Minor road",
"residential": "Residential road",
"living_street": "Living street",
"service": "Service road",
"track": "Track",
"footway": "Footway",
"path": "Path",
"pedestrian": "Pedestrian",
"cycleway": "Cycleway",
"steps": "Steps",
"road": "Road",
"bridleway": "Horseriding path",
"platform": "Platform",
"raceway": "Racing circuit",
"rest_area": "Rest area",
"abandoned": "Abandoned",
"services": "Services",
"corridor": "Corridor",
"bus_stop": "Bus stop",
"busway": "Busway",
"elevator": "Elevator",
"via_ferrata": "Via ferrata",
"proposed": "Road to be built",
"construction": "Road under construction"
},
"sac_scale": {
"hiking": "Hiking",
"mountain_hiking": "Mountain hiking",
"demanding_mountain_hiking": "Demanding mountain hiking",
"alpine_hiking": "Alpine hiking",
"demanding_alpine_hiking": "Demanding alpine hiking",
"difficult_alpine_hiking": "Difficult alpine hiking"
},
"mtb_scale": "MTB scale",
"error": { "error": {
"from": "O ponto de partida está muito longe da estrada mais próxima", "from": "O ponto de partida está muito longe da estrada mais próxima",
"via": "O ponto intermediário está muito longe da estrada mais próxima", "via": "O ponto intermediário está muito longe da estrada mais próxima",
@@ -214,7 +173,6 @@
"merge_traces": "Conecte as trilhas", "merge_traces": "Conecte as trilhas",
"merge_contents": "Mesclar o conteúdo e manter as trilhas desconectadas", "merge_contents": "Mesclar o conteúdo e manter as trilhas desconectadas",
"merge_selection": "Unir seleção", "merge_selection": "Unir seleção",
"remove_gaps": "Remove time gaps between traces",
"tooltip": "Unir itens", "tooltip": "Unir itens",
"help_merge_traces": "Conectar as trilhas selecionadas criará uma trilha única contínua.", "help_merge_traces": "Conectar as trilhas selecionadas criará uma trilha única contínua.",
"help_cannot_merge_traces": "Sua seleção deve conter várias trilhas para conectá-las.", "help_cannot_merge_traces": "Sua seleção deve conter várias trilhas para conectá-las.",
@@ -313,9 +271,8 @@
"ignFrPlan": "IGN Plan", "ignFrPlan": "IGN Plan",
"ignFrTopo": "IGN Topo", "ignFrTopo": "IGN Topo",
"ignFrScan25": "IGN SCAN25", "ignFrScan25": "IGN SCAN25",
"ignFrSatellite": "IGN Satélite", "ignFrSatellite": "Satélite IGN",
"ignEs": "IGN Topo", "ignEs": "IGN",
"ignEsSatellite": "IGN Satélite",
"ordnanceSurvey": "Ordnance Survey", "ordnanceSurvey": "Ordnance Survey",
"norwayTopo": "Topografisk Norgeskart 4", "norwayTopo": "Topografisk Norgeskart 4",
"swedenTopo": "Lantmäteriet Topo", "swedenTopo": "Lantmäteriet Topo",
@@ -334,7 +291,7 @@
"swisstopoMountainBikeClosures": "swisstopo MTB Closures", "swisstopoMountainBikeClosures": "swisstopo MTB Closures",
"swisstopoSkiTouring": "swisstopo Ski Touring", "swisstopoSkiTouring": "swisstopo Ski Touring",
"ignFrCadastre": "IGN Cadastre", "ignFrCadastre": "IGN Cadastre",
"ignSlope": "IGN Inclinação", "ignSlope": "Inclinação IGN",
"ignSkiTouring": "IGN Ski Touring", "ignSkiTouring": "IGN Ski Touring",
"waymarked_trails": "Trilhas demarcadas", "waymarked_trails": "Trilhas demarcadas",
"waymarkedTrailsHiking": "Caminhada", "waymarkedTrailsHiking": "Caminhada",
@@ -388,7 +345,14 @@
} }
}, },
"chart": { "chart": {
"settings": "Elevation profile settings" "show_slope": "Mostrar dados de inclinação",
"show_surface": "Mostrar dados do terreno",
"show_speed": "Mostrar dados de velocidade",
"show_pace": "Mostrar dados de ritmo",
"show_heartrate": "Mostrar dados de frequência cardíaca",
"show_cadence": "Mostrar dados de cadência",
"show_temperature": "Mostrar dados de temperatura",
"show_power": "Mostrar dados de potência"
}, },
"quantities": { "quantities": {
"distance": "Distância", "distance": "Distância",
@@ -402,11 +366,9 @@
"power": "Potência", "power": "Potência",
"slope": "Inclinação", "slope": "Inclinação",
"surface": "Superfície", "surface": "Superfície",
"highway": "Category",
"time": "Tempo", "time": "Tempo",
"moving": "Movimento", "moving": "Movimento",
"total": "Total", "total": "Total"
"osm_extensions": "OpenStreetMap data"
}, },
"units": { "units": {
"meters": "m", "meters": "m",

Some files were not shown because too many files have changed in this diff Show More