mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-05 01:42:54 +00:00
routing controls class
This commit is contained in:
@@ -1,162 +1,27 @@
|
||||
import type { Coordinates, GPXFile } from "gpx";
|
||||
import type { Coordinates } from "gpx";
|
||||
import { TrackPoint } from "gpx";
|
||||
import mapboxgl from "mapbox-gl";
|
||||
import { get, writable } from "svelte/store";
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export function getMarker(coordinates: Coordinates, draggable: boolean = false): mapboxgl.Marker {
|
||||
let element = document.createElement('div');
|
||||
element.className = `h-3 w-3 rounded-full bg-background border-2 border-black cursor-pointer`;
|
||||
return new mapboxgl.Marker({
|
||||
draggable,
|
||||
element
|
||||
}).setLngLat(coordinates);
|
||||
}
|
||||
export const brouterProfiles: { [key: string]: string } = {
|
||||
bike: 'Trekking-dry',
|
||||
racing_bike: 'fastbike',
|
||||
mountain_bike: 'MTB',
|
||||
foot: 'Hiking-Alpine-SAC6',
|
||||
motorcycle: 'Car-FastEco',
|
||||
water: 'river',
|
||||
railway: 'rail'
|
||||
};
|
||||
export const routingProfile = writable({
|
||||
value: 'bike',
|
||||
label: get(_)('toolbar.routing.activities.bike')
|
||||
});
|
||||
export const routing = writable(true);
|
||||
export const privateRoads = writable(false);
|
||||
|
||||
export type SimplifiedTrackPoint = { point: TrackPoint, index: number, distance?: number, segment?: number, zoom?: number };
|
||||
|
||||
export class AnchorPointHierarchy {
|
||||
points: SimplifiedTrackPoint[];
|
||||
|
||||
constructor() {
|
||||
this.points = [];
|
||||
}
|
||||
|
||||
getMarkers(): mapboxgl.Marker[] {
|
||||
let markers = [];
|
||||
for (let point of this.points) {
|
||||
let marker = getMarker(point.point.getCoordinates(), true);
|
||||
Object.defineProperty(marker, '_simplified', { value: point });
|
||||
markers.push(marker);
|
||||
}
|
||||
return markers;
|
||||
}
|
||||
|
||||
static create(file: GPXFile, epsilon: number = 50): AnchorPointHierarchy {
|
||||
let hierarchy = new AnchorPointHierarchy();
|
||||
|
||||
let s = 0;
|
||||
for (let track of file.getChildren()) {
|
||||
for (let segment of track.getChildren()) {
|
||||
let points = segment.trkpt;
|
||||
let simplified = ramerDouglasPeucker(points, epsilon);
|
||||
// Assign segment number to each point
|
||||
simplified.forEach((point) => {
|
||||
point.segment = s;
|
||||
point.zoom = getZoomLevelForDistance(point.point.getLatitude(), point.distance);
|
||||
hierarchy.points.push(point);
|
||||
});
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
return hierarchy;
|
||||
}
|
||||
}
|
||||
|
||||
function getZoomLevelForDistance(latitude: number, distance?: number): number {
|
||||
if (distance === undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const rad = Math.PI / 180;
|
||||
const lat = latitude * rad;
|
||||
|
||||
return Math.min(20, Math.max(0, Math.floor(Math.log2((earthRadius * Math.cos(lat)) / distance))));
|
||||
}
|
||||
|
||||
function ramerDouglasPeucker(points: TrackPoint[], epsilon: number, start: number = 0, end: number = points.length - 1): SimplifiedTrackPoint[] {
|
||||
let simplified = [{
|
||||
point: points[start],
|
||||
index: start,
|
||||
|
||||
}];
|
||||
ramerDouglasPeuckerRecursive(points, epsilon, start, end, simplified);
|
||||
simplified.push({
|
||||
point: points[end],
|
||||
index: end
|
||||
});
|
||||
return simplified;
|
||||
}
|
||||
|
||||
function ramerDouglasPeuckerRecursive(points: TrackPoint[], epsilon: number, start: number, end: number, simplified: SimplifiedTrackPoint[]) {
|
||||
let largest = {
|
||||
index: 0,
|
||||
distance: 0
|
||||
};
|
||||
|
||||
for (let i = start + 1; i < end; i++) {
|
||||
let distance = crossarc(points[start].getCoordinates(), points[end].getCoordinates(), points[i].getCoordinates());
|
||||
if (distance > largest.distance) {
|
||||
largest.index = i;
|
||||
largest.distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (largest.distance > epsilon) {
|
||||
ramerDouglasPeuckerRecursive(points, epsilon, start, largest.index, simplified);
|
||||
simplified.push({ point: points[largest.index], index: largest.index, distance: largest.distance });
|
||||
ramerDouglasPeuckerRecursive(points, epsilon, largest.index, end, simplified);
|
||||
}
|
||||
}
|
||||
|
||||
const earthRadius = 6371008.8;
|
||||
|
||||
function crossarc(coord1: Coordinates, coord2: Coordinates, coord3: Coordinates): number {
|
||||
// Calculates the shortest distance in meters
|
||||
// between an arc (defined by p1 and p2) and a third point, p3.
|
||||
// Input lat1,lon1,lat2,lon2,lat3,lon3 in degrees.
|
||||
|
||||
const rad = Math.PI / 180;
|
||||
const lat1 = coord1.lat * rad;
|
||||
const lat2 = coord2.lat * rad;
|
||||
const lat3 = coord3.lat * rad;
|
||||
|
||||
const lon1 = coord1.lon * rad;
|
||||
const lon2 = coord2.lon * rad;
|
||||
const lon3 = coord3.lon * rad;
|
||||
|
||||
// Prerequisites for the formulas
|
||||
const bear12 = bearing(lat1, lon1, lat2, lon2);
|
||||
const bear13 = bearing(lat1, lon1, lat3, lon3);
|
||||
let dis13 = distance(lat1, lon1, lat3, lon3);
|
||||
|
||||
let diff = Math.abs(bear13 - bear12);
|
||||
if (diff > Math.PI) {
|
||||
diff = 2 * Math.PI - diff;
|
||||
}
|
||||
|
||||
// Is relative bearing obtuse?
|
||||
if (diff > (Math.PI / 2)) {
|
||||
return dis13;
|
||||
}
|
||||
|
||||
// Find the cross-track distance.
|
||||
let dxt = Math.asin(Math.sin(dis13 / earthRadius) * Math.sin(bear13 - bear12)) * earthRadius;
|
||||
|
||||
// Is p4 beyond the arc?
|
||||
let dis12 = distance(lat1, lon1, lat2, lon2);
|
||||
let dis14 = Math.acos(Math.cos(dis13 / earthRadius) / Math.cos(dxt / earthRadius)) * earthRadius;
|
||||
if (dis14 > dis12) {
|
||||
return distance(lat2, lon2, lat3, lon3);
|
||||
} else {
|
||||
return Math.abs(dxt);
|
||||
}
|
||||
}
|
||||
|
||||
function distance(latA: number, lonA: number, latB: number, lonB: number): number {
|
||||
// Finds the distance between two lat / lon points.
|
||||
return Math.acos(Math.sin(latA) * Math.sin(latB) + Math.cos(latA) * Math.cos(latB) * Math.cos(lonB - lonA)) * earthRadius;
|
||||
}
|
||||
|
||||
|
||||
function bearing(latA: number, lonA: number, latB: number, lonB: number): number {
|
||||
// Finds the bearing from one lat / lon point to another.
|
||||
return Math.atan2(Math.sin(lonB - lonA) * Math.cos(latB),
|
||||
Math.cos(latA) * Math.sin(latB) - Math.sin(latA) * Math.cos(latB) * Math.cos(lonB - lonA));
|
||||
}
|
||||
|
||||
export function route(points: Coordinates[], brouterProfile: string, privateRoads: boolean, routing: boolean): Promise<TrackPoint[]> {
|
||||
if (routing) {
|
||||
return getRoute(points, brouterProfile, privateRoads);
|
||||
export function route(points: Coordinates[]): Promise<TrackPoint[]> {
|
||||
if (get(routing)) {
|
||||
return getRoute(points, brouterProfiles[get(routingProfile).value], get(privateRoads));
|
||||
} else {
|
||||
return new Promise((resolve) => {
|
||||
resolve(points.map(point => new TrackPoint({
|
||||
|
Reference in New Issue
Block a user