diff --git a/website/src/lib/components/toolbar/tools/routing/Routing.svelte b/website/src/lib/components/toolbar/tools/routing/Routing.svelte
index 5e581a4b..e3aba95d 100644
--- a/website/src/lib/components/toolbar/tools/routing/Routing.svelte
+++ b/website/src/lib/components/toolbar/tools/routing/Routing.svelte
@@ -3,6 +3,7 @@
import { Switch } from '$lib/components/ui/switch';
import { Label } from '$lib/components/ui/label/index.js';
import { Button } from '$lib/components/ui/button';
+ import * as RadioGroup from '$lib/components/ui/radio-group';
import Help from '$lib/components/Help.svelte';
import ButtonWithTooltip from '$lib/components/ButtonWithTooltip.svelte';
import Tooltip from '$lib/components/Tooltip.svelte';
@@ -19,16 +20,22 @@
RouteOff,
Repeat,
SquareArrowUpLeft,
- SquareArrowOutDownRight
+ SquareArrowOutDownRight,
+ Timer
} from 'lucide-svelte';
- import { map, newGPXFile, routingControls, selectFileWhenLoaded } from '$lib/stores';
+ import {
+ gpxStatistics,
+ map,
+ newGPXFile,
+ routingControls,
+ selectFileWhenLoaded
+ } from '$lib/stores';
import { dbUtils, getFile, getFileIds, settings } from '$lib/db';
import { brouterProfiles, routingProfileSelectItem } from './Routing';
import { _, locale } from 'svelte-i18n';
import { RoutingControls } from './RoutingControls';
- import mapboxgl from 'mapbox-gl';
import { fileObservers } from '$lib/db';
import { slide } from 'svelte/transition';
import { getOrderedSelection, selection } from '$lib/components/file-list/Selection';
@@ -40,6 +47,7 @@
type ListItem
} from '$lib/components/file-list/FileList';
import { flyAndScale, getURLForLanguage, resetCursor, setCrosshairCursor } from '$lib/utils';
+ import { TimestampsMode } from '$lib/types';
import { onDestroy, onMount } from 'svelte';
import { TrackPoint } from 'gpx';
@@ -49,7 +57,7 @@
export let popupElement: HTMLElement | undefined = undefined;
let selectedItem: ListItem | null = null;
- const { privateRoads, routing } = settings;
+ const { privateRoads, routing, timestampsMode } = settings;
$: if ($map && popup && popupElement) {
// remove controls for deleted files
@@ -161,7 +169,7 @@
{/if}
+ {#if $gpxStatistics.global.time.total > 0}
+
+
+
+
+
+
+
+
+
+
+ {/if}
0) {
+ let replacingTime = 0;
+
+ if (get(timestampsMode) === TimestampsMode.PRESERVE_TIMESTAMPS) {
+ this.extendResponseToContiguousAdaptedTimePoints(segment, anchors, response);
+
+ response.forEach((point) => point.time = undefined);
+ startTime = anchors[0].point.time;
+
+ replacingTime = stats.local.time.total[anchors[anchors.length - 1].point._data.index] - stats.local.time.total[anchors[0].point._data.index];
+
+ if (replacingTime > 0) {
+ for (let i = 1; i < anchors.length - 1; i++) {
+ anchors[i].point._data['adapted_time'] = true;
+ }
+ }
+ }
+
let replacingDistance = 0;
for (let i = 1; i < response.length; i++) {
replacingDistance += distance(response[i - 1].getCoordinates(), response[i].getCoordinates()) / 1000;
}
- let replacedDistance = stats.local.distance.moving[anchors[anchors.length - 1].point._data.index] - stats.local.distance.moving[anchors[0].point._data.index];
- let newDistance = stats.global.distance.moving + replacingDistance - replacedDistance;
- let newTime = newDistance / stats.global.speed.moving * 3600;
+ if (get(timestampsMode) === TimestampsMode.PRESERVE_AVERAGE_SPEED || replacingTime === 0) {
+ let replacedDistance = stats.local.distance.moving[anchors[anchors.length - 1].point._data.index] - stats.local.distance.moving[anchors[0].point._data.index];
- let remainingTime = stats.global.time.moving - (stats.local.time.moving[anchors[anchors.length - 1].point._data.index] - stats.local.time.moving[anchors[0].point._data.index]);
- let replacingTime = newTime - remainingTime;
+ let newDistance = stats.global.distance.moving + replacingDistance - replacedDistance;
+ let newTime = newDistance / stats.global.speed.moving * 3600;
- if (replacingTime <= 0) { // Fallback to simple time difference
- replacingTime = stats.local.time.total[anchors[anchors.length - 1].point._data.index] - stats.local.time.total[anchors[0].point._data.index];
+ let remainingTime = stats.global.time.moving - (stats.local.time.moving[anchors[anchors.length - 1].point._data.index] - stats.local.time.moving[anchors[0].point._data.index]);
+ replacingTime = newTime - remainingTime;
+
+ if (replacingTime <= 0) { // Fallback to simple time difference
+ replacingTime = stats.local.time.total[anchors[anchors.length - 1].point._data.index] - stats.local.time.total[anchors[0].point._data.index];
+ }
}
speed = replacingDistance / replacingTime * 3600;
@@ -636,6 +661,41 @@ export class RoutingControls {
return true;
}
+ extendResponseToContiguousAdaptedTimePoints(segment: TrackSegment, anchors: Anchor[], response: TrackPoint[]) {
+ while (anchors[0].point._data.adapted_time) {
+ let previousAnchor = null;
+ for (let i = 0; i < this.anchors.length; i++) {
+ if (this.anchors[i].point._data.index < anchors[0].point._data.index) {
+ previousAnchor = this.anchors[i];
+ } else {
+ break;
+ }
+ }
+ if (previousAnchor === null) {
+ break;
+ } else {
+ response.splice(0, 0, ...segment.trkpt.slice(previousAnchor.point._data.index, anchors[0].point._data.index).map((point) => point.clone()));
+ anchors.splice(0, 0, previousAnchor);
+ }
+ }
+ while (anchors[anchors.length - 1].point._data.adapted_time) {
+ let nextAnchor = null;
+ for (let i = this.anchors.length - 1; i >= 0; i--) {
+ if (this.anchors[i].point._data.index > anchors[anchors.length - 1].point._data.index) {
+ nextAnchor = this.anchors[i];
+ } else {
+ break;
+ }
+ }
+ if (nextAnchor === null) {
+ break;
+ } else {
+ response.push(...segment.trkpt.slice(anchors[anchors.length - 1].point._data.index + 1, nextAnchor.point._data.index + 1).map((point) => point.clone()));
+ anchors.push(nextAnchor);
+ }
+ }
+ }
+
destroy() {
this.remove();
this.unsubscribes.forEach((unsubscribe) => unsubscribe());
diff --git a/website/src/lib/db.ts b/website/src/lib/db.ts
index cebfbc8d..051141a0 100644
--- a/website/src/lib/db.ts
+++ b/website/src/lib/db.ts
@@ -9,6 +9,7 @@ import { ListFileItem, ListItem, ListTrackItem, ListLevel, ListTrackSegmentItem,
import { updateAnchorPoints } from '$lib/components/toolbar/tools/routing/Simplify';
import { SplitType } from '$lib/components/toolbar/tools/scissors/Scissors.svelte';
import { getClosestLinePoint, getElevation } from '$lib/utils';
+import { TimestampsMode } from '$lib/types';
import { browser } from '$app/environment';
enableMapSet();
@@ -90,6 +91,7 @@ export const settings = {
routing: dexieSettingStore('routing', true),
routingProfile: dexieSettingStore('routingProfile', 'bike'),
privateRoads: dexieSettingStore('privateRoads', false),
+ timestampsMode: dexieSettingStore('timestampsMode', TimestampsMode.PRESERVE_AVERAGE_SPEED),
currentBasemap: dexieSettingStore('currentBasemap', defaultBasemap),
previousBasemap: dexieSettingStore('previousBasemap', defaultBasemap),
selectedBasemapTree: dexieSettingStore('selectedBasemapTree', defaultBasemapTree),
diff --git a/website/src/lib/types.ts b/website/src/lib/types.ts
new file mode 100644
index 00000000..2c9f72cf
--- /dev/null
+++ b/website/src/lib/types.ts
@@ -0,0 +1,4 @@
+export enum TimestampsMode {
+ PRESERVE_TIMESTAMPS = 'preserve_timestamps',
+ PRESERVE_AVERAGE_SPEED = 'preserve_average_speed',
+};
\ No newline at end of file
diff --git a/website/src/locales/en.json b/website/src/locales/en.json
index b73f5771..3ca79dda 100644
--- a/website/src/locales/en.json
+++ b/website/src/locales/en.json
@@ -88,6 +88,8 @@
"use_routing": "Routing",
"use_routing_tooltip": "Connect anchor points via road network, or in a straight line if disabled",
"allow_private": "Allow private roads",
+ "preserve_average_speed": "Maintain average speed",
+ "preserve_timestamps": "Adjust timestamps to fill gaps",
"reverse": {
"button": "Reverse",
"tooltip": "Reverse the direction of the route"