Files
gpx.studio/website/src/lib/components/toolbar/tools/routing/Routing.ts

160 lines
4.9 KiB
TypeScript
Raw Normal View History

import type { Coordinates } from 'gpx';
import { TrackPoint, distance } from 'gpx';
import { derived, get, writable } from 'svelte/store';
import { settings } from '$lib/db';
import { _, isLoading, locale } from 'svelte-i18n';
import { getElevation } from '$lib/utils';
2024-04-25 16:41:06 +02:00
2024-05-04 15:10:30 +02:00
const { routing, routingProfile, privateRoads } = settings;
2024-04-25 16:41:06 +02:00
export const brouterProfiles: { [key: string]: string } = {
bike: 'Trekking-dry',
racing_bike: 'fastbike',
2024-07-23 19:26:11 +02:00
gravel_bike: 'gravel',
2024-04-25 16:41:06 +02:00
mountain_bike: 'MTB',
foot: 'Hiking-Alpine-SAC6',
motorcycle: 'Car-FastEco',
water: 'river',
railway: 'rail',
2024-04-25 16:41:06 +02:00
};
2024-05-04 15:10:30 +02:00
export const routingProfileSelectItem = writable({
2024-07-09 22:15:05 +02:00
value: '',
label: '',
2024-04-25 16:41:06 +02:00
});
derived([routingProfile, locale, isLoading], ([profile, l, i]) => [profile, l, i]).subscribe(
([profile, l, i]) => {
if (
!i &&
profile !== '' &&
(profile !== get(routingProfileSelectItem).value ||
get(_)(`toolbar.routing.activities.${profile}`) !==
get(routingProfileSelectItem).label) &&
l !== null
) {
routingProfileSelectItem.update((item) => {
item.value = profile;
item.label = get(_)(`toolbar.routing.activities.${profile}`);
return item;
});
}
2024-05-04 15:10:30 +02:00
}
);
2024-05-04 15:10:30 +02:00
routingProfileSelectItem.subscribe((item) => {
if (item.value !== '' && item.value !== get(routingProfile)) {
2024-05-04 15:10:30 +02:00
routingProfile.set(item.value);
}
});
2024-04-25 16:41:06 +02:00
export function route(points: Coordinates[]): Promise<TrackPoint[]> {
if (get(routing)) {
2024-05-04 15:17:44 +02:00
return getRoute(points, brouterProfiles[get(routingProfile)], get(privateRoads));
2024-04-23 18:36:16 +02:00
} else {
2024-05-09 00:02:27 +02:00
return getIntermediatePoints(points);
2024-04-23 18:36:16 +02:00
}
}
async function getRoute(
points: Coordinates[],
brouterProfile: string,
privateRoads: boolean
): Promise<TrackPoint[]> {
2025-03-22 13:38:07 +01:00
let url = `https://routing.gpx.studio?lonlats=${points.map((point) => `${point.lon.toFixed(8)},${point.lat.toFixed(8)}`).join('|')}&profile=${brouterProfile + (privateRoads ? '-private' : '')}&format=geojson&alternativeidx=0`;
2024-04-24 19:32:55 +02:00
let response = await fetch(url);
2024-04-27 11:16:59 +02:00
// Check if the response is ok
if (!response.ok) {
throw new Error(`${await response.text()}`);
}
2024-04-24 19:32:55 +02:00
let geojson = await response.json();
let route: TrackPoint[] = [];
let coordinates = geojson.features[0].geometry.coordinates;
2024-04-26 22:35:42 +02:00
let messages = geojson.features[0].properties.messages;
const lngIdx = messages[0].indexOf('Longitude');
const latIdx = messages[0].indexOf('Latitude');
const tagIdx = messages[0].indexOf('WayTags');
2024-04-26 22:35:42 +02:00
let messageIdx = 1;
let tags = messageIdx < messages.length ? getTags(messages[messageIdx][tagIdx]) : {};
2024-04-26 22:35:42 +02:00
2024-04-24 19:32:55 +02:00
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] ?? (i > 0 ? route[i - 1].ele : 0),
})
);
if (
messageIdx < messages.length &&
2024-04-26 22:35:42 +02:00
coordinates[i][0] == Number(messages[messageIdx][lngIdx]) / 1000000 &&
coordinates[i][1] == Number(messages[messageIdx][latIdx]) / 1000000
) {
2024-04-26 22:35:42 +02:00
messageIdx++;
if (messageIdx == messages.length) tags = {};
else tags = getTags(messages[messageIdx][tagIdx]);
2024-04-26 22:35:42 +02:00
}
2024-09-22 13:05:28 +02:00
route[route.length - 1].setExtensions(tags);
2024-04-24 19:32:55 +02:00
}
return route;
2024-04-26 22:35:42 +02:00
}
function getTags(message: string): { [key: string]: string } {
const fields = message.split(' ');
let tags: { [key: string]: string } = {};
for (let i = 0; i < fields.length; i++) {
let [key, value] = fields[i].split('=');
2024-10-08 12:04:07 +02:00
key = key.replace(/:/g, '_');
tags[key] = value;
2024-04-26 22:35:42 +02:00
}
return tags;
}
2024-05-09 00:02:27 +02:00
function getIntermediatePoints(points: Coordinates[]): Promise<TrackPoint[]> {
let route: TrackPoint[] = [];
let step = 0.05;
for (let i = 0; i < points.length - 1; i++) {
// Add intermediate points between each pair of points
2024-05-09 00:02:27 +02:00
let dist = distance(points[i], points[i + 1]) / 1000;
for (let d = 0; d < dist; d += step) {
let lat = points[i].lat + (d / dist) * (points[i + 1].lat - points[i].lat);
let lon = points[i].lon + (d / dist) * (points[i + 1].lon - points[i].lon);
route.push(
new TrackPoint({
attributes: {
lat: lat,
lon: lon,
},
})
);
2024-05-09 00:02:27 +02:00
}
}
route.push(
new TrackPoint({
attributes: {
lat: points[points.length - 1].lat,
lon: points[points.length - 1].lon,
},
})
);
2024-05-09 00:02:27 +02:00
2024-09-04 19:11:56 +02:00
return getElevation(route).then((elevations) => {
route.forEach((point, i) => {
point.ele = elevations[i];
});
return route;
2024-05-09 00:02:27 +02:00
});
}