adds upload dialog and duplicates
This commit is contained in:
@@ -4,6 +4,8 @@ import Route from './route';
|
||||
import Track from './track';
|
||||
import { allDatesToISOString, haversineDistance, removeEmpty } from './utils';
|
||||
import Waypoint from './waypoint';
|
||||
//@ts-ignore
|
||||
import geohash from "ngeohash"
|
||||
|
||||
const defaultAttributes = {
|
||||
version: '1.1',
|
||||
@@ -14,6 +16,17 @@ const defaultAttributes = {
|
||||
'http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd'
|
||||
}
|
||||
|
||||
|
||||
type GPXFeature = {
|
||||
centroid: { lat: number; lon: number };
|
||||
boundingBox: { minLat: number; maxLat: number; minLon: number; maxLon: number };
|
||||
distance: number;
|
||||
elevationGain?: number;
|
||||
elevationLoss?: number;
|
||||
duration: number;
|
||||
hash: string; // MinHash or Geohash for track shape
|
||||
};
|
||||
|
||||
export default class GPX {
|
||||
$: {
|
||||
version: string;
|
||||
@@ -27,6 +40,7 @@ export default class GPX {
|
||||
wpt?: Waypoint[];
|
||||
rte?: Route[];
|
||||
trk?: Track[];
|
||||
features: GPXFeature
|
||||
|
||||
constructor(object: {
|
||||
$?: {
|
||||
@@ -69,18 +83,26 @@ export default class GPX {
|
||||
this.trk = object.trk.map(trk => new Track(trk))
|
||||
}
|
||||
|
||||
this.features = this.getTotals();
|
||||
|
||||
removeEmpty(this);
|
||||
}
|
||||
|
||||
getTotals() {
|
||||
getTotals(): GPXFeature {
|
||||
let totalElevationGain = 0;
|
||||
let totalElevationLoss = 0;
|
||||
let totalDuration = 0;
|
||||
let totalDistance = 0;
|
||||
let totalLat = 0
|
||||
let totalLon = 0
|
||||
|
||||
let minLat = Infinity, maxLat = -Infinity, minLon = Infinity, maxLon = -Infinity;
|
||||
|
||||
const allPoints: Waypoint[] = []
|
||||
for (const track of this.trk ?? []) {
|
||||
for (const segment of track.trkseg ?? []) {
|
||||
const points = segment.trkpt ?? [];
|
||||
allPoints.push(...points);
|
||||
|
||||
if (points.length >= 2) {
|
||||
const startTime = points[0].time;
|
||||
@@ -110,12 +132,51 @@ export default class GPX {
|
||||
point.$.lat ?? 0,
|
||||
point.$.lon ?? 0,
|
||||
);
|
||||
|
||||
totalLat += point.$.lat ?? 0;
|
||||
totalLon += point.$.lon ?? 0;
|
||||
|
||||
minLat = Math.min(minLat, point.$.lat ?? Infinity);
|
||||
maxLat = Math.max(maxLat, point.$.lat ?? -Infinity);
|
||||
minLon = Math.min(minLon, point.$.lon ?? Infinity);
|
||||
maxLon = Math.max(maxLon, point.$.lon ?? -Infinity);
|
||||
totalDistance += distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { distance: totalDistance, elevationGain: totalElevationGain, elevationLoss: totalElevationLoss, duration: totalDuration }
|
||||
const boundingBox = { minLat, maxLat, minLon, maxLon };
|
||||
const centroid = { lat: totalLat / allPoints.length, lon: totalLon / allPoints.length };
|
||||
|
||||
return {
|
||||
centroid,
|
||||
boundingBox,
|
||||
distance: totalDistance,
|
||||
elevationGain: totalElevationGain,
|
||||
elevationLoss: totalElevationLoss,
|
||||
duration: totalDuration,
|
||||
hash: this.generateMinHash(allPoints)
|
||||
}
|
||||
}
|
||||
|
||||
private generateMinHash(points: Waypoint[]): string {
|
||||
const hashes = points.map(pt => geohash.encode(pt.$.lat, pt.$.lon));
|
||||
return hashes.sort().join('').slice(0, 10);
|
||||
}
|
||||
|
||||
isSimilar(other: GPX, distanceThreshold = 50): boolean {
|
||||
const f1 = this.features;
|
||||
const f2 = other.features;
|
||||
const centroidDistance = haversineDistance(f1.centroid.lat, f1.centroid.lon, f2.centroid.lat, f2.centroid.lon);
|
||||
const boundingBoxOverlap =
|
||||
f1.boundingBox.minLat <= f2.boundingBox.maxLat && f1.boundingBox.maxLat >= f2.boundingBox.minLat &&
|
||||
f1.boundingBox.minLon <= f2.boundingBox.maxLon && f1.boundingBox.maxLon >= f2.boundingBox.minLon;
|
||||
|
||||
const lengthDifference = Math.abs(f1.distance - f2.distance);
|
||||
const hashSimilarity = f1.hash === f2.hash;
|
||||
|
||||
return centroidDistance < distanceThreshold || boundingBoxOverlap || (lengthDifference < 100 && hashSimilarity);
|
||||
|
||||
}
|
||||
|
||||
static parse(gpxString: string): Promise<GPX | Error> {
|
||||
|
||||
Reference in New Issue
Block a user