mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-08-30 23:30:04 +00:00
add new anchors by clicking on the temporary one
This commit is contained in:
@@ -101,4 +101,55 @@ 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 projectedPoint(point1: TrackPoint, point2: TrackPoint, point3: TrackPoint | Coordinates): Coordinates {
|
||||
return projected(point1.getCoordinates(), point2.getCoordinates(), point3 instanceof TrackPoint ? point3.getCoordinates() : point3);
|
||||
}
|
||||
|
||||
function projected(coord1: Coordinates, coord2: Coordinates, coord3: Coordinates): Coordinates {
|
||||
// Calculates the point on the line defined by p1 and p2
|
||||
// that is closest to the 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 coord1;
|
||||
}
|
||||
|
||||
// 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 coord2;
|
||||
} else {
|
||||
// Determine the closest point (p4) on the great circle
|
||||
const f = dis14 / earthRadius;
|
||||
const lat4 = Math.asin(Math.sin(lat1) * Math.cos(f) + Math.cos(lat1) * Math.sin(f) * Math.cos(bear12));
|
||||
const lon4 = lon1 + Math.atan2(Math.sin(bear12) * Math.sin(f) * Math.cos(lat1), Math.cos(f) - Math.sin(lat1) * Math.sin(lat4));
|
||||
|
||||
return { lat: lat4 / rad, lon: lon4 / rad };
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
import { distance, type Coordinates, TrackPoint, TrackSegment, Track, crossarcDistance } from "gpx";
|
||||
import { distance, type Coordinates, TrackPoint, TrackSegment, Track, projectedPoint } from "gpx";
|
||||
import { get, writable, type Readable } from "svelte/store";
|
||||
import mapboxgl from "mapbox-gl";
|
||||
import { route } from "./Routing";
|
||||
@@ -10,7 +10,7 @@ import { dbUtils, type GPXFileWithStatistics } from "$lib/db";
|
||||
import { getOrderedSelection, selection } from "$lib/components/file-list/Selection";
|
||||
import { ListFileItem, ListTrackItem, ListTrackSegmentItem } from "$lib/components/file-list/FileList";
|
||||
import { currentTool, streetViewEnabled, Tool } from "$lib/stores";
|
||||
import { getClosestLinePoint, resetCursor, setGrabbingCursor } from "$lib/utils";
|
||||
import { getClosestLinePoint, getElevation, resetCursor, setGrabbingCursor } from "$lib/utils";
|
||||
|
||||
export const canChangeStart = writable(false);
|
||||
|
||||
@@ -185,11 +185,13 @@ export class RoutingControls {
|
||||
return (e: any) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (marker === this.temporaryAnchor.marker) {
|
||||
|
||||
if (Date.now() - this.lastDragEvent < 100) { // Prevent click event during drag
|
||||
return;
|
||||
}
|
||||
|
||||
if (Date.now() - this.lastDragEvent < 100) { // Prevent click event during drag
|
||||
if (marker === this.temporaryAnchor.marker) {
|
||||
this.turnIntoPermanentAnchor();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -364,6 +366,52 @@ export class RoutingControls {
|
||||
return minAnchor;
|
||||
}
|
||||
|
||||
turnIntoPermanentAnchor() {
|
||||
let file = get(this.file)?.file;
|
||||
|
||||
// Find the point closest to the temporary anchor
|
||||
let minDetails: any = { distance: Number.MAX_VALUE };
|
||||
let minInfo = {
|
||||
point: this.temporaryAnchor.point,
|
||||
trackIndex: -1,
|
||||
segmentIndex: -1,
|
||||
trkptIndex: -1
|
||||
};
|
||||
|
||||
file?.forEachSegment((segment, trackIndex, segmentIndex) => {
|
||||
if (get(selection).hasAnyParent(new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex))) {
|
||||
let details: any = {};
|
||||
getClosestLinePoint(segment.trkpt, this.temporaryAnchor.point, details);
|
||||
if (details.distance < minDetails.distance) {
|
||||
minDetails = details;
|
||||
let before = details.before ? details.index : details.index - 1;
|
||||
|
||||
let projectedPt = projectedPoint(segment.trkpt[before], segment.trkpt[before + 1], this.temporaryAnchor.point);
|
||||
let ratio = distance(segment.trkpt[before], projectedPt) / distance(segment.trkpt[before], segment.trkpt[before + 1]);
|
||||
|
||||
minInfo = {
|
||||
point: new TrackPoint({
|
||||
attributes: projectedPt,
|
||||
ele: (1 - ratio) * (segment.trkpt[before].ele ?? 0) + ratio * (segment.trkpt[before + 1].ele ?? 0),
|
||||
time: (segment.trkpt[before].time && segment.trkpt[before + 1].time) ? new Date((1 - ratio) * segment.trkpt[before].time.getTime() + ratio * segment.trkpt[before + 1].time.getTime()) : undefined,
|
||||
_data: {
|
||||
anchor: true,
|
||||
zoom: 0
|
||||
}
|
||||
}),
|
||||
trackIndex,
|
||||
segmentIndex,
|
||||
trkptIndex: before + 1
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (minInfo.trackIndex !== -1) {
|
||||
dbUtils.applyToFile(this.fileId, (file) => file.replaceTrackPoints(minInfo.trackIndex, minInfo.segmentIndex, minInfo.trkptIndex, minInfo.trkptIndex - 1, [minInfo.point]));
|
||||
}
|
||||
}
|
||||
|
||||
getDeleteAnchor(anchor: Anchor) {
|
||||
return () => this.deleteAnchor(anchor);
|
||||
}
|
||||
|
@@ -78,9 +78,11 @@ export function getClosestLinePoint(points: TrackPoint[], point: TrackPoint | Co
|
||||
if (distance(points[i], point) <= distance(points[i + 1], point)) {
|
||||
closest = points[i];
|
||||
details['before'] = true;
|
||||
details['index'] = i;
|
||||
} else {
|
||||
closest = points[i + 1];
|
||||
details['before'] = false;
|
||||
details['index'] = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user