mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-04-12 14:50:21 +00:00
Compare commits
9 Commits
6d52ee2cc8
...
graphhoppe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ff11a32c9 | ||
|
|
01a7ec916e | ||
|
|
dd94a7d613 | ||
|
|
089b88c62d | ||
|
|
a01ca79a82 | ||
|
|
c91baf7c83 | ||
|
|
5062de8ddf | ||
|
|
9ca46b9d35 | ||
|
|
7c2e24bbc4 |
42
README.md
42
README.md
@@ -27,8 +27,8 @@ Any help is greatly appreciated!
|
||||
|
||||
The code is split into two parts:
|
||||
|
||||
- `gpx`: a Typescript library for parsing and manipulating GPX files,
|
||||
- `website`: the website itself, which is a [SvelteKit](https://kit.svelte.dev/) application.
|
||||
- `gpx`: a Typescript library for parsing and manipulating GPX files,
|
||||
- `website`: the website itself, which is a [SvelteKit](https://kit.svelte.dev/) application.
|
||||
|
||||
You will need [Node.js](https://nodejs.org/) to build and run these two parts.
|
||||
|
||||
@@ -55,25 +55,25 @@ npm run dev
|
||||
|
||||
This project has been made possible thanks to the following open source projects:
|
||||
|
||||
- Development:
|
||||
- [Svelte](https://github.com/sveltejs/svelte) and [SvelteKit](https://github.com/sveltejs/kit) — seamless development experience
|
||||
- [MDsveX](https://github.com/pngwn/MDsveX) — allowing a Markdown-based documentation
|
||||
- Design:
|
||||
- [shadcn-svelte](https://github.com/huntabyte/shadcn-svelte) — beautiful components
|
||||
- [@lucide/svelte](https://github.com/lucide-icons/lucide/tree/main/packages/svelte) — beautiful icons
|
||||
- [tailwindcss](https://github.com/tailwindlabs/tailwindcss) — easy styling
|
||||
- [Chart.js](https://github.com/chartjs/Chart.js) — beautiful and fast charts
|
||||
- Logic:
|
||||
- [immer](https://github.com/immerjs/immer) — complex state management
|
||||
- [Dexie.js](https://github.com/dexie/Dexie.js) — IndexedDB wrapper
|
||||
- [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) — fast GPX file parsing
|
||||
- [SortableJS](https://github.com/SortableJS/Sortable) — creating a sortable file tree
|
||||
- Mapping:
|
||||
- [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) — beautiful and fast interactive maps
|
||||
- [brouter](https://github.com/abrensch/brouter) — routing engine
|
||||
- [OpenStreetMap](https://www.openstreetmap.org) — map data used by Mapbox and brouter
|
||||
- Search:
|
||||
- [DocSearch](https://github.com/algolia/docsearch) — search engine for the documentation
|
||||
- Development:
|
||||
- [Svelte](https://github.com/sveltejs/svelte) and [SvelteKit](https://github.com/sveltejs/kit) — seamless development experience
|
||||
- [MDsveX](https://github.com/pngwn/MDsveX) — allowing a Markdown-based documentation
|
||||
- Design:
|
||||
- [shadcn-svelte](https://github.com/huntabyte/shadcn-svelte) — beautiful components
|
||||
- [@lucide/svelte](https://github.com/lucide-icons/lucide/tree/main/packages/svelte) — beautiful icons
|
||||
- [tailwindcss](https://github.com/tailwindlabs/tailwindcss) — easy styling
|
||||
- [Chart.js](https://github.com/chartjs/Chart.js) — beautiful and fast charts
|
||||
- Logic:
|
||||
- [immer](https://github.com/immerjs/immer) — complex state management
|
||||
- [Dexie.js](https://github.com/dexie/Dexie.js) — IndexedDB wrapper
|
||||
- [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) — fast GPX file parsing
|
||||
- [SortableJS](https://github.com/SortableJS/Sortable) — creating a sortable file tree
|
||||
- Mapping:
|
||||
- [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) — beautiful and fast interactive maps
|
||||
- [GraphHopper](https://github.com/graphhopper/graphhopper) — routing engine
|
||||
- [OpenStreetMap](https://www.openstreetmap.org) — map data used by Mapbox and GraphHopper
|
||||
- Search:
|
||||
- [DocSearch](https://github.com/algolia/docsearch) — search engine for the documentation
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1398,10 +1398,7 @@ export class TrackPoint {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
setExtensions(extensions: Record<string, string>) {
|
||||
if (Object.keys(extensions).length === 0) {
|
||||
return;
|
||||
}
|
||||
setExtension(key: string, value: string) {
|
||||
if (!this.extensions) {
|
||||
this.extensions = {};
|
||||
}
|
||||
@@ -1411,8 +1408,12 @@ export class TrackPoint {
|
||||
if (!this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions']) {
|
||||
this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] = {};
|
||||
}
|
||||
this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'][key] = value;
|
||||
}
|
||||
|
||||
setExtensions(extensions: Record<string, string>) {
|
||||
Object.entries(extensions).forEach(([key, value]) => {
|
||||
this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'][key] = value;
|
||||
this.setExtension(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
}
|
||||
},
|
||||
"sprite": "https://demotiles.maplibre.org/styles/osm-bright-gl-style/sprite",
|
||||
"glyphs": "https://api.maptiler.com/fonts/{fontstack}/{range}.pbf?key={key}",
|
||||
"layers": [
|
||||
{
|
||||
"id": "background",
|
||||
|
||||
@@ -34,10 +34,11 @@
|
||||
import { editStyle } from '$lib/components/file-list/style/utils.svelte';
|
||||
import { getSymbolKey, symbols } from '$lib/assets/symbols';
|
||||
import { selection, copied, cut } from '$lib/logic/selection';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { fileActions, pasteSelection } from '$lib/logic/file-actions';
|
||||
import { allHidden } from '$lib/logic/hidden';
|
||||
import { boundsManager } from '$lib/logic/bounds';
|
||||
import { gpxColors, gpxLayers } from '$lib/components/map/gpx-layer/gpx-layers';
|
||||
import { gpxLayers } from '$lib/components/map/gpx-layer/gpx-layers';
|
||||
import { fileStateCollection } from '$lib/logic/file-state';
|
||||
import { waypointPopup } from '$lib/components/map/gpx-layer/gpx-layer-popup';
|
||||
import { allowedPastes } from './sortable-file-list';
|
||||
@@ -57,11 +58,19 @@
|
||||
|
||||
let singleSelection = $derived($selection.size === 1);
|
||||
|
||||
let nodeColors: string[] = $derived.by(() => {
|
||||
let nodeColors: string[] = $state([]);
|
||||
|
||||
$effect.pre(() => {
|
||||
let colors: string[] = [];
|
||||
if (node) {
|
||||
if (node && $map) {
|
||||
if (node instanceof GPXFile) {
|
||||
let defaultColor = $gpxColors.get(item.getFileId());
|
||||
let defaultColor = undefined;
|
||||
|
||||
let layer = gpxLayers.getLayer(item.getFileId());
|
||||
if (layer) {
|
||||
defaultColor = layer.layerColor;
|
||||
}
|
||||
|
||||
let style = node.getStyle(defaultColor);
|
||||
colors = style.color;
|
||||
} else if (node instanceof Track) {
|
||||
@@ -74,14 +83,14 @@
|
||||
colors.push(style['gpx_style:color']);
|
||||
}
|
||||
if (colors.length === 0) {
|
||||
let defaultColor = $gpxColors.get(item.getFileId());
|
||||
if (defaultColor) {
|
||||
colors.push(defaultColor);
|
||||
let layer = gpxLayers.getLayer(item.getFileId());
|
||||
if (layer) {
|
||||
colors.push(layer.layerColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return colors;
|
||||
nodeColors = colors;
|
||||
});
|
||||
|
||||
let symbolKey = $derived(node instanceof Waypoint ? getSymbolKey(node.sym) : undefined);
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
language = 'en';
|
||||
}
|
||||
|
||||
map.init(language, hash, geocoder, geolocate);
|
||||
map.init(PUBLIC_MAPBOX_TOKEN, language, hash, geocoder, geolocate);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { gpxLayers } from '$lib/components/map/gpx-layer/gpx-layers';
|
||||
import { DistanceMarkers } from '$lib/components/map/gpx-layer/distance-markers';
|
||||
import { StartEndMarkers } from '$lib/components/map/gpx-layer/start-end-markers';
|
||||
@@ -9,10 +9,13 @@
|
||||
let distanceMarkers: DistanceMarkers;
|
||||
let startEndMarkers: StartEndMarkers;
|
||||
|
||||
map.onLoad((map_) => {
|
||||
onMount(() => {
|
||||
gpxLayers.init();
|
||||
startEndMarkers = new StartEndMarkers();
|
||||
distanceMarkers = new DistanceMarkers();
|
||||
});
|
||||
|
||||
map.onLoad((map_) => {
|
||||
createPopups(map_);
|
||||
});
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
class="justify-start"
|
||||
href={`https://www.openstreetmap.org/edit?#map=${(($map?.getZoom() ?? 17) + 1).toFixed(0)}/${trackpoint.item.getLatitude().toFixed(5)}/${trackpoint.item.getLongitude().toFixed(5)}`}
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { gpxStatistics } from '$lib/logic/statistics';
|
||||
import { getConvertedDistanceToKilometers } from '$lib/units';
|
||||
import type { GeoJSONSource } from 'mapbox-gl';
|
||||
import { get } from 'svelte/store';
|
||||
import { ANCHOR_LAYER_KEY, map } from '$lib/components/map/map';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { allHidden } from '$lib/logic/hidden';
|
||||
|
||||
const { distanceMarkers, distanceUnits } = settings;
|
||||
@@ -44,45 +44,44 @@ export class DistanceMarkers {
|
||||
});
|
||||
}
|
||||
if (!map_.getLayer('distance-markers')) {
|
||||
map_.addLayer(
|
||||
{
|
||||
id: 'distance-markers',
|
||||
type: 'symbol',
|
||||
source: 'distance-markers',
|
||||
filter: [
|
||||
'match',
|
||||
['get', 'level'],
|
||||
100,
|
||||
['>=', ['zoom'], 0],
|
||||
50,
|
||||
['>=', ['zoom'], 7],
|
||||
25,
|
||||
[
|
||||
'any',
|
||||
['all', ['>=', ['zoom'], 8], ['<=', ['zoom'], 9]],
|
||||
['>=', ['zoom'], 11],
|
||||
],
|
||||
10,
|
||||
['>=', ['zoom'], 10],
|
||||
5,
|
||||
map_.addLayer({
|
||||
id: 'distance-markers',
|
||||
type: 'symbol',
|
||||
source: 'distance-markers',
|
||||
filter: [
|
||||
'match',
|
||||
['get', 'level'],
|
||||
100,
|
||||
['>=', ['zoom'], 0],
|
||||
50,
|
||||
['>=', ['zoom'], 7],
|
||||
25,
|
||||
[
|
||||
'any',
|
||||
['all', ['>=', ['zoom'], 8], ['<=', ['zoom'], 9]],
|
||||
['>=', ['zoom'], 11],
|
||||
1,
|
||||
['>=', ['zoom'], 13],
|
||||
false,
|
||||
],
|
||||
layout: {
|
||||
'text-field': ['get', 'distance'],
|
||||
'text-size': 14,
|
||||
'text-font': ['Open Sans Bold'],
|
||||
},
|
||||
paint: {
|
||||
'text-color': 'black',
|
||||
'text-halo-width': 2,
|
||||
'text-halo-color': 'white',
|
||||
},
|
||||
10,
|
||||
['>=', ['zoom'], 10],
|
||||
5,
|
||||
['>=', ['zoom'], 11],
|
||||
1,
|
||||
['>=', ['zoom'], 13],
|
||||
false,
|
||||
],
|
||||
layout: {
|
||||
'text-field': ['get', 'distance'],
|
||||
'text-size': 14,
|
||||
'text-font': ['Open Sans Bold'],
|
||||
},
|
||||
ANCHOR_LAYER_KEY.distanceMarkers
|
||||
);
|
||||
paint: {
|
||||
'text-color': 'black',
|
||||
'text-halo-width': 2,
|
||||
'text-halo-color': 'white',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
map_.moveLayer('distance-markers');
|
||||
}
|
||||
} else {
|
||||
if (map_.getLayer('distance-markers')) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { get, type Readable } from 'svelte/store';
|
||||
import mapboxgl, { type FilterSpecification } from 'mapbox-gl';
|
||||
import { ANCHOR_LAYER_KEY, map } from '$lib/components/map/map';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { waypointPopup, trackpointPopup } from './gpx-layer-popup';
|
||||
import {
|
||||
ListTrackSegmentItem,
|
||||
@@ -22,7 +22,6 @@ import { fileActionManager } from '$lib/logic/file-action-manager';
|
||||
import { fileActions } from '$lib/logic/file-actions';
|
||||
import { splitAs } from '$lib/components/toolbar/tools/scissors/scissors';
|
||||
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
|
||||
import { gpxColors } from '$lib/components/map/gpx-layer/gpx-layers';
|
||||
|
||||
const colors = [
|
||||
'#ff0000',
|
||||
@@ -44,35 +43,16 @@ for (let color of colors) {
|
||||
}
|
||||
|
||||
// Get the color with the least amount of uses
|
||||
function getColor(fileId: string) {
|
||||
function getColor() {
|
||||
let color = colors.reduce((a, b) => (colorCount[a] <= colorCount[b] ? a : b));
|
||||
colorCount[color]++;
|
||||
gpxColors.update((colors) => {
|
||||
colors.set(fileId, color);
|
||||
return colors;
|
||||
});
|
||||
return color;
|
||||
}
|
||||
|
||||
function replaceColor(fileId: string, oldColor: string, newColor: string) {
|
||||
if (colorCount.hasOwnProperty(oldColor)) {
|
||||
colorCount[oldColor]--;
|
||||
}
|
||||
colorCount[newColor]++;
|
||||
gpxColors.update((colors) => {
|
||||
colors.set(fileId, newColor);
|
||||
return colors;
|
||||
});
|
||||
}
|
||||
|
||||
function removeColor(fileId: string, color: string) {
|
||||
function decrementColor(color: string) {
|
||||
if (colorCount.hasOwnProperty(color)) {
|
||||
colorCount[color]--;
|
||||
}
|
||||
gpxColors.update((colors) => {
|
||||
colors.delete(fileId);
|
||||
return colors;
|
||||
});
|
||||
}
|
||||
|
||||
export function getSvgForSymbol(symbol?: string | undefined, layerColor?: string | undefined) {
|
||||
@@ -141,7 +121,7 @@ export class GPXLayer {
|
||||
constructor(fileId: string, file: Readable<GPXFileWithStatistics | undefined>) {
|
||||
this.fileId = fileId;
|
||||
this.file = file;
|
||||
this.layerColor = getColor(fileId);
|
||||
this.layerColor = getColor();
|
||||
this.unsubscribe.push(
|
||||
map.subscribe(($map) => {
|
||||
if ($map) {
|
||||
@@ -178,7 +158,7 @@ export class GPXLayer {
|
||||
file._data.style.color &&
|
||||
this.layerColor !== `#${file._data.style.color}`
|
||||
) {
|
||||
replaceColor(this.fileId, this.layerColor, `#${file._data.style.color}`);
|
||||
decrementColor(this.layerColor);
|
||||
this.layerColor = `#${file._data.style.color}`;
|
||||
}
|
||||
|
||||
@@ -196,23 +176,20 @@ export class GPXLayer {
|
||||
}
|
||||
|
||||
if (!_map.getLayer(this.fileId)) {
|
||||
_map.addLayer(
|
||||
{
|
||||
id: this.fileId,
|
||||
type: 'line',
|
||||
source: this.fileId,
|
||||
layout: {
|
||||
'line-join': 'round',
|
||||
'line-cap': 'round',
|
||||
},
|
||||
paint: {
|
||||
'line-color': ['get', 'color'],
|
||||
'line-width': ['get', 'width'],
|
||||
'line-opacity': ['get', 'opacity'],
|
||||
},
|
||||
_map.addLayer({
|
||||
id: this.fileId,
|
||||
type: 'line',
|
||||
source: this.fileId,
|
||||
layout: {
|
||||
'line-join': 'round',
|
||||
'line-cap': 'round',
|
||||
},
|
||||
ANCHOR_LAYER_KEY.tracks
|
||||
);
|
||||
paint: {
|
||||
'line-color': ['get', 'color'],
|
||||
'line-width': ['get', 'width'],
|
||||
'line-opacity': ['get', 'opacity'],
|
||||
},
|
||||
});
|
||||
|
||||
_map.on('click', this.fileId, this.layerOnClickBinded);
|
||||
_map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded);
|
||||
@@ -235,21 +212,18 @@ export class GPXLayer {
|
||||
}
|
||||
|
||||
if (!_map.getLayer(this.fileId + '-waypoints')) {
|
||||
_map.addLayer(
|
||||
{
|
||||
id: this.fileId + '-waypoints',
|
||||
type: 'symbol',
|
||||
source: this.fileId + '-waypoints',
|
||||
layout: {
|
||||
'icon-image': ['get', 'icon'],
|
||||
'icon-size': 0.3,
|
||||
'icon-anchor': 'bottom',
|
||||
'icon-padding': 0,
|
||||
'icon-allow-overlap': true,
|
||||
},
|
||||
_map.addLayer({
|
||||
id: this.fileId + '-waypoints',
|
||||
type: 'symbol',
|
||||
source: this.fileId + '-waypoints',
|
||||
layout: {
|
||||
'icon-image': ['get', 'icon'],
|
||||
'icon-size': 0.3,
|
||||
'icon-anchor': 'bottom',
|
||||
'icon-padding': 0,
|
||||
'icon-allow-overlap': true,
|
||||
},
|
||||
ANCHOR_LAYER_KEY.waypoints
|
||||
);
|
||||
});
|
||||
|
||||
_map.on(
|
||||
'mouseenter',
|
||||
@@ -298,7 +272,7 @@ export class GPXLayer {
|
||||
'text-halo-color': 'white',
|
||||
},
|
||||
},
|
||||
ANCHOR_LAYER_KEY.directionMarkers
|
||||
_map.getLayer('distance-markers') ? 'distance-markers' : undefined
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -390,7 +364,7 @@ export class GPXLayer {
|
||||
|
||||
this.unsubscribe.forEach((unsubscribe) => unsubscribe());
|
||||
|
||||
removeColor(this.fileId, this.layerColor);
|
||||
decrementColor(this.layerColor);
|
||||
}
|
||||
|
||||
moveToFront() {
|
||||
@@ -399,13 +373,13 @@ export class GPXLayer {
|
||||
return;
|
||||
}
|
||||
if (_map.getLayer(this.fileId)) {
|
||||
_map.moveLayer(this.fileId, ANCHOR_LAYER_KEY.tracks);
|
||||
_map.moveLayer(this.fileId);
|
||||
}
|
||||
if (_map.getLayer(this.fileId + '-waypoints')) {
|
||||
_map.moveLayer(this.fileId + '-waypoints', ANCHOR_LAYER_KEY.waypoints);
|
||||
_map.moveLayer(this.fileId + '-waypoints');
|
||||
}
|
||||
if (_map.getLayer(this.fileId + '-direction')) {
|
||||
_map.moveLayer(this.fileId + '-direction', ANCHOR_LAYER_KEY.directionMarkers);
|
||||
_map.moveLayer(this.fileId + '-direction');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { GPXFileStateCollectionObserver } from '$lib/logic/file-state';
|
||||
import { writable } from 'svelte/store';
|
||||
import { GPXLayer } from './gpx-layer';
|
||||
|
||||
export class GPXLayerCollection {
|
||||
@@ -43,4 +42,3 @@ export class GPXLayerCollection {
|
||||
}
|
||||
|
||||
export const gpxLayers = new GPXLayerCollection();
|
||||
export const gpxColors = writable(new Map<string, string>());
|
||||
|
||||
@@ -54,27 +54,28 @@
|
||||
|
||||
<Card.Root class="border-none shadow-md text-base p-2 max-w-[50dvw] gap-0">
|
||||
<Card.Header class="p-0 gap-0">
|
||||
<Card.Title class="text-md flex flex-row">
|
||||
<div class="flex flex-col">
|
||||
<p>{name}</p>
|
||||
<div class="text-muted-foreground text-xs font-normal">
|
||||
{poi.item.lat.toFixed(6)}° {poi.item.lon.toFixed(6)}°
|
||||
<Card.Title class="text-md">
|
||||
<div class="flex flex-row gap-3">
|
||||
<div class="flex flex-col">
|
||||
{name}
|
||||
<div class="text-muted-foreground text-xs font-normal">
|
||||
{poi.item.lat.toFixed(6)}° {poi.item.lon.toFixed(6)}°
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
class="ml-auto"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
href="https://www.openstreetmap.org/edit?editor=id&{poi.item.type ??
|
||||
'node'}={poi.item.id}"
|
||||
target="_blank"
|
||||
>
|
||||
<PencilLine size="16" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
class="ml-auto"
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
href="https://www.openstreetmap.org/edit?editor=id&{poi.item.type ?? 'node'}={poi
|
||||
.item.id}"
|
||||
target="_blank"
|
||||
>
|
||||
<PencilLine size="16" />
|
||||
</Button>
|
||||
</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex flex-col gap-1 p-0 text-sm whitespace-normal break-all">
|
||||
<Card.Content class="flex flex-col p-0 text-sm mt-1 whitespace-normal break-all">
|
||||
<ScrollArea class="flex flex-col max-h-[30dvh]">
|
||||
{#if tags.image || tags['image:0']}
|
||||
<div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto">
|
||||
@@ -99,14 +100,8 @@
|
||||
{/each}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
<Button
|
||||
size="sm"
|
||||
class="mt-1 justify-start"
|
||||
variant="outline"
|
||||
disabled={$selection.size === 0}
|
||||
onclick={addToFile}
|
||||
>
|
||||
<MapPin size="14" />
|
||||
<Button class="mt-2" variant="outline" disabled={$selection.size === 0} onclick={addToFile}>
|
||||
<MapPin size="16" />
|
||||
{i18n._('toolbar.waypoint.add')}
|
||||
</Button>
|
||||
</Card.Content>
|
||||
|
||||
@@ -6,7 +6,6 @@ import { overpassQueryData } from '$lib/assets/layers';
|
||||
import { MapPopup } from '$lib/components/map/map-popup';
|
||||
import { settings } from '$lib/logic/settings';
|
||||
import { db } from '$lib/db';
|
||||
import { ANCHOR_LAYER_KEY } from '$lib/components/map/map';
|
||||
|
||||
const { currentOverpassQueries } = settings;
|
||||
|
||||
@@ -86,20 +85,17 @@ export class OverpassLayer {
|
||||
}
|
||||
|
||||
if (!this.map.getLayer('overpass')) {
|
||||
this.map.addLayer(
|
||||
{
|
||||
id: 'overpass',
|
||||
type: 'symbol',
|
||||
source: 'overpass',
|
||||
layout: {
|
||||
'icon-image': ['get', 'icon'],
|
||||
'icon-size': 0.25,
|
||||
'icon-padding': 0,
|
||||
'icon-allow-overlap': ['step', ['zoom'], false, 14, true],
|
||||
},
|
||||
this.map.addLayer({
|
||||
id: 'overpass',
|
||||
type: 'symbol',
|
||||
source: 'overpass',
|
||||
layout: {
|
||||
'icon-image': ['get', 'icon'],
|
||||
'icon-size': 0.25,
|
||||
'icon-padding': 0,
|
||||
'icon-allow-overlap': ['step', ['zoom'], false, 14, true],
|
||||
},
|
||||
ANCHOR_LAYER_KEY.overpass
|
||||
);
|
||||
});
|
||||
|
||||
this.map.on('mouseenter', 'overpass', this.onHoverBinded);
|
||||
this.map.on('click', 'overpass', this.onHoverBinded);
|
||||
|
||||
@@ -20,28 +20,6 @@ let fitBoundsOptions: mapboxgl.MapOptions['fitBoundsOptions'] = {
|
||||
easing: () => 1,
|
||||
};
|
||||
|
||||
const emptySource: mapboxgl.GeoJSONSourceSpecification = {
|
||||
type: 'geojson',
|
||||
data: {
|
||||
type: 'FeatureCollection',
|
||||
features: [],
|
||||
},
|
||||
};
|
||||
export const ANCHOR_LAYER_KEY = {
|
||||
mapillary: 'mapillary-end',
|
||||
tracks: 'tracks-end',
|
||||
directionMarkers: 'direction-markers-end',
|
||||
distanceMarkers: 'distance-markers-end',
|
||||
interactions: 'interactions-end',
|
||||
overpass: 'overpass-end',
|
||||
waypoints: 'waypoints-end',
|
||||
};
|
||||
const anchorLayers: mapboxgl.LayerSpecification[] = Object.values(ANCHOR_LAYER_KEY).map((id) => ({
|
||||
id: id,
|
||||
type: 'symbol',
|
||||
source: 'empty-source',
|
||||
}));
|
||||
|
||||
export class MapboxGLMap {
|
||||
private _map: Writable<mapboxgl.Map | null> = writable(null);
|
||||
private _onLoadCallbacks: ((map: mapboxgl.Map) => void)[] = [];
|
||||
@@ -51,15 +29,19 @@ export class MapboxGLMap {
|
||||
return this._map.subscribe(run, invalidate);
|
||||
}
|
||||
|
||||
init(language: string, hash: boolean, geocoder: boolean, geolocate: boolean) {
|
||||
init(
|
||||
accessToken: string,
|
||||
language: string,
|
||||
hash: boolean,
|
||||
geocoder: boolean,
|
||||
geolocate: boolean
|
||||
) {
|
||||
const map = new mapboxgl.Map({
|
||||
container: 'map',
|
||||
style: {
|
||||
version: 8,
|
||||
sources: {
|
||||
'empty-source': emptySource,
|
||||
},
|
||||
layers: anchorLayers,
|
||||
sources: {},
|
||||
layers: [],
|
||||
imports: [
|
||||
{
|
||||
id: 'basemap',
|
||||
@@ -68,6 +50,11 @@ export class MapboxGLMap {
|
||||
{
|
||||
id: 'overlays',
|
||||
url: '',
|
||||
data: {
|
||||
version: 8,
|
||||
sources: {},
|
||||
layers: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -225,21 +212,16 @@ export class MapboxGLMap {
|
||||
const map = get(this._map);
|
||||
if (map) {
|
||||
const source = get(terrainSource);
|
||||
try {
|
||||
if (!map.getSource(source)) {
|
||||
map.addSource(source, terrainSources[source]);
|
||||
}
|
||||
if (map.getPitch() > 0) {
|
||||
map.setTerrain({
|
||||
source: source,
|
||||
exaggeration: 1,
|
||||
});
|
||||
} else {
|
||||
map.setTerrain(null);
|
||||
}
|
||||
} catch (e) {
|
||||
// No reliable way to check if the map is ready to add sources and layers
|
||||
return;
|
||||
if (!map.getSource(source)) {
|
||||
map.addSource(source, terrainSources[source]);
|
||||
}
|
||||
if (map.getPitch() > 0) {
|
||||
map.setTerrain({
|
||||
source: source,
|
||||
exaggeration: 1,
|
||||
});
|
||||
} else {
|
||||
map.setTerrain(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import mapboxgl, { type LayerSpecification, type VectorSourceSpecification } fro
|
||||
import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module';
|
||||
import 'mapillary-js/dist/mapillary.css';
|
||||
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
|
||||
import { ANCHOR_LAYER_KEY } from '$lib/components/map/map';
|
||||
|
||||
const mapillarySource: VectorSourceSpecification = {
|
||||
type: 'vector',
|
||||
@@ -100,10 +99,10 @@ export class MapillaryLayer {
|
||||
this.map.addSource('mapillary', mapillarySource);
|
||||
}
|
||||
if (!this.map.getLayer('mapillary-sequence')) {
|
||||
this.map.addLayer(mapillarySequenceLayer, ANCHOR_LAYER_KEY.mapillary);
|
||||
this.map.addLayer(mapillarySequenceLayer);
|
||||
}
|
||||
if (!this.map.getLayer('mapillary-image')) {
|
||||
this.map.addLayer(mapillaryImageLayer, ANCHOR_LAYER_KEY.mapillary);
|
||||
this.map.addLayer(mapillaryImageLayer);
|
||||
}
|
||||
this.map.on('style.load', this.addBinded);
|
||||
this.map.on('mouseenter', 'mapillary-image', this.onMouseEnterBinded);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { getURLForLanguage } from '$lib/utils';
|
||||
import { Trash2 } from '@lucide/svelte';
|
||||
import { ANCHOR_LAYER_KEY, map } from '$lib/components/map/map';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import type { GeoJSONSource } from 'mapbox-gl';
|
||||
import { selection } from '$lib/logic/selection';
|
||||
import { fileActions } from '$lib/logic/file-actions';
|
||||
@@ -63,18 +63,15 @@
|
||||
});
|
||||
}
|
||||
if (!$map.getLayer('rectangle')) {
|
||||
$map.addLayer(
|
||||
{
|
||||
id: 'rectangle',
|
||||
type: 'fill',
|
||||
source: 'rectangle',
|
||||
paint: {
|
||||
'fill-color': 'SteelBlue',
|
||||
'fill-opacity': 0.5,
|
||||
},
|
||||
$map.addLayer({
|
||||
id: 'rectangle',
|
||||
type: 'fill',
|
||||
source: 'rectangle',
|
||||
paint: {
|
||||
'fill-color': 'SteelBlue',
|
||||
'fill-opacity': 0.5,
|
||||
},
|
||||
ANCHOR_LAYER_KEY.interactions
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import Help from '$lib/components/Help.svelte';
|
||||
import { MountainSnow } from '@lucide/svelte';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { i18n } from '$lib/i18n.svelte';
|
||||
import { getURLForLanguage } from '$lib/utils';
|
||||
import { selection } from '$lib/logic/selection';
|
||||
@@ -19,7 +20,11 @@
|
||||
variant="outline"
|
||||
class="whitespace-normal h-fit"
|
||||
disabled={!validSelection}
|
||||
onclick={() => fileActions.addElevationToSelection()}
|
||||
onclick={() => {
|
||||
if ($map) {
|
||||
fileActions.addElevationToSelection($map);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MountainSnow size="16" class="shrink-0" />
|
||||
{i18n._('toolbar.elevation.button')}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ListItem, ListTrackSegmentItem } from '$lib/components/file-list/file-list';
|
||||
import { ANCHOR_LAYER_KEY, map } from '$lib/components/map/map';
|
||||
import { map } from '$lib/components/map/map';
|
||||
import { fileActions } from '$lib/logic/file-actions';
|
||||
import { GPXFileStateCollectionObserver, type GPXFileState } from '$lib/logic/file-state';
|
||||
import { selection } from '$lib/logic/selection';
|
||||
@@ -144,18 +144,17 @@ export class ReducedGPXLayerCollection {
|
||||
});
|
||||
}
|
||||
if (!map_.getLayer('simplified')) {
|
||||
map_.addLayer(
|
||||
{
|
||||
id: 'simplified',
|
||||
type: 'line',
|
||||
source: 'simplified',
|
||||
paint: {
|
||||
'line-color': 'white',
|
||||
'line-width': 3,
|
||||
},
|
||||
map_.addLayer({
|
||||
id: 'simplified',
|
||||
type: 'line',
|
||||
source: 'simplified',
|
||||
paint: {
|
||||
'line-color': 'white',
|
||||
'line-width': 3,
|
||||
},
|
||||
ANCHOR_LAYER_KEY.interactions
|
||||
);
|
||||
});
|
||||
} else {
|
||||
map_.moveLayer('simplified');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
SquareArrowUpLeft,
|
||||
SquareArrowOutDownRight,
|
||||
} from '@lucide/svelte';
|
||||
import { brouterProfiles } from '$lib/components/toolbar/tools/routing/routing';
|
||||
import { routingProfiles } from '$lib/components/toolbar/tools/routing/routing';
|
||||
import { i18n } from '$lib/i18n.svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import {
|
||||
@@ -167,7 +167,7 @@
|
||||
{i18n._(`toolbar.routing.activities.${$routingProfile}`)}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each Object.keys(brouterProfiles) as profile}
|
||||
{#each Object.keys(routingProfiles) as profile}
|
||||
<Select.Item value={profile}
|
||||
>{i18n._(
|
||||
`toolbar.routing.activities.${profile}`
|
||||
|
||||
@@ -731,17 +731,7 @@ export class RoutingControls {
|
||||
try {
|
||||
response = await route(targetCoordinates);
|
||||
} catch (e: any) {
|
||||
if (e.message.includes('from-position not mapped in existing datafile')) {
|
||||
toast.error(i18n._('toolbar.routing.error.from'));
|
||||
} else if (e.message.includes('via1-position not mapped in existing datafile')) {
|
||||
toast.error(i18n._('toolbar.routing.error.via'));
|
||||
} else if (e.message.includes('to-position not mapped in existing datafile')) {
|
||||
toast.error(i18n._('toolbar.routing.error.to'));
|
||||
} else if (e.message.includes('Time-out')) {
|
||||
toast.error(i18n._('toolbar.routing.error.timeout'));
|
||||
} else {
|
||||
toast.error(e.message);
|
||||
}
|
||||
toast.error(i18n._(e.message, e.message));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,37 +6,213 @@ import { get } from 'svelte/store';
|
||||
|
||||
const { routing, routingProfile, privateRoads } = settings;
|
||||
|
||||
export const brouterProfiles: { [key: string]: string } = {
|
||||
bike: 'Trekking-dry',
|
||||
racing_bike: 'fastbike',
|
||||
gravel_bike: 'gravel',
|
||||
mountain_bike: 'MTB',
|
||||
foot: 'Hiking-Alpine-SAC6',
|
||||
motorcycle: 'Car-FastEco',
|
||||
water: 'river',
|
||||
railway: 'rail',
|
||||
export type RoutingProfile = {
|
||||
engine: 'graphhopper' | 'brouter';
|
||||
profile: string;
|
||||
};
|
||||
|
||||
export const routingProfiles: { [key: string]: RoutingProfile } = {
|
||||
bike: { engine: 'graphhopper', profile: 'bike' },
|
||||
racing_bike: { engine: 'graphhopper', profile: 'racingbike' },
|
||||
gravel_bike: { engine: 'graphhopper', profile: 'gravelbike' },
|
||||
mountain_bike: { engine: 'graphhopper', profile: 'mtb' },
|
||||
foot: { engine: 'graphhopper', profile: 'foot' },
|
||||
motorcycle: { engine: 'graphhopper', profile: 'motorcycle' },
|
||||
water: { engine: 'brouter', profile: 'river' },
|
||||
railway: { engine: 'brouter', profile: 'rail' },
|
||||
};
|
||||
|
||||
export function route(points: Coordinates[]): Promise<TrackPoint[]> {
|
||||
if (get(routing)) {
|
||||
return getRoute(points, brouterProfiles[get(routingProfile)], get(privateRoads));
|
||||
const profile = routingProfiles[get(routingProfile)];
|
||||
if (profile.engine === 'graphhopper') {
|
||||
return getGraphHopperRoute(points, profile.profile, get(privateRoads));
|
||||
} else {
|
||||
return getBRouterRoute(points, profile.profile);
|
||||
}
|
||||
} else {
|
||||
return getIntermediatePoints(points);
|
||||
}
|
||||
}
|
||||
|
||||
async function getRoute(
|
||||
const graphhopperDetails = ['road_class', 'surface', 'hike_rating', 'mtb_rating'];
|
||||
const hikeRatingToSACScale: { [key: string]: string } = {
|
||||
'1': 'hiking',
|
||||
'2': 'mountain_hiking',
|
||||
'3': 'demanding_mountain_hiking',
|
||||
'4': 'alpine_hiking',
|
||||
'5': 'demanding_alpine_hiking',
|
||||
'6': 'difficult_alpine_hiking',
|
||||
};
|
||||
const mtbRatingToScale: { [key: string]: string } = {
|
||||
'1': '0',
|
||||
'2': '1',
|
||||
'3': '2',
|
||||
'4': '3',
|
||||
'5': '4',
|
||||
'6': '5',
|
||||
'7': '6',
|
||||
};
|
||||
|
||||
const graphhopperBlockPrivateCustomModels: { [key: string]: any } = {
|
||||
bike: {
|
||||
priority: [
|
||||
{
|
||||
if: 'bike_road_access == PRIVATE',
|
||||
multiply_by: '0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
racingbike: {
|
||||
priority: [
|
||||
{
|
||||
if: 'bike_road_access == PRIVATE',
|
||||
multiply_by: '0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
gravelbike: {
|
||||
priority: [
|
||||
{
|
||||
if: 'bike_road_access == PRIVATE',
|
||||
multiply_by: '0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
mtb: {
|
||||
priority: [
|
||||
{
|
||||
if: 'bike_road_access == PRIVATE',
|
||||
multiply_by: '0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
foot: {
|
||||
priority: [
|
||||
{
|
||||
if: 'foot_road_access == PRIVATE',
|
||||
multiply_by: '0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
motorcycle: {
|
||||
priority: [
|
||||
{
|
||||
if: 'road_access == PRIVATE',
|
||||
multiply_by: '0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
async function getGraphHopperRoute(
|
||||
points: Coordinates[],
|
||||
brouterProfile: string,
|
||||
graphHopperProfile: string,
|
||||
privateRoads: boolean
|
||||
): Promise<TrackPoint[]> {
|
||||
let url = `https://brouter.gpx.studio?lonlats=${points.map((point) => `${point.lon.toFixed(8)},${point.lat.toFixed(8)}`).join('|')}&profile=${brouterProfile + (privateRoads ? '-private' : '')}&format=geojson&alternativeidx=0`;
|
||||
let response = await fetch('https://graphhopper.gpx.studio/route', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
points: points.map((point) => [point.lon, point.lat]),
|
||||
profile: graphHopperProfile,
|
||||
elevation: true,
|
||||
points_encoded: false,
|
||||
details: graphhopperDetails,
|
||||
custom_model: privateRoads
|
||||
? {}
|
||||
: graphhopperBlockPrivateCustomModels[graphHopperProfile] || {},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
if (error.message.includes('Cannot find point 0')) {
|
||||
throw new Error('toolbar.routing.error.from');
|
||||
} else if (error.message.includes('Cannot find point 1')) {
|
||||
if (points.length == 3) {
|
||||
throw new Error('toolbar.routing.error.via');
|
||||
} else {
|
||||
throw new Error('toolbar.routing.error.to');
|
||||
}
|
||||
} else if (error.hints[0].details.includes('PointDistanceExceededException')) {
|
||||
throw new Error('toolbar.routing.error.distance');
|
||||
} else if (error.hints[0].details.includes('ConnectionNotFoundException')) {
|
||||
throw new Error('toolbar.routing.error.connection');
|
||||
} else {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
let json = await response.json();
|
||||
|
||||
let route: TrackPoint[] = [];
|
||||
let coordinates = json.paths[0].points.coordinates;
|
||||
let details = json.paths[0].details;
|
||||
|
||||
for (let i = 0; i < coordinates.length; i++) {
|
||||
route.push(
|
||||
new TrackPoint({
|
||||
attributes: {
|
||||
lat: coordinates[i][1],
|
||||
lon: coordinates[i][0],
|
||||
},
|
||||
ele: coordinates[i][2] ?? (i > 0 ? route[i - 1].ele : 0),
|
||||
extensions: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
for (let key of graphhopperDetails) {
|
||||
let detail = details[key];
|
||||
for (let i = 0; i < detail.length; i++) {
|
||||
for (let j = detail[i][0]; j < detail[i][1] + (i == detail.length - 1); j++) {
|
||||
if (detail[i][2] !== undefined && detail[i][2] !== 'missing') {
|
||||
if (key === 'road_class') {
|
||||
route[j].setExtension('highway', detail[i][2]);
|
||||
} else if (key === 'hike_rating') {
|
||||
const sacScale = hikeRatingToSACScale[detail[i][2]];
|
||||
if (sacScale) {
|
||||
route[j].setExtension('sac_scale', sacScale);
|
||||
}
|
||||
} else if (key === 'mtb_rating') {
|
||||
const mtbScale = mtbRatingToScale[detail[i][2]];
|
||||
if (mtbScale) {
|
||||
route[j].setExtension('mtb_scale', mtbScale);
|
||||
}
|
||||
} else if (key === 'surface' && detail[i][2] !== 'other') {
|
||||
route[j].setExtension('surface', detail[i][2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
async function getBRouterRoute(
|
||||
points: Coordinates[],
|
||||
brouterProfile: string
|
||||
): Promise<TrackPoint[]> {
|
||||
let url = `https://brouter.de/brouter?lonlats=${points.map((point) => `${point.lon.toFixed(8)},${point.lat.toFixed(8)}`).join('|')}&profile=${brouterProfile}&format=geojson&alternativeidx=0`;
|
||||
|
||||
let response = await fetch(url);
|
||||
|
||||
// Check if the response is ok
|
||||
if (!response.ok) {
|
||||
throw new Error(`${await response.text()}`);
|
||||
const error = await response.text();
|
||||
if (error.includes('from-position not mapped in existing datafile')) {
|
||||
throw new Error('toolbar.routing.error.from');
|
||||
} else if (error.includes('via1-position not mapped in existing datafile')) {
|
||||
throw new Error('toolbar.routing.error.via');
|
||||
} else if (error.includes('to-position not mapped in existing datafile')) {
|
||||
throw new Error('toolbar.routing.error.to');
|
||||
} else if (error.includes('Time-out')) {
|
||||
throw new Error('toolbar.routing.error.timeout');
|
||||
} else {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
let geojson = await response.json();
|
||||
@@ -52,14 +228,13 @@ async function getRoute(
|
||||
let tags = messageIdx < messages.length ? getTags(messages[messageIdx][tagIdx]) : {};
|
||||
|
||||
for (let i = 0; i < coordinates.length; i++) {
|
||||
let coord = coordinates[i];
|
||||
route.push(
|
||||
new TrackPoint({
|
||||
attributes: {
|
||||
lat: coord[1],
|
||||
lon: coord[0],
|
||||
lat: coordinates[i][1],
|
||||
lon: coordinates[i][0],
|
||||
},
|
||||
ele: coord[2] ?? (i > 0 ? route[i - 1].ele : 0),
|
||||
ele: coordinates[i][2] ?? (i > 0 ? route[i - 1].ele : 0),
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import { get } from 'svelte/store';
|
||||
import { fileStateCollection } from '$lib/logic/file-state';
|
||||
import { fileActions } from '$lib/logic/file-actions';
|
||||
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
|
||||
import { ANCHOR_LAYER_KEY } from '$lib/components/map/map';
|
||||
|
||||
export class SplitControls {
|
||||
map: mapboxgl.Map;
|
||||
@@ -109,25 +108,24 @@ export class SplitControls {
|
||||
}
|
||||
|
||||
if (!this.map.getLayer('split-controls')) {
|
||||
this.map.addLayer(
|
||||
{
|
||||
id: 'split-controls',
|
||||
type: 'symbol',
|
||||
source: 'split-controls',
|
||||
layout: {
|
||||
'icon-image': 'split-control',
|
||||
'icon-size': 0.25,
|
||||
'icon-padding': 0,
|
||||
},
|
||||
filter: ['<=', ['get', 'minZoom'], ['zoom']],
|
||||
this.map.addLayer({
|
||||
id: 'split-controls',
|
||||
type: 'symbol',
|
||||
source: 'split-controls',
|
||||
layout: {
|
||||
'icon-image': 'split-control',
|
||||
'icon-size': 0.25,
|
||||
'icon-padding': 0,
|
||||
},
|
||||
ANCHOR_LAYER_KEY.interactions
|
||||
);
|
||||
filter: ['<=', ['get', 'minZoom'], ['zoom']],
|
||||
});
|
||||
|
||||
this.map.on('mouseenter', 'split-controls', this.layerOnMouseEnterBinded);
|
||||
this.map.on('mouseleave', 'split-controls', this.layerOnMouseLeaveBinded);
|
||||
this.map.on('click', 'split-controls', this.layerOnClickBinded);
|
||||
}
|
||||
|
||||
this.map.moveLayer('split-controls');
|
||||
} catch (e) {
|
||||
// No reliable way to check if the map is ready to add sources and layers
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ Pots arrossegar y deixar arxius directament des del seu sistema d'arxius cap a l
|
||||
|
||||
Crear una còpia dels arxius seleccionats.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Esborra
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete
|
||||
|
||||
Esborra l'arxiu seleccinat.
|
||||
Delete the currently selected files.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Esborra-ho tot
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
||||
|
||||
Esborra tots els fitxers.
|
||||
Delete all files.
|
||||
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Exportar...
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Mapbox stellt einige der auf dieser Website verwendeten Karten bereit.
|
||||
Sie entwickeln auch die <a href="https://github.com/mapbox/mapbox-gl-js" target="_blank">Karten-Engine</a>, die **gpx.studio** unterstützt.
|
||||
Mapbox ist das Unternehmen, das einige der schönen Karten auf dieser Website zur Verfügung stellt.
|
||||
Sie entwickeln auch die <a href="https://github.com/mapbox/mapbox-gl-js" target="_blank">Karten-Engine</a> welche **gpx.studio** unterstützt.
|
||||
|
||||
Wir sind froh und dankbar, Teil ihres <a href="https://mapbox.com/community" target="_blank">Community</a> Programms zu sein, das gemeinnützige Organisationen, Bildungseinrichtungen und Organisationen unterstützt.
|
||||
Wir sind äußerst glücklich und dankbar, Teil ihres <a href="https://mapbox.com/community" target="_blank">Community</a> Programms zu sein, das gemeinnützige Organisationen, Bildungseinrichtungen und Organisationen mit positivem Einfluss unterstützt.
|
||||
Diese Partnerschaft ermöglicht es **gpx.studio**, von den Mapbox-Tools zu ermäßigten Preisen zu profitieren, was erheblich zur finanziellen Tragfähigkeit des Projekts beiträgt und es uns ermöglicht, die bestmögliche Benutzererfahrung zu bieten.
|
||||
|
||||
@@ -35,7 +35,7 @@ Supprimer les fichiers sélectionnés.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Supprimer tout
|
||||
|
||||
Supprimer tous les fichiers.
|
||||
Supprimer toutes les fichiers.
|
||||
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Exporter...
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ title: FAQ
|
||||
|
||||
# { title }
|
||||
|
||||
### Czy muszę przekazać darowiznę za korzystanie ze strony internetowej?
|
||||
### Do I need to donate to use the website?
|
||||
|
||||
Nie.
|
||||
Strona internetowa jest darmowa i zawsze będzie (o ile będzie się zgadzał rachunek).
|
||||
Darowizny są jednak doceniane i pomagają utrzymać funkcjonowanie strony.
|
||||
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.
|
||||
|
||||
### Why is this route chosen over that one? _Or_ how can I add something to the map?
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ title: Wprowadzenie
|
||||
|
||||
# { title }
|
||||
|
||||
Witamy w oficjalnym przewodniku dla **gpx.studio**!
|
||||
Ten przewodnik przeprowadzi Cię przez wszystkie komponenty i narzędzia interfejsu, co pomoże stać się wydajnym użytkownikiem aplikacji.
|
||||
Welcome to the official guide for **gpx.studio**!
|
||||
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="The gpx.studio interface." />
|
||||
|
||||
Jak pokazano na zrzucie ekranu powyżej, interfejs jest podzielony na cztery główne sekcje zorganizowane wokół mapy.
|
||||
As shown in the screenshot above, the interface is divided into four main sections organized around the map.
|
||||
Before we dive into the details of each section, let's have a quick overview of the interface.
|
||||
|
||||
## Menu główne
|
||||
@@ -31,7 +31,7 @@ In the [dedicated section](./files-and-stats), we will explain how to select mul
|
||||
|
||||
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.
|
||||
|
||||
## Sterowanie mapą
|
||||
## Map controls
|
||||
|
||||
Na koniec, po prawej stronie interfejsu znajdziesz [sterowanie mapą] (./map-controls).
|
||||
Za pomocą tych elementów sterujących można poruszać się po mapie, powiększać i pomniejszać widok oraz przełączać się między różnymi stylami mapy.
|
||||
Finally, on the right side of the interface, you will find the [map controls](./map-controls).
|
||||
These controls allow you to navigate the map, zoom in and out, and switch between different map styles.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { HeartHandshake } from '@lucide/svelte';
|
||||
</script>
|
||||
|
||||
## <HeartHandshake size="18" class="inline-block align-baseline" /> Pomóż nam utrzymać tę stronę jako bezpłatną (i wolną od reklam)
|
||||
## <HeartHandshake size="18" class="inline-block align-baseline" /> Help keep the website free (and ad-free)
|
||||
|
||||
Za każdym razem, gdy dodasz lub przenosisz punkty GPS, nasze serwery obliczają najlepszą trasę w sieci drogowej.
|
||||
Używamy również API z <a href="https://mapbox.com" target="_blank">Mapbox</a> do wyświetlania pięknych map, pobierania danych wysokości i wyszukiwania miejsc.
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import { Languages } from '@lucide/svelte';
|
||||
</script>
|
||||
|
||||
## <Languages size="18" class="inline-block align-baseline" /> Tłumaczenie
|
||||
## <Languages size="18" class="inline-block align-baseline" /> Translation
|
||||
|
||||
Strona internetowa jest tłumaczona przez wolontariuszy na platformie do współpracy w tłumaczeniu.
|
||||
Możesz przyczynić się do rozwoju naszego projektu, dodając lub ulepszając tłumaczenia w ramach <a href="https://crowdin.com/project/gpxstudio" target="_blank">projektu Crowdin</a>.
|
||||
Możesz pomóc dodając i sprawdzając istniejące tłumaczenie w <a href="https://crowdin.com/project/gpxstudio" target="_blank">projekcie Crowdin</a>.
|
||||
|
||||
Jeśli chciałbyś dodać język, którego nie ma na liście <a href="#contact">skontaktuj się z nami</a>.
|
||||
Jeśli chciałbyś dodać język, którego nie ma na liście <a href="#contact">skontaktuj się</a>.
|
||||
|
||||
Każda pomoc jest bardzo mile widziana!
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Sterowanie mapą
|
||||
title: Map controls
|
||||
---
|
||||
|
||||
<script>
|
||||
@@ -10,10 +10,10 @@ title: Sterowanie mapą
|
||||
|
||||
# { title }
|
||||
|
||||
Elementy sterujące mapą znajdują się po prawej stronie interfejsu.
|
||||
Za pomocą tych elementów sterujących można poruszać się po mapie, powiększać i pomniejszać widok oraz przełączać się między różnymi stylami mapy.
|
||||
The map controls are located on the right side of the interface.
|
||||
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" /> Nawigacja mapą
|
||||
### <Diff size="16" class="inline-block" style="margin-bottom: 2px" /> Map navigation
|
||||
|
||||
The controls at the top allow you to zoom in <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> and out <Minus size="16" class="inline-block" style="margin-bottom: 2px" />, and to change the orientation and tilt of the map <Compass size="16" class="inline-block" style="margin-bottom: 2px" />.
|
||||
|
||||
@@ -39,7 +39,7 @@ This only works if you have allowed your browser and <b>gpx.studio</b> to access
|
||||
|
||||
### <PersonStanding size="16" class="inline-block" style="margin-bottom: 2px" /> Street view
|
||||
|
||||
Ten przycisk może być użyty do włączenia trybu street view na mapie.
|
||||
This button can be used to enable street view mode on the map.
|
||||
Depending on the street view source chosen in the [settings](./menu/settings), street view imagery can be accessed differently.
|
||||
|
||||
- <a href="https://www.mapillary.com/" target="_blank">Mapillary</a>: the street view coverage will appear as green lines on the map. When zoomed in enough, green dots will show the exact locations where street view imagery is available. Hovering over a green dot will show the street view image at that location.
|
||||
|
||||
@@ -9,44 +9,44 @@ title: Akcje menu Plik
|
||||
|
||||
# { title }
|
||||
|
||||
Menu operacji na plikach zawiera zestaw funkcji, których przeznaczenie jest dość oczywiste.
|
||||
The file actions menu contains a set of pretty self-explanatory file operations.
|
||||
|
||||
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> Nowy
|
||||
### <Plus size="16" class="inline-block" style="margin-bottom: 2px" /> New
|
||||
|
||||
Tworzy nowy pusty plik.
|
||||
|
||||
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Otwórz...
|
||||
### <FolderOpen size="16" class="inline-block" style="margin-bottom: 2px" /> Open...
|
||||
|
||||
Otwórz pliki z komputera.
|
||||
Open files from your computer.
|
||||
|
||||
<DocsNote>
|
||||
|
||||
Można również przeciągać i upuszczać pliki bezpośrednio z systemu plików do okna.
|
||||
You can also drag and drop files directly from your file system into the window.
|
||||
|
||||
</DocsNote>
|
||||
|
||||
### <Copy size="16" class="inline-block" style="margin-bottom: 2px" /> Duplikuj
|
||||
### <Copy size="16" class="inline-block" style="margin-bottom: 2px" /> Duplicate
|
||||
|
||||
Utwórz kopię aktualnie zaznaczonych plików.
|
||||
Create a copy of the currently selected files.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Usuń
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete
|
||||
|
||||
Usuń aktualnie zaznaczone pliki.
|
||||
Delete the currently selected files.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Usuń wszystko
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
||||
|
||||
Usuń wszystkie pliki.
|
||||
Delete all files.
|
||||
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Eksport...
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Export...
|
||||
|
||||
Otwórz okno dialogowe eksportu, aby zapisać aktualnie wybrane pliki na komputerze.
|
||||
Open the export dialog to save the currently selected files to your computer.
|
||||
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Eksportuj wszystko...
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Export all...
|
||||
|
||||
Otwórz okno dialogowe eksportu, aby zapisać wszystkie pliki na komputerze.
|
||||
Open the export dialog to save all files to your computer.
|
||||
|
||||
<DocsNote type="warning">
|
||||
|
||||
Jeśli po kliknięciu przycisku pobierania plik nie zacznie się pobierać, sprawdź ustawienia przeglądarki i upewnij się, że zezwalają one na pobieranie plików z witryny <b>gpx.studio</b>.
|
||||
If your download does not start after clicking the download button, please check your browser settings to allow downloads from <b>gpx.studio</b>.
|
||||
|
||||
</DocsNote>
|
||||
|
||||
@@ -11,11 +11,11 @@ title: Settings
|
||||
|
||||
### <Ruler size="16" class="inline-block" style="margin-bottom: 2px" /> Distance units
|
||||
|
||||
Zmień jednostki stosowane do wyświetlania odległości w interfejsie.
|
||||
Change the units used to display distances in the interface.
|
||||
|
||||
### <Zap size="16" class="inline-block" style="margin-bottom: 2px" /> Velocity units
|
||||
|
||||
Zmień jednostki używane do wyświetlania prędkości w interfejsie.
|
||||
Change the units used to display velocities in the interface.
|
||||
You can choose between distance per hour or minutes per distance, which can be more suitable for running activities.
|
||||
|
||||
### <Thermometer size="16" class="inline-block" style="margin-bottom: 2px" /> Temperature units
|
||||
@@ -28,8 +28,8 @@ Change the language used in the interface.
|
||||
|
||||
<DocsNote>
|
||||
|
||||
Możesz przyczynić się do rozwoju naszego projektu, dodając lub ulepszając tłumaczenia w ramach <a href="https://crowdin.com/project/gpxstudio" target="_blank">projektu Crowdin</a>.
|
||||
Jeśli chciałbyś dodać język, którego nie ma na liście <a href="#contact">skontaktuj się z nami</a>.
|
||||
Możesz pomóc dodając i sprawdzając istniejące tłumaczenie w <a href="https://crowdin.com/project/gpxstudio" target="_blank">projekcie Crowdin</a>.
|
||||
Jeśli chciałbyś dodać język, którego nie ma na liście <a href="#contact">skontaktuj się</a>.
|
||||
Każda pomoc jest bardzo mile widziana!
|
||||
|
||||
</DocsNote>
|
||||
|
||||
@@ -9,21 +9,21 @@ title: Akcje menu Widok
|
||||
|
||||
# { title }
|
||||
|
||||
To menu zawiera opcje zmiany kolejności interfejsu i widoku mapy.
|
||||
This menu provides options to rearrange the interface and the map view.
|
||||
|
||||
### <ChartArea size="16" class="inline-block" style="margin-bottom: 2px" /> Profil wysokościowy
|
||||
### <ChartArea size="16" class="inline-block" style="margin-bottom: 2px" /> Elevation profile
|
||||
|
||||
Ukryj profil ukształtowania terenu, aby zrobić miejsce na mapie lub pokaż go, aby sprawdzić bieżący wybór.
|
||||
Hide the elevation profile to make room for the map, or show it to inspect the current selection.
|
||||
|
||||
### <ListTree size="16" class="inline-block" style="margin-bottom: 2px" /> Drzewo plików
|
||||
### <ListTree size="16" class="inline-block" style="margin-bottom: 2px" /> File tree
|
||||
|
||||
Przełącz układ drzewa na [listy plików](../files-and-stats).
|
||||
Toggle the tree layout for the [file list](../files-and-stats).
|
||||
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
|
||||
Dodatkowo, widok drzewa plików umożliwia sprawdzenie [tras, segmentów, oraz punktów zainteresowania](../gpx) zawarte w plikach poprzez zwijalne sekcje.
|
||||
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](../gpx) contained inside the files through collapsible sections.
|
||||
|
||||
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Przełącz na poprzednią mapę
|
||||
### <Map size="16" class="inline-block" style="margin-bottom: 2px" /> Switch to previous basemap
|
||||
|
||||
Zmień mapę na mapę wybraną poprzednio przez [sterowanie warstwą map](../map-controls).
|
||||
Change the basemap to the one previously selected through the [map layer control](../map-controls).
|
||||
|
||||
### <Layers2 size="16" class="inline-block" style="margin-bottom: 2px" /> Toggle overlays
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Planowanie i edycja trasy
|
||||
title: Route planning and editing
|
||||
---
|
||||
|
||||
<script>
|
||||
@@ -11,11 +11,11 @@ title: Planowanie i edycja trasy
|
||||
|
||||
# <Pencil size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
|
||||
|
||||
Narzędzie do planowania i edycji trasy pozwala na tworzenie i edytowanie tras poprzez umieszczanie lub przesuwanie punktów kotwiczenia na mapie.
|
||||
The route planning and editing tool allows you to create and edit routes by placing or moving anchor points on the map.
|
||||
|
||||
## Settings
|
||||
|
||||
Jak pokazano poniżej, okno dialogowe narzędzia zawiera kilka ustawień do kontrolowania zachowania rutingu.
|
||||
As shown below, the tool dialog contains a few settings to control the routing behavior.
|
||||
You can minimize the dialog to save space by clicking on <button><SquareArrowUpLeft size="16" class="inline-block" style="margin-bottom: 2px" /></button>.
|
||||
|
||||
<div class="flex flex-row justify-center">
|
||||
|
||||
@@ -24,7 +24,7 @@ Validate the selection when you are satisfied with the result.
|
||||
## Podziel
|
||||
|
||||
To split the selected trace into two parts, click on one of the split markers displayed along the trace.
|
||||
Aby podzielić w wybranym przez Ciebie punkcie, zaznacz punkt na śladzie trasy.
|
||||
To split at a specific point of your choice, hover over the trace on the map.
|
||||
Scissors will appear at the cursor position, showing that you can split the trace at that point.
|
||||
|
||||
You can choose to split the trace into two GPX files, or to keep the split parts in the same file as [tracks or segments](../gpx).
|
||||
|
||||
@@ -10,18 +10,18 @@ title: Czas
|
||||
|
||||
# <CalendarClock size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
|
||||
|
||||
To narzędzie pozwala na zmianę lub dodanie znaczników czasu do śladu.
|
||||
Musisz po prostu użyć poniższego formularza i potwierdzić go po zakończeniu.
|
||||
This tool allows you to change or add timestamps to a trace.
|
||||
You simply need to use the form shown below and validate it when you are done.
|
||||
|
||||
<div class="flex flex-row justify-center">
|
||||
<Time class="text-foreground p-3 border rounded-md shadow-lg" />
|
||||
</div>
|
||||
|
||||
Podczas edycji prędkości, czas poruszania się jest odpowiednio dostosowywany w formularzu i odwrotnie.
|
||||
Podobnie, kiedy edytujesz czas rozpoczęcia, czas zakończenia jest aktualizowany, aby zachować ten sam czas trwania i odwrotnie.
|
||||
When you edit the speed, the moving time is adapted accordingly in the form, and vice versa.
|
||||
Similarly, when you edit the start time, the end time is updated to keep the same total duration, and vice versa.
|
||||
|
||||
<DocsNote>
|
||||
|
||||
Gdy używasz to narzędzie z istniejącymi znacznikami czasu, zmiana czasu lub prędkości po prostu się zmieni, rozciągnij lub kompresuj je.
|
||||
When using this tool with existing timestamps, changing the time or speed will simply shift, stretch, or compress them accordingly.
|
||||
|
||||
</DocsNote>
|
||||
|
||||
@@ -50,7 +50,7 @@ Clicando com o botão direito em uma aba arquivo, você pode acessar as mesmas a
|
||||
|
||||
As mentioned in the [view options section](./menu/view), you can switch to a tree layout for the files list.
|
||||
This layout is ideal for managing a large number of open files, as it organizes them into a vertical list on the right side of the map.
|
||||
Além disso, a exibição de árvore de arquivos permite que você inspecione as [faixas, segmentos, e pontos de interesse](./gpx) contidos dentro dos arquivos através de seções recolhidas.
|
||||
In addition, the file tree view enables you to inspect the [tracks, segments, and points of interest](./gpx) contained inside the files through collapsible sections.
|
||||
|
||||
Você também pode aplicar as [ações de edição](./menu/edit) e [ferramentas](./toolbar) para itens de arquivos internos.
|
||||
Além disso, você pode arrastar e soltar os itens internos para reordená-los, ou movê-los na hierarquia ou até mesmo para outro arquivo.
|
||||
@@ -105,6 +105,6 @@ Using the <kbd><ChartNoAxesColumn size="16" class="inline-block" style="margin-b
|
||||
|
||||
- **slope** 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.
|
||||
Isso só está disponível para arquivos criados com **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.
|
||||
|
||||
@@ -35,7 +35,7 @@ Delete the currently selected files.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
||||
|
||||
Apagar todos os arquivos.
|
||||
Delete all files.
|
||||
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Exportar...
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import { HeartHandshake } from '@lucide/svelte';
|
||||
</script>
|
||||
|
||||
## <HeartHandshake size="18" class="inline-block align-baseline" />
|
||||
Допоможіть нам залишати цей сайт безкоштовним (та без реклами)
|
||||
## <HeartHandshake size="18" class="inline-block align-baseline" /> Help keep the website free (and ad-free)
|
||||
|
||||
Кожного разу, коли ви додаєте або переміщуєте GPS точки, наші сервери обчислюють найкращий маршрут на мережі доріг.
|
||||
Ми також використовуємо API від <a href="https://mapbox.com" target="_blank">Mapbox</a> для зображення красивих карт, отримання даних висот та можливості пошуку місць.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { Languages } from '@lucide/svelte';
|
||||
</script>
|
||||
|
||||
## <Languages size="18" class="inline-block align-baseline" /> Переклад
|
||||
## <Languages size="18" class="inline-block align-baseline" /> Translation
|
||||
|
||||
Сайт перекладається волонтерами з використанням платформи для спільного перекладу.
|
||||
Ви можете зробити свій внесок, додаючи або покращуючи переклади в нашому <a href="https://crowdin.com/project/gpxstudio" target="_blank">проєкті Crowdin</a>.
|
||||
|
||||
@@ -47,6 +47,6 @@ Open the export dialog to save all files to your computer.
|
||||
|
||||
<DocsNote type="warning">
|
||||
|
||||
Якщо завантаження не починається після натискання кнопки завантаження, будь ласка, перевірте налаштування браузера, щоб дозволити завантаження з <b>gpx.studio</b>.
|
||||
If your download does not start after clicking the download button, please check your browser settings to allow downloads from <b>gpx.studio</b>.
|
||||
|
||||
</DocsNote>
|
||||
|
||||
@@ -31,7 +31,7 @@ Create a copy of the currently selected files.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete
|
||||
|
||||
.
|
||||
Delete the currently selected files.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
||||
|
||||
|
||||
@@ -807,7 +807,7 @@ export const fileActions = {
|
||||
});
|
||||
});
|
||||
},
|
||||
addElevationToSelection: async () => {
|
||||
addElevationToSelection: async (map: mapboxgl.Map) => {
|
||||
if (get(selection).size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"undo": "Desfer",
|
||||
"redo": "Refer",
|
||||
"delete": "Elimina el track",
|
||||
"delete_all": "Esborra-ho tot",
|
||||
"delete_all": "Delete all",
|
||||
"select_all": "Seleccionar-ho tot",
|
||||
"view": "Vista",
|
||||
"elevation_profile": "Perfil d’elevacions",
|
||||
@@ -80,7 +80,7 @@
|
||||
"center": "Centrar",
|
||||
"open_in": "Obrir amb",
|
||||
"copy_coordinates": "Copiar coordenades",
|
||||
"edit_osm": "Edita a OpenStreetMap"
|
||||
"edit_osm": "Edit in OpenStreetMap"
|
||||
},
|
||||
"toolbar": {
|
||||
"routing": {
|
||||
@@ -190,8 +190,6 @@
|
||||
"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",
|
||||
"to": "El punt final és massa lluny de la via més propera",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "El càlcul de la ruta tarda més del compte, prova d'afegir punts més propers entre si"
|
||||
}
|
||||
},
|
||||
@@ -284,7 +282,7 @@
|
||||
"update": "Actualitza la capa"
|
||||
},
|
||||
"opacity": "Opacitat de la superposició",
|
||||
"terrain": "Terreny",
|
||||
"terrain": "Terrain source",
|
||||
"label": {
|
||||
"basemaps": "Mapes base",
|
||||
"overlays": "Capes",
|
||||
@@ -358,7 +356,7 @@
|
||||
"water": "Aigua",
|
||||
"shower": "Dutxa",
|
||||
"shelter": "Refugi",
|
||||
"cemetery": "Cementiri",
|
||||
"cemetery": "Cemetery",
|
||||
"motorized": "Cotxes i motos",
|
||||
"fuel-station": "Gasolinera",
|
||||
"parking": "Aparcament",
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Počáteční bod je příliš daleko od nejbližší cesty",
|
||||
"via": "Průchozí bod je příliš daleko od nejbližší cesty",
|
||||
"to": "Koncový bod je příliš daleko od nejbližší cesty",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Výpočet trasy trval příliš dlouho, zkuste přidat body blíže k sobě"
|
||||
}
|
||||
},
|
||||
@@ -284,7 +282,7 @@
|
||||
"update": "Aktualizovat vrstvu"
|
||||
},
|
||||
"opacity": "Průhlednost překryvu",
|
||||
"terrain": "Zdroj terénu",
|
||||
"terrain": "Terrain source",
|
||||
"label": {
|
||||
"basemaps": "Základní mapy",
|
||||
"overlays": "Překrytí",
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -21,14 +21,14 @@
|
||||
"export_all": "Alle exportieren...",
|
||||
"export_options": "Export-Einstellungen",
|
||||
"support_message": "Das Tool darf frei benutzt werden, aber es darf nicht woanders aufgesetzt werden. Bitte unterstützen Sie die Website, wenn Sie sie häufig benutzen. Vielen Dank!",
|
||||
"support_button": "Hilf uns, die Website weiterhin kostenlos bereitzustellen",
|
||||
"support_button": "Hilf dabei, die Webseite kostenlos zu belassen",
|
||||
"download_file": "Datei herunterladen",
|
||||
"download_files": "Dateien herunterladen",
|
||||
"edit": "Bearbeiten",
|
||||
"undo": "Rückgängig",
|
||||
"redo": "Wiederholen",
|
||||
"delete": "Löschen",
|
||||
"delete_all": "Alle löschen",
|
||||
"delete_all": "Delete all",
|
||||
"select_all": "Alle auswählen",
|
||||
"view": "Ansicht",
|
||||
"elevation_profile": "Höhenprofil",
|
||||
@@ -80,7 +80,7 @@
|
||||
"center": "Zentrieren",
|
||||
"open_in": "Öffnen in",
|
||||
"copy_coordinates": "Koordinaten kopieren",
|
||||
"edit_osm": "In OpenStreetMap bearbeiten"
|
||||
"edit_osm": "Edit in OpenStreetMap"
|
||||
},
|
||||
"toolbar": {
|
||||
"routing": {
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Der Startpunkt ist zu weit von der nächsten Straße entfernt",
|
||||
"via": "Der Via-Punkt ist zu weit entfernt von der nächsten Straße",
|
||||
"to": "Der Endpunkt ist zu weit von der nächsten Straße entfernt",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route-Berechnung benötigte zu viel Zeit; versuche, die Punkte näher aneinander zu setzen"
|
||||
}
|
||||
},
|
||||
@@ -284,7 +282,7 @@
|
||||
"update": "Layer aktualisieren"
|
||||
},
|
||||
"opacity": "Deckkraft der Überlagerung",
|
||||
"terrain": "Geländequelle",
|
||||
"terrain": "Terrain source",
|
||||
"label": {
|
||||
"basemaps": "Basiskarte",
|
||||
"overlays": "Ebenen",
|
||||
@@ -328,7 +326,7 @@
|
||||
"usgs": "USGS",
|
||||
"bikerouterGravel": "bikerouter.de Gravel",
|
||||
"cyclOSMlite": "CyclOSM Lite",
|
||||
"mapterhornHillshade": "MapTiler Hillshade",
|
||||
"mapterhornHillshade": "Mapterhorn Hillshade",
|
||||
"openRailwayMap": "OpenRailwayMap",
|
||||
"swisstopoSlope": "swisstopo Neigung",
|
||||
"swisstopoHiking": "swisstopo Wandern",
|
||||
@@ -358,7 +356,7 @@
|
||||
"water": "Trinkwasser",
|
||||
"shower": "Dusche",
|
||||
"shelter": "Unterstand",
|
||||
"cemetery": "Friedhof",
|
||||
"cemetery": "Cemetery",
|
||||
"motorized": "Autos und Motorräder",
|
||||
"fuel-station": "Tankstelle",
|
||||
"parking": "Parken",
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,6 +190,8 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is to far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"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",
|
||||
"to": "El punto final está demasiado lejos de la carretera más cercana",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Calcular la ruta llevó demasiado tiempo, intente añadir puntos más cercanos entre ellos"
|
||||
}
|
||||
},
|
||||
@@ -284,7 +282,7 @@
|
||||
"update": "Actualizar capa"
|
||||
},
|
||||
"opacity": "Opacidad de la capa superpuesta",
|
||||
"terrain": "Origen del terreno",
|
||||
"terrain": "Terrain source",
|
||||
"label": {
|
||||
"basemaps": "Mapas base",
|
||||
"overlays": "Capas",
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Hasiera puntua errepide hurbilenetik oso hurrun dago",
|
||||
"via": "Puntua errepide hurbilenetik oso hurrun dago",
|
||||
"to": "Bukaera puntua errepide hurbilenetik oso hurrun dago",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Ibilbidea kalkulatzea luzeegi joan da, saiatu hurbilago dauden puntuak gehitzen"
|
||||
}
|
||||
},
|
||||
@@ -284,7 +282,7 @@
|
||||
"update": "Eguneratu geruza"
|
||||
},
|
||||
"opacity": "Geruzaren opakutasuna",
|
||||
"terrain": "Lurrazala",
|
||||
"terrain": "Terrain source",
|
||||
"label": {
|
||||
"basemaps": "Oinarrizko mapak",
|
||||
"overlays": "Geruzak",
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Aloituspiste on liian kaukana lähimmästä tiestä",
|
||||
"via": "Reittipiste on liian kaukana lähimmästä tiestä",
|
||||
"to": "Päätepiste on liian kaukana lähimmästä tiestä",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Reitin laskenta kesti liian kauan. Tihennä reittipisteitä"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"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",
|
||||
"to": "Le point d'arrivée est trop éloigné de la route la plus proche",
|
||||
"distance": "Le point d'arrivée est trop loin du point de départ",
|
||||
"connection": "Aucune connexion trouvée entre les points",
|
||||
"timeout": "Le calcul de l'itinéraire a pris trop de temps, essayez d'ajouter des points plus rapprochés"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "נקודת ההתחלה רחוקה מדיי מהכביש הקרוב ביותר",
|
||||
"via": "נקודת המעבר רחוקה מדיי מהכביש הקרוב ביותר",
|
||||
"to": "נקודת הסיום רחוקה מדיי מהכביש הקרוב ביותר",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"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",
|
||||
"to": "A végpont túl messze van a legközelebbi úttól",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Az útvonal kiszámítása túl sokáig tartott. Próbáljon meg közelebbi pontokat adni egymáshoz"
|
||||
}
|
||||
},
|
||||
@@ -237,13 +235,13 @@
|
||||
"help_no_selection": "Select a file item to request elevation data."
|
||||
},
|
||||
"waypoint": {
|
||||
"tooltip": "PoI létrehozása és módosítása",
|
||||
"icon": "Ikon",
|
||||
"tooltip": "Create and edit points of interest",
|
||||
"icon": "Icon",
|
||||
"link": "Link",
|
||||
"longitude": "Földrajzi hosszúság",
|
||||
"longitude": "Longitude",
|
||||
"latitude": "Szélesség",
|
||||
"create": "POI fájlba mentése",
|
||||
"add": "PoI fájlhoz adása",
|
||||
"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_no_selection": "Select a file to create or edit points of interest."
|
||||
},
|
||||
@@ -251,8 +249,8 @@
|
||||
"tooltip": "Reduce the number of GPS points",
|
||||
"tolerance": "Tűréshatár",
|
||||
"number_of_points": "GPS pontok száma",
|
||||
"button": "Minimalizálás",
|
||||
"help": "",
|
||||
"button": "Minify",
|
||||
"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": {
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Titik awal terlalu jauh dari jalan terdekat",
|
||||
"via": "Titik via terlalu jauh dari jalan terdekat",
|
||||
"to": "Titik akhir terlalu jauh dari jalan terdekat",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Perhitungan rute memakan waktu terlalu lama, coba tambahkan titik-titik yang lebih dekat satu sama lain"
|
||||
}
|
||||
},
|
||||
@@ -493,7 +491,7 @@
|
||||
"support_button": "Support gpx.studio on Ko-fi",
|
||||
"route_planning": "Route planning",
|
||||
"route_planning_description": "An intuitive interface to create itineraries tailored to each sport, based on OpenStreetMap data.",
|
||||
"file_processing": "Pengolahan file lanjutan",
|
||||
"file_processing": "Advanced file processing",
|
||||
"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": "Global and local maps",
|
||||
"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.",
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Il punto di partenza è troppo lontano dalla strada più vicina",
|
||||
"via": "Il punto di arrivo è troppo lontano dalla strada più vicina",
|
||||
"to": "Il punto di arrivo è troppo lontano dalla strada più vicina",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Il calcolo del percorso ha richiesto troppo tempo, prova ad aggiungere punti più vicini"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Pradžios taškas yra per toli nuo artimiausio kelio",
|
||||
"via": "Tarpinis taškas yra per toli nuo artimiausio kelio",
|
||||
"to": "Pabaigos taškas yra per toli nuo artimiausio kelio",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Maršruto skaičiavimas užtruko per ilgai, pabandykite pridėti taškus arčiau vienas kito"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"mapillary": "Mapillary",
|
||||
"google": "Google",
|
||||
"toggle_street_view": "Street view",
|
||||
"layers": "Kaartlagen...",
|
||||
"layers": "Kaart lagen...",
|
||||
"distance_markers": "Afstandsmarkeringen",
|
||||
"direction_markers": "Richtingspijlen",
|
||||
"help": "Help",
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Het startpunt ligt te ver van de dichtstbijzijnde weg",
|
||||
"via": "Het via punt ligt te ver van de dichtstbijzijnde weg",
|
||||
"to": "Het eindpunt is te ver van de dichtstbijzijnde weg",
|
||||
"distance": "Het eindpunt is te ver vanaf het startpunt",
|
||||
"connection": "Geen verbinding gevonden tussen de punten",
|
||||
"timeout": "Routeberekening heeft te lang geduurd, probeer punten dichter bij elkaar toe te voegen"
|
||||
}
|
||||
},
|
||||
@@ -284,7 +282,7 @@
|
||||
"update": "Update laag"
|
||||
},
|
||||
"opacity": "Laag Transparantie",
|
||||
"terrain": "Terrein bron",
|
||||
"terrain": "Terrain source",
|
||||
"label": {
|
||||
"basemaps": "Basis kaarten",
|
||||
"overlays": "Lagen",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"undo": "Angre",
|
||||
"redo": "Gjenta",
|
||||
"delete": "Slett",
|
||||
"delete_all": "Slett alle",
|
||||
"delete_all": "Delete all",
|
||||
"select_all": "Velg alle",
|
||||
"view": "Visning",
|
||||
"elevation_profile": "Høydeprofil",
|
||||
@@ -80,7 +80,7 @@
|
||||
"center": "Sentrer",
|
||||
"open_in": "Åpne I",
|
||||
"copy_coordinates": "Kopier koordinater",
|
||||
"edit_osm": "Rediger i OpenStreetMap"
|
||||
"edit_osm": "Edit in OpenStreetMap"
|
||||
},
|
||||
"toolbar": {
|
||||
"routing": {
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Startpunktet er for langt unna nærmeste vei",
|
||||
"via": "Via-punktet er for langt fra nærmeste vei",
|
||||
"to": "Sluttpunktet er for langt unna nærmeste vei",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Ruteberegning tok for lang tid, prøv å legge til flere punkter"
|
||||
}
|
||||
},
|
||||
@@ -358,7 +356,7 @@
|
||||
"water": "Vann",
|
||||
"shower": "Dusj",
|
||||
"shelter": "Ly",
|
||||
"cemetery": "Gravplass",
|
||||
"cemetery": "Cemetery",
|
||||
"motorized": "Biler og motorsykler",
|
||||
"fuel-station": "Bensinstasjon",
|
||||
"parking": "Parkering",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"app_title": "Aplikacja",
|
||||
"embed_title": "Edytor plików GPX online",
|
||||
"help_title": "pomoc",
|
||||
"404_title": "Strona nie odnaleziona",
|
||||
"404_title": "nie odnaleziono strony",
|
||||
"description": "Przeglądaj, edytuj i twórz pliki GPX online z zaawansowanymi możliwościami planowania trasy i narzędziami do przetwarzania plików, pięknymi mapami i szczegółowymi wizualizacjami danych."
|
||||
},
|
||||
"menu": {
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Punkt początkowy jest zbyt daleko od najbliższej drogi",
|
||||
"via": "Punkt przelotowy jest zbyt daleko od najbliższej drogi",
|
||||
"to": "Punkt końcowy jest zbyt daleko od najbliższej drogi",
|
||||
"distance": "Punkt końcowy jest zbyt daleko od punktu początkowego",
|
||||
"connection": "Nie znaleziono połączenia między punktami",
|
||||
"timeout": "Obliczanie trasy trwało zbyt długo, spróbuj dodać punkty bliżej siebie"
|
||||
}
|
||||
},
|
||||
@@ -284,7 +282,7 @@
|
||||
"update": "Zaktualizuj warstwę"
|
||||
},
|
||||
"opacity": "Przezroczystość nakładki",
|
||||
"terrain": "Źródło danych terenowych",
|
||||
"terrain": "Terrain source",
|
||||
"label": {
|
||||
"basemaps": "Mapy bazowe",
|
||||
"overlays": "Nakładki",
|
||||
@@ -328,7 +326,7 @@
|
||||
"usgs": "USGS",
|
||||
"bikerouterGravel": "bikerouter.de Gravel",
|
||||
"cyclOSMlite": "CyclOSM Lite",
|
||||
"mapterhornHillshade": "Cieniowanie terenu Mapterhorn",
|
||||
"mapterhornHillshade": "Mapterhorn Hillshade",
|
||||
"openRailwayMap": "OpenRailwayMap",
|
||||
"swisstopoSlope": "swisstopo Stoki",
|
||||
"swisstopoHiking": "swisstopo Szlaki Turystyczne",
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"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",
|
||||
"to": "O ponto de chegada está muito longe da estrada mais próxima",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "O cálculo da rota demorou muito tempo, tente adicionar pontos mais próximos"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "O ponto de partida está muito longe da estrada mais próxima",
|
||||
"via": "O ponto de partida está muito longe da estrada mais próxima",
|
||||
"to": "O ponto final está muito longe do ponto de chegada",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "O recalculo da demorou muito tempo, tente adicionar ponto mais próximos"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Начальная точка слишком далеко от ближайшей дороги",
|
||||
"via": "Точка маршрута слишком далеко от ближайшей дороги",
|
||||
"to": "Конечная точка слишком далеко от ближайшей дороги",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Расчет маршрута занял слишком много времени, попробуйте добавить точки ближе"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Početna tačka je predaleko od najbližeg puta",
|
||||
"via": "Putna tačka je predaleko od najbližeg puta",
|
||||
"to": "Krajnja tačka je predaleko od najbližeg puta",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Računanje rute je trajalo predugo, pokušajte da dodate putne tačke bliže jednu drugoj"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Startpunkten är för långt bort från närmaste väg",
|
||||
"via": "Ruttpunkten är för långt bort från närmaste väg",
|
||||
"to": "Slutpunkten är för långt bort från närmaste väg",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Ruttberäkningen tog för lång tid, försök att lägga punkterna närmare varandra"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Başlangıç noktası en yakın yoldan çok uzak",
|
||||
"via": "Geçiş noktası en yakın yoldan çok uzak",
|
||||
"to": "Bitiş noktası en yakın yoldan çok uzak",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Rota hesaplaması uzun sürdü, daha yakın noktalar eklemeyi deneyin"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "Початкова точка знаходиться занадто далеко від найближчої дороги",
|
||||
"via": "Проміжна точка знаходиться занадто далеко від найближчої дороги",
|
||||
"to": "Кінцева точка знаходиться занадто далеко від найближчої дороги",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Розрахунок маршруту є занадто довгий, спробуйте додати ближчі точки"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "The start point is too far from the nearest road",
|
||||
"via": "The via point is too far from the nearest road",
|
||||
"to": "The end point is too far from the nearest road",
|
||||
"distance": "The end point is too far from the start point",
|
||||
"connection": "No connection found between the points",
|
||||
"timeout": "Route calculation took too long, try adding points closer together"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -190,8 +190,6 @@
|
||||
"from": "起点离最近的道路太远",
|
||||
"via": "途径点离最近的道路太远",
|
||||
"to": "终点离最近的道路太远",
|
||||
"distance": "起点到终点的距离太远",
|
||||
"connection": "找不到经过途径点的路线",
|
||||
"timeout": "轨迹计算耗时太长,请尝试添加更靠近路网的点"
|
||||
}
|
||||
},
|
||||
@@ -284,7 +282,7 @@
|
||||
"update": "更新图层"
|
||||
},
|
||||
"opacity": "图层透明度",
|
||||
"terrain": "地形来源",
|
||||
"terrain": "Terrain source",
|
||||
"label": {
|
||||
"basemaps": "底图",
|
||||
"overlays": "叠加层",
|
||||
@@ -328,7 +326,7 @@
|
||||
"usgs": "USGS",
|
||||
"bikerouterGravel": "bikerouter.de Gravel",
|
||||
"cyclOSMlite": "CyclOSM Lite",
|
||||
"mapterhornHillshade": "山体阴影",
|
||||
"mapterhornHillshade": "Mapterhorn Hillshade",
|
||||
"openRailwayMap": "OpenRailwayMap",
|
||||
"swisstopoSlope": "Swisstopo Slope",
|
||||
"swisstopoHiking": "Swisstopo Hiking",
|
||||
|
||||
Reference in New Issue
Block a user