From 15f7aea300000722ca9a52cbfb0d21dc9aa56925 Mon Sep 17 00:00:00 2001 From: vcoppe Date: Mon, 21 Oct 2024 22:56:18 +0200 Subject: [PATCH] preserve timestamps option --- .../toolbar/tools/routing/Routing.svelte | 40 ++++++++-- .../toolbar/tools/routing/RoutingControls.ts | 76 +++++++++++++++++-- website/src/lib/db.ts | 2 + website/src/lib/types.ts | 4 + website/src/locales/en.json | 2 + 5 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 website/src/lib/types.ts 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"