* Change icon to Maximize
* Use default animation * Rename feature to "Fly to" * Move logic to stores so that it can be reused in the main menu (and eventually a keyboard shortcut)
This commit is contained in:
parent
6559fdd125
commit
c6919f518a
4 changed files with 843 additions and 783 deletions
|
|
@ -41,7 +41,8 @@
|
||||||
FileStack,
|
FileStack,
|
||||||
FileX,
|
FileX,
|
||||||
BookOpenText,
|
BookOpenText,
|
||||||
ChartArea
|
ChartArea,
|
||||||
|
Maximize
|
||||||
} from 'lucide-svelte';
|
} from 'lucide-svelte';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -54,7 +55,9 @@
|
||||||
editMetadata,
|
editMetadata,
|
||||||
editStyle,
|
editStyle,
|
||||||
exportState,
|
exportState,
|
||||||
ExportState
|
ExportState,
|
||||||
|
flyToBounds,
|
||||||
|
gpxStatistics
|
||||||
} from '$lib/stores';
|
} from '$lib/stores';
|
||||||
import {
|
import {
|
||||||
copied,
|
copied,
|
||||||
|
|
@ -222,6 +225,13 @@
|
||||||
<PaintBucket size="16" class="mr-1" />
|
<PaintBucket size="16" class="mr-1" />
|
||||||
{$_('menu.style.button')}
|
{$_('menu.style.button')}
|
||||||
</Menubar.Item>
|
</Menubar.Item>
|
||||||
|
<Menubar.Item
|
||||||
|
disabled={$selection.size === 0}
|
||||||
|
on:click={() => flyToBounds($gpxStatistics.global.bounds, $map)}
|
||||||
|
>
|
||||||
|
<Maximize size="16" class="mr-1" />
|
||||||
|
{$_('menu.fly_to_selection')}
|
||||||
|
</Menubar.Item>
|
||||||
<Menubar.Item
|
<Menubar.Item
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if ($allHidden) {
|
if ($allHidden) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
EyeOff,
|
EyeOff,
|
||||||
ClipboardCopy,
|
ClipboardCopy,
|
||||||
ClipboardPaste,
|
ClipboardPaste,
|
||||||
Maximize2,
|
Maximize,
|
||||||
Scissors,
|
Scissors,
|
||||||
FileStack,
|
FileStack,
|
||||||
FileX
|
FileX
|
||||||
|
|
@ -45,9 +45,10 @@
|
||||||
editMetadata,
|
editMetadata,
|
||||||
editStyle,
|
editStyle,
|
||||||
embedding,
|
embedding,
|
||||||
|
flyToBounds,
|
||||||
gpxLayers,
|
gpxLayers,
|
||||||
map,
|
gpxStatistics,
|
||||||
updateTargetMapBounds
|
map
|
||||||
} from '$lib/stores';
|
} from '$lib/stores';
|
||||||
import {
|
import {
|
||||||
GPXTreeElement,
|
GPXTreeElement,
|
||||||
|
|
@ -226,30 +227,14 @@
|
||||||
{$_('menu.style.button')}
|
{$_('menu.style.button')}
|
||||||
</ContextMenu.Item>
|
</ContextMenu.Item>
|
||||||
{/if}
|
{/if}
|
||||||
{#if node instanceof GPXTreeElement}
|
<ContextMenu.Item
|
||||||
<ContextMenu.Item
|
on:click={() => {
|
||||||
on:click={() => {
|
flyToBounds($gpxStatistics.global.bounds, $map);
|
||||||
const targetBounds = node.getStatistics().global.bounds;
|
}}
|
||||||
const mapBoxBounds = new mapboxgl.LngLatBounds([
|
>
|
||||||
[targetBounds.northEast.lon, targetBounds.northEast.lat],
|
<Maximize size="16" class="mr-1" />
|
||||||
[targetBounds.southWest.lon, targetBounds.southWest.lat]
|
{$_('menu.fly_to')}
|
||||||
]);
|
</ContextMenu.Item>
|
||||||
$map?.fitBounds(mapBoxBounds, {
|
|
||||||
padding: 80,
|
|
||||||
linear: true,
|
|
||||||
duration: 1000,
|
|
||||||
easing: (t) => {
|
|
||||||
return t < 0.5
|
|
||||||
? (1 - Math.sqrt(1 - Math.pow(2 * t, 2))) / 2
|
|
||||||
: (Math.sqrt(1 - Math.pow(-2 * t + 2, 2)) + 1) / 2;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Maximize2 size="16" class="mr-1" />
|
|
||||||
{$_('menu.fit_map')}
|
|
||||||
</ContextMenu.Item>
|
|
||||||
{/if}
|
|
||||||
<ContextMenu.Item
|
<ContextMenu.Item
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if ($allHidden) {
|
if ($allHidden) {
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,21 @@ import { tick } from 'svelte';
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import type { GPXLayer } from '$lib/components/gpx-layer/GPXLayer';
|
import type { GPXLayer } from '$lib/components/gpx-layer/GPXLayer';
|
||||||
import { dbUtils, fileObservers, getFile, getStatistics, settings } from './db';
|
import { dbUtils, fileObservers, getFile, getStatistics, settings } from './db';
|
||||||
import { addSelectItem, applyToOrderedSelectedItemsFromFile, selectFile, selectItem, selection } from '$lib/components/file-list/Selection';
|
import {
|
||||||
import { ListFileItem, ListItem, ListTrackItem, ListTrackSegmentItem, ListWaypointItem, ListWaypointsItem } from '$lib/components/file-list/FileList';
|
addSelectItem,
|
||||||
|
applyToOrderedSelectedItemsFromFile,
|
||||||
|
selectFile,
|
||||||
|
selectItem,
|
||||||
|
selection
|
||||||
|
} from '$lib/components/file-list/Selection';
|
||||||
|
import {
|
||||||
|
ListFileItem,
|
||||||
|
ListItem,
|
||||||
|
ListTrackItem,
|
||||||
|
ListTrackSegmentItem,
|
||||||
|
ListWaypointItem,
|
||||||
|
ListWaypointsItem
|
||||||
|
} from '$lib/components/file-list/FileList';
|
||||||
import type { RoutingControls } from '$lib/components/toolbar/tools/routing/RoutingControls';
|
import type { RoutingControls } from '$lib/components/toolbar/tools/routing/RoutingControls';
|
||||||
import { SplitType } from '$lib/components/toolbar/tools/scissors/Scissors.svelte';
|
import { SplitType } from '$lib/components/toolbar/tools/scissors/Scissors.svelte';
|
||||||
|
|
||||||
|
|
@ -18,369 +31,420 @@ export const embedding = writable(false);
|
||||||
export const selectFiles = writable<{ [key: string]: (fileId?: string) => void }>({});
|
export const selectFiles = writable<{ [key: string]: (fileId?: string) => void }>({});
|
||||||
|
|
||||||
export const gpxStatistics: Writable<GPXStatistics> = writable(new GPXStatistics());
|
export const gpxStatistics: Writable<GPXStatistics> = writable(new GPXStatistics());
|
||||||
export const slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined> = writable(undefined);
|
export const slicedGPXStatistics: Writable<[GPXStatistics, number, number] | undefined> =
|
||||||
|
writable(undefined);
|
||||||
|
|
||||||
export function updateGPXData() {
|
export function updateGPXData() {
|
||||||
let statistics = new GPXStatistics();
|
let statistics = new GPXStatistics();
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let stats = getStatistics(fileId);
|
let stats = getStatistics(fileId);
|
||||||
if (stats) {
|
if (stats) {
|
||||||
let first = true;
|
let first = true;
|
||||||
items.forEach((item) => {
|
items.forEach((item) => {
|
||||||
if (!(item instanceof ListWaypointItem || item instanceof ListWaypointsItem) || first) {
|
if (!(item instanceof ListWaypointItem || item instanceof ListWaypointsItem) || first) {
|
||||||
statistics.mergeWith(stats.getStatisticsFor(item));
|
statistics.mergeWith(stats.getStatisticsFor(item));
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
gpxStatistics.set(statistics);
|
gpxStatistics.set(statistics);
|
||||||
}
|
}
|
||||||
|
|
||||||
let unsubscribes: Map<string, () => void> = new Map();
|
let unsubscribes: Map<string, () => void> = new Map();
|
||||||
selection.subscribe(($selection) => { // Maintain up-to-date statistics for the current selection
|
selection.subscribe(($selection) => {
|
||||||
updateGPXData();
|
// Maintain up-to-date statistics for the current selection
|
||||||
|
updateGPXData();
|
||||||
|
|
||||||
while (unsubscribes.size > 0) {
|
while (unsubscribes.size > 0) {
|
||||||
let [fileId, unsubscribe] = unsubscribes.entries().next().value;
|
let [fileId, unsubscribe] = unsubscribes.entries().next().value;
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
unsubscribes.delete(fileId);
|
unsubscribes.delete(fileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
$selection.forEach((item) => {
|
$selection.forEach((item) => {
|
||||||
let fileId = item.getFileId();
|
let fileId = item.getFileId();
|
||||||
if (!unsubscribes.has(fileId)) {
|
if (!unsubscribes.has(fileId)) {
|
||||||
let fileObserver = get(fileObservers).get(fileId);
|
let fileObserver = get(fileObservers).get(fileId);
|
||||||
if (fileObserver) {
|
if (fileObserver) {
|
||||||
let first = true;
|
let first = true;
|
||||||
unsubscribes.set(fileId, fileObserver.subscribe(() => {
|
unsubscribes.set(
|
||||||
if (first) first = false;
|
fileId,
|
||||||
else updateGPXData();
|
fileObserver.subscribe(() => {
|
||||||
}));
|
if (first) first = false;
|
||||||
}
|
else updateGPXData();
|
||||||
}
|
})
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
gpxStatistics.subscribe(() => {
|
gpxStatistics.subscribe(() => {
|
||||||
slicedGPXStatistics.set(undefined);
|
slicedGPXStatistics.set(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
const targetMapBounds = writable({
|
const targetMapBounds = writable({
|
||||||
bounds: new mapboxgl.LngLatBounds([180, 90, -180, -90]),
|
bounds: new mapboxgl.LngLatBounds([180, 90, -180, -90]),
|
||||||
count: 0,
|
count: 0,
|
||||||
total: -1
|
total: -1
|
||||||
});
|
});
|
||||||
|
|
||||||
derived([targetMapBounds, map], x => x).subscribe(([bounds, $map]) => {
|
derived([targetMapBounds, map], (x) => x).subscribe(([bounds, $map]) => {
|
||||||
if ($map === null || bounds.count !== bounds.total || (bounds.bounds.getSouth() === 90 && bounds.bounds.getWest() === 180 && bounds.bounds.getNorth() === -90 && bounds.bounds.getEast() === -180)) {
|
if (
|
||||||
return;
|
$map === null ||
|
||||||
}
|
bounds.count !== bounds.total ||
|
||||||
|
(bounds.bounds.getSouth() === 90 &&
|
||||||
|
bounds.bounds.getWest() === 180 &&
|
||||||
|
bounds.bounds.getNorth() === -90 &&
|
||||||
|
bounds.bounds.getEast() === -180)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let currentBounds = $map.getBounds();
|
let currentBounds = $map.getBounds();
|
||||||
if (bounds.count !== get(fileObservers).size && currentBounds) {
|
if (bounds.count !== get(fileObservers).size && currentBounds) {
|
||||||
// There are other files on the map
|
// There are other files on the map
|
||||||
|
|
||||||
if (currentBounds.contains(bounds.bounds.getSouthEast()) && currentBounds.contains(bounds.bounds.getNorthWest())) {
|
if (
|
||||||
return;
|
currentBounds.contains(bounds.bounds.getSouthEast()) &&
|
||||||
}
|
currentBounds.contains(bounds.bounds.getNorthWest())
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bounds.bounds.extend(currentBounds.getSouthWest());
|
bounds.bounds.extend(currentBounds.getSouthWest());
|
||||||
bounds.bounds.extend(currentBounds.getNorthEast());
|
bounds.bounds.extend(currentBounds.getNorthEast());
|
||||||
}
|
}
|
||||||
|
|
||||||
$map.fitBounds(bounds.bounds, {
|
$map.fitBounds(bounds.bounds, { padding: 80, linear: true, easing: () => 1 });
|
||||||
padding: 80,
|
|
||||||
linear: true,
|
|
||||||
easing: () => 1
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export function initTargetMapBounds(total: number) {
|
export function initTargetMapBounds(total: number) {
|
||||||
targetMapBounds.set({
|
targetMapBounds.set({
|
||||||
bounds: new mapboxgl.LngLatBounds([180, 90, -180, -90]),
|
bounds: new mapboxgl.LngLatBounds([180, 90, -180, -90]),
|
||||||
count: 0,
|
count: 0,
|
||||||
total: total
|
total: total
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateTargetMapBounds(bounds: {
|
export function updateTargetMapBounds(bounds: { southWest: Coordinates; northEast: Coordinates }) {
|
||||||
southWest: Coordinates,
|
if (
|
||||||
northEast: Coordinates
|
bounds.southWest.lat == 90 &&
|
||||||
}) {
|
bounds.southWest.lon == 180 &&
|
||||||
if (bounds.southWest.lat == 90 && bounds.southWest.lon == 180 && bounds.northEast.lat == -90 && bounds.northEast.lon == -180) { // Avoid update for empty (new) files
|
bounds.northEast.lat == -90 &&
|
||||||
targetMapBounds.update((target) => {
|
bounds.northEast.lon == -180
|
||||||
target.count += 1;
|
) {
|
||||||
return target;
|
// Avoid update for empty (new) files
|
||||||
});
|
targetMapBounds.update((target) => {
|
||||||
return;
|
target.count += 1;
|
||||||
}
|
return target;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
targetMapBounds.update((target) => {
|
targetMapBounds.update((target) => {
|
||||||
target.bounds.extend(bounds.southWest);
|
target.bounds.extend(bounds.southWest);
|
||||||
target.bounds.extend(bounds.northEast);
|
target.bounds.extend(bounds.northEast);
|
||||||
target.count += 1;
|
target.count += 1;
|
||||||
return target;
|
return target;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function flyToBounds(
|
||||||
|
bounds: { southWest: Coordinates; northEast: Coordinates },
|
||||||
|
map: mapboxgl.Map | null
|
||||||
|
) {
|
||||||
|
const mapBoxBounds = new mapboxgl.LngLatBounds([
|
||||||
|
[bounds.northEast.lon, bounds.northEast.lat],
|
||||||
|
[bounds.southWest.lon, bounds.southWest.lat]
|
||||||
|
]);
|
||||||
|
map?.fitBounds(mapBoxBounds, {
|
||||||
|
padding: 80
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const gpxLayers: Map<string, GPXLayer> = new Map();
|
export const gpxLayers: Map<string, GPXLayer> = new Map();
|
||||||
export const routingControls: Map<string, RoutingControls> = new Map();
|
export const routingControls: Map<string, RoutingControls> = new Map();
|
||||||
|
|
||||||
export enum Tool {
|
export enum Tool {
|
||||||
ROUTING,
|
ROUTING,
|
||||||
WAYPOINT,
|
WAYPOINT,
|
||||||
SCISSORS,
|
SCISSORS,
|
||||||
TIME,
|
TIME,
|
||||||
MERGE,
|
MERGE,
|
||||||
EXTRACT,
|
EXTRACT,
|
||||||
REDUCE,
|
REDUCE,
|
||||||
CLEAN
|
CLEAN
|
||||||
}
|
}
|
||||||
export const currentTool = writable<Tool | null>(null);
|
export const currentTool = writable<Tool | null>(null);
|
||||||
export const splitAs = writable(SplitType.FILES);
|
export const splitAs = writable(SplitType.FILES);
|
||||||
export const streetViewEnabled = writable(false);
|
export const streetViewEnabled = writable(false);
|
||||||
|
|
||||||
export function newGPXFile() {
|
export function newGPXFile() {
|
||||||
const newFileName = get(_)("menu.new_file");
|
const newFileName = get(_)('menu.new_file');
|
||||||
|
|
||||||
let file = new GPXFile();
|
let file = new GPXFile();
|
||||||
|
|
||||||
let maxNewFileNumber = 0;
|
let maxNewFileNumber = 0;
|
||||||
get(fileObservers).forEach((f) => {
|
get(fileObservers).forEach((f) => {
|
||||||
let file = get(f)?.file;
|
let file = get(f)?.file;
|
||||||
if (file && file.metadata.name && file.metadata.name.startsWith(newFileName)) {
|
if (file && file.metadata.name && file.metadata.name.startsWith(newFileName)) {
|
||||||
let number = parseInt(file.metadata.name.split(' ').pop() ?? '0');
|
let number = parseInt(file.metadata.name.split(' ').pop() ?? '0');
|
||||||
if (!isNaN(number) && number > maxNewFileNumber) {
|
if (!isNaN(number) && number > maxNewFileNumber) {
|
||||||
maxNewFileNumber = number;
|
maxNewFileNumber = number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
file.metadata.name = `${newFileName} ${maxNewFileNumber + 1}`;
|
file.metadata.name = `${newFileName} ${maxNewFileNumber + 1}`;
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createFile() {
|
export function createFile() {
|
||||||
let file = newGPXFile();
|
let file = newGPXFile();
|
||||||
|
|
||||||
dbUtils.add(file);
|
dbUtils.add(file);
|
||||||
|
|
||||||
selectFileWhenLoaded(file._data.id);
|
selectFileWhenLoaded(file._data.id);
|
||||||
currentTool.set(Tool.ROUTING);
|
currentTool.set(Tool.ROUTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function triggerFileInput() {
|
export function triggerFileInput() {
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.accept = '.gpx';
|
input.accept = '.gpx';
|
||||||
input.multiple = true;
|
input.multiple = true;
|
||||||
input.className = 'hidden';
|
input.className = 'hidden';
|
||||||
input.onchange = () => {
|
input.onchange = () => {
|
||||||
if (input.files) {
|
if (input.files) {
|
||||||
loadFiles(input.files);
|
loadFiles(input.files);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadFiles(list: FileList | File[]) {
|
export async function loadFiles(list: FileList | File[]) {
|
||||||
let files = [];
|
let files = [];
|
||||||
for (let i = 0; i < list.length; i++) {
|
for (let i = 0; i < list.length; i++) {
|
||||||
let file = await loadFile(list[i]);
|
let file = await loadFile(list[i]);
|
||||||
if (file) {
|
if (file) {
|
||||||
files.push(file);
|
files.push(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initTargetMapBounds(list.length);
|
initTargetMapBounds(list.length);
|
||||||
|
|
||||||
dbUtils.addMultiple(files);
|
dbUtils.addMultiple(files);
|
||||||
|
|
||||||
selectFileWhenLoaded(files[0]._data.id);
|
selectFileWhenLoaded(files[0]._data.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadFile(file: File): Promise<GPXFile | null> {
|
export async function loadFile(file: File): Promise<GPXFile | null> {
|
||||||
let result = await new Promise<GPXFile | null>((resolve) => {
|
let result = await new Promise<GPXFile | null>((resolve) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = () => {
|
reader.onload = () => {
|
||||||
let data = reader.result?.toString() ?? null;
|
let data = reader.result?.toString() ?? null;
|
||||||
if (data) {
|
if (data) {
|
||||||
let gpx = parseGPX(data);
|
let gpx = parseGPX(data);
|
||||||
if (gpx.metadata === undefined) {
|
if (gpx.metadata === undefined) {
|
||||||
gpx.metadata = { name: file.name.split('.').slice(0, -1).join('.') };
|
gpx.metadata = { name: file.name.split('.').slice(0, -1).join('.') };
|
||||||
} else if (gpx.metadata.name === undefined) {
|
} else if (gpx.metadata.name === undefined) {
|
||||||
gpx.metadata['name'] = file.name.split('.').slice(0, -1).join('.');
|
gpx.metadata['name'] = file.name.split('.').slice(0, -1).join('.');
|
||||||
}
|
}
|
||||||
resolve(gpx);
|
resolve(gpx);
|
||||||
} else {
|
} else {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectFileWhenLoaded(fileId: string) {
|
export function selectFileWhenLoaded(fileId: string) {
|
||||||
const unsubscribe = fileObservers.subscribe((files) => {
|
const unsubscribe = fileObservers.subscribe((files) => {
|
||||||
if (files.has(fileId)) {
|
if (files.has(fileId)) {
|
||||||
tick().then(() => {
|
tick().then(() => {
|
||||||
selectFile(fileId);
|
selectFile(fileId);
|
||||||
});
|
});
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateSelectionFromKey(down: boolean, shift: boolean) {
|
export function updateSelectionFromKey(down: boolean, shift: boolean) {
|
||||||
let selected = get(selection).getSelected();
|
let selected = get(selection).getSelected();
|
||||||
if (selected.length === 0) {
|
if (selected.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let next: ListItem | undefined = undefined;
|
let next: ListItem | undefined = undefined;
|
||||||
if (selected[0] instanceof ListFileItem) {
|
if (selected[0] instanceof ListFileItem) {
|
||||||
let order = get(fileOrder);
|
let order = get(fileOrder);
|
||||||
let limitIndex: number | undefined = undefined;
|
let limitIndex: number | undefined = undefined;
|
||||||
selected.forEach((item) => {
|
selected.forEach((item) => {
|
||||||
let index = order.indexOf(item.getFileId());
|
let index = order.indexOf(item.getFileId());
|
||||||
if (limitIndex === undefined || (down && index > limitIndex) || (!down && index < limitIndex)) {
|
if (
|
||||||
limitIndex = index;
|
limitIndex === undefined ||
|
||||||
}
|
(down && index > limitIndex) ||
|
||||||
});
|
(!down && index < limitIndex)
|
||||||
|
) {
|
||||||
|
limitIndex = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (limitIndex !== undefined) {
|
if (limitIndex !== undefined) {
|
||||||
let nextIndex = down ? limitIndex + 1 : limitIndex - 1;
|
let nextIndex = down ? limitIndex + 1 : limitIndex - 1;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (nextIndex < 0) {
|
if (nextIndex < 0) {
|
||||||
nextIndex = order.length - 1;
|
nextIndex = order.length - 1;
|
||||||
} else if (nextIndex >= order.length) {
|
} else if (nextIndex >= order.length) {
|
||||||
nextIndex = 0;
|
nextIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextIndex === limitIndex) {
|
if (nextIndex === limitIndex) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
next = new ListFileItem(order[nextIndex]);
|
next = new ListFileItem(order[nextIndex]);
|
||||||
if (!get(selection).has(next)) {
|
if (!get(selection).has(next)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextIndex += down ? 1 : -1;
|
nextIndex += down ? 1 : -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (selected[0] instanceof ListTrackItem && selected[selected.length - 1] instanceof ListTrackItem) {
|
} else if (
|
||||||
let fileId = selected[0].getFileId();
|
selected[0] instanceof ListTrackItem &&
|
||||||
let file = getFile(fileId);
|
selected[selected.length - 1] instanceof ListTrackItem
|
||||||
if (file) {
|
) {
|
||||||
let numberOfTracks = file.trk.length;
|
let fileId = selected[0].getFileId();
|
||||||
let trackIndex = down ? selected[selected.length - 1].getTrackIndex() : selected[0].getTrackIndex();
|
let file = getFile(fileId);
|
||||||
if (down && trackIndex < numberOfTracks - 1) {
|
if (file) {
|
||||||
next = new ListTrackItem(fileId, trackIndex + 1);
|
let numberOfTracks = file.trk.length;
|
||||||
} else if (!down && trackIndex > 0) {
|
let trackIndex = down
|
||||||
next = new ListTrackItem(fileId, trackIndex - 1);
|
? selected[selected.length - 1].getTrackIndex()
|
||||||
}
|
: selected[0].getTrackIndex();
|
||||||
}
|
if (down && trackIndex < numberOfTracks - 1) {
|
||||||
} else if (selected[0] instanceof ListTrackSegmentItem && selected[selected.length - 1] instanceof ListTrackSegmentItem) {
|
next = new ListTrackItem(fileId, trackIndex + 1);
|
||||||
let fileId = selected[0].getFileId();
|
} else if (!down && trackIndex > 0) {
|
||||||
let file = getFile(fileId);
|
next = new ListTrackItem(fileId, trackIndex - 1);
|
||||||
if (file) {
|
}
|
||||||
let trackIndex = selected[0].getTrackIndex();
|
}
|
||||||
let numberOfSegments = file.trk[trackIndex].trkseg.length;
|
} else if (
|
||||||
let segmentIndex = down ? selected[selected.length - 1].getSegmentIndex() : selected[0].getSegmentIndex();
|
selected[0] instanceof ListTrackSegmentItem &&
|
||||||
if (down && segmentIndex < numberOfSegments - 1) {
|
selected[selected.length - 1] instanceof ListTrackSegmentItem
|
||||||
next = new ListTrackSegmentItem(fileId, trackIndex, segmentIndex + 1);
|
) {
|
||||||
} else if (!down && segmentIndex > 0) {
|
let fileId = selected[0].getFileId();
|
||||||
next = new ListTrackSegmentItem(fileId, trackIndex, segmentIndex - 1);
|
let file = getFile(fileId);
|
||||||
}
|
if (file) {
|
||||||
}
|
let trackIndex = selected[0].getTrackIndex();
|
||||||
} else if (selected[0] instanceof ListWaypointItem && selected[selected.length - 1] instanceof ListWaypointItem) {
|
let numberOfSegments = file.trk[trackIndex].trkseg.length;
|
||||||
let fileId = selected[0].getFileId();
|
let segmentIndex = down
|
||||||
let file = getFile(fileId);
|
? selected[selected.length - 1].getSegmentIndex()
|
||||||
if (file) {
|
: selected[0].getSegmentIndex();
|
||||||
let numberOfWaypoints = file.wpt.length;
|
if (down && segmentIndex < numberOfSegments - 1) {
|
||||||
let waypointIndex = down ? selected[selected.length - 1].getWaypointIndex() : selected[0].getWaypointIndex();
|
next = new ListTrackSegmentItem(fileId, trackIndex, segmentIndex + 1);
|
||||||
if (down && waypointIndex < numberOfWaypoints - 1) {
|
} else if (!down && segmentIndex > 0) {
|
||||||
next = new ListWaypointItem(fileId, waypointIndex + 1);
|
next = new ListTrackSegmentItem(fileId, trackIndex, segmentIndex - 1);
|
||||||
} else if (!down && waypointIndex > 0) {
|
}
|
||||||
next = new ListWaypointItem(fileId, waypointIndex - 1);
|
}
|
||||||
}
|
} else if (
|
||||||
}
|
selected[0] instanceof ListWaypointItem &&
|
||||||
}
|
selected[selected.length - 1] instanceof ListWaypointItem
|
||||||
|
) {
|
||||||
|
let fileId = selected[0].getFileId();
|
||||||
|
let file = getFile(fileId);
|
||||||
|
if (file) {
|
||||||
|
let numberOfWaypoints = file.wpt.length;
|
||||||
|
let waypointIndex = down
|
||||||
|
? selected[selected.length - 1].getWaypointIndex()
|
||||||
|
: selected[0].getWaypointIndex();
|
||||||
|
if (down && waypointIndex < numberOfWaypoints - 1) {
|
||||||
|
next = new ListWaypointItem(fileId, waypointIndex + 1);
|
||||||
|
} else if (!down && waypointIndex > 0) {
|
||||||
|
next = new ListWaypointItem(fileId, waypointIndex - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (next && (!get(selection).has(next) || !shift)) {
|
if (next && (!get(selection).has(next) || !shift)) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
addSelectItem(next);
|
addSelectItem(next);
|
||||||
} else {
|
} else {
|
||||||
selectItem(next);
|
selectItem(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function exportFiles(fileIds: string[], exclude: string[]) {
|
async function exportFiles(fileIds: string[], exclude: string[]) {
|
||||||
for (let fileId of fileIds) {
|
for (let fileId of fileIds) {
|
||||||
let file = getFile(fileId);
|
let file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
exportFile(file, exclude);
|
exportFile(file, exclude);
|
||||||
await new Promise(resolve => setTimeout(resolve, 200));
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exportSelectedFiles(exclude: string[]) {
|
export function exportSelectedFiles(exclude: string[]) {
|
||||||
let fileIds: string[] = [];
|
let fileIds: string[] = [];
|
||||||
applyToOrderedSelectedItemsFromFile(async (fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile(async (fileId, level, items) => {
|
||||||
fileIds.push(fileId);
|
fileIds.push(fileId);
|
||||||
});
|
});
|
||||||
exportFiles(fileIds, exclude);
|
exportFiles(fileIds, exclude);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exportAllFiles(exclude: string[]) {
|
export function exportAllFiles(exclude: string[]) {
|
||||||
exportFiles(get(fileOrder), exclude);
|
exportFiles(get(fileOrder), exclude);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exportFile(file: GPXFile, exclude: string[]) {
|
export function exportFile(file: GPXFile, exclude: string[]) {
|
||||||
let blob = new Blob([buildGPX(file, exclude)], { type: 'application/gpx+xml' });
|
let blob = new Blob([buildGPX(file, exclude)], { type: 'application/gpx+xml' });
|
||||||
let url = URL.createObjectURL(blob);
|
let url = URL.createObjectURL(blob);
|
||||||
let a = document.createElement('a');
|
let a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = file.metadata.name + '.gpx';
|
a.download = file.metadata.name + '.gpx';
|
||||||
a.click();
|
a.click();
|
||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const allHidden = writable(false);
|
export const allHidden = writable(false);
|
||||||
|
|
||||||
export function updateAllHidden() {
|
export function updateAllHidden() {
|
||||||
let hidden = true;
|
let hidden = true;
|
||||||
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
let file = getFile(fileId);
|
let file = getFile(fileId);
|
||||||
if (file) {
|
if (file) {
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
if (!hidden) {
|
if (!hidden) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item instanceof ListFileItem) {
|
if (item instanceof ListFileItem) {
|
||||||
hidden = hidden && (file._data.hidden === true);
|
hidden = hidden && file._data.hidden === true;
|
||||||
} else if (item instanceof ListTrackItem && item.getTrackIndex() < file.trk.length) {
|
} else if (item instanceof ListTrackItem && item.getTrackIndex() < file.trk.length) {
|
||||||
hidden = hidden && (file.trk[item.getTrackIndex()]._data.hidden === true);
|
hidden = hidden && file.trk[item.getTrackIndex()]._data.hidden === true;
|
||||||
} else if (item instanceof ListTrackSegmentItem && item.getTrackIndex() < file.trk.length && item.getSegmentIndex() < file.trk[item.getTrackIndex()].trkseg.length) {
|
} else if (
|
||||||
hidden = hidden && (file.trk[item.getTrackIndex()].trkseg[item.getSegmentIndex()]._data.hidden === true);
|
item instanceof ListTrackSegmentItem &&
|
||||||
} else if (item instanceof ListWaypointsItem) {
|
item.getTrackIndex() < file.trk.length &&
|
||||||
hidden = hidden && (file._data.hiddenWpt === true);
|
item.getSegmentIndex() < file.trk[item.getTrackIndex()].trkseg.length
|
||||||
} else if (item instanceof ListWaypointItem && item.getWaypointIndex() < file.wpt.length) {
|
) {
|
||||||
hidden = hidden && (file.wpt[item.getWaypointIndex()]._data.hidden === true);
|
hidden =
|
||||||
}
|
hidden &&
|
||||||
}
|
file.trk[item.getTrackIndex()].trkseg[item.getSegmentIndex()]._data.hidden === true;
|
||||||
}
|
} else if (item instanceof ListWaypointsItem) {
|
||||||
});
|
hidden = hidden && file._data.hiddenWpt === true;
|
||||||
allHidden.set(hidden);
|
} else if (item instanceof ListWaypointItem && item.getWaypointIndex() < file.wpt.length) {
|
||||||
|
hidden = hidden && file.wpt[item.getWaypointIndex()]._data.hidden === true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
allHidden.set(hidden);
|
||||||
}
|
}
|
||||||
selection.subscribe(updateAllHidden);
|
selection.subscribe(updateAllHidden);
|
||||||
|
|
||||||
|
|
@ -388,8 +452,8 @@ export const editMetadata = writable(false);
|
||||||
export const editStyle = writable(false);
|
export const editStyle = writable(false);
|
||||||
|
|
||||||
export enum ExportState {
|
export enum ExportState {
|
||||||
NONE,
|
NONE,
|
||||||
SELECTION,
|
SELECTION,
|
||||||
ALL
|
ALL
|
||||||
}
|
}
|
||||||
export const exportState = writable<ExportState>(ExportState.NONE);
|
export const exportState = writable<ExportState>(ExportState.NONE);
|
||||||
|
|
|
||||||
|
|
@ -1,476 +1,477 @@
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"home_title": "home",
|
"home_title": "home",
|
||||||
"app_title": "the online GPX file editor",
|
"app_title": "the online GPX file editor",
|
||||||
"embed_title": "the online GPX file editor",
|
"embed_title": "the online GPX file editor",
|
||||||
"help_title": "help",
|
"help_title": "help",
|
||||||
"404_title": "page not found",
|
"404_title": "page not found",
|
||||||
"description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations."
|
"description": "View, edit, and create GPX files online with advanced route planning capabilities and file processing tools, beautiful maps and detailed data visualizations."
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"new": "New",
|
"new": "New",
|
||||||
"new_file": "New file",
|
"new_file": "New file",
|
||||||
"new_track": "New track",
|
"new_track": "New track",
|
||||||
"new_segment": "New segment",
|
"new_segment": "New segment",
|
||||||
"open": "Open...",
|
"open": "Open...",
|
||||||
"duplicate": "Duplicate",
|
"duplicate": "Duplicate",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"close_all": "Close all",
|
"close_all": "Close all",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"paste": "Paste",
|
"paste": "Paste",
|
||||||
"cut": "Cut",
|
"cut": "Cut",
|
||||||
"export": "Export...",
|
"export": "Export...",
|
||||||
"export_all": "Export all...",
|
"export_all": "Export all...",
|
||||||
"export_options": "Export options",
|
"export_options": "Export options",
|
||||||
"support_message": "The tool is free to use, but not free to run. Please consider supporting the website if you use it frequently. Thank you!",
|
"support_message": "The tool is free to use, but not free to run. Please consider supporting the website if you use it frequently. Thank you!",
|
||||||
"support_button": "Help keep the website free",
|
"support_button": "Help keep the website free",
|
||||||
"download_file": "Download file",
|
"download_file": "Download file",
|
||||||
"download_files": "Download files",
|
"download_files": "Download files",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"undo": "Undo",
|
"undo": "Undo",
|
||||||
"redo": "Redo",
|
"redo": "Redo",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"select_all": "Select all",
|
"select_all": "Select all",
|
||||||
"view": "View",
|
"view": "View",
|
||||||
"elevation_profile": "Elevation profile",
|
"elevation_profile": "Elevation profile",
|
||||||
"vertical_file_view": "Vertical file list",
|
"vertical_file_view": "Vertical file list",
|
||||||
"switch_basemap": "Switch to previous basemap",
|
"switch_basemap": "Switch to previous basemap",
|
||||||
"toggle_overlays": "Toggle overlays",
|
"toggle_overlays": "Toggle overlays",
|
||||||
"toggle_3d": "Toggle 3D",
|
"toggle_3d": "Toggle 3D",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"distance_units": "Distance units",
|
"distance_units": "Distance units",
|
||||||
"metric": "Metric",
|
"metric": "Metric",
|
||||||
"imperial": "Imperial",
|
"imperial": "Imperial",
|
||||||
"velocity_units": "Velocity units",
|
"velocity_units": "Velocity units",
|
||||||
"temperature_units": "Temperature units",
|
"temperature_units": "Temperature units",
|
||||||
"celsius": "Celsius",
|
"celsius": "Celsius",
|
||||||
"fahrenheit": "Fahrenheit",
|
"fahrenheit": "Fahrenheit",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"mode": "Theme",
|
"mode": "Theme",
|
||||||
"system": "System",
|
"system": "System",
|
||||||
"light": "Light",
|
"light": "Light",
|
||||||
"dark": "Dark",
|
"dark": "Dark",
|
||||||
"street_view_source": "Street view source",
|
"street_view_source": "Street view source",
|
||||||
"mapillary": "Mapillary",
|
"mapillary": "Mapillary",
|
||||||
"google": "Google",
|
"google": "Google",
|
||||||
"layers": "Map layers...",
|
"layers": "Map layers...",
|
||||||
"distance_markers": "Distance markers",
|
"distance_markers": "Distance markers",
|
||||||
"direction_markers": "Direction arrows",
|
"direction_markers": "Direction arrows",
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"more": "More...",
|
"more": "More...",
|
||||||
"donate": "Donate",
|
"donate": "Donate",
|
||||||
"ctrl": "Ctrl",
|
"ctrl": "Ctrl",
|
||||||
"click": "Click",
|
"click": "Click",
|
||||||
"drag": "Drag",
|
"drag": "Drag",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"button": "Info...",
|
"button": "Info...",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"save": "Save"
|
"save": "Save"
|
||||||
},
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"button": "Appearance...",
|
"button": "Appearance...",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"opacity": "Opacity",
|
"opacity": "Opacity",
|
||||||
"width": "Width"
|
"width": "Width"
|
||||||
},
|
},
|
||||||
"hide": "Hide",
|
"hide": "Hide",
|
||||||
"unhide": "Unhide",
|
"unhide": "Unhide",
|
||||||
"open_in": "Open in",
|
"open_in": "Open in",
|
||||||
"fit_map": "Fit on map"
|
"fly_to": "Fly to",
|
||||||
},
|
"fly_to_selection": "Fly to selection"
|
||||||
"toolbar": {
|
},
|
||||||
"routing": {
|
"toolbar": {
|
||||||
"tooltip": "Plan or edit a route",
|
"routing": {
|
||||||
"activity": "Activity",
|
"tooltip": "Plan or edit a route",
|
||||||
"use_routing": "Routing",
|
"activity": "Activity",
|
||||||
"use_routing_tooltip": "Connect anchor points via road network, or in a straight line if disabled",
|
"use_routing": "Routing",
|
||||||
"allow_private": "Allow private roads",
|
"use_routing_tooltip": "Connect anchor points via road network, or in a straight line if disabled",
|
||||||
"reverse": {
|
"allow_private": "Allow private roads",
|
||||||
"button": "Reverse",
|
"reverse": {
|
||||||
"tooltip": "Reverse the direction of the route"
|
"button": "Reverse",
|
||||||
},
|
"tooltip": "Reverse the direction of the route"
|
||||||
"route_back_to_start": {
|
},
|
||||||
"button": "Back to start",
|
"route_back_to_start": {
|
||||||
"tooltip": "Connect the last point of the route with the starting point"
|
"button": "Back to start",
|
||||||
},
|
"tooltip": "Connect the last point of the route with the starting point"
|
||||||
"round_trip": {
|
},
|
||||||
"button": "Round trip",
|
"round_trip": {
|
||||||
"tooltip": "Return to the starting point by the same route"
|
"button": "Round trip",
|
||||||
},
|
"tooltip": "Return to the starting point by the same route"
|
||||||
"start_loop_here": "Start loop here",
|
},
|
||||||
"help_no_file": "Select a trace to use the routing tool, or click on the map to start creating a new route.",
|
"start_loop_here": "Start loop here",
|
||||||
"help": "Click on the map to add a new anchor point, or drag existing ones to change the route.",
|
"help_no_file": "Select a trace to use the routing tool, or click on the map to start creating a new route.",
|
||||||
"activities": {
|
"help": "Click on the map to add a new anchor point, or drag existing ones to change the route.",
|
||||||
"bike": "Bike",
|
"activities": {
|
||||||
"racing_bike": "Road bike",
|
"bike": "Bike",
|
||||||
"gravel_bike": "Gravel bike",
|
"racing_bike": "Road bike",
|
||||||
"mountain_bike": "Mountain bike",
|
"gravel_bike": "Gravel bike",
|
||||||
"foot": "Run/hike",
|
"mountain_bike": "Mountain bike",
|
||||||
"motorcycle": "Motorcycle",
|
"foot": "Run/hike",
|
||||||
"water": "Water",
|
"motorcycle": "Motorcycle",
|
||||||
"railway": "Railway"
|
"water": "Water",
|
||||||
},
|
"railway": "Railway"
|
||||||
"surface": {
|
},
|
||||||
"unknown": "Unknown",
|
"surface": {
|
||||||
"paved": "Paved",
|
"unknown": "Unknown",
|
||||||
"unpaved": "Unpaved",
|
"paved": "Paved",
|
||||||
"asphalt": "Asphalt",
|
"unpaved": "Unpaved",
|
||||||
"concrete": "Concrete",
|
"asphalt": "Asphalt",
|
||||||
"chipseal": "Chipseal",
|
"concrete": "Concrete",
|
||||||
"cobblestone": "Cobblestone",
|
"chipseal": "Chipseal",
|
||||||
"unhewn_cobblestone": "Unhewn cobblestone",
|
"cobblestone": "Cobblestone",
|
||||||
"paving_stones": "Paving stones",
|
"unhewn_cobblestone": "Unhewn cobblestone",
|
||||||
"stepping_stones": "Stepping stones",
|
"paving_stones": "Paving stones",
|
||||||
"sett": "Sett",
|
"stepping_stones": "Stepping stones",
|
||||||
"metal": "Metal",
|
"sett": "Sett",
|
||||||
"wood": "Wood",
|
"metal": "Metal",
|
||||||
"compacted": "Compacted gravel",
|
"wood": "Wood",
|
||||||
"fine_gravel": "Fine gravel",
|
"compacted": "Compacted gravel",
|
||||||
"gravel": "Gravel",
|
"fine_gravel": "Fine gravel",
|
||||||
"pebblestone": "Pebblestone",
|
"gravel": "Gravel",
|
||||||
"rock": "Rock",
|
"pebblestone": "Pebblestone",
|
||||||
"dirt": "Dirt",
|
"rock": "Rock",
|
||||||
"ground": "Ground",
|
"dirt": "Dirt",
|
||||||
"earth": "Earth",
|
"ground": "Ground",
|
||||||
"snow": "Snow",
|
"earth": "Earth",
|
||||||
"ice": "Ice",
|
"snow": "Snow",
|
||||||
"salt": "Salt",
|
"ice": "Ice",
|
||||||
"mud": "Mud",
|
"salt": "Salt",
|
||||||
"sand": "Sand",
|
"mud": "Mud",
|
||||||
"woodchips": "Woodchips",
|
"sand": "Sand",
|
||||||
"grass": "Grass",
|
"woodchips": "Woodchips",
|
||||||
"grass_paver": "Grass paver"
|
"grass": "Grass",
|
||||||
},
|
"grass_paver": "Grass paver"
|
||||||
"error": {
|
},
|
||||||
"from": "The start point is too far from the nearest road",
|
"error": {
|
||||||
"via": "The via point is too far from the nearest road",
|
"from": "The start point is too far from the nearest road",
|
||||||
"to": "The end point is too far from the nearest road",
|
"via": "The via point is too far from the nearest road",
|
||||||
"timeout": "Route calculation took too long, try adding points closer together"
|
"to": "The end point is too far from the nearest road",
|
||||||
}
|
"timeout": "Route calculation took too long, try adding points closer together"
|
||||||
},
|
}
|
||||||
"scissors": {
|
},
|
||||||
"tooltip": "Crop or split",
|
"scissors": {
|
||||||
"crop": "Crop",
|
"tooltip": "Crop or split",
|
||||||
"split_as": "Split the trace into",
|
"crop": "Crop",
|
||||||
"help_invalid_selection": "Select a trace to crop or split.",
|
"split_as": "Split the trace into",
|
||||||
"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_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."
|
||||||
"time": {
|
},
|
||||||
"tooltip": "Manage time data",
|
"time": {
|
||||||
"start": "Start",
|
"tooltip": "Manage time data",
|
||||||
"end": "End",
|
"start": "Start",
|
||||||
"total_time": "Moving time",
|
"end": "End",
|
||||||
"pick_date": "Pick a date",
|
"total_time": "Moving time",
|
||||||
"artificial": "Create realistic time data",
|
"pick_date": "Pick a date",
|
||||||
"update": "Update time data",
|
"artificial": "Create realistic time data",
|
||||||
"help": "Use the form to set new time data.",
|
"update": "Update time data",
|
||||||
"help_invalid_selection": "Select a single trace to manage its time data."
|
"help": "Use the form to set new time data.",
|
||||||
},
|
"help_invalid_selection": "Select a single trace to manage its time data."
|
||||||
"merge": {
|
},
|
||||||
"merge_traces": "Connect the traces",
|
"merge": {
|
||||||
"merge_contents": "Merge the contents and keep the traces disconnected",
|
"merge_traces": "Connect the traces",
|
||||||
"merge_selection": "Merge selection",
|
"merge_contents": "Merge the contents and keep the traces disconnected",
|
||||||
"tooltip": "Merge items together",
|
"merge_selection": "Merge selection",
|
||||||
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
|
"tooltip": "Merge items together",
|
||||||
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
|
"help_merge_traces": "Connecting the selected traces will create a single continuous trace.",
|
||||||
"help_merge_contents": "Merging the contents of the selected items will group all the contents inside the first item.",
|
"help_cannot_merge_traces": "Your selection must contain several traces to connect them.",
|
||||||
"help_cannot_merge_contents": "Your selection must contain several items to merge their contents."
|
"help_merge_contents": "Merging the contents of the selected items will group all the contents inside the first item.",
|
||||||
},
|
"help_cannot_merge_contents": "Your selection must contain several items to merge their contents."
|
||||||
"extract": {
|
},
|
||||||
"tooltip": "Extract contents to separate items",
|
"extract": {
|
||||||
"button": "Extract",
|
"tooltip": "Extract contents to separate items",
|
||||||
"help": "Extracting the contents of the selected items will create a separate item for each of their contents.",
|
"button": "Extract",
|
||||||
"help_invalid_selection": "Your selection must contain items with multiple traces to extract them."
|
"help": "Extracting the contents of the selected items will create a separate item for each of their contents.",
|
||||||
},
|
"help_invalid_selection": "Your selection must contain items with multiple traces to extract them."
|
||||||
"waypoint": {
|
},
|
||||||
"tooltip": "Create and edit points of interest",
|
"waypoint": {
|
||||||
"icon": "Icon",
|
"tooltip": "Create and edit points of interest",
|
||||||
"link": "Link",
|
"icon": "Icon",
|
||||||
"longitude": "Longitude",
|
"link": "Link",
|
||||||
"latitude": "Latitude",
|
"longitude": "Longitude",
|
||||||
"create": "Create point of interest",
|
"latitude": "Latitude",
|
||||||
"add": "Add point of interest to file",
|
"create": "Create point of interest",
|
||||||
"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.",
|
"add": "Add point of interest to file",
|
||||||
"help_no_selection": "Select a file to create or edit points of interest."
|
"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."
|
||||||
"reduce": {
|
},
|
||||||
"tooltip": "Reduce the number of GPS points",
|
"reduce": {
|
||||||
"tolerance": "Tolerance",
|
"tooltip": "Reduce the number of GPS points",
|
||||||
"number_of_points": "Number of GPS points",
|
"tolerance": "Tolerance",
|
||||||
"button": "Minify",
|
"number_of_points": "Number of GPS points",
|
||||||
"help": "Use the slider to choose the number of GPS points to keep.",
|
"button": "Minify",
|
||||||
"help_no_selection": "Select a trace to reduce the number of its GPS points."
|
"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."
|
||||||
"clean": {
|
},
|
||||||
"tooltip": "Clean GPS points and points of interest with a rectangle selection",
|
"clean": {
|
||||||
"delete_trackpoints": "Delete GPS points",
|
"tooltip": "Clean GPS points and points of interest with a rectangle selection",
|
||||||
"delete_waypoints": "Delete points of interest",
|
"delete_trackpoints": "Delete GPS points",
|
||||||
"delete_inside": "Delete inside selection",
|
"delete_waypoints": "Delete points of interest",
|
||||||
"delete_outside": "Delete outside selection",
|
"delete_inside": "Delete inside selection",
|
||||||
"button": "Delete",
|
"delete_outside": "Delete outside selection",
|
||||||
"help": "Select a rectangle area on the map to remove GPS points and points of interest.",
|
"button": "Delete",
|
||||||
"help_no_selection": "Select a trace to clean GPS points and points of interest."
|
"help": "Select a rectangle area on the map to remove GPS points and points of interest.",
|
||||||
}
|
"help_no_selection": "Select a trace to clean GPS points and points of interest."
|
||||||
},
|
}
|
||||||
"layers": {
|
},
|
||||||
"settings": "Layer settings",
|
"layers": {
|
||||||
"settings_help": "Select the map layers you want to show in the interface, add custom ones, and adjust their settings.",
|
"settings": "Layer settings",
|
||||||
"selection": "Layer selection",
|
"settings_help": "Select the map layers you want to show in the interface, add custom ones, and adjust their settings.",
|
||||||
"custom_layers": {
|
"selection": "Layer selection",
|
||||||
"title": "Custom layers",
|
"custom_layers": {
|
||||||
"new": "New custom layer",
|
"title": "Custom layers",
|
||||||
"edit": "Edit custom layer",
|
"new": "New custom layer",
|
||||||
"urls": "URL(s)",
|
"edit": "Edit custom layer",
|
||||||
"url_placeholder": "WMTS, WMS or Mapbox style JSON",
|
"urls": "URL(s)",
|
||||||
"max_zoom": "Max zoom",
|
"url_placeholder": "WMTS, WMS or Mapbox style JSON",
|
||||||
"layer_type": "Layer type",
|
"max_zoom": "Max zoom",
|
||||||
"basemap": "Basemap",
|
"layer_type": "Layer type",
|
||||||
"overlay": "Overlay",
|
"basemap": "Basemap",
|
||||||
"create": "Create layer",
|
"overlay": "Overlay",
|
||||||
"update": "Update layer"
|
"create": "Create layer",
|
||||||
},
|
"update": "Update layer"
|
||||||
"opacity": "Overlay opacity",
|
},
|
||||||
"label": {
|
"opacity": "Overlay opacity",
|
||||||
"basemaps": "Basemaps",
|
"label": {
|
||||||
"overlays": "Overlays",
|
"basemaps": "Basemaps",
|
||||||
"custom": "Custom",
|
"overlays": "Overlays",
|
||||||
"world": "World",
|
"custom": "Custom",
|
||||||
"countries": "Countries",
|
"world": "World",
|
||||||
"belgium": "Belgium",
|
"countries": "Countries",
|
||||||
"bulgaria": "Bulgaria",
|
"belgium": "Belgium",
|
||||||
"finland": "Finland",
|
"bulgaria": "Bulgaria",
|
||||||
"france": "France",
|
"finland": "Finland",
|
||||||
"new_zealand": "New Zealand",
|
"france": "France",
|
||||||
"norway": "Norway",
|
"new_zealand": "New Zealand",
|
||||||
"spain": "Spain",
|
"norway": "Norway",
|
||||||
"sweden": "Sweden",
|
"spain": "Spain",
|
||||||
"switzerland": "Switzerland",
|
"sweden": "Sweden",
|
||||||
"united_kingdom": "United Kingdom",
|
"switzerland": "Switzerland",
|
||||||
"united_states": "United States",
|
"united_kingdom": "United Kingdom",
|
||||||
"mapboxOutdoors": "Mapbox Outdoors",
|
"united_states": "United States",
|
||||||
"mapboxSatellite": "Mapbox Satellite",
|
"mapboxOutdoors": "Mapbox Outdoors",
|
||||||
"openStreetMap": "OpenStreetMap",
|
"mapboxSatellite": "Mapbox Satellite",
|
||||||
"openTopoMap": "OpenTopoMap",
|
"openStreetMap": "OpenStreetMap",
|
||||||
"openHikingMap": "OpenHikingMap",
|
"openTopoMap": "OpenTopoMap",
|
||||||
"cyclOSM": "CyclOSM",
|
"openHikingMap": "OpenHikingMap",
|
||||||
"linz": "LINZ Topo",
|
"cyclOSM": "CyclOSM",
|
||||||
"linzTopo": "LINZ Topo50",
|
"linz": "LINZ Topo",
|
||||||
"swisstopoRaster": "swisstopo Raster",
|
"linzTopo": "LINZ Topo50",
|
||||||
"swisstopoVector": "swisstopo Vector",
|
"swisstopoRaster": "swisstopo Raster",
|
||||||
"swisstopoSatellite": "swisstopo Satellite",
|
"swisstopoVector": "swisstopo Vector",
|
||||||
"ignBe": "IGN Topo",
|
"swisstopoSatellite": "swisstopo Satellite",
|
||||||
"ignFrPlan": "IGN Plan",
|
"ignBe": "IGN Topo",
|
||||||
"ignFrTopo": "IGN Topo",
|
"ignFrPlan": "IGN Plan",
|
||||||
"ignFrScan25": "IGN SCAN25",
|
"ignFrTopo": "IGN Topo",
|
||||||
"ignFrSatellite": "IGN Satellite",
|
"ignFrScan25": "IGN SCAN25",
|
||||||
"ignEs": "IGN",
|
"ignFrSatellite": "IGN Satellite",
|
||||||
"ordnanceSurvey": "Ordnance Survey",
|
"ignEs": "IGN",
|
||||||
"norwayTopo": "Topografisk Norgeskart 4",
|
"ordnanceSurvey": "Ordnance Survey",
|
||||||
"swedenTopo": "Lantmäteriet Topo",
|
"norwayTopo": "Topografisk Norgeskart 4",
|
||||||
"finlandTopo": "Lantmäteriverket Terrängkarta",
|
"swedenTopo": "Lantmäteriet Topo",
|
||||||
"bgMountains": "BGMountains",
|
"finlandTopo": "Lantmäteriverket Terrängkarta",
|
||||||
"usgs": "USGS",
|
"bgMountains": "BGMountains",
|
||||||
"cyclOSMlite": "CyclOSM Lite",
|
"usgs": "USGS",
|
||||||
"swisstopoSlope": "swisstopo Slope",
|
"cyclOSMlite": "CyclOSM Lite",
|
||||||
"swisstopoHiking": "swisstopo Hiking",
|
"swisstopoSlope": "swisstopo Slope",
|
||||||
"swisstopoHikingClosures": "swisstopo Hiking Closures",
|
"swisstopoHiking": "swisstopo Hiking",
|
||||||
"swisstopoCycling": "swisstopo Cycling",
|
"swisstopoHikingClosures": "swisstopo Hiking Closures",
|
||||||
"swisstopoCyclingClosures": "swisstopo Cycling Closures",
|
"swisstopoCycling": "swisstopo Cycling",
|
||||||
"swisstopoMountainBike": "swisstopo MTB",
|
"swisstopoCyclingClosures": "swisstopo Cycling Closures",
|
||||||
"swisstopoMountainBikeClosures": "swisstopo MTB Closures",
|
"swisstopoMountainBike": "swisstopo MTB",
|
||||||
"swisstopoSkiTouring": "swisstopo Ski Touring",
|
"swisstopoMountainBikeClosures": "swisstopo MTB Closures",
|
||||||
"ignFrCadastre": "IGN Cadastre",
|
"swisstopoSkiTouring": "swisstopo Ski Touring",
|
||||||
"ignSlope": "IGN Slope",
|
"ignFrCadastre": "IGN Cadastre",
|
||||||
"ignSkiTouring": "IGN Ski Touring",
|
"ignSlope": "IGN Slope",
|
||||||
"waymarked_trails": "Waymarked Trails",
|
"ignSkiTouring": "IGN Ski Touring",
|
||||||
"waymarkedTrailsHiking": "Hiking",
|
"waymarked_trails": "Waymarked Trails",
|
||||||
"waymarkedTrailsCycling": "Cycling",
|
"waymarkedTrailsHiking": "Hiking",
|
||||||
"waymarkedTrailsMTB": "MTB",
|
"waymarkedTrailsCycling": "Cycling",
|
||||||
"waymarkedTrailsSkating": "Skating",
|
"waymarkedTrailsMTB": "MTB",
|
||||||
"waymarkedTrailsHorseRiding": "Horse Riding",
|
"waymarkedTrailsSkating": "Skating",
|
||||||
"waymarkedTrailsWinter": "Winter",
|
"waymarkedTrailsHorseRiding": "Horse Riding",
|
||||||
"points_of_interest": "Points of interest",
|
"waymarkedTrailsWinter": "Winter",
|
||||||
"food": "Food",
|
"points_of_interest": "Points of interest",
|
||||||
"bakery": "Bakery",
|
"food": "Food",
|
||||||
"food-store": "Food Store",
|
"bakery": "Bakery",
|
||||||
"eat-and-drink": "Eat and Drink",
|
"food-store": "Food Store",
|
||||||
"amenities": "Amenities",
|
"eat-and-drink": "Eat and Drink",
|
||||||
"toilets": "Toilets",
|
"amenities": "Amenities",
|
||||||
"water": "Water",
|
"toilets": "Toilets",
|
||||||
"shower": "Shower",
|
"water": "Water",
|
||||||
"shelter": "Shelter",
|
"shower": "Shower",
|
||||||
"motorized": "Cars and Motorcycles",
|
"shelter": "Shelter",
|
||||||
"fuel-station": "Fuel Station",
|
"motorized": "Cars and Motorcycles",
|
||||||
"parking": "Parking",
|
"fuel-station": "Fuel Station",
|
||||||
"garage": "Garage",
|
"parking": "Parking",
|
||||||
"barrier": "Barrier",
|
"garage": "Garage",
|
||||||
"tourism": "Tourism",
|
"barrier": "Barrier",
|
||||||
"attraction": "Attraction",
|
"tourism": "Tourism",
|
||||||
"viewpoint": "Viewpoint",
|
"attraction": "Attraction",
|
||||||
"hotel": "Hotel",
|
"viewpoint": "Viewpoint",
|
||||||
"campsite": "Campsite",
|
"hotel": "Hotel",
|
||||||
"hut": "Hut",
|
"campsite": "Campsite",
|
||||||
"picnic": "Picnic Area",
|
"hut": "Hut",
|
||||||
"summit": "Summit",
|
"picnic": "Picnic Area",
|
||||||
"pass": "Pass",
|
"summit": "Summit",
|
||||||
"climbing": "Climbing",
|
"pass": "Pass",
|
||||||
"bicycle": "Bicycle",
|
"climbing": "Climbing",
|
||||||
"bicycle-parking": "Bicycle Parking",
|
"bicycle": "Bicycle",
|
||||||
"bicycle-rental": "Bicycle Rental",
|
"bicycle-parking": "Bicycle Parking",
|
||||||
"bicycle-shop": "Bicycle Shop",
|
"bicycle-rental": "Bicycle Rental",
|
||||||
"public-transport": "Public Transport",
|
"bicycle-shop": "Bicycle Shop",
|
||||||
"railway-station": "Railway Station",
|
"public-transport": "Public Transport",
|
||||||
"tram-stop": "Tram Stop",
|
"railway-station": "Railway Station",
|
||||||
"bus-stop": "Bus Stop",
|
"tram-stop": "Tram Stop",
|
||||||
"ferry": "Ferry"
|
"bus-stop": "Bus Stop",
|
||||||
},
|
"ferry": "Ferry"
|
||||||
"color": {
|
},
|
||||||
"blue": "Blue",
|
"color": {
|
||||||
"bluered": "Blue Red",
|
"blue": "Blue",
|
||||||
"gray": "Gray",
|
"bluered": "Blue Red",
|
||||||
"hot": "Hot",
|
"gray": "Gray",
|
||||||
"purple": "Purple",
|
"hot": "Hot",
|
||||||
"orange": "Orange"
|
"purple": "Purple",
|
||||||
}
|
"orange": "Orange"
|
||||||
},
|
}
|
||||||
"chart": {
|
},
|
||||||
"show_slope": "Show slope data",
|
"chart": {
|
||||||
"show_surface": "Show surface data",
|
"show_slope": "Show slope data",
|
||||||
"show_speed": "Show speed data",
|
"show_surface": "Show surface data",
|
||||||
"show_pace": "Show pace data",
|
"show_speed": "Show speed data",
|
||||||
"show_heartrate": "Show heart rate data",
|
"show_pace": "Show pace data",
|
||||||
"show_cadence": "Show cadence data",
|
"show_heartrate": "Show heart rate data",
|
||||||
"show_temperature": "Show temperature data",
|
"show_cadence": "Show cadence data",
|
||||||
"show_power": "Show power data"
|
"show_temperature": "Show temperature data",
|
||||||
},
|
"show_power": "Show power data"
|
||||||
"quantities": {
|
},
|
||||||
"distance": "Distance",
|
"quantities": {
|
||||||
"elevation": "Elevation",
|
"distance": "Distance",
|
||||||
"temperature": "Temperature",
|
"elevation": "Elevation",
|
||||||
"speed": "Speed",
|
"temperature": "Temperature",
|
||||||
"pace": "Pace",
|
"speed": "Speed",
|
||||||
"heartrate": "Heart rate",
|
"pace": "Pace",
|
||||||
"cadence": "Cadence",
|
"heartrate": "Heart rate",
|
||||||
"power": "Power",
|
"cadence": "Cadence",
|
||||||
"slope": "Slope",
|
"power": "Power",
|
||||||
"surface": "Surface",
|
"slope": "Slope",
|
||||||
"time": "Time",
|
"surface": "Surface",
|
||||||
"moving": "Moving",
|
"time": "Time",
|
||||||
"total": "Total"
|
"moving": "Moving",
|
||||||
},
|
"total": "Total"
|
||||||
"units": {
|
},
|
||||||
"meters": "m",
|
"units": {
|
||||||
"feet": "ft",
|
"meters": "m",
|
||||||
"kilometers": "km",
|
"feet": "ft",
|
||||||
"miles": "mi",
|
"kilometers": "km",
|
||||||
"celsius": "°C",
|
"miles": "mi",
|
||||||
"fahrenheit": "°F",
|
"celsius": "°C",
|
||||||
"kilometers_per_hour": "km/h",
|
"fahrenheit": "°F",
|
||||||
"miles_per_hour": "mph",
|
"kilometers_per_hour": "km/h",
|
||||||
"minutes_per_kilometer": "min/km",
|
"miles_per_hour": "mph",
|
||||||
"minutes_per_mile": "min/mi",
|
"minutes_per_kilometer": "min/km",
|
||||||
"heartrate": "bpm",
|
"minutes_per_mile": "min/mi",
|
||||||
"cadence": "rpm",
|
"heartrate": "bpm",
|
||||||
"power": "W"
|
"cadence": "rpm",
|
||||||
},
|
"power": "W"
|
||||||
"gpx": {
|
},
|
||||||
"file": "File",
|
"gpx": {
|
||||||
"files": "Files",
|
"file": "File",
|
||||||
"track": "Track",
|
"files": "Files",
|
||||||
"tracks": "Tracks",
|
"track": "Track",
|
||||||
"segment": "Segment",
|
"tracks": "Tracks",
|
||||||
"segments": "Segments",
|
"segment": "Segment",
|
||||||
"waypoint": "Point of interest",
|
"segments": "Segments",
|
||||||
"waypoints": "Points of interest",
|
"waypoint": "Point of interest",
|
||||||
"symbol": {
|
"waypoints": "Points of interest",
|
||||||
"alert": "Alert",
|
"symbol": {
|
||||||
"anchor": "Anchor",
|
"alert": "Alert",
|
||||||
"bank": "Bank",
|
"anchor": "Anchor",
|
||||||
"beach": "Beach",
|
"bank": "Bank",
|
||||||
"bike_trail": "Bike Trail",
|
"beach": "Beach",
|
||||||
"binoculars": "Binoculars",
|
"bike_trail": "Bike Trail",
|
||||||
"bridge": "Bridge",
|
"binoculars": "Binoculars",
|
||||||
"building": "Building",
|
"bridge": "Bridge",
|
||||||
"campground": "Campsite",
|
"building": "Building",
|
||||||
"car": "Car",
|
"campground": "Campsite",
|
||||||
"car_repair": "Garage",
|
"car": "Car",
|
||||||
"convenience_store": "Convenience Store",
|
"car_repair": "Garage",
|
||||||
"crossing": "Crossing",
|
"convenience_store": "Convenience Store",
|
||||||
"department_store": "Department Store",
|
"crossing": "Crossing",
|
||||||
"drinking_water": "Water",
|
"department_store": "Department Store",
|
||||||
"exit": "Exit",
|
"drinking_water": "Water",
|
||||||
"lodge": "Hut",
|
"exit": "Exit",
|
||||||
"lodging": "Accommodation",
|
"lodge": "Hut",
|
||||||
"forest": "Forest",
|
"lodging": "Accommodation",
|
||||||
"gas_station": "Fuel Station",
|
"forest": "Forest",
|
||||||
"ground_transportation": "Ground Transportation",
|
"gas_station": "Fuel Station",
|
||||||
"hotel": "Hotel",
|
"ground_transportation": "Ground Transportation",
|
||||||
"house": "House",
|
"hotel": "Hotel",
|
||||||
"information": "Information",
|
"house": "House",
|
||||||
"park": "Park",
|
"information": "Information",
|
||||||
"parking_area": "Parking",
|
"park": "Park",
|
||||||
"pharmacy": "Pharmacy",
|
"parking_area": "Parking",
|
||||||
"picnic_area": "Picnic Area",
|
"pharmacy": "Pharmacy",
|
||||||
"restaurant": "Restaurant",
|
"picnic_area": "Picnic Area",
|
||||||
"restricted_area": "Restricted Area",
|
"restaurant": "Restaurant",
|
||||||
"restroom": "Toilets",
|
"restricted_area": "Restricted Area",
|
||||||
"road": "Road",
|
"restroom": "Toilets",
|
||||||
"scenic_area": "Scenic Area",
|
"road": "Road",
|
||||||
"shelter": "Shelter",
|
"scenic_area": "Scenic Area",
|
||||||
"shopping_center": "Shopping Center",
|
"shelter": "Shelter",
|
||||||
"shower": "Shower",
|
"shopping_center": "Shopping Center",
|
||||||
"summit": "Summit",
|
"shower": "Shower",
|
||||||
"telephone": "Telephone",
|
"summit": "Summit",
|
||||||
"tunnel": "Tunnel",
|
"telephone": "Telephone",
|
||||||
"water_source": "Water Source"
|
"tunnel": "Tunnel",
|
||||||
}
|
"water_source": "Water Source"
|
||||||
},
|
}
|
||||||
"homepage": {
|
},
|
||||||
"website": "Website",
|
"homepage": {
|
||||||
"home": "Home",
|
"website": "Website",
|
||||||
"app": "App",
|
"home": "Home",
|
||||||
"contact": "Contact",
|
"app": "App",
|
||||||
"x": "X",
|
"contact": "Contact",
|
||||||
"facebook": "Facebook",
|
"x": "X",
|
||||||
"github": "GitHub",
|
"facebook": "Facebook",
|
||||||
"crowdin": "Crowdin",
|
"github": "GitHub",
|
||||||
"email": "Email",
|
"crowdin": "Crowdin",
|
||||||
"contribute": "Contribute",
|
"email": "Email",
|
||||||
"supported_by": "supported by",
|
"contribute": "Contribute",
|
||||||
"support_button": "Support gpx.studio on Ko-fi",
|
"supported_by": "supported by",
|
||||||
"route_planning": "Route planning",
|
"support_button": "Support gpx.studio on Ko-fi",
|
||||||
"route_planning_description": "An intuitive interface to create itineraries tailored to each sport, based on OpenStreetMap data.",
|
"route_planning": "Route planning",
|
||||||
"file_processing": "Advanced file processing",
|
"route_planning_description": "An intuitive interface to create itineraries tailored to each sport, based on OpenStreetMap data.",
|
||||||
"file_processing_description": "A suite of tools for performing all common file processing tasks, and which can be applied to multiple files at once.",
|
"file_processing": "Advanced file processing",
|
||||||
"maps": "Global and local maps",
|
"file_processing_description": "A suite of tools for performing all common file processing tasks, and which can be applied to multiple files at once.",
|
||||||
"maps_description": "A large collection of basemaps, overlays and points of interest to help you craft your next outdoor adventure, or visualize your latest achievement.",
|
"maps": "Global and local maps",
|
||||||
"data_visualization": "Data visualization",
|
"maps_description": "A large collection of basemaps, overlays and points of interest to help you craft your next outdoor adventure, or visualize your latest achievement.",
|
||||||
"data_visualization_description": "An interactive elevation profile with detailed statistics to analyze recorded activities and future objectives.",
|
"data_visualization": "Data visualization",
|
||||||
"identity": "Free, ad-free and open source",
|
"data_visualization_description": "An interactive elevation profile with detailed statistics to analyze recorded activities and future objectives.",
|
||||||
"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": "Free, ad-free and open source",
|
||||||
},
|
"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."
|
||||||
"embedding": {
|
},
|
||||||
"title": "Create your own map",
|
"embedding": {
|
||||||
"mapbox_token": "Mapbox access token",
|
"title": "Create your own map",
|
||||||
"file_urls": "File URLs (separated by commas)",
|
"mapbox_token": "Mapbox access token",
|
||||||
"drive_ids": "Google Drive file IDs (separated by commas)",
|
"file_urls": "File URLs (separated by commas)",
|
||||||
"basemap": "Basemap",
|
"drive_ids": "Google Drive file IDs (separated by commas)",
|
||||||
"height": "Height",
|
"basemap": "Basemap",
|
||||||
"fill_by": "Fill by",
|
"height": "Height",
|
||||||
"none": "None",
|
"fill_by": "Fill by",
|
||||||
"show_controls": "Show controls",
|
"none": "None",
|
||||||
"manual_camera": "Manual camera",
|
"show_controls": "Show controls",
|
||||||
"manual_camera_description": "You can move the map below to adjust the camera position.",
|
"manual_camera": "Manual camera",
|
||||||
"latitude": "Latitude",
|
"manual_camera_description": "You can move the map below to adjust the camera position.",
|
||||||
"longitude": "Longitude",
|
"latitude": "Latitude",
|
||||||
"zoom": "Zoom",
|
"longitude": "Longitude",
|
||||||
"pitch": "Pitch",
|
"zoom": "Zoom",
|
||||||
"bearing": "Bearing",
|
"pitch": "Pitch",
|
||||||
"preview": "Preview",
|
"bearing": "Bearing",
|
||||||
"code": "Integration code"
|
"preview": "Preview",
|
||||||
},
|
"code": "Integration code"
|
||||||
"webgl2_required": "WebGL 2 is required to display the map.",
|
},
|
||||||
"enable_webgl2": "Learn how to enable WebGL 2 in your browser",
|
"webgl2_required": "WebGL 2 is required to display the map.",
|
||||||
"page_not_found": "page not found"
|
"enable_webgl2": "Learn how to enable WebGL 2 in your browser",
|
||||||
|
"page_not_found": "page not found"
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue