diff --git a/gpx/src/gpx.ts b/gpx/src/gpx.ts index 29b19544..f9276b47 100644 --- a/gpx/src/gpx.ts +++ b/gpx/src/gpx.ts @@ -16,6 +16,7 @@ abstract class GPXTreeElement> { abstract computeStatistics(): GPXStatistics; + abstract append(points: TrackPoint[]): void; abstract reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date): void; abstract getStartTimestamp(): Date; @@ -44,6 +45,16 @@ abstract class GPXTreeNode> extends GPXTreeElement return statistics; } + append(points: TrackPoint[]): void { + let children = this.getChildren(); + + if (children.length === 0) { + return; + } + + children[children.length - 1].append(points); + } + reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date): void { const children = this.getChildren(); @@ -130,7 +141,10 @@ export class GPXFiles extends GPXTreeNode { super(); this.files = files; - this.computeStatistics(); + this.statistics = new GPXStatistics(); + for (let file of files) { + this.statistics.mergeWith(file.statistics); + } } getChildren(): GPXFile[] { @@ -357,6 +371,11 @@ export class TrackSegment extends GPXTreeLeaf { return distanceWindowSmoothingWithDistanceAccumulator(points, 50, (accumulated, start, end) => 100 * (points[end].ele - points[start].ele) / accumulated); } + append(points: TrackPoint[]): void { + this.trkpt = this.trkpt.concat(points); + //this.computeStatistics(); + } + reverse(originalNextTimestamp: Date | undefined, newPreviousTimestamp: Date | undefined): void { if (originalNextTimestamp !== undefined && newPreviousTimestamp !== undefined) { let originalEndTimestamp = this.getEndTimestamp(); diff --git a/website/src/lib/components/App.svelte b/website/src/lib/components/App.svelte index 7d5e882a..05a61467 100644 --- a/website/src/lib/components/App.svelte +++ b/website/src/lib/components/App.svelte @@ -1,5 +1,5 @@ {#each $fileCollection.files as file} - + {/each} diff --git a/website/src/lib/components/toolbar/routing/Routing.svelte b/website/src/lib/components/toolbar/routing/Routing.svelte index a0d83edd..f28036f8 100644 --- a/website/src/lib/components/toolbar/routing/Routing.svelte +++ b/website/src/lib/components/toolbar/routing/Routing.svelte @@ -7,8 +7,8 @@ import * as Alert from '$lib/components/ui/alert'; import { CircleHelp } from 'lucide-svelte'; - import { map, selectedFiles } from '$lib/stores'; - import { AnchorPointHierarchy, getMarker } from './routing'; + import { map, selectedFiles, fileCollection } from '$lib/stores'; + import { AnchorPointHierarchy, getMarker, route } from './routing'; import { onDestroy } from 'svelte'; import mapboxgl from 'mapbox-gl'; import KDBush from 'kdbush'; @@ -17,22 +17,23 @@ import { _ } from 'svelte-i18n'; - let routingProfile = { - value: 'bike', - label: 'bike' - }; - let brouterProfiles = { + let brouterProfiles: { [key: string]: string } = { bike: 'Trekking-dry', - racingBike: 'fastbike', - mountainBike: 'MTB', + racing_bike: 'fastbike', + mountain_bike: 'MTB', foot: 'Hiking-Alpine-SAC6', motorcycle: 'Car-FastEco', water: 'river', railway: 'rail' }; + let routingProfile = { + value: 'bike', + label: $_('toolbar.routing.activities.bike') + }; let routing = true; let privateRoads = false; + let anchorPointHierarchy: AnchorPointHierarchy | null = null; let markers: mapboxgl.Marker[] = []; let file: GPXFile | null = null; let kdbush: KDBush | null = null; @@ -50,8 +51,21 @@ } } - function extendFile(e: mapboxgl.MapMouseEvent) { - console.log(e.lngLat); + async function extendFile(e: mapboxgl.MapMouseEvent) { + if (file && anchorPointHierarchy && anchorPointHierarchy.points.length > 0) { + let lastPoint = anchorPointHierarchy.points[anchorPointHierarchy.points.length - 1]; + let newPoint = { + lon: e.lngLat.lng, + lat: e.lngLat.lat + }; + let response = await route( + [lastPoint.point.getCoordinates(), newPoint], + brouterProfiles[routingProfile.value], + privateRoads, + routing + ); + console.log(response); + } } let insertableMarker: mapboxgl.Marker | null = null; @@ -107,12 +121,12 @@ file = $selectedFiles.values().next().value; // record time let start = performance.now(); - let anchorPoints = AnchorPointHierarchy.create(file); + anchorPointHierarchy = AnchorPointHierarchy.create(file); // record time let end = performance.now(); console.log('Time to create anchor points: ' + (end - start) + 'ms'); - markers = anchorPoints.getMarkers(); + markers = anchorPointHierarchy.getMarkers(); toggleMarkersForZoomLevelAndBounds(); $map.on('zoom', toggleMarkersForZoomLevelAndBounds); @@ -150,7 +164,9 @@ {#each Object.keys(brouterProfiles) as profile} - {profile} + {$_(`toolbar.routing.activities.${profile}`)} {/each} diff --git a/website/src/lib/components/toolbar/routing/routing.ts b/website/src/lib/components/toolbar/routing/routing.ts index d7c055ea..ad965de2 100644 --- a/website/src/lib/components/toolbar/routing/routing.ts +++ b/website/src/lib/components/toolbar/routing/routing.ts @@ -1,4 +1,5 @@ -import type { Coordinates, GPXFile, TrackPoint } from "gpx"; +import type { Coordinates, GPXFile } from "gpx"; +import { TrackPoint } from "gpx"; import mapboxgl from "mapbox-gl"; export function getMarker(coordinates: Coordinates, draggable: boolean = false): mapboxgl.Marker { @@ -13,23 +14,18 @@ export function getMarker(coordinates: Coordinates, draggable: boolean = false): export type SimplifiedTrackPoint = { point: TrackPoint, index: number, distance?: number, segment?: number, zoom?: number }; export class AnchorPointHierarchy { - points: SimplifiedTrackPoint[][]; + points: SimplifiedTrackPoint[]; constructor() { this.points = []; - for (let i = 0; i <= 20; i++) { - this.points.push([]); - } } getMarkers(): mapboxgl.Marker[] { let markers = []; - for (let points of this.points) { - for (let point of points) { - let marker = getMarker(point.point.getCoordinates(), true); - Object.defineProperty(marker, '_simplified', { value: point }); - markers.push(marker); - } + for (let point of this.points) { + let marker = getMarker(point.point.getCoordinates(), true); + Object.defineProperty(marker, '_simplified', { value: point }); + markers.push(marker); } return markers; } @@ -46,7 +42,7 @@ export class AnchorPointHierarchy { simplified.forEach((point) => { point.segment = s; point.zoom = getZoomLevelForDistance(point.point.getLatitude(), point.distance); - hierarchy.points[point.zoom].push(point); + hierarchy.points.push(point); }); s++; } @@ -158,19 +154,39 @@ function bearing(latA: number, lonA: number, latB: number, lonB: number): number Math.cos(latA) * Math.sin(latB) - Math.sin(latA) * Math.cos(latB) * Math.cos(lonB - lonA)); } -export function route(points: TrackPoint[], brouterProfile: string, privateRoads: boolean, routing: boolean) { +export function route(points: Coordinates[], brouterProfile: string, privateRoads: boolean, routing: boolean): Promise { if (routing) { - getRoute(points, brouterProfile, privateRoads).then(response => { - return response.json(); - }); + return getRoute(points, brouterProfile, privateRoads); } else { return new Promise((resolve) => { - resolve(points); + resolve(points.map(point => new TrackPoint({ + attributes: { + lat: point.lat, + lon: point.lon + } + }))); }); } } -function getRoute(points: TrackPoint[], brouterProfile: string, privateRoads: boolean): Promise { - let url = `https://routing.gpx.studio?profile=${brouterProfile + privateRoads ? '-private' : ''}&lonlats=${points.map(point => `${point.getLongitude()},${point.getLatitude()}`).join('|')}&format=geojson&alternativeidx=0`; - return fetch(url); +async function getRoute(points: Coordinates[], brouterProfile: string, privateRoads: boolean): Promise { + let url = `https://routing.gpx.studio?lonlats=${points.map(point => `${point.lon},${point.lat}`).join('|')}&profile=${brouterProfile + (privateRoads ? '-private' : '')}&format=geojson&alternativeidx=0`; + + let response = await fetch(url); + let geojson = await response.json(); + + let route: TrackPoint[] = []; + let coordinates = geojson.features[0].geometry.coordinates; + for (let i = 0; i < coordinates.length; i++) { + let coord = coordinates[i]; + route.push(new TrackPoint({ + attributes: { + lat: coord[1], + lon: coord[0] + }, + ele: coord[2] ?? undefined + })); + } + + return route; } \ No newline at end of file diff --git a/website/src/locales/en.json b/website/src/locales/en.json index 6c4c59af..435c1268 100644 --- a/website/src/locales/en.json +++ b/website/src/locales/en.json @@ -33,7 +33,16 @@ "allow_private": "Allow private roads", "help_no_file": "Select a file to use the routing tool, or create a new file from the menu", "help_multiple_files": "Select a single file to use the routing tool", - "help": "Click on the map to add a new point, or drag existing points to change the route" + "help": "Click on the map to add a new point, or drag existing points to change the route", + "activities": { + "bike": "Bike", + "racing_bike": "Racing bike", + "mountain_bike": "Mountain bike", + "foot": "Run/hike", + "motorcycle": "Motorcycle", + "water": "Water", + "railway": "Railway" + } }, "time_tooltip": "Change the time and speed data", "reverse_tooltip": "Reverse the direction",