23 Commits

Author SHA1 Message Date
vcoppe
17559913fd Merge branch 'dev' 2026-04-19 16:49:41 +02:00
vcoppe
dd9aba3adb fine tuning 2026-04-19 16:49:32 +02:00
vcoppe
55590d68a6 Merge branch 'dev' 2026-04-19 16:34:17 +02:00
vcoppe
bd40fbae74 fix routing controls on mobile 2026-04-19 16:34:06 +02:00
vcoppe
690cbc49cc avoid querying features when dragging 2026-04-19 16:31:50 +02:00
vcoppe
8e9f16c460 Merge branch 'dev' 2026-04-19 14:47:35 +02:00
vcoppe
36b16ddeef catch errors when fetching styles and add fallback one 2026-04-19 14:47:19 +02:00
vcoppe
c60b64f24f Merge branch 'dev' 2026-04-17 22:10:40 +02:00
vcoppe
16b8988fa7 fix layer filtering, must allow unknown intermediary keys 2026-04-17 22:10:30 +02:00
vcoppe
fdb6fe4e52 Merge branch 'dev' 2026-04-17 20:11:25 +02:00
Pablo Ovelleiro Corral
40f97b7c35 Fix: overlays bikerouterGravel, cyclOSMlite, mapterhornHillshade, openRailwayMap cannot be toggled in Layer settings (#329) 2026-04-17 20:07:51 +02:00
vcoppe
54b3113480 New Crowdin updates (#326)
* New translations en.json (Spanish)

* New translations merge.mdx (Spanish)

* New translations elevation.mdx (Spanish)

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Spanish)

* New translations integration.mdx (Spanish)

* New translations merge.mdx (Spanish)

* New translations integration.mdx (Spanish)

* New translations map-controls.mdx (Spanish)
2026-04-17 20:00:51 +02:00
vcoppe
0bf168e67e Merge branch 'dev' 2026-04-09 21:10:56 +02:00
vcoppe
7e9140492a fix max zoom 2026-04-09 21:10:45 +02:00
vcoppe
d762a45eb9 Merge branch 'dev' 2026-04-09 20:57:30 +02:00
vcoppe
79c0aed54f New Crowdin updates (#323)
* New translations en.json (Basque)

* New translations en.json (Dutch)

* New translations en.json (Catalan)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Belarusian)

* New translations en.json (Czech)

* New translations en.json (Danish)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Korean)

* New translations en.json (Lithuanian)

* New translations en.json (Norwegian)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Vietnamese)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Indonesian)

* New translations en.json (Thai)

* New translations en.json (Latvian)

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Serbian (Latin))
2026-04-09 20:57:02 +02:00
vcoppe
cb5a74de00 add esri satellite 2026-04-09 19:53:53 +02:00
vcoppe
31f25f346a New Crowdin updates (#322)
* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations files-and-stats.mdx (Ukrainian)

* New translations getting-started.mdx (Ukrainian)

* New translations edit.mdx (Ukrainian)

* New translations view.mdx (Ukrainian)

* New translations en.json (Basque)
2026-04-09 19:33:42 +02:00
vcoppe
548ab9a459 Merge branch 'dev' 2026-04-07 22:19:15 +02:00
vcoppe
b3a11125a5 adapt temporary anchor layer 2026-04-07 22:19:03 +02:00
vcoppe
71cdc03da5 file specific routing controls layers 2026-04-07 22:15:17 +02:00
vcoppe
315c1f6a61 Merge branch 'dev' 2026-04-07 22:02:14 +02:00
vcoppe
694e73a677 reinit shadcn-svelte, fixes #318 2026-04-07 22:01:58 +02:00
53 changed files with 421 additions and 222 deletions

View File

@@ -1,17 +1,20 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"style": "default",
"tailwind": {
"css": "src/app.css",
"baseColor": "slate"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks",
"lib": "$lib"
},
"typescript": true,
"registry": "https://shadcn-svelte.com/registry"
"$schema": "https://shadcn-svelte.com/schema.json",
"tailwind": {
"css": "src/app.css",
"baseColor": "neutral"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks",
"lib": "$lib"
},
"typescript": true,
"registry": "https://shadcn-svelte.com/registry",
"style": "nova",
"iconLibrary": "lucide",
"menuColor": "default",
"menuAccent": "subtle"
}

View File

@@ -14,7 +14,6 @@
"@maplibre/maplibre-gl-geocoder": "^1.9.4",
"chart.js": "^4.5.1",
"chartjs-plugin-zoom": "^2.2.0",
"clsx": "^2.1.1",
"dexie": "^4.0.11",
"file-saver": "^2.0.5",
"gpx": "file:../gpx",
@@ -23,12 +22,12 @@
"mapillary-js": "^4.1.2",
"maplibre-gl": "^5.21.1",
"sanitize-html": "^2.17.0",
"sortablejs": "^1.15.6",
"tailwind-merge": "^3.3.0"
"sortablejs": "^1.15.6"
},
"devDependencies": {
"@fontsource-variable/inter": "^5.2.8",
"@internationalized/date": "^3.12.0",
"@lucide/svelte": "^0.544.0",
"@lucide/svelte": "^1.7.0",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/enhanced-img": "^0.6.0",
"@sveltejs/kit": "^2.21.2",
@@ -45,6 +44,7 @@
"@typescript-eslint/eslint-plugin": "^8.33.1",
"@typescript-eslint/parser": "^8.33.1",
"bits-ui": "^2.17.2",
"clsx": "^2.1.1",
"eslint": "^9.28.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-svelte": "^3.9.1",
@@ -57,15 +57,17 @@
"postcss": "^8.4.47",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0",
"shadcn-svelte": "^1.2.7",
"svelte": "^5.33.18",
"svelte-check": "^4.0.0",
"svelte-dnd-action": "^0.9.65",
"svelte-sonner": "^1.0.5",
"tailwind-variants": "^3.1.1",
"svelte-sonner": "^1.1.0",
"tailwind-merge": "^3.5.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.8",
"tslib": "^2.8.1",
"tsx": "^4.19.1",
"tw-animate-css": "^1.3.4",
"tw-animate-css": "^1.4.0",
"typescript": "^5.8.3",
"vaul-svelte": "^1.0.0-next.7",
"vite": "^6.3.5"
@@ -2567,6 +2569,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/@fontsource-variable/inter": {
"version": "5.2.8",
"resolved": "https://registry.npmjs.org/@fontsource-variable/inter/-/inter-5.2.8.tgz",
"integrity": "sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==",
"dev": true,
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"dev": true,
@@ -3214,9 +3226,9 @@
"license": "MIT"
},
"node_modules/@lucide/svelte": {
"version": "0.544.0",
"resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-0.544.0.tgz",
"integrity": "sha512-9f9O6uxng2pLB01sxNySHduJN3HTl5p0HDu4H26VR51vhZfiMzyOMe9Mhof3XAk4l813eTtl+/DYRvGyoRR+yw==",
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.7.0.tgz",
"integrity": "sha512-YytBKOUBGox7yWcykZnYxOkn5WpR5G1qYXLYXV/j1B79SOTTEKzB+s5yF5Rq9l9OkweDStNH2b4yTqfvhEhV8g==",
"dev": true,
"license": "ISC",
"peerDependencies": {
@@ -4726,6 +4738,9 @@
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -4751,6 +4766,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/commander": {
"version": "14.0.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -6348,6 +6373,13 @@
"version": "0.2.2",
"license": "MIT"
},
"node_modules/node-fetch-native": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
"integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
"dev": true,
"license": "MIT"
},
"node_modules/optionator": {
"version": "0.9.4",
"dev": true,
@@ -6958,6 +6990,25 @@
"version": "1.0.5",
"license": "MIT"
},
"node_modules/shadcn-svelte": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/shadcn-svelte/-/shadcn-svelte-1.2.7.tgz",
"integrity": "sha512-mWuQk4H4gtV+J2wJQ7nEPKNnB/v86AALFryZU0SSM7ChHmJJMZ1kH+qIuxYKrXm9vOOOcSWHRsWzPDB71DnjYA==",
"dev": true,
"license": "MIT",
"dependencies": {
"commander": "^14.0.0",
"node-fetch-native": "^1.6.4",
"postcss": "^8.5.5",
"tailwind-merge": "^3.0.0"
},
"bin": {
"shadcn-svelte": "dist/index.mjs"
},
"peerDependencies": {
"svelte": "^5.0.0"
}
},
"node_modules/sharp": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
@@ -7235,6 +7286,8 @@
},
"node_modules/svelte-sonner": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.1.0.tgz",
"integrity": "sha512-3lYM6ZIqWe+p9vwwWHGWP/ZdvHiUtzURsud2quIxivrX4rvpXh6i+geBGn0m3JS6KwW6W8VgbOl3xQMcDuh6gg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7285,6 +7338,9 @@
},
"node_modules/tailwind-merge": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz",
"integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==",
"dev": true,
"license": "MIT",
"funding": {
"type": "github",
@@ -7293,6 +7349,8 @@
},
"node_modules/tailwind-variants": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz",
"integrity": "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -7392,6 +7450,8 @@
},
"node_modules/tw-animate-css": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
"integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==",
"dev": true,
"license": "MIT",
"funding": {

View File

@@ -14,8 +14,9 @@
"format": "prettier --write . --config ../.prettierrc --ignore-path ../.prettierignore --ignore-path ./.gitignore"
},
"devDependencies": {
"@fontsource-variable/inter": "^5.2.8",
"@internationalized/date": "^3.12.0",
"@lucide/svelte": "^0.544.0",
"@lucide/svelte": "^1.7.0",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/enhanced-img": "^0.6.0",
"@sveltejs/kit": "^2.21.2",
@@ -32,6 +33,7 @@
"@typescript-eslint/eslint-plugin": "^8.33.1",
"@typescript-eslint/parser": "^8.33.1",
"bits-ui": "^2.17.2",
"clsx": "^2.1.1",
"eslint": "^9.28.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-svelte": "^3.9.1",
@@ -44,15 +46,17 @@
"postcss": "^8.4.47",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0",
"shadcn-svelte": "^1.2.7",
"svelte": "^5.33.18",
"svelte-check": "^4.0.0",
"svelte-dnd-action": "^0.9.65",
"svelte-sonner": "^1.0.5",
"tailwind-variants": "^3.1.1",
"svelte-sonner": "^1.1.0",
"tailwind-merge": "^3.5.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.8",
"tslib": "^2.8.1",
"tsx": "^4.19.1",
"tw-animate-css": "^1.3.4",
"tw-animate-css": "^1.4.0",
"typescript": "^5.8.3",
"vaul-svelte": "^1.0.0-next.7",
"vite": "^6.3.5"
@@ -65,7 +69,6 @@
"@maplibre/maplibre-gl-geocoder": "^1.9.4",
"chart.js": "^4.5.1",
"chartjs-plugin-zoom": "^2.2.0",
"clsx": "^2.1.1",
"dexie": "^4.0.11",
"file-saver": "^2.0.5",
"gpx": "file:../gpx",
@@ -74,7 +77,6 @@
"mapillary-js": "^4.1.2",
"maplibre-gl": "^5.21.1",
"sanitize-html": "^2.17.0",
"sortablejs": "^1.15.6",
"tailwind-merge": "^3.3.0"
"sortablejs": "^1.15.6"
}
}

View File

@@ -1,76 +1,93 @@
@import 'tailwindcss';
@import 'tw-animate-css';
@import "shadcn-svelte/tailwind.css";
@import "@fontsource-variable/inter";
@custom-variant dark (&:is(.dark *));
:root {
--background: hsl(0 0% 100%) /* <- Wrap in HSL */;
--foreground: hsl(240 10% 3.9%);
--muted: hsl(240 4.8% 95.9%);
--muted-foreground: hsl(240 3.8% 46.1%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(240 10% 3.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(240 10% 3.9%);
--border: hsl(240 5.9% 90%);
--input: hsl(240 5.9% 90%);
--primary: hsl(240 5.9% 10%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(240 4.8% 95.9%);
--secondary-foreground: hsl(240 5.9% 10%);
--accent: hsl(240 4.8% 95.9%);
--accent-foreground: hsl(240 5.9% 10%);
--destructive: hsl(0 72.2% 50.6%);
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: hsl(0 0% 98%);
--ring: hsl(240 10% 3.9%);
--sidebar: hsl(0 0% 98%);
--sidebar-foreground: hsl(240 5.3% 26.1%);
--sidebar-primary: hsl(240 5.9% 10%);
--sidebar-primary-foreground: hsl(0 0% 98%);
--sidebar-accent: hsl(240 4.8% 95.9%);
--sidebar-accent-foreground: hsl(240 5.9% 10%);
--sidebar-border: hsl(220 13% 91%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
--ring: oklch(0.708 0 0);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--support: rgb(220 15 130);
--link: rgb(0 110 180);
--selection: hsl(240 4.8% 93%);
--radius: 0.5rem;
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
}
.dark {
--background: hsl(240 10% 3.9%);
--foreground: hsl(0 0% 98%);
--muted: hsl(240 3.7% 15.9%);
--muted-foreground: hsl(240 5% 64.9%);
--popover: hsl(240 10% 3.9%);
--popover-foreground: hsl(0 0% 98%);
--card: hsl(240 10% 3.9%);
--card-foreground: hsl(0 0% 98%);
--border: hsl(240 3.7% 15.9%);
--input: hsl(240 3.7% 15.9%);
--primary: hsl(0 0% 98%);
--primary-foreground: hsl(240 5.9% 10%);
--secondary: hsl(240 3.7% 15.9%);
--secondary-foreground: hsl(0 0% 98%);
--accent: hsl(240 3.7% 15.9%);
--accent-foreground: hsl(0 0% 98%);
--destructive: hsl(0 62.8% 30.6%);
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: hsl(0 0% 98%);
--ring: hsl(240 4.9% 83.9%);
--sidebar: hsl(240 5.9% 10%);
--sidebar-foreground: hsl(240 4.8% 95.9%);
--sidebar-primary: hsl(224.3 76.3% 48%);
--sidebar-primary-foreground: hsl(0 0% 100%);
--sidebar-accent: hsl(240 3.7% 15.9%);
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
--sidebar-border: hsl(240 3.7% 15.9%);
--sidebar-ring: hsl(217.2 91.2% 59.8%);
--ring: oklch(0.556 0 0);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
--support: rgb(255 110 190);
--link: rgb(80 190 255);
--selection: hsl(240 3.7% 22%);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
}
@theme inline {
@@ -113,14 +130,35 @@
--color-link: var(--link);
--breakpoint-xs: 540px;
--font-sans: 'Inter Variable', sans-serif;
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
}
@layer base {
* {
@apply border-border;
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
html {
@apply font-sans;
}
}

View File

@@ -35,6 +35,28 @@ export const basemaps: { [key: string]: string | StyleSpecification } = {
maptilerTopo: `https://api.maptiler.com/maps/topo-v4/style.json?key=${maptilerKeyPlaceHolder}`,
maptilerOutdoors: `https://api.maptiler.com/maps/outdoor-v4/style.json?key=${maptilerKeyPlaceHolder}`,
maptilerSatellite: `https://api.maptiler.com/maps/hybrid-v4/style.json?key=${maptilerKeyPlaceHolder}`,
esriSatellite: {
version: 8,
sources: {
esriSatellite: {
type: 'raster',
tiles: [
'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/WMTS/tile/1.0.0/World_Imagery/default/default028mm/{z}/{y}/{x}.jpg',
],
tileSize: 256,
maxzoom: 19,
attribution:
'© <a href="https://www.esri.com/" target="_blank">Esri</a>, Vantor, Earthstar Geographics, and the GIS User Community',
},
},
layers: [
{
id: 'esriSatellite',
type: 'raster',
source: 'esriSatellite',
},
],
},
openStreetMap: {
version: 8,
sources: {
@@ -781,6 +803,7 @@ export const basemapTree: LayerTreeType = {
maptilerTopo: true,
maptilerOutdoors: true,
maptilerSatellite: true,
esriSatellite: true,
openStreetMap: true,
openTopoMap: true,
openHikingMap: true,
@@ -1006,6 +1029,7 @@ export const defaultBasemapTree: LayerTreeType = {
maptilerTopo: true,
maptilerOutdoors: true,
maptilerSatellite: true,
esriSatellite: false,
openStreetMap: true,
openTopoMap: true,
openHikingMap: true,

View File

@@ -14,12 +14,12 @@
} = $props();
</script>
<div class="text-sm bg-secondary rounded border flex flex-row items-center p-2 {className}">
<div class="text-[13px] bg-secondary rounded border flex flex-row items-center p-2 {className}">
<CircleQuestionMark size="16" class="w-4 mr-2 shrink-0 grow-0" />
<div>
{@render children()}
{#if link}
<a href={link} target="_blank" class="text-sm text-link hover:underline">
<a href={link} target="_blank" class="text-[13px] text-link hover:underline">
{i18n._('menu.more')}
</a>
{/if}

View File

@@ -142,6 +142,7 @@ export class MapLayerEventManager {
}
private _handleMouseMove(e: maplibregl.MapMouseEvent) {
if (e.originalEvent.buttons > 0) return;
const featuresByLayer = this._getRenderedFeaturesByLayer(e);
Object.keys(this._listeners).forEach((layerId) => {
const features = featuresByLayer[layerId] || [];

View File

@@ -81,8 +81,13 @@ export class StyleManager {
let basemap = get(currentBasemap);
const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap];
const basemapStyle = await this.get(basemapInfo);
let basemapStyle = basemaps.openStreetMap as maplibregl.StyleSpecification;
try {
basemapStyle = await this.get(basemapInfo);
} catch (e) {
console.error(e.message);
}
this.merge(style, basemapStyle);
if (this._maptilerKey !== '') {
@@ -109,45 +114,52 @@ export class StyleManager {
if (!layers[overlay]) {
if (this._pastOverlays.has(overlay)) {
const overlayInfo = custom[overlay]?.value ?? overlays[overlay];
const overlayStyle = await this.get(overlayInfo);
for (let layer of overlayStyle.layers ?? []) {
if (map_.getLayer(layer.id)) {
map_.removeLayer(layer.id);
try {
const overlayStyle = await this.get(overlayInfo);
for (let layer of overlayStyle.layers ?? []) {
if (map_.getLayer(layer.id)) {
map_.removeLayer(layer.id);
}
}
} catch (e) {
// Should not happen
}
this._pastOverlays.delete(overlay);
}
} else {
const overlayInfo = custom[overlay]?.value ?? overlays[overlay];
const overlayStyle = await this.get(overlayInfo);
const opacity = overlayOpacities[overlay];
try {
const overlayStyle = await this.get(overlayInfo);
const opacity = overlayOpacities[overlay];
for (let sourceId in overlayStyle.sources) {
if (!map_.getSource(sourceId)) {
map_.addSource(sourceId, overlayStyle.sources[sourceId]);
}
}
for (let layer of overlayStyle.layers ?? []) {
if (!map_.getLayer(layer.id)) {
if (opacity !== undefined) {
if (layer.type === 'raster') {
if (!layer.paint) {
layer.paint = {};
}
layer.paint['raster-opacity'] = opacity;
} else if (layer.type === 'hillshade') {
if (!layer.paint) {
layer.paint = {};
}
layer.paint['hillshade-exaggeration'] = opacity / 2;
}
for (let sourceId in overlayStyle.sources) {
if (!map_.getSource(sourceId)) {
map_.addSource(sourceId, overlayStyle.sources[sourceId]);
}
map_.addLayer(layer, ANCHOR_LAYER_KEY.overlays);
}
}
this._pastOverlays.add(overlay);
for (let layer of overlayStyle.layers ?? []) {
if (!map_.getLayer(layer.id)) {
if (opacity !== undefined) {
if (layer.type === 'raster') {
if (!layer.paint) {
layer.paint = {};
}
layer.paint['raster-opacity'] = opacity;
} else if (layer.type === 'hillshade') {
if (!layer.paint) {
layer.paint = {};
}
layer.paint['hillshade-exaggeration'] = opacity / 2;
}
}
map_.addLayer(layer, ANCHOR_LAYER_KEY.overlays);
}
}
this._pastOverlays.add(overlay);
} catch (e) {
console.error(e.message);
}
}
}
} catch (e) {}
@@ -181,6 +193,9 @@ export class StyleManager {
styleUrl = styleUrl.replace(maptilerKeyPlaceHolder, this._maptilerKey);
}
const response = await fetch(styleUrl, { cache: 'force-cache' });
if (!response.ok) {
throw new Error(`HTTP error fetching style "${styleInfo}": ${response.status}`);
}
const style = await response.json();
return style;
} else {

View File

@@ -57,8 +57,10 @@ export class RoutingControls {
updateControlsBinded: () => void = this.updateControls.bind(this);
appendAnchorBinded: (e: MapMouseEvent) => void = this.appendAnchor.bind(this);
addIntermediateAnchorBinded: (e: MapMouseEvent) => void = this.addIntermediateAnchor.bind(this);
draggedAnchorIndex: number | null = null;
lastDraggedAnchorEventTime: number = 0;
draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0);
onMouseEnterBinded: () => void = this.onMouseEnter.bind(this);
onMouseLeaveBinded: () => void = this.onMouseLeave.bind(this);
@@ -85,7 +87,7 @@ export class RoutingControls {
this.file = file;
for (let zoom = MIN_ANCHOR_ZOOM; zoom <= MAX_ANCHOR_ZOOM; zoom++) {
this.layers.set(zoom, {
id: `routing-controls-${zoom}`,
id: `routing-controls-${this.fileId}-${zoom}`,
anchors: [],
});
}
@@ -133,6 +135,7 @@ export class RoutingControls {
map_.on('style.load', this.updateControlsBinded);
map_.on('click', this.appendAnchorBinded);
layerEventManager.on('mousemove', this.fileId, this.showTemporaryAnchorBinded);
layerEventManager.on('click', this.fileId, this.addIntermediateAnchorBinded);
this.fileUnsubscribe = this.file.subscribe(this.updateControlsBinded);
}
@@ -237,6 +240,7 @@ export class RoutingControls {
map_?.off('style.load', this.updateControlsBinded);
map_?.off('click', this.appendAnchorBinded);
layerEventManager?.off('mousemove', this.fileId, this.showTemporaryAnchorBinded);
layerEventManager?.off('click', this.fileId, this.addIntermediateAnchorBinded);
map_?.off('mousemove', this.updateTemporaryAnchorBinded);
this.layers.forEach((layer) => {
@@ -521,12 +525,19 @@ export class RoutingControls {
if (get(streetViewEnabled) && get(streetViewSource) === 'google') {
return;
}
if (
this.draggedAnchorIndex !== null ||
Date.now() - this.lastDraggedAnchorEventTime < 100
) {
// Exit if anchor is being dragged
return;
}
if (
e.target.queryRenderedFeatures(e.point, {
layers: [...this.layers.values()].map((layer) => layer.id),
layers: [this.fileId, ...[...this.layers.values()].map((layer) => layer.id)],
}).length
) {
// Clicked on routing control, ignoring
// Clicked on routing control or layer, ignoring
return;
}
this.appendAnchorWithCoordinates({
@@ -598,6 +609,15 @@ export class RoutingControls {
await this.routeBetweenAnchors([lastAnchor, newAnchor], [lastAnchorPoint, newAnchorPoint]);
}
addIntermediateAnchor(e: maplibregl.MapMouseEvent) {
e.preventDefault();
if (this.temporaryAnchor !== null) {
this.turnIntoPermanentAnchor();
return;
}
}
getNeighbouringAnchors(anchor: Anchor): [Anchor | null, Anchor | null] {
let previousAnchor: Anchor | null = null;
let nextAnchor: Anchor | null = null;
@@ -818,8 +838,11 @@ export class RoutingControls {
onClick(e: MapLayerMouseEvent) {
e.preventDefault();
if (this.temporaryAnchor !== null) {
this.turnIntoPermanentAnchor();
if (
this.draggedAnchorIndex !== null ||
Date.now() - this.lastDraggedAnchorEventTime < 100
) {
// Exit if anchor is being dragged
return;
}
@@ -908,6 +931,8 @@ export class RoutingControls {
lat: e.lngLat.lat,
lon: e.lngLat.lng,
});
this.lastDraggedAnchorEventTime = Date.now();
}
onMouseUp(e: MapLayerMouseEvent | MapLayerTouchEvent) {
@@ -946,6 +971,7 @@ export class RoutingControls {
}
this.draggedAnchorIndex = null;
this.lastDraggedAnchorEventTime = Date.now();
}
showTemporaryAnchor(e: MapLayerMouseEvent) {
@@ -1073,7 +1099,9 @@ export class RoutingControls {
if (!this.temporaryAnchor) {
return;
}
let source = get(map)?.getSource('routing-controls-0') as GeoJSONSource | undefined;
let source = get(map)?.getSource(`routing-controls-${this.fileId}-0`) as
| GeoJSONSource
| undefined;
if (source) {
if (this.temporaryAnchor) {
source.updateData({
@@ -1088,7 +1116,9 @@ export class RoutingControls {
return;
}
const map_ = get(map);
let source = map_?.getSource('routing-controls-0') as GeoJSONSource | undefined;
let source = map_?.getSource(`routing-controls-${this.fileId}-0`) as
| GeoJSONSource
| undefined;
if (source) {
if (this.temporaryAnchor) {
source.updateData({

View File

@@ -31,13 +31,13 @@ get along, so we shut typescript up by casting `value` to `never`.
data-slot="slider-track"
data-orientation={orientation}
class={cn(
"h-1.5 bg-muted rounded-full data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1 bg-muted relative grow overflow-hidden data-horizontal:w-full data-vertical:h-full"
"bg-muted rounded-full data-horizontal:h-1.5 data-horizontal:w-full data-vertical:h-full data-vertical:w-1 bg-muted relative grow overflow-hidden data-horizontal:w-full data-vertical:h-full"
)}
>
<SliderPrimitive.Range
data-slot="slider-range"
class={cn(
"h-full bg-black absolute select-none data-horizontal:h-full data-vertical:w-full"
"bg-primary absolute select-none data-horizontal:h-full data-vertical:w-full"
)}
/>
</span>

View File

@@ -13,8 +13,8 @@ Puede usar **gpx.studio** para crear mapas que muestren sus archivos GPX e integ
Todo lo que necesita es:
1. GPX files hosted on your server or on Google Drive, or accessible via a public URL;
2. _Optional:_ a <a href="https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/" target="_blank">MapTiler key</a> to load MapTiler maps.
1. Archivos GPX alojados en su servidor o en Google Drive, o accesibles a través de una URL pública;
2. _Opcional:_ una <a href="https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/" target="_blank">Tecla MapTiler</a> para cargar mapas MapTiler.
Luego puede jugar con el configurador de abajo para personalizar su mapa y generar el código HTML correspondiente.

View File

@@ -69,4 +69,4 @@ Pueden activarse en la [configuración de capas del mapa](./menu/settings).
En estos ajustes, también puede administrar la opacidad de las capas superpuestas.
For advanced users, it is possible to add custom basemaps and overlays by providing <a href="https://en.wikipedia.org/wiki/Web_Map_Tile_Service" target="_blank">WMTS</a>, <a href="https://en.wikipedia.org/wiki/Web_Map_Service" target="_blank">WMS</a>, or <a href="https://maplibre.org/maplibre-style-spec/" target="_blank">MapLibre style JSON</a> URLs.
Para los usuarios avanzados, es posible añadir mapas base y superposiciones personalizadas proporcionando <a href="https://en.wikipedia.org/wiki/Web_Map_Tile_Service" target="_blank">WMTS</a>, <a href="https://en.wikipedia.org/wiki/Web_Map_Service" target="_blank">WMS</a> o URLs <a href="https://docs.mapbox.com/help/glossary/style/" target="_blank">JSON estilo Mapbox</a>.

View File

@@ -10,7 +10,7 @@ title: Desnivel
# <MountainSnow size="24" class="inline-block" style="margin-bottom: 5px" /> { title }
Le permite añadir datos de desnivel a trazas y [puntos de interés](../gpx), o reemplazar los datos existentes.
Esta herramienta permite añadir datos de elevación a los rastros y [puntos de interés](../gpx), o reemplazar los datos existentes.
<div class="flex flex-row justify-center">
<Elevation class="text-foreground p-3 border rounded-md shadow-lg" />
@@ -18,7 +18,7 @@ Le permite añadir datos de desnivel a trazas y [puntos de interés](../gpx), o
<DocsNote>
Elevation data is provided by <a href="https://maptiler.com" target="_blank">MapTiler</a>.
You can learn more about its origin and accuracy in the <a href="https://docs.maptiler.com/guides/map-tiling-hosting/data-hosting/rgb-terrain-by-maptiler/" target="_blank">documentation</a>.
Los datos de notificación son proporcionados por <a href="https://maptiler.com" target="_blank">MapTiler</a>.
Puedes aprender más sobre su origen y precisión en la <a href="https://docs.maptiler.com/guides/map-tiling-hosting/data-hosting/rgb-terrain-by-maptiler/" target="_blank">documentación</a>.
</DocsNote>

View File

@@ -18,8 +18,8 @@ Para usar esta herramienta, necesita [seleccionar](../files-and-stats) múltiple
<DocsNote>
Selected items are merged in the order they appear in the files list.
Reorder items by drag-and-drop if needed.
Los elementos seleccionados se combinan en el orden en que aparecen en la lista de archivos.
Si es necesario, puede reordenar los elementos arrastrando y soltando.
</DocsNote>

View File

@@ -1,5 +1,5 @@
---
title: Files and statistics
title: Файли та статистика
---
<script lang="ts">
@@ -19,41 +19,41 @@ title: Files and statistics
# { title }
## File list
## Список файлів
Once you have [opened](./menu/file) files, they will be shown as tabs in the file list located at the bottom of the map.
You can reorder them by dragging and dropping the tabs.
And when many files are open, you can scroll through the list of tabs to navigate between them.
Після того як ви [відкриєте](./menu/file) файли, вони будуть показуватися у вигляді вкладок у списку файлів, розташованому внизу карти.
Ви можете змінити їх порядок, перетягуючи вкладки.
А коли відкрито багато файлів, ви можете прокручувати список вкладок, щоб переходити між ними.
<DocsNote>
When using a mouse, you need to hold <kbd>Shift</kbd> to scroll horizontally.
Під час роботи з мишею для горизонтальної прокрутки потрібно утримувати клавіші <kbd>Shift</kbd>.
</DocsNote>
### File selection
### Вибір файлу
By clicking on a tab, you can switch between the files to inspect their statistics, and apply [edit actions](./menu/edit) and [tools](./toolbar) to them.
By holding the <kbd>Ctrl/Cmd</kbd> key, you can add files to the selection or remove them, and by holding <kbd>Shift</kbd>, you can select a range of files.
Most of the [edit actions](./menu/edit) and [tools](./toolbar) can be applied to multiple files at once.
Натиснувши на вкладку, ви можете перемикатися між файлами, щоб переглянути їхні статистичні дані, а також застосовувати до них [дії редагування](./menu/edit) та [інструменти](./toolbar).
Утримуючи клавішу <kbd>Ctrl/Cmd</kbd>, ви можете додавати файли до виділення або видаляти їх, а утримуючи клавішу <kbd>Shift</kbd>, ви можете виділити діапазон файлів.
Більшість [операцій редагування](./menu/edit) та [інструментів](./toolbar) можна застосовувати одразу до декількох файлів.
<DocsNote>
You can also navigate through the files using the arrow keys on your keyboard, and use <kbd>Shift</kbd> to add files to the selection.
Ви також можете переходити між файлами за допомогою клавіш зі стрілками на клавіатурі, а також натискати <kbd>Shift</kbd>, щоб додати файли до виділення.
</DocsNote>
### Edit actions
### Редагувати дії
By right-clicking on a file tab, you can access the same actions as in the [edit menu](./menu/edit).
Клацнувши правою кнопкою миші на вкладці файлу, ви отримаєте доступ до тих самих дій, що й у [меню редагування](./menu/edit).
### Tree layout
### Структура дерева
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.
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.
Як зазначено в [розділі налаштувань перегляду](./menu/view), ви можете перейти до деревоподібного показу списку файлів.
Ця схема ідеально підходить для роботи з великою кількістю відкритих файлів, оскільки вони розміщуються у вигляді вертикального списку в правій частині екрана.
Крім того, у дереві файлів можна переглядати [треки, сегменти та цікаві місця](./gpx), що містяться у файлах, за допомогою розгортальних розділів.
You can also apply [edit actions](./menu/edit) and [tools](./toolbar) to internal file items.
Ви також можете застосовувати [дії редагування](./menu/edit) та [інструменти](./toolbar) до елементів внутрішніх файлів.
Furthermore, you can drag and drop the inner items to reorder them, or move them in the hierarchy or even to another file.
<DocsNote>

View File

@@ -21,7 +21,7 @@ Before we dive into the details of each section, let's have a quick overview of
At the top of the interface, you will find the [main menu](./menu).
This is where you can access common actions such as opening, closing, and exporting files, undoing and redoing actions, and adjusting the application settings.
## Files and statistics
## Файли та статистика
At the bottom of the interface, you will find the list of files currently open in the application.
You can click on a file to select it and display its statistics below the list.

View File

@@ -1,5 +1,5 @@
---
title: Edit actions
title: Редагувати дії
---
<script lang="ts">

View File

@@ -18,7 +18,7 @@ Hide the elevation profile to make room for the map, or show it to inspect the c
### <ListTree size="16" class="inline-block" style="margin-bottom: 2px" /> File tree
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.
Ця схема ідеально підходить для роботи з великою кількістю відкритих файлів, оскільки вони розміщуються у вигляді вертикального списку в правій частині екрана.
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" /> Switch to previous basemap

View File

@@ -162,38 +162,33 @@ function getLayerValidator(allowed: Record<string, any>, fallback: string) {
function filterLayerTree(t: LayerTreeType, allowed: LayerTreeType | undefined): LayerTreeType {
const filtered: LayerTreeType = {};
const values = Object.values(t);
if (values.length == 0) return filtered;
if (typeof values[0] === 'boolean') {
if (allowed) {
Object.keys(allowed).forEach((key) => {
if (Object.hasOwn(t, key)) {
if (allowed) {
Object.entries(allowed).forEach(([key, value]) => {
if (Object.hasOwn(t, key)) {
if (typeof value === 'boolean') {
filtered[key] = t[key];
} else {
filtered[key] = allowed[key];
} else if (typeof value === 'object') {
filtered[key] = filterLayerTree(
typeof t[key] === 'object' ? t[key] : {},
value
);
}
});
}
Object.entries(t).forEach(([key, value]) => {
if (
!Object.hasOwn(filtered, key) &&
(key.startsWith('custom-') || key.startsWith('extension-'))
) {
} else {
filtered[key] = value;
}
});
} else {
Object.entries(t).forEach(([key, value]) => {
if (typeof value === 'object') {
filtered[key] = filterLayerTree(
value,
typeof allowed === 'object' && typeof allowed[key] === 'object'
? allowed[key]
: undefined
);
}
});
}
Object.entries(t).forEach(([key, value]) => {
if (!Object.hasOwn(filtered, key)) {
if (typeof value === 'boolean') {
if (key.startsWith('custom-') || key.startsWith('extension-')) {
filtered[key] = value;
}
} else if (typeof value === 'object') {
filtered[key] = filterLayerTree(value, undefined);
}
}
});
return filtered;
}

View File

@@ -1,4 +1,4 @@
import { type ClassValue, clsx } from 'clsx';
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { base } from '$app/paths';
import { languages } from '$lib/languages';
@@ -18,9 +18,7 @@ export type WithoutChild<T> = T extends { child?: any } ? Omit<T, 'child'> : T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, 'children'> : T;
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & {
ref?: U | null;
};
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null };
export function getClosestLinePoint(
points: TrackPoint[],

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -2,10 +2,10 @@
"metadata": {
"home_title": "el editor online de archivos GPX",
"app_title": "app",
"embed_title": " editor online de archivos GPX",
"embed_title": "Editor online de archivos GPX",
"help_title": "ayuda",
"404_title": "página no encontrada",
"description": "Mira, edita y crea archivos GPX online con planificación avanzada de rutas y herramientas de procesamiento de archivos, bonitos mapas y visualizaciones detalladas de datos."
"description": "Visualiza, edita y crea archivos GPX online con planificación avanzada de rutas y herramientas de procesamiento de archivos, bonitos mapas y visualizaciones detalladas de datos."
},
"menu": {
"new": "Nuevo",
@@ -234,7 +234,7 @@
},
"elevation": {
"button": "Solicitar datos de desnivel",
"help": "Requesting elevation data will erase the existing elevation data, if any, and replace it with data from MapTiler.",
"help": "La solicitud de datos de desnivel borrará los datos de desnivel existentes, si los hay, y los reemplazará con datos de Mapbox.",
"help_no_selection": "Seleccione un elemento del archivo para solicitar datos de desnivel."
},
"waypoint": {
@@ -276,7 +276,7 @@
"new": "Nueva capa personalizada",
"edit": "Editar capa personalizada",
"urls": "URL(s)",
"url_placeholder": "WMTS, WMS or MapLibre style JSON",
"url_placeholder": "WMTS, WMS o JSON estilo Mapbox",
"max_zoom": "Zoom máximo",
"layer_type": "Tipo de capa",
"basemap": "Mapa base",
@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",
@@ -493,7 +494,7 @@
"email": "Email",
"contribute": "Contribuir",
"supported_by": "con el apoyo de",
"features": "Features",
"features": "Características",
"route_planning": "Planificación de ruta",
"route_planning_description": "Una interfaz intuitiva para crear itinerarios adaptados a cada deporte, basada en datos de OpenStreetMap.",
"file_processing": "Procesamiento avanzado de archivo",
@@ -502,15 +503,15 @@
"maps_description": "Una gran colección de mapas base, capas y puntos de interés para ayudarle a crear su próxima aventura al aire libre o visualizar su último logro.",
"data_visualization": "Visualización de datos",
"data_visualization_description": "Un perfil de elevación interactivo con estadísticas detalladas para analizar actividades registradas y futuros objetivos.",
"philosophy": "Philosophy",
"foss": "Free, ad-free and open source",
"foss_description": "The website is free to use, without ads, and the source code is publicly available on GitHub.",
"privacy": "Privacy-friendly",
"philosophy": "Filosofía",
"foss": "Gratis, sin anuncios y código abierto",
"foss_description": "El sitio web es de uso gratuito, sin anuncios, y el código fuente está disponible públicamente en GitHub.",
"privacy": "Respetuosa con la privacidad",
"privacy_description": "Tus archivos GPX nunca abandonan tu navegador. Sin seguimiento, sin recopilación de datos.",
"community": "Made possible by the community",
"community": "Posible gracias a la comunidad",
"community_description": "gpx.studio tiene una comunidad asombrosa que ha cubierto sus costes a través de donaciones durante años, mientras ha dado forma al proyecto a través de sugerencias de características, informes de fallos y traducciones a muchos idiomas.",
"support_button": "Apoya a gpx.studio en Open Collective",
"translate_button": "Help translate the website on Crowdin"
"translate_button": "Ayuda a traducir el sitio web en Crowdin"
},
"docs": {
"translate": "Mejorar la traducción en Crowdin",
@@ -535,7 +536,7 @@
},
"embedding": {
"title": "Crear su propio mapa",
"maptiler_key": "MapTiler key (optional, only required for MapTiler maps)",
"maptiler_key": "Clave MapTiler (opcional, sólo se requiere para mapas MapTiler)",
"file_urls": "URLs de archivo (separados por comas)",
"drive_ids": "IDs de archivo de Google Drive (separados por comas)",
"basemap": "Mapa base",

View File

@@ -4,7 +4,7 @@
"app_title": "aplikazioa",
"embed_title": "lineako GPX fitxategi editorea",
"help_title": "laguntza",
"404_title": "Ez da orrialdea aurkitu",
"404_title": "ez da orrialdea aurkitu",
"description": "Ikusi, editatu eta sortu GPX fitxategiak bideak bilatzeko aukera aurreratuarekin eta fitxategiak prozesatzeko tresnekin, mapa ederrekin eta datuak ikusteko xehetasun handiarekin."
},
"menu": {
@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "Túraútvonal jelzések",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "오픈스트리트맵",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Buiten",
"maptilerSatellite": "MapTiler satelliet",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -28,7 +28,7 @@
"undo": "Скасувати",
"redo": "Повторити",
"delete": "Видалити",
"delete_all": "Delete all",
"delete_all": "Видалити все",
"select_all": "Вибрати все",
"view": "Вигляд",
"elevation_profile": "Профіль рельєфу",
@@ -63,7 +63,7 @@
"ctrl": "Ctrl",
"click": "Клац",
"drag": "Перетягти",
"right_click_drag": "Right-click drag",
"right_click_drag": "Перетягування правою кнопкою миші",
"metadata": {
"button": "Інформація...",
"name": "Назва",
@@ -81,7 +81,7 @@
"center": "Центр",
"open_in": "Відкрити в",
"copy_coordinates": "Копіювати координати",
"edit_osm": "Edit in OpenStreetMap"
"edit_osm": "Редагувати в OpenStreetMap"
},
"toolbar": {
"routing": {
@@ -191,8 +191,8 @@
"from": "Початкова точка знаходиться занадто далеко від найближчої дороги",
"via": "Проміжна точка знаходиться занадто далеко від найближчої дороги",
"to": "Кінцева точка знаходиться занадто далеко від найближчої дороги",
"distance": "The end point is too far from the start point",
"connection": "No connection found between the points",
"distance": "Кінцева точка знаходиться занадто далеко від початкової точки",
"connection": "Зв'язку між точками не виявлено",
"timeout": "Розрахунок маршруту є занадто довгий, спробуйте додати ближчі точки"
}
},
@@ -234,7 +234,7 @@
},
"elevation": {
"button": "Запит даних висот",
"help": "Requesting elevation data will erase the existing elevation data, if any, and replace it with data from MapTiler.",
"help": "Запит даних про висоту призведе до видалення наявних даних про висоту (якщо такі є) та їх заміни даними з MapTiler.",
"help_no_selection": "Виберіть елемент файлу, щоб запросити дані про висоту."
},
"waypoint": {
@@ -276,7 +276,7 @@
"new": "Новий користувацький шар",
"edit": "Редагувати користувацький шар",
"urls": "URL-адреса(и)",
"url_placeholder": "WMTS, WMS or MapLibre style JSON",
"url_placeholder": "JSON у форматі WMTS, WMS або MapLibre",
"max_zoom": "Максимальний зум",
"layer_type": "Тип шару",
"basemap": "Базова карта",
@@ -285,7 +285,7 @@
"update": "Оновити шар"
},
"opacity": "Непрозорість накладання",
"terrain": "Terrain source",
"terrain": "Джерело інформації про місцевість",
"label": {
"basemaps": "Базові карти",
"overlays": "Накладання",
@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",
@@ -331,7 +332,7 @@
"usgs": "USGS",
"bikerouterGravel": "bikerouter.de Gravel",
"cyclOSMlite": "CyclOSM Lite",
"mapterhornHillshade": "Mapterhorn Hillshade",
"mapterhornHillshade": "Рельєфна карта Маптерхорна",
"openRailwayMap": "OpenRailwayMap",
"swisstopoSlope": "swisstopo Slope",
"swisstopoHiking": "swisstopo Hiking",
@@ -361,7 +362,7 @@
"water": "Вода",
"shower": "Душ",
"shelter": "Укриття",
"cemetery": "Cemetery",
"cemetery": "Кладовище",
"motorized": "Автомобілі та Мотоцикли",
"fuel-station": "Паливна станція",
"parking": "Парковка",
@@ -493,7 +494,7 @@
"email": "Електронна пошта",
"contribute": "Зробити внесок",
"supported_by": "за підтримки",
"features": "Features",
"features": "Об'єкти",
"route_planning": "Планування маршруту",
"route_planning_description": "Інтуїтивно зрозумілий інтерфейс для створення маршрутів, адаптованих під кожен вид спорту, на основі даних OpenStreetMap.",
"file_processing": "Розширена обробка файлу",
@@ -502,15 +503,15 @@
"maps_description": "Велика колекція базових карт, шарів та точок інтересу, щоб допомогти вам спланувати наступну outdoor-пригоду або візуалізувати своє останнє досягнення.",
"data_visualization": "Візуалізація даних",
"data_visualization_description": "Інтерактивний профіль висот з детальною статистикою для аналізу записаних активностей та майбутніх цілей.",
"philosophy": "Philosophy",
"foss": "Free, ad-free and open source",
"foss_description": "The website is free to use, without ads, and the source code is publicly available on GitHub.",
"privacy": "Privacy-friendly",
"privacy_description": "Your GPX files never leave your browser. No tracking, no data collection.",
"community": "Made possible by the community",
"community_description": "gpx.studio has an amazing community that has covered its costs through donations for years, while shaping the project through feature suggestions, bug reports, and translations into many languages.",
"support_button": "Support gpx.studio on Open Collective",
"translate_button": "Help translate the website on Crowdin"
"philosophy": "Філософія",
"foss": "Безкоштовно, без реклами та з відкритим кодом",
"foss_description": "Користування вебсайт є безкоштовним, він не містить реклами, а його вихідний код оприлюднено на GitHub.",
"privacy": "З повагою до конфіденційності",
"privacy_description": "Ваші файли GPX ніколи не залишають меж вашого браузера. Жодного відстеження, жодного збору даних.",
"community": "Завдяки підтримці спільноти",
"community_description": "gpx.studio має чудову спільноту, яка вже багато років покриває витрати проєкту коштом пожертв, а також сприяє його розвитку, пропонуючи нові функції, повідомляючи про помилки та перекладаючи сайт на багато мов.",
"support_button": "Підтримайте gpx.studio на Open Collective",
"translate_button": "Допоможіть перекласти вебсайт на Crowdin"
},
"docs": {
"translate": "Покращити переклад на Crowdin",
@@ -535,7 +536,7 @@
},
"embedding": {
"title": "Створити власну карту",
"maptiler_key": "MapTiler key (optional, only required for MapTiler maps)",
"maptiler_key": "Ключ MapTiler (не обов’язково, потрібен лише для карт MapTiler)",
"file_urls": "URL-адреси файлів (розділені комами)",
"drive_ids": "Ідентифікатори файлів Google Drive (розділені комами)",
"basemap": "Базова карта",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -16,9 +16,9 @@
"duplicate": "Duplicate",
"copy": "Copy",
"paste": "Paste",
"cut": "Cut",
"export": "Export...",
"export_all": "Export all...",
"cut": "剪下",
"export": "匯出……",
"export_all": "匯出所有……",
"export_options": "Export options",
"support_message": "The tool is free to use, but not free to run. Please consider supporting the website if you use it frequently. Thank you!",
"support_button": "Help keep the website free",
@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",

View File

@@ -307,6 +307,7 @@
"maptilerTopo": "MapTiler Topo",
"maptilerOutdoors": "MapTiler Outdoors",
"maptilerSatellite": "MapTiler Satellite",
"esriSatellite": "Esri Satellite",
"openStreetMap": "OpenStreetMap",
"openTopoMap": "OpenTopoMap",
"openHikingMap": "OpenHikingMap",