mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-12-27 13:39:59 +00:00
speed up wpt to segment matching
This commit is contained in:
@@ -59,13 +59,13 @@ function ramerDouglasPeuckerRecursive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function crossarcDistance(
|
export function crossarcDistance(
|
||||||
point1: TrackPoint,
|
point1: TrackPoint | Coordinates,
|
||||||
point2: TrackPoint,
|
point2: TrackPoint | Coordinates,
|
||||||
point3: TrackPoint | Coordinates
|
point3: TrackPoint | Coordinates
|
||||||
): number {
|
): number {
|
||||||
return crossarc(
|
return crossarc(
|
||||||
point1.getCoordinates(),
|
point1 instanceof TrackPoint ? point1.getCoordinates() : point1,
|
||||||
point2.getCoordinates(),
|
point2 instanceof TrackPoint ? point2.getCoordinates() : point2,
|
||||||
point3 instanceof TrackPoint ? point3.getCoordinates() : point3
|
point3 instanceof TrackPoint ? point3.getCoordinates() : point3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
import { i18n } from '$lib/i18n.svelte';
|
import { i18n } from '$lib/i18n.svelte';
|
||||||
import { freeze, type WritableDraft } from 'immer';
|
import { freeze, type WritableDraft } from 'immer';
|
||||||
import {
|
import {
|
||||||
distance,
|
|
||||||
GPXFile,
|
GPXFile,
|
||||||
parseGPX,
|
parseGPX,
|
||||||
Track,
|
Track,
|
||||||
@@ -30,7 +29,7 @@ import {
|
|||||||
} from 'gpx';
|
} from 'gpx';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import { settings } from '$lib/logic/settings';
|
import { settings } from '$lib/logic/settings';
|
||||||
import { getClosestLinePoint, getElevation } from '$lib/utils';
|
import { getClosestLinePoint, getClosestTrackSegments, getElevation } from '$lib/utils';
|
||||||
import { gpxStatistics } from '$lib/logic/statistics';
|
import { gpxStatistics } from '$lib/logic/statistics';
|
||||||
import { boundsManager } from './bounds';
|
import { boundsManager } from './bounds';
|
||||||
|
|
||||||
@@ -453,34 +452,13 @@ export const fileActions = {
|
|||||||
selection.applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
selection.applyToOrderedSelectedItemsFromFile((fileId, level, items) => {
|
||||||
if (level === ListLevel.FILE) {
|
if (level === ListLevel.FILE) {
|
||||||
let file = fileStateCollection.getFile(fileId);
|
let file = fileStateCollection.getFile(fileId);
|
||||||
if (file) {
|
let statistics = fileStateCollection.getStatistics(fileId);
|
||||||
|
if (file && statistics) {
|
||||||
if (file.trk.length > 1) {
|
if (file.trk.length > 1) {
|
||||||
let fileIds = getFileIds(file.trk.length);
|
let fileIds = getFileIds(file.trk.length);
|
||||||
let closest = file.wpt.map((wpt, wptIndex) => {
|
let closest = file.wpt.map((wpt) =>
|
||||||
return {
|
getClosestTrackSegments(file, statistics, wpt.getCoordinates())
|
||||||
wptIndex: wptIndex,
|
|
||||||
index: [0],
|
|
||||||
distance: Number.MAX_VALUE,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
file.trk.forEach((track, index) => {
|
|
||||||
track.getSegments().forEach((segment) => {
|
|
||||||
segment.trkpt.forEach((point) => {
|
|
||||||
file.wpt.forEach((wpt, wptIndex) => {
|
|
||||||
let dist = distance(
|
|
||||||
point.getCoordinates(),
|
|
||||||
wpt.getCoordinates()
|
|
||||||
);
|
);
|
||||||
if (dist < closest[wptIndex].distance) {
|
|
||||||
closest[wptIndex].distance = dist;
|
|
||||||
closest[wptIndex].index = [index];
|
|
||||||
} else if (dist === closest[wptIndex].distance) {
|
|
||||||
closest[wptIndex].index.push(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
file.trk.forEach((track, index) => {
|
file.trk.forEach((track, index) => {
|
||||||
let newFile = file.clone();
|
let newFile = file.clone();
|
||||||
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
let tracks = track.trkseg.map((segment, segmentIndex) => {
|
||||||
@@ -496,8 +474,12 @@ export const fileActions = {
|
|||||||
0,
|
0,
|
||||||
file.wpt.length - 1,
|
file.wpt.length - 1,
|
||||||
closest
|
closest
|
||||||
.filter((c) => c.index.includes(index))
|
.filter((c) =>
|
||||||
.map((c) => file.wpt[c.wptIndex])
|
c.some(
|
||||||
|
([trackIndex, segmentIndex]) => trackIndex === index
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map((c, wptIndex) => file.wpt[wptIndex])
|
||||||
);
|
);
|
||||||
newFile._data.id = fileIds[index];
|
newFile._data.id = fileIds[index];
|
||||||
newFile.metadata.name =
|
newFile.metadata.name =
|
||||||
@@ -506,29 +488,9 @@ export const fileActions = {
|
|||||||
});
|
});
|
||||||
} else if (file.trk.length === 1) {
|
} else if (file.trk.length === 1) {
|
||||||
let fileIds = getFileIds(file.trk[0].trkseg.length);
|
let fileIds = getFileIds(file.trk[0].trkseg.length);
|
||||||
let closest = file.wpt.map((wpt, wptIndex) => {
|
let closest = file.wpt.map((wpt) =>
|
||||||
return {
|
getClosestTrackSegments(file, statistics, wpt.getCoordinates())
|
||||||
wptIndex: wptIndex,
|
|
||||||
index: [0],
|
|
||||||
distance: Number.MAX_VALUE,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
file.trk[0].trkseg.forEach((segment, index) => {
|
|
||||||
segment.trkpt.forEach((point) => {
|
|
||||||
file.wpt.forEach((wpt, wptIndex) => {
|
|
||||||
let dist = distance(
|
|
||||||
point.getCoordinates(),
|
|
||||||
wpt.getCoordinates()
|
|
||||||
);
|
);
|
||||||
if (dist < closest[wptIndex].distance) {
|
|
||||||
closest[wptIndex].distance = dist;
|
|
||||||
closest[wptIndex].index = [index];
|
|
||||||
} else if (dist === closest[wptIndex].distance) {
|
|
||||||
closest[wptIndex].index.push(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
file.trk[0].trkseg.forEach((segment, index) => {
|
file.trk[0].trkseg.forEach((segment, index) => {
|
||||||
let newFile = file.clone();
|
let newFile = file.clone();
|
||||||
newFile.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [
|
newFile.replaceTrackSegments(0, 0, file.trk[0].trkseg.length - 1, [
|
||||||
@@ -538,8 +500,13 @@ export const fileActions = {
|
|||||||
0,
|
0,
|
||||||
file.wpt.length - 1,
|
file.wpt.length - 1,
|
||||||
closest
|
closest
|
||||||
.filter((c) => c.index.includes(index))
|
.filter((c) =>
|
||||||
.map((c) => file.wpt[c.wptIndex])
|
c.some(
|
||||||
|
([trackIndex, segmentIndex]) =>
|
||||||
|
segmentIndex === index
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map((c, wptIndex) => file.wpt[wptIndex])
|
||||||
);
|
);
|
||||||
newFile._data.id = fileIds[index];
|
newFile._data.id = fileIds[index];
|
||||||
newFile.metadata.name = `${file.trk[0].name ?? file.metadata.name} (${index + 1})`;
|
newFile.metadata.name = `${file.trk[0].name ?? file.metadata.name} (${index + 1})`;
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ import { type ClassValue, clsx } from 'clsx';
|
|||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
import { base } from '$app/paths';
|
import { base } from '$app/paths';
|
||||||
import { languages } from '$lib/languages';
|
import { languages } from '$lib/languages';
|
||||||
import { TrackPoint, Waypoint, type Coordinates, crossarcDistance, distance } from 'gpx';
|
import { TrackPoint, Waypoint, type Coordinates, crossarcDistance, distance, GPXFile } from 'gpx';
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl from 'mapbox-gl';
|
||||||
import { pointToTile, pointToTileFraction } from '@mapbox/tilebelt';
|
import { pointToTile, pointToTileFraction } from '@mapbox/tilebelt';
|
||||||
import { PUBLIC_MAPBOX_TOKEN } from '$env/static/public';
|
import { PUBLIC_MAPBOX_TOKEN } from '$env/static/public';
|
||||||
import PNGReader from 'png.js';
|
import PNGReader from 'png.js';
|
||||||
|
import type { GPXStatisticsTree } from '$lib/logic/statistics-tree';
|
||||||
|
import { ListTrackSegmentItem } from '$lib/components/file-list/file-list';
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
@@ -47,6 +49,54 @@ export function getClosestLinePoint(
|
|||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getClosestTrackSegments(
|
||||||
|
file: GPXFile,
|
||||||
|
statistics: GPXStatisticsTree,
|
||||||
|
point: Coordinates
|
||||||
|
): [number, number][] {
|
||||||
|
let segmentBoundsDistances: [number, number, number][] = [];
|
||||||
|
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
||||||
|
let segmentStatistics = statistics.getStatisticsFor(
|
||||||
|
new ListTrackSegmentItem(file._data.id, trackIndex, segmentIndex)
|
||||||
|
);
|
||||||
|
let segmentBounds = segmentStatistics.global.bounds;
|
||||||
|
let northEast = segmentBounds.northEast;
|
||||||
|
let southWest = segmentBounds.southWest;
|
||||||
|
let northWest: Coordinates = { lat: northEast.lat, lon: southWest.lon };
|
||||||
|
let southEast: Coordinates = { lat: southWest.lat, lon: northEast.lon };
|
||||||
|
let distanceToSegment = Math.min(
|
||||||
|
crossarcDistance(northWest, northEast, point),
|
||||||
|
crossarcDistance(northEast, southEast, point),
|
||||||
|
crossarcDistance(southEast, southWest, point),
|
||||||
|
crossarcDistance(southWest, northWest, point)
|
||||||
|
);
|
||||||
|
segmentBoundsDistances.push([distanceToSegment, trackIndex, segmentIndex]);
|
||||||
|
});
|
||||||
|
segmentBoundsDistances.sort((a, b) => a[0] - b[0]);
|
||||||
|
|
||||||
|
let closest: { distance: number; indices: [number, number][] } = {
|
||||||
|
distance: Number.MAX_VALUE,
|
||||||
|
indices: [],
|
||||||
|
};
|
||||||
|
for (let s = 0; s < segmentBoundsDistances.length; s++) {
|
||||||
|
if (segmentBoundsDistances[s][0] > closest.distance) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const segment = file.getSegment(segmentBoundsDistances[s][1], segmentBoundsDistances[s][2]);
|
||||||
|
segment.trkpt.forEach((pt) => {
|
||||||
|
let dist = distance(pt.getCoordinates(), point);
|
||||||
|
if (dist < closest.distance) {
|
||||||
|
closest.distance = dist;
|
||||||
|
closest.indices = [[segmentBoundsDistances[s][1], segmentBoundsDistances[s][2]]];
|
||||||
|
} else if (dist === closest.distance) {
|
||||||
|
closest.indices.push([segmentBoundsDistances[s][1], segmentBoundsDistances[s][2]]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return closest.indices;
|
||||||
|
}
|
||||||
|
|
||||||
export function getElevation(
|
export function getElevation(
|
||||||
points: (TrackPoint | Waypoint | Coordinates)[],
|
points: (TrackPoint | Waypoint | Coordinates)[],
|
||||||
ELEVATION_ZOOM: number = 13,
|
ELEVATION_ZOOM: number = 13,
|
||||||
|
|||||||
Reference in New Issue
Block a user