adds trail reversing

This commit is contained in:
Christian Beutel
2025-07-04 13:53:55 +02:00
parent 247f02c2fc
commit ce9c737ca6
16 changed files with 122 additions and 17 deletions

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { import type {
RoutingOptions, RoutingOptions,
ValhallaBicycleCostingOptions ValhallaBicycleCostingOptions,
} from "$lib/models/valhalla"; } from "$lib/models/valhalla";
import { formatSpeed } from "$lib/util/format_util"; import { formatSpeed } from "$lib/util/format_util";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
@@ -11,9 +11,11 @@
import Toggle from "../base/toggle.svelte"; import Toggle from "../base/toggle.svelte";
interface Props { interface Props {
options: RoutingOptions; options: RoutingOptions;
onReverse: () => void;
onReset: () => void;
} }
let { options = $bindable() }: Props = $props(); let { options = $bindable(), onReverse, onReset }: Props = $props();
const modesOfTransport: SelectItem[] = [ const modesOfTransport: SelectItem[] = [
{ text: $_("hiking"), value: "pedestrian" }, { text: $_("hiking"), value: "pedestrian" },
@@ -59,10 +61,10 @@
} }
$effect(() => { $effect(() => {
if(!options.autoRouting) { if (!options.autoRouting) {
showSettings = false; showSettings = false;
} }
}) });
let showSettings = $state(false); let showSettings = $state(false);
@@ -92,7 +94,7 @@
} }
</script> </script>
<div class="p-4 my-2 rounded-xl bg-background space-y-4"> <div class="px-6 py-4 my-2 rounded-xl bg-background space-y-4">
<Toggle bind:value={options.autoRouting} label={$_("enable-auto-routing")} <Toggle bind:value={options.autoRouting} label={$_("enable-auto-routing")}
></Toggle> ></Toggle>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
@@ -107,10 +109,27 @@
disabled={!options.autoRouting} disabled={!options.autoRouting}
onclick={() => (showSettings = !showSettings)} onclick={() => (showSettings = !showSettings)}
aria-label="Toggle routing settings" aria-label="Toggle routing settings"
><i class="fa fa-cogs" class:text-gray-500={!options.autoRouting}></i></button ><i class="fa fa-cogs" class:text-gray-500={!options.autoRouting}
></i></button
>
</div>
<div class="flex items-center gap-4">
<button
class="btn-icon tooltip"
type="button"
onclick={() => onReverse()}
aria-label="Reverse trail direction"
data-title={$_("reverse-direction")}
><i class="fa fa-arrow-right-arrow-left"></i></button
>
<button
class="btn-icon tooltip"
type="button"
onclick={() => onReset()}
aria-label="Reset route"
data-title={$_("reset")}><i class="fa fa-trash"></i></button
> >
</div> </div>
{#if showSettings} {#if showSettings}
<div in:slide out:slide> <div in:slide out:slide>
{#if options.modeOfTransport === "pedestrian" && options.pedestrianOptions} {#if options.modeOfTransport === "pedestrian" && options.pedestrianOptions}

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Route entfernt aus", "removed-trail-from": "Route entfernt aus",
"removed-trails-from": "Routen entfernt aus", "removed-trails-from": "Routen entfernt aus",
"required": "Pflichtfeld", "required": "Pflichtfeld",
"reset": "",
"reset-password": "Passwort zurücksetzen", "reset-password": "Passwort zurücksetzen",
"reverse-direction": "Reverse direction",
"road": "Straße", "road": "Straße",
"route": "{n, plural, =1 {Route} other {Routen}}", "route": "{n, plural, =1 {Route} other {Routen}}",
"route-point": "Punkt auf Route", "route-point": "Punkt auf Route",
@@ -349,12 +351,12 @@
"upload-gpx": "GPX hochladen", "upload-gpx": "GPX hochladen",
"upload-new-file": "Neue Datei hochladen", "upload-new-file": "Neue Datei hochladen",
"uploaded": "hochgeladen", "uploaded": "hochgeladen",
"use-hills": "", "use-hills": "Hügel einbeziehen",
"use-roads": "Nutze Straßen", "use-roads": "Nutze Straßen",
"username": "Nutzername", "username": "Nutzername",
"view": "Ansehen", "view": "Ansehen",
"visibilty-status": "Sichtbarkeit", "visibilty-status": "Sichtbarkeit",
"walking-speed": "", "walking-speed": "Laufgeschwindigkeit",
"waypoints": "{n, plural, =1 {Wegpunkt} other {Wegpunkte}}", "waypoints": "{n, plural, =1 {Wegpunkt} other {Wegpunkte}}",
"welcome_to": "Willkommen bei", "welcome_to": "Willkommen bei",
"width": "Breite", "width": "Breite",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Removed trail from", "removed-trail-from": "Removed trail from",
"removed-trails-from": "Removed trails from", "removed-trails-from": "Removed trails from",
"required": "Required", "required": "Required",
"reset": "Reset",
"reset-password": "Reset Password", "reset-password": "Reset Password",
"reverse-direction": "",
"road": "Road", "road": "Road",
"route": "{n, plural, =1 {Route} other {Routes}}", "route": "{n, plural, =1 {Route} other {Routes}}",
"route-point": "Route Point", "route-point": "Route Point",
@@ -349,12 +351,12 @@
"upload-gpx": "Upload GPX", "upload-gpx": "Upload GPX",
"upload-new-file": "Upload new file", "upload-new-file": "Upload new file",
"uploaded": "uploaded", "uploaded": "uploaded",
"use-hills": "", "use-hills": "Use hills",
"use-roads": "Use Roads", "use-roads": "Use Roads",
"username": "Username", "username": "Username",
"view": "View", "view": "View",
"visibilty-status": "Visibility status", "visibilty-status": "Visibility status",
"walking-speed": "", "walking-speed": "Walking speed",
"waypoints": "{n, plural, =1 {Waypoint} other {Waypoints}}", "waypoints": "{n, plural, =1 {Waypoint} other {Waypoints}}",
"welcome_to": "Welcome to", "welcome_to": "Welcome to",
"width": "Width", "width": "Width",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Ruta borrada de", "removed-trail-from": "Ruta borrada de",
"removed-trails-from": "Rutas borrada de", "removed-trails-from": "Rutas borrada de",
"required": "Obligatorio", "required": "Obligatorio",
"reset": "",
"reset-password": "Restablecer Contraseña", "reset-password": "Restablecer Contraseña",
"reverse-direction": "",
"road": "Carretera", "road": "Carretera",
"route": "{n, plural, =1 {Route} other {Routes}}", "route": "{n, plural, =1 {Route} other {Routes}}",
"route-point": "Route Point", "route-point": "Route Point",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Removed trail from", "removed-trail-from": "Removed trail from",
"removed-trails-from": "Removed trails from", "removed-trails-from": "Removed trails from",
"required": "Required", "required": "Required",
"reset": "",
"reset-password": "Reset Password", "reset-password": "Reset Password",
"reverse-direction": "",
"road": "Road", "road": "Road",
"route": "{n, plural, =1 {Route} other {Routes}}", "route": "{n, plural, =1 {Route} other {Routes}}",
"route-point": "Route Point", "route-point": "Route Point",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Enlever l'itinéraire de", "removed-trail-from": "Enlever l'itinéraire de",
"removed-trails-from": "Enlever les itinéraires de", "removed-trails-from": "Enlever les itinéraires de",
"required": "Requis", "required": "Requis",
"reset": "",
"reset-password": "Réinitialiser le mot de passe", "reset-password": "Réinitialiser le mot de passe",
"reverse-direction": "",
"road": "Route", "road": "Route",
"route": "{n, plural, =1 {Itinéraire} other {Itinéraires}}", "route": "{n, plural, =1 {Itinéraire} other {Itinéraires}}",
"route-point": "Étape", "route-point": "Étape",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Eltávolított nyomvonal a", "removed-trail-from": "Eltávolított nyomvonal a",
"removed-trails-from": "Eltávolított nyomvonalak a", "removed-trails-from": "Eltávolított nyomvonalak a",
"required": "Kötelező", "required": "Kötelező",
"reset": "",
"reset-password": "Reset Password", "reset-password": "Reset Password",
"reverse-direction": "",
"road": "Road", "road": "Road",
"route": "{n, plural, =1 {Route} other {Routes}}", "route": "{n, plural, =1 {Route} other {Routes}}",
"route-point": "Route Point", "route-point": "Route Point",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Percorso rimosso da", "removed-trail-from": "Percorso rimosso da",
"removed-trails-from": "Percorsi rimosso da", "removed-trails-from": "Percorsi rimosso da",
"required": "Obbligatorio", "required": "Obbligatorio",
"reset": "",
"reset-password": "Ripristinare Password", "reset-password": "Ripristinare Password",
"reverse-direction": "",
"road": "Road", "road": "Road",
"route": "{n, plural, =1 {Route} other {Routes}}", "route": "{n, plural, =1 {Route} other {Routes}}",
"route-point": "Route Point", "route-point": "Route Point",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Route verwijderd van", "removed-trail-from": "Route verwijderd van",
"removed-trails-from": "Routes verwijderd van", "removed-trails-from": "Routes verwijderd van",
"required": "Verplicht", "required": "Verplicht",
"reset": "",
"reset-password": "Wachtwoord opnieuw instellen", "reset-password": "Wachtwoord opnieuw instellen",
"reverse-direction": "",
"road": "Weg", "road": "Weg",
"route": "{n, plural,=1 {Tocht} other {Tochten}}", "route": "{n, plural,=1 {Tocht} other {Tochten}}",
"route-point": "Routepunt", "route-point": "Routepunt",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Usunięto szlak z", "removed-trail-from": "Usunięto szlak z",
"removed-trails-from": "Usunięto szlaki z", "removed-trails-from": "Usunięto szlaki z",
"required": "Wymagane", "required": "Wymagane",
"reset": "",
"reset-password": "Resetuj hasło", "reset-password": "Resetuj hasło",
"reverse-direction": "",
"road": "Road", "road": "Road",
"route": "{n, plural,=1 {Trasa} other {Trasy}}", "route": "{n, plural,=1 {Trasa} other {Trasy}}",
"route-point": "Punkt trasy", "route-point": "Punkt trasy",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Trilha removida de", "removed-trail-from": "Trilha removida de",
"removed-trails-from": "Trilhos removidos de", "removed-trails-from": "Trilhos removidos de",
"required": "Obrigatório", "required": "Obrigatório",
"reset": "",
"reset-password": "Reset Password", "reset-password": "Reset Password",
"reverse-direction": "",
"road": "Road", "road": "Road",
"route": "{n, plural, =1 {Route} other {Routes}}", "route": "{n, plural, =1 {Route} other {Routes}}",
"route-point": "Route Point", "route-point": "Route Point",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "Трек удалён из", "removed-trail-from": "Трек удалён из",
"removed-trails-from": "Removed trails from", "removed-trails-from": "Removed trails from",
"required": "Обязательно", "required": "Обязательно",
"reset": "",
"reset-password": "Сбросить пароль", "reset-password": "Сбросить пароль",
"reverse-direction": "",
"road": "Шоссе", "road": "Шоссе",
"route": "{n, plural, =1 {Маршрут} other {Маршрутов}}", "route": "{n, plural, =1 {Маршрут} other {Маршрутов}}",
"route-point": "Точка маршрута", "route-point": "Точка маршрута",

View File

@@ -278,7 +278,9 @@
"removed-trail-from": "路线已删除自", "removed-trail-from": "路线已删除自",
"removed-trails-from": "路线已删除自", "removed-trails-from": "路线已删除自",
"required": "必填", "required": "必填",
"reset": "",
"reset-password": "重置密码", "reset-password": "重置密码",
"reverse-direction": "",
"road": "Road", "road": "Road",
"route": "{n, plural, =1 {Route} other {Routes}}", "route": "{n, plural, =1 {Route} other {Routes}}",
"route-point": "Route Point", "route-point": "Route Point",

View File

@@ -148,7 +148,7 @@ export default class GPX {
distance: totalDistance, distance: totalDistance,
elevationGain: totalElevationGain, elevationGain: totalElevationGain,
elevationLoss: totalElevationLoss, elevationLoss: totalElevationLoss,
duration: totalDuration, duration: Math.abs(totalDuration),
hash: this.generateMinHash(allPoints) hash: this.generateMinHash(allPoints)
} }
} }

View File

@@ -5,6 +5,8 @@ import Waypoint from "$lib/models/gpx/waypoint";
import { type RoutingOptions, type ValhallaAnchor, type ValhallaHeightResponse, type ValhallaRouteResponse } from "$lib/models/valhalla"; import { type RoutingOptions, type ValhallaAnchor, type ValhallaHeightResponse, type ValhallaRouteResponse } from "$lib/models/valhalla";
import { APIError } from "$lib/util/api_util"; import { APIError } from "$lib/util/api_util";
import { decodePolyline, encodePolyline } from "$lib/util/polyline_util"; import { decodePolyline, encodePolyline } from "$lib/util/polyline_util";
import { get } from "svelte/store";
import { _ } from "svelte-i18n";
const emtpyTrack: Track = { trkseg: [] } const emtpyTrack: Track = { trkseg: [] }
@@ -100,6 +102,49 @@ export function deleteFromRoute(index: number) {
route.features = route.getTotals(); route.features = route.getTotals();
} }
export function reverseRoute() {
for (const trk of route.trk ?? []) {
for (const seg of trk.trkseg ?? []) {
seg.trkpt?.reverse()
}
trk.trkseg?.reverse()
}
route.trk?.reverse()
route.features = route.getTotals();
anchors.reverse();
anchors.forEach((a, i) => {
if (!a.marker) {
return;
}
a.marker.getElement().textContent = "" + (i + 1);
const anchorPopupHeading = a.marker
.getPopup()
._content.getElementsByTagName("h5")[0];
if (anchorPopupHeading) {
anchorPopupHeading.textContent =
get(_)("route-point") + " #" + (i + 1);
}
});
}
export function resetRoute() {
route = new GPX({ trk: [emtpyTrack] });
anchors.forEach((a) => {
if(!a.marker) {
return;
}
a.marker.remove();
})
anchors = []
}
export function normalizeRouteTime() { export function normalizeRouteTime() {
let currentTime = new Date(); let currentTime = new Date();

View File

@@ -42,6 +42,8 @@
editRoute, editRoute,
insertIntoRoute, insertIntoRoute,
normalizeRouteTime, normalizeRouteTime,
resetRoute,
reverseRoute,
route, route,
setRoute, setRoute,
} from "$lib/stores/valhalla_store"; } from "$lib/stores/valhalla_store";
@@ -854,6 +856,18 @@
} }
} }
function reverseTrail() {
reverseRoute();
updateTrailWithRouteData();
}
function resetTrail() {
resetRoute();
updateTrailWithRouteData();
}
function updateTrailWithRouteData() { function updateTrailWithRouteData() {
overwriteGPX = true; overwriteGPX = true;
const totals = route.features; const totals = route.features;
@@ -1120,7 +1134,7 @@
></TextField> ></TextField>
<Datepicker label={$_("date")} bind:value={$formData.date}></Datepicker> <Datepicker label={$_("date")} bind:value={$formData.date}></Datepicker>
<Editor <Editor
extraClasses="min-h-24" extraClasses="min-h-24"
bind:value={$formData.description} bind:value={$formData.description}
label={$_("describe-your-trail")} label={$_("describe-your-trail")}
></Editor> ></Editor>
@@ -1276,9 +1290,12 @@
<div <div
in:scale={{ easing: backInOut }} in:scale={{ easing: backInOut }}
out:scale={{ easing: backInOut }} out:scale={{ easing: backInOut }}
class="absolute top-0 left-16 z-50" class="absolute top-8 left-2 z-50"
> >
<RoutingOptionsPopup bind:options={routingOptions} <RoutingOptionsPopup
bind:options={routingOptions}
onReverse={reverseTrail}
onReset={resetTrail}
></RoutingOptionsPopup> ></RoutingOptionsPopup>
</div> </div>
{/if} {/if}