2 Commits

Author SHA1 Message Date
vcoppe
182ef1a51b New translations en.json (Czech) 2026-01-16 21:44:56 +01:00
vcoppe
06cf04d346 New translations en.json (Dutch) 2026-01-16 20:32:24 +01:00
36 changed files with 230 additions and 277 deletions

View File

@@ -17,6 +17,7 @@
} }
}, },
"sprite": "https://demotiles.maplibre.org/styles/osm-bright-gl-style/sprite", "sprite": "https://demotiles.maplibre.org/styles/osm-bright-gl-style/sprite",
"glyphs": "https://api.maptiler.com/fonts/{fontstack}/{range}.pbf?key={key}",
"layers": [ "layers": [
{ {
"id": "background", "id": "background",

View File

@@ -34,10 +34,11 @@
import { editStyle } from '$lib/components/file-list/style/utils.svelte'; import { editStyle } from '$lib/components/file-list/style/utils.svelte';
import { getSymbolKey, symbols } from '$lib/assets/symbols'; import { getSymbolKey, symbols } from '$lib/assets/symbols';
import { selection, copied, cut } from '$lib/logic/selection'; import { selection, copied, cut } from '$lib/logic/selection';
import { map } from '$lib/components/map/map';
import { fileActions, pasteSelection } from '$lib/logic/file-actions'; import { fileActions, pasteSelection } from '$lib/logic/file-actions';
import { allHidden } from '$lib/logic/hidden'; import { allHidden } from '$lib/logic/hidden';
import { boundsManager } from '$lib/logic/bounds'; 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 { fileStateCollection } from '$lib/logic/file-state';
import { waypointPopup } from '$lib/components/map/gpx-layer/gpx-layer-popup'; import { waypointPopup } from '$lib/components/map/gpx-layer/gpx-layer-popup';
import { allowedPastes } from './sortable-file-list'; import { allowedPastes } from './sortable-file-list';
@@ -57,11 +58,19 @@
let singleSelection = $derived($selection.size === 1); let singleSelection = $derived($selection.size === 1);
let nodeColors: string[] = $derived.by(() => { let nodeColors: string[] = $state([]);
$effect.pre(() => {
let colors: string[] = []; let colors: string[] = [];
if (node) { if (node && $map) {
if (node instanceof GPXFile) { 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); let style = node.getStyle(defaultColor);
colors = style.color; colors = style.color;
} else if (node instanceof Track) { } else if (node instanceof Track) {
@@ -74,14 +83,14 @@
colors.push(style['gpx_style:color']); colors.push(style['gpx_style:color']);
} }
if (colors.length === 0) { if (colors.length === 0) {
let defaultColor = $gpxColors.get(item.getFileId()); let layer = gpxLayers.getLayer(item.getFileId());
if (defaultColor) { if (layer) {
colors.push(defaultColor); colors.push(layer.layerColor);
} }
} }
} }
} }
return colors; nodeColors = colors;
}); });
let symbolKey = $derived(node instanceof Waypoint ? getSymbolKey(node.sym) : undefined); let symbolKey = $derived(node instanceof Waypoint ? getSymbolKey(node.sym) : undefined);

View File

@@ -48,7 +48,7 @@
language = 'en'; language = 'en';
} }
map.init(language, hash, geocoder, geolocate); map.init(PUBLIC_MAPBOX_TOKEN, language, hash, geocoder, geolocate);
}); });
onDestroy(() => { onDestroy(() => {

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { onDestroy } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import { gpxLayers } from '$lib/components/map/gpx-layer/gpx-layers'; import { gpxLayers } from '$lib/components/map/gpx-layer/gpx-layers';
import { DistanceMarkers } from '$lib/components/map/gpx-layer/distance-markers'; import { DistanceMarkers } from '$lib/components/map/gpx-layer/distance-markers';
import { StartEndMarkers } from '$lib/components/map/gpx-layer/start-end-markers'; import { StartEndMarkers } from '$lib/components/map/gpx-layer/start-end-markers';
@@ -9,10 +9,13 @@
let distanceMarkers: DistanceMarkers; let distanceMarkers: DistanceMarkers;
let startEndMarkers: StartEndMarkers; let startEndMarkers: StartEndMarkers;
map.onLoad((map_) => { onMount(() => {
gpxLayers.init(); gpxLayers.init();
startEndMarkers = new StartEndMarkers(); startEndMarkers = new StartEndMarkers();
distanceMarkers = new DistanceMarkers(); distanceMarkers = new DistanceMarkers();
});
map.onLoad((map_) => {
createPopups(map_); createPopups(map_);
}); });

View File

@@ -41,7 +41,6 @@
<Button <Button
size="sm" size="sm"
variant="outline" 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)}`} 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" target="_blank"
> >

View File

@@ -3,7 +3,7 @@ import { gpxStatistics } from '$lib/logic/statistics';
import { getConvertedDistanceToKilometers } from '$lib/units'; import { getConvertedDistanceToKilometers } from '$lib/units';
import type { GeoJSONSource } from 'mapbox-gl'; import type { GeoJSONSource } from 'mapbox-gl';
import { get } from 'svelte/store'; 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'; import { allHidden } from '$lib/logic/hidden';
const { distanceMarkers, distanceUnits } = settings; const { distanceMarkers, distanceUnits } = settings;
@@ -44,8 +44,7 @@ export class DistanceMarkers {
}); });
} }
if (!map_.getLayer('distance-markers')) { if (!map_.getLayer('distance-markers')) {
map_.addLayer( map_.addLayer({
{
id: 'distance-markers', id: 'distance-markers',
type: 'symbol', type: 'symbol',
source: 'distance-markers', source: 'distance-markers',
@@ -80,9 +79,9 @@ export class DistanceMarkers {
'text-halo-width': 2, 'text-halo-width': 2,
'text-halo-color': 'white', 'text-halo-color': 'white',
}, },
}, });
ANCHOR_LAYER_KEY.distanceMarkers } else {
); map_.moveLayer('distance-markers');
} }
} else { } else {
if (map_.getLayer('distance-markers')) { if (map_.getLayer('distance-markers')) {

View File

@@ -1,6 +1,6 @@
import { get, type Readable } from 'svelte/store'; import { get, type Readable } from 'svelte/store';
import mapboxgl, { type FilterSpecification } from 'mapbox-gl'; 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 { waypointPopup, trackpointPopup } from './gpx-layer-popup';
import { import {
ListTrackSegmentItem, ListTrackSegmentItem,
@@ -22,7 +22,6 @@ import { fileActionManager } from '$lib/logic/file-action-manager';
import { fileActions } from '$lib/logic/file-actions'; import { fileActions } from '$lib/logic/file-actions';
import { splitAs } from '$lib/components/toolbar/tools/scissors/scissors'; import { splitAs } from '$lib/components/toolbar/tools/scissors/scissors';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import { gpxColors } from '$lib/components/map/gpx-layer/gpx-layers';
const colors = [ const colors = [
'#ff0000', '#ff0000',
@@ -44,35 +43,16 @@ for (let color of colors) {
} }
// Get the color with the least amount of uses // 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)); let color = colors.reduce((a, b) => (colorCount[a] <= colorCount[b] ? a : b));
colorCount[color]++; colorCount[color]++;
gpxColors.update((colors) => {
colors.set(fileId, color);
return colors;
});
return color; return color;
} }
function replaceColor(fileId: string, oldColor: string, newColor: string) { function decrementColor(color: string) {
if (colorCount.hasOwnProperty(oldColor)) {
colorCount[oldColor]--;
}
colorCount[newColor]++;
gpxColors.update((colors) => {
colors.set(fileId, newColor);
return colors;
});
}
function removeColor(fileId: string, color: string) {
if (colorCount.hasOwnProperty(color)) { if (colorCount.hasOwnProperty(color)) {
colorCount[color]--; colorCount[color]--;
} }
gpxColors.update((colors) => {
colors.delete(fileId);
return colors;
});
} }
export function getSvgForSymbol(symbol?: string | undefined, layerColor?: string | undefined) { export function getSvgForSymbol(symbol?: string | undefined, layerColor?: string | undefined) {
@@ -141,7 +121,7 @@ export class GPXLayer {
constructor(fileId: string, file: Readable<GPXFileWithStatistics | undefined>) { constructor(fileId: string, file: Readable<GPXFileWithStatistics | undefined>) {
this.fileId = fileId; this.fileId = fileId;
this.file = file; this.file = file;
this.layerColor = getColor(fileId); this.layerColor = getColor();
this.unsubscribe.push( this.unsubscribe.push(
map.subscribe(($map) => { map.subscribe(($map) => {
if ($map) { if ($map) {
@@ -178,7 +158,7 @@ export class GPXLayer {
file._data.style.color && file._data.style.color &&
this.layerColor !== `#${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}`; this.layerColor = `#${file._data.style.color}`;
} }
@@ -196,8 +176,7 @@ export class GPXLayer {
} }
if (!_map.getLayer(this.fileId)) { if (!_map.getLayer(this.fileId)) {
_map.addLayer( _map.addLayer({
{
id: this.fileId, id: this.fileId,
type: 'line', type: 'line',
source: this.fileId, source: this.fileId,
@@ -210,9 +189,7 @@ export class GPXLayer {
'line-width': ['get', 'width'], 'line-width': ['get', 'width'],
'line-opacity': ['get', 'opacity'], 'line-opacity': ['get', 'opacity'],
}, },
}, });
ANCHOR_LAYER_KEY.tracks
);
_map.on('click', this.fileId, this.layerOnClickBinded); _map.on('click', this.fileId, this.layerOnClickBinded);
_map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded); _map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded);
@@ -235,8 +212,7 @@ export class GPXLayer {
} }
if (!_map.getLayer(this.fileId + '-waypoints')) { if (!_map.getLayer(this.fileId + '-waypoints')) {
_map.addLayer( _map.addLayer({
{
id: this.fileId + '-waypoints', id: this.fileId + '-waypoints',
type: 'symbol', type: 'symbol',
source: this.fileId + '-waypoints', source: this.fileId + '-waypoints',
@@ -247,9 +223,7 @@ export class GPXLayer {
'icon-padding': 0, 'icon-padding': 0,
'icon-allow-overlap': true, 'icon-allow-overlap': true,
}, },
}, });
ANCHOR_LAYER_KEY.waypoints
);
_map.on( _map.on(
'mouseenter', 'mouseenter',
@@ -298,7 +272,7 @@ export class GPXLayer {
'text-halo-color': 'white', 'text-halo-color': 'white',
}, },
}, },
ANCHOR_LAYER_KEY.directionMarkers _map.getLayer('distance-markers') ? 'distance-markers' : undefined
); );
} }
} else { } else {
@@ -390,7 +364,7 @@ export class GPXLayer {
this.unsubscribe.forEach((unsubscribe) => unsubscribe()); this.unsubscribe.forEach((unsubscribe) => unsubscribe());
removeColor(this.fileId, this.layerColor); decrementColor(this.layerColor);
} }
moveToFront() { moveToFront() {
@@ -399,13 +373,13 @@ export class GPXLayer {
return; return;
} }
if (_map.getLayer(this.fileId)) { if (_map.getLayer(this.fileId)) {
_map.moveLayer(this.fileId, ANCHOR_LAYER_KEY.tracks); _map.moveLayer(this.fileId);
} }
if (_map.getLayer(this.fileId + '-waypoints')) { 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')) { if (_map.getLayer(this.fileId + '-direction')) {
_map.moveLayer(this.fileId + '-direction', ANCHOR_LAYER_KEY.directionMarkers); _map.moveLayer(this.fileId + '-direction');
} }
} }

View File

@@ -1,5 +1,4 @@
import { GPXFileStateCollectionObserver } from '$lib/logic/file-state'; import { GPXFileStateCollectionObserver } from '$lib/logic/file-state';
import { writable } from 'svelte/store';
import { GPXLayer } from './gpx-layer'; import { GPXLayer } from './gpx-layer';
export class GPXLayerCollection { export class GPXLayerCollection {
@@ -43,4 +42,3 @@ export class GPXLayerCollection {
} }
export const gpxLayers = new GPXLayerCollection(); export const gpxLayers = new GPXLayerCollection();
export const gpxColors = writable(new Map<string, string>());

View File

@@ -54,27 +54,28 @@
<Card.Root class="border-none shadow-md text-base p-2 max-w-[50dvw] gap-0"> <Card.Root class="border-none shadow-md text-base p-2 max-w-[50dvw] gap-0">
<Card.Header class="p-0 gap-0"> <Card.Header class="p-0 gap-0">
<Card.Title class="text-md flex flex-row"> <Card.Title class="text-md">
<div class="flex flex-row gap-3">
<div class="flex flex-col"> <div class="flex flex-col">
<p>{name}</p> {name}
<div class="text-muted-foreground text-xs font-normal"> <div class="text-muted-foreground text-xs font-normal">
{poi.item.lat.toFixed(6)}&deg; {poi.item.lon.toFixed(6)}&deg; {poi.item.lat.toFixed(6)}&deg; {poi.item.lon.toFixed(6)}&deg;
</div> </div>
</div> </div>
<Button <Button
class="ml-auto" class="ml-auto"
variant="outline" variant="outline"
size="icon-sm" size="icon"
href="https://www.openstreetmap.org/edit?editor=id&{poi.item.type ?? 'node'}={poi href="https://www.openstreetmap.org/edit?editor=id&{poi.item.type ??
.item.id}" 'node'}={poi.item.id}"
target="_blank" target="_blank"
> >
<PencilLine size="16" /> <PencilLine size="16" />
</Button> </Button>
</div>
</Card.Title> </Card.Title>
</Card.Header> </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]"> <ScrollArea class="flex flex-col max-h-[30dvh]">
{#if tags.image || tags['image:0']} {#if tags.image || tags['image:0']}
<div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto"> <div class="w-full rounded-md overflow-clip my-2 max-w-96 mx-auto">
@@ -99,14 +100,8 @@
{/each} {/each}
</div> </div>
</ScrollArea> </ScrollArea>
<Button <Button class="mt-2" variant="outline" disabled={$selection.size === 0} onclick={addToFile}>
size="sm" <MapPin size="16" />
class="mt-1 justify-start"
variant="outline"
disabled={$selection.size === 0}
onclick={addToFile}
>
<MapPin size="14" />
{i18n._('toolbar.waypoint.add')} {i18n._('toolbar.waypoint.add')}
</Button> </Button>
</Card.Content> </Card.Content>

View File

@@ -6,7 +6,6 @@ import { overpassQueryData } from '$lib/assets/layers';
import { MapPopup } from '$lib/components/map/map-popup'; import { MapPopup } from '$lib/components/map/map-popup';
import { settings } from '$lib/logic/settings'; import { settings } from '$lib/logic/settings';
import { db } from '$lib/db'; import { db } from '$lib/db';
import { ANCHOR_LAYER_KEY } from '$lib/components/map/map';
const { currentOverpassQueries } = settings; const { currentOverpassQueries } = settings;
@@ -86,8 +85,7 @@ export class OverpassLayer {
} }
if (!this.map.getLayer('overpass')) { if (!this.map.getLayer('overpass')) {
this.map.addLayer( this.map.addLayer({
{
id: 'overpass', id: 'overpass',
type: 'symbol', type: 'symbol',
source: 'overpass', source: 'overpass',
@@ -97,9 +95,7 @@ export class OverpassLayer {
'icon-padding': 0, 'icon-padding': 0,
'icon-allow-overlap': ['step', ['zoom'], false, 14, true], 'icon-allow-overlap': ['step', ['zoom'], false, 14, true],
}, },
}, });
ANCHOR_LAYER_KEY.overpass
);
this.map.on('mouseenter', 'overpass', this.onHoverBinded); this.map.on('mouseenter', 'overpass', this.onHoverBinded);
this.map.on('click', 'overpass', this.onHoverBinded); this.map.on('click', 'overpass', this.onHoverBinded);

View File

@@ -20,28 +20,6 @@ let fitBoundsOptions: mapboxgl.MapOptions['fitBoundsOptions'] = {
easing: () => 1, 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 { export class MapboxGLMap {
private _map: Writable<mapboxgl.Map | null> = writable(null); private _map: Writable<mapboxgl.Map | null> = writable(null);
private _onLoadCallbacks: ((map: mapboxgl.Map) => void)[] = []; private _onLoadCallbacks: ((map: mapboxgl.Map) => void)[] = [];
@@ -51,15 +29,19 @@ export class MapboxGLMap {
return this._map.subscribe(run, invalidate); 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({ const map = new mapboxgl.Map({
container: 'map', container: 'map',
style: { style: {
version: 8, version: 8,
sources: { sources: {},
'empty-source': emptySource, layers: [],
},
layers: anchorLayers,
imports: [ imports: [
{ {
id: 'basemap', id: 'basemap',
@@ -68,6 +50,11 @@ export class MapboxGLMap {
{ {
id: 'overlays', id: 'overlays',
url: '', url: '',
data: {
version: 8,
sources: {},
layers: [],
},
}, },
], ],
}, },
@@ -225,7 +212,6 @@ export class MapboxGLMap {
const map = get(this._map); const map = get(this._map);
if (map) { if (map) {
const source = get(terrainSource); const source = get(terrainSource);
try {
if (!map.getSource(source)) { if (!map.getSource(source)) {
map.addSource(source, terrainSources[source]); map.addSource(source, terrainSources[source]);
} }
@@ -237,10 +223,6 @@ export class MapboxGLMap {
} else { } else {
map.setTerrain(null); map.setTerrain(null);
} }
} catch (e) {
// No reliable way to check if the map is ready to add sources and layers
return;
}
} }
} }
} }

View File

@@ -2,7 +2,6 @@ import mapboxgl, { type LayerSpecification, type VectorSourceSpecification } fro
import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module'; import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module';
import 'mapillary-js/dist/mapillary.css'; import 'mapillary-js/dist/mapillary.css';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import { ANCHOR_LAYER_KEY } from '$lib/components/map/map';
const mapillarySource: VectorSourceSpecification = { const mapillarySource: VectorSourceSpecification = {
type: 'vector', type: 'vector',
@@ -100,10 +99,10 @@ export class MapillaryLayer {
this.map.addSource('mapillary', mapillarySource); this.map.addSource('mapillary', mapillarySource);
} }
if (!this.map.getLayer('mapillary-sequence')) { if (!this.map.getLayer('mapillary-sequence')) {
this.map.addLayer(mapillarySequenceLayer, ANCHOR_LAYER_KEY.mapillary); this.map.addLayer(mapillarySequenceLayer);
} }
if (!this.map.getLayer('mapillary-image')) { 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('style.load', this.addBinded);
this.map.on('mouseenter', 'mapillary-image', this.onMouseEnterBinded); this.map.on('mouseenter', 'mapillary-image', this.onMouseEnterBinded);

View File

@@ -15,7 +15,7 @@
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
import { Trash2 } from '@lucide/svelte'; 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 type { GeoJSONSource } from 'mapbox-gl';
import { selection } from '$lib/logic/selection'; import { selection } from '$lib/logic/selection';
import { fileActions } from '$lib/logic/file-actions'; import { fileActions } from '$lib/logic/file-actions';
@@ -63,8 +63,7 @@
}); });
} }
if (!$map.getLayer('rectangle')) { if (!$map.getLayer('rectangle')) {
$map.addLayer( $map.addLayer({
{
id: 'rectangle', id: 'rectangle',
type: 'fill', type: 'fill',
source: 'rectangle', source: 'rectangle',
@@ -72,9 +71,7 @@
'fill-color': 'SteelBlue', 'fill-color': 'SteelBlue',
'fill-opacity': 0.5, 'fill-opacity': 0.5,
}, },
}, });
ANCHOR_LAYER_KEY.interactions
);
} }
} }
} }

View File

@@ -2,6 +2,7 @@
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import Help from '$lib/components/Help.svelte'; import Help from '$lib/components/Help.svelte';
import { MountainSnow } from '@lucide/svelte'; import { MountainSnow } from '@lucide/svelte';
import { map } from '$lib/components/map/map';
import { i18n } from '$lib/i18n.svelte'; import { i18n } from '$lib/i18n.svelte';
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
import { selection } from '$lib/logic/selection'; import { selection } from '$lib/logic/selection';
@@ -19,7 +20,11 @@
variant="outline" variant="outline"
class="whitespace-normal h-fit" class="whitespace-normal h-fit"
disabled={!validSelection} disabled={!validSelection}
onclick={() => fileActions.addElevationToSelection()} onclick={() => {
if ($map) {
fileActions.addElevationToSelection($map);
}
}}
> >
<MountainSnow size="16" class="shrink-0" /> <MountainSnow size="16" class="shrink-0" />
{i18n._('toolbar.elevation.button')} {i18n._('toolbar.elevation.button')}

View File

@@ -1,5 +1,5 @@
import { ListItem, ListTrackSegmentItem } from '$lib/components/file-list/file-list'; 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 { fileActions } from '$lib/logic/file-actions';
import { GPXFileStateCollectionObserver, type GPXFileState } from '$lib/logic/file-state'; import { GPXFileStateCollectionObserver, type GPXFileState } from '$lib/logic/file-state';
import { selection } from '$lib/logic/selection'; import { selection } from '$lib/logic/selection';
@@ -144,8 +144,7 @@ export class ReducedGPXLayerCollection {
}); });
} }
if (!map_.getLayer('simplified')) { if (!map_.getLayer('simplified')) {
map_.addLayer( map_.addLayer({
{
id: 'simplified', id: 'simplified',
type: 'line', type: 'line',
source: 'simplified', source: 'simplified',
@@ -153,9 +152,9 @@ export class ReducedGPXLayerCollection {
'line-color': 'white', 'line-color': 'white',
'line-width': 3, 'line-width': 3,
}, },
}, });
ANCHOR_LAYER_KEY.interactions } else {
); map_.moveLayer('simplified');
} }
} }

View File

@@ -8,7 +8,6 @@ import { get } from 'svelte/store';
import { fileStateCollection } from '$lib/logic/file-state'; import { fileStateCollection } from '$lib/logic/file-state';
import { fileActions } from '$lib/logic/file-actions'; import { fileActions } from '$lib/logic/file-actions';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import { ANCHOR_LAYER_KEY } from '$lib/components/map/map';
export class SplitControls { export class SplitControls {
map: mapboxgl.Map; map: mapboxgl.Map;
@@ -109,8 +108,7 @@ export class SplitControls {
} }
if (!this.map.getLayer('split-controls')) { if (!this.map.getLayer('split-controls')) {
this.map.addLayer( this.map.addLayer({
{
id: 'split-controls', id: 'split-controls',
type: 'symbol', type: 'symbol',
source: 'split-controls', source: 'split-controls',
@@ -120,14 +118,14 @@ export class SplitControls {
'icon-padding': 0, 'icon-padding': 0,
}, },
filter: ['<=', ['get', 'minZoom'], ['zoom']], filter: ['<=', ['get', 'minZoom'], ['zoom']],
}, });
ANCHOR_LAYER_KEY.interactions
);
this.map.on('mouseenter', 'split-controls', this.layerOnMouseEnterBinded); this.map.on('mouseenter', 'split-controls', this.layerOnMouseEnterBinded);
this.map.on('mouseleave', 'split-controls', this.layerOnMouseLeaveBinded); this.map.on('mouseleave', 'split-controls', this.layerOnMouseLeaveBinded);
this.map.on('click', 'split-controls', this.layerOnClickBinded); this.map.on('click', 'split-controls', this.layerOnClickBinded);
} }
this.map.moveLayer('split-controls');
} catch (e) { } catch (e) {
// No reliable way to check if the map is ready to add sources and layers // No reliable way to check if the map is ready to add sources and layers
} }

View File

@@ -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. 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... ### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Exportar...

View File

@@ -1,5 +1,5 @@
Mapbox stellt einige der auf dieser Website verwendeten Karten bereit. 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>, die **gpx.studio** unterstützt. 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. 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.

View File

@@ -35,7 +35,7 @@ Supprimer les fichiers sélectionnés.
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Supprimer tout ### <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... ### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Exporter...

View File

@@ -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. 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. 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. 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. 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 - **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. - **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. If your selection includes it, you can also visualize: **speed**, **heart rate**, **cadence**, **temperature** and **power** data on the elevation profile.

View File

@@ -35,7 +35,7 @@ Delete the currently selected files.
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all ### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
Apagar todos os arquivos. Delete all files.
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Exportar... ### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Exportar...

View File

@@ -2,8 +2,7 @@
import { HeartHandshake } from '@lucide/svelte'; import { HeartHandshake } from '@lucide/svelte';
</script> </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 точки, наші сервери обчислюють найкращий маршрут на мережі доріг. Кожного разу, коли ви додаєте або переміщуєте GPS точки, наші сервери обчислюють найкращий маршрут на мережі доріг.
Ми також використовуємо API від <a href="https://mapbox.com" target="_blank">Mapbox</a> для зображення красивих карт, отримання даних висот та можливості пошуку місць. Ми також використовуємо API від <a href="https://mapbox.com" target="_blank">Mapbox</a> для зображення красивих карт, отримання даних висот та можливості пошуку місць.

View File

@@ -2,7 +2,7 @@
import { Languages } from '@lucide/svelte'; import { Languages } from '@lucide/svelte';
</script> </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>. Ви можете зробити свій внесок, додаючи або покращуючи переклади в нашому <a href="https://crowdin.com/project/gpxstudio" target="_blank">проєкті Crowdin</a>.

View File

@@ -47,6 +47,6 @@ Open the export dialog to save all files to your computer.
<DocsNote type="warning"> <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> </DocsNote>

View File

@@ -31,7 +31,7 @@ Create a copy of the currently selected files.
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete ### <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 ### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all

View File

@@ -807,7 +807,7 @@ export const fileActions = {
}); });
}); });
}, },
addElevationToSelection: async () => { addElevationToSelection: async (map: mapboxgl.Map) => {
if (get(selection).size === 0) { if (get(selection).size === 0) {
return; return;
} }

View File

@@ -28,7 +28,7 @@
"undo": "Desfer", "undo": "Desfer",
"redo": "Refer", "redo": "Refer",
"delete": "Elimina el track", "delete": "Elimina el track",
"delete_all": "Esborra-ho tot", "delete_all": "Delete all",
"select_all": "Seleccionar-ho tot", "select_all": "Seleccionar-ho tot",
"view": "Vista", "view": "Vista",
"elevation_profile": "Perfil delevacions", "elevation_profile": "Perfil delevacions",
@@ -80,7 +80,7 @@
"center": "Centrar", "center": "Centrar",
"open_in": "Obrir amb", "open_in": "Obrir amb",
"copy_coordinates": "Copiar coordenades", "copy_coordinates": "Copiar coordenades",
"edit_osm": "Edita a OpenStreetMap" "edit_osm": "Edit in OpenStreetMap"
}, },
"toolbar": { "toolbar": {
"routing": { "routing": {
@@ -282,7 +282,7 @@
"update": "Actualitza la capa" "update": "Actualitza la capa"
}, },
"opacity": "Opacitat de la superposició", "opacity": "Opacitat de la superposició",
"terrain": "Terreny", "terrain": "Terrain source",
"label": { "label": {
"basemaps": "Mapes base", "basemaps": "Mapes base",
"overlays": "Capes", "overlays": "Capes",
@@ -356,7 +356,7 @@
"water": "Aigua", "water": "Aigua",
"shower": "Dutxa", "shower": "Dutxa",
"shelter": "Refugi", "shelter": "Refugi",
"cemetery": "Cementiri", "cemetery": "Cemetery",
"motorized": "Cotxes i motos", "motorized": "Cotxes i motos",
"fuel-station": "Gasolinera", "fuel-station": "Gasolinera",
"parking": "Aparcament", "parking": "Aparcament",

View File

@@ -21,14 +21,14 @@
"export_all": "Alle exportieren...", "export_all": "Alle exportieren...",
"export_options": "Export-Einstellungen", "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_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_file": "Datei herunterladen",
"download_files": "Dateien herunterladen", "download_files": "Dateien herunterladen",
"edit": "Bearbeiten", "edit": "Bearbeiten",
"undo": "Rückgängig", "undo": "Rückgängig",
"redo": "Wiederholen", "redo": "Wiederholen",
"delete": "Löschen", "delete": "Löschen",
"delete_all": "Alle löschen", "delete_all": "Delete all",
"select_all": "Alle auswählen", "select_all": "Alle auswählen",
"view": "Ansicht", "view": "Ansicht",
"elevation_profile": "Höhenprofil", "elevation_profile": "Höhenprofil",
@@ -80,7 +80,7 @@
"center": "Zentrieren", "center": "Zentrieren",
"open_in": "Öffnen in", "open_in": "Öffnen in",
"copy_coordinates": "Koordinaten kopieren", "copy_coordinates": "Koordinaten kopieren",
"edit_osm": "In OpenStreetMap bearbeiten" "edit_osm": "Edit in OpenStreetMap"
}, },
"toolbar": { "toolbar": {
"routing": { "routing": {
@@ -282,7 +282,7 @@
"update": "Layer aktualisieren" "update": "Layer aktualisieren"
}, },
"opacity": "Deckkraft der Überlagerung", "opacity": "Deckkraft der Überlagerung",
"terrain": "Geländequelle", "terrain": "Terrain source",
"label": { "label": {
"basemaps": "Basiskarte", "basemaps": "Basiskarte",
"overlays": "Ebenen", "overlays": "Ebenen",
@@ -326,7 +326,7 @@
"usgs": "USGS", "usgs": "USGS",
"bikerouterGravel": "bikerouter.de Gravel", "bikerouterGravel": "bikerouter.de Gravel",
"cyclOSMlite": "CyclOSM Lite", "cyclOSMlite": "CyclOSM Lite",
"mapterhornHillshade": "MapTiler Hillshade", "mapterhornHillshade": "Mapterhorn Hillshade",
"openRailwayMap": "OpenRailwayMap", "openRailwayMap": "OpenRailwayMap",
"swisstopoSlope": "swisstopo Neigung", "swisstopoSlope": "swisstopo Neigung",
"swisstopoHiking": "swisstopo Wandern", "swisstopoHiking": "swisstopo Wandern",
@@ -356,7 +356,7 @@
"water": "Trinkwasser", "water": "Trinkwasser",
"shower": "Dusche", "shower": "Dusche",
"shelter": "Unterstand", "shelter": "Unterstand",
"cemetery": "Friedhof", "cemetery": "Cemetery",
"motorized": "Autos und Motorräder", "motorized": "Autos und Motorräder",
"fuel-station": "Tankstelle", "fuel-station": "Tankstelle",
"parking": "Parken", "parking": "Parken",

View File

@@ -282,7 +282,7 @@
"update": "Actualizar capa" "update": "Actualizar capa"
}, },
"opacity": "Opacidad de la capa superpuesta", "opacity": "Opacidad de la capa superpuesta",
"terrain": "Origen del terreno", "terrain": "Terrain source",
"label": { "label": {
"basemaps": "Mapas base", "basemaps": "Mapas base",
"overlays": "Capas", "overlays": "Capas",

View File

@@ -282,7 +282,7 @@
"update": "Eguneratu geruza" "update": "Eguneratu geruza"
}, },
"opacity": "Geruzaren opakutasuna", "opacity": "Geruzaren opakutasuna",
"terrain": "Lurrazala", "terrain": "Terrain source",
"label": { "label": {
"basemaps": "Oinarrizko mapak", "basemaps": "Oinarrizko mapak",
"overlays": "Geruzak", "overlays": "Geruzak",

View File

@@ -235,13 +235,13 @@
"help_no_selection": "Select a file item to request elevation data." "help_no_selection": "Select a file item to request elevation data."
}, },
"waypoint": { "waypoint": {
"tooltip": "PoI létrehozása és módosítása", "tooltip": "Create and edit points of interest",
"icon": "Ikon", "icon": "Icon",
"link": "Link", "link": "Link",
"longitude": "Földrajzi hosszúság", "longitude": "Longitude",
"latitude": "Szélesség", "latitude": "Szélesség",
"create": "POI fájlba mentése", "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": "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." "help_no_selection": "Select a file to create or edit points of interest."
}, },
@@ -249,8 +249,8 @@
"tooltip": "Reduce the number of GPS points", "tooltip": "Reduce the number of GPS points",
"tolerance": "Tűréshatár", "tolerance": "Tűréshatár",
"number_of_points": "GPS pontok száma", "number_of_points": "GPS pontok száma",
"button": "Minimalizálás", "button": "Minify",
"help": "", "help": "Use the slider to choose the number of GPS points to keep.",
"help_no_selection": "Select a trace to reduce the number of its GPS points." "help_no_selection": "Select a trace to reduce the number of its GPS points."
}, },
"clean": { "clean": {

View File

@@ -491,7 +491,7 @@
"support_button": "Support gpx.studio on Ko-fi", "support_button": "Support gpx.studio on Ko-fi",
"route_planning": "Route planning", "route_planning": "Route planning",
"route_planning_description": "An intuitive interface to create itineraries tailored to each sport, based on OpenStreetMap data.", "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.", "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": "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.", "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.",

View File

@@ -54,7 +54,7 @@
"mapillary": "Mapillary", "mapillary": "Mapillary",
"google": "Google", "google": "Google",
"toggle_street_view": "Street view", "toggle_street_view": "Street view",
"layers": "Kaartlagen...", "layers": "Kaart lagen...",
"distance_markers": "Afstandsmarkeringen", "distance_markers": "Afstandsmarkeringen",
"direction_markers": "Richtingspijlen", "direction_markers": "Richtingspijlen",
"help": "Help", "help": "Help",

View File

@@ -28,7 +28,7 @@
"undo": "Angre", "undo": "Angre",
"redo": "Gjenta", "redo": "Gjenta",
"delete": "Slett", "delete": "Slett",
"delete_all": "Slett alle", "delete_all": "Delete all",
"select_all": "Velg alle", "select_all": "Velg alle",
"view": "Visning", "view": "Visning",
"elevation_profile": "Høydeprofil", "elevation_profile": "Høydeprofil",
@@ -80,7 +80,7 @@
"center": "Sentrer", "center": "Sentrer",
"open_in": "Åpne I", "open_in": "Åpne I",
"copy_coordinates": "Kopier koordinater", "copy_coordinates": "Kopier koordinater",
"edit_osm": "Rediger i OpenStreetMap" "edit_osm": "Edit in OpenStreetMap"
}, },
"toolbar": { "toolbar": {
"routing": { "routing": {
@@ -356,7 +356,7 @@
"water": "Vann", "water": "Vann",
"shower": "Dusj", "shower": "Dusj",
"shelter": "Ly", "shelter": "Ly",
"cemetery": "Gravplass", "cemetery": "Cemetery",
"motorized": "Biler og motorsykler", "motorized": "Biler og motorsykler",
"fuel-station": "Bensinstasjon", "fuel-station": "Bensinstasjon",
"parking": "Parkering", "parking": "Parkering",

View File

@@ -282,7 +282,7 @@
"update": "Zaktualizuj warstwę" "update": "Zaktualizuj warstwę"
}, },
"opacity": "Przezroczystość nakładki", "opacity": "Przezroczystość nakładki",
"terrain": "Źródło danych terenowych", "terrain": "Terrain source",
"label": { "label": {
"basemaps": "Mapy bazowe", "basemaps": "Mapy bazowe",
"overlays": "Nakładki", "overlays": "Nakładki",

View File

@@ -282,7 +282,7 @@
"update": "更新图层" "update": "更新图层"
}, },
"opacity": "图层透明度", "opacity": "图层透明度",
"terrain": "地形来源", "terrain": "Terrain source",
"label": { "label": {
"basemaps": "底图", "basemaps": "底图",
"overlays": "叠加层", "overlays": "叠加层",
@@ -326,7 +326,7 @@
"usgs": "USGS", "usgs": "USGS",
"bikerouterGravel": "bikerouter.de Gravel", "bikerouterGravel": "bikerouter.de Gravel",
"cyclOSMlite": "CyclOSM Lite", "cyclOSMlite": "CyclOSM Lite",
"mapterhornHillshade": "山体阴影", "mapterhornHillshade": "Mapterhorn Hillshade",
"openRailwayMap": "OpenRailwayMap", "openRailwayMap": "OpenRailwayMap",
"swisstopoSlope": "Swisstopo Slope", "swisstopoSlope": "Swisstopo Slope",
"swisstopoHiking": "Swisstopo Hiking", "swisstopoHiking": "Swisstopo Hiking",