mirror of
				https://github.com/gpxstudio/gpx.studio.git
				synced 2025-11-04 05:21:09 +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