Files
promptstory/utils/gpxUtils.ts
2026-02-15 13:22:48 +01:00

100 lines
3.2 KiB
TypeScript

import { ActivityStats } from '../types';
export const parseGpxFile = async (file: File): Promise<ActivityStats> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const text = e.target?.result as string;
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(text, 'text/xml');
const trkpts = Array.from(xmlDoc.getElementsByTagName('trkpt'));
if (trkpts.length === 0) {
// Fallback for rtept if no trkpt
const rtepts = Array.from(xmlDoc.getElementsByTagName('rtept'));
if (rtepts.length === 0) {
throw new Error('No track points found in GPX');
}
}
let totalDistance = 0;
let totalTime = 0;
let elevationGain = 0;
let startTime: Date | null = null;
let endTime: Date | null = null;
let lastEle: number | null = null;
for (let i = 0; i < trkpts.length; i++) {
const lat1 = parseFloat(trkpts[i].getAttribute('lat') || '0');
const lon1 = parseFloat(trkpts[i].getAttribute('lon') || '0');
const ele = parseFloat(trkpts[i].getElementsByTagName('ele')[0]?.textContent || '0');
const timeStr = trkpts[i].getElementsByTagName('time')[0]?.textContent;
if (i > 0) {
const lat2 = parseFloat(trkpts[i - 1].getAttribute('lat') || '0');
const lon2 = parseFloat(trkpts[i - 1].getAttribute('lon') || '0');
totalDistance += getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2);
// Elevation gain
if (lastEle !== null && ele > lastEle) {
elevationGain += (ele - lastEle);
}
}
if (timeStr) {
const time = new Date(timeStr);
if (!startTime) startTime = time;
endTime = time;
}
if (!isNaN(ele)) lastEle = ele;
}
if (startTime && endTime) {
totalTime = (endTime.getTime() - startTime.getTime()); // ms
}
resolve({
distance: `${totalDistance.toFixed(2)} km`,
duration: msToTime(totalTime),
elevation: `${Math.round(elevationGain)}m`,
});
} catch (error) {
reject(error);
}
};
reader.onerror = () => reject(new Error('Error reading file'));
reader.readAsText(file);
});
};
// Haversine formula
function getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number) {
const R = 6371; // Radius of the earth in km
const dLat = deg2rad(lat2 - lat1);
const dLon = deg2rad(lon2 - lon1);
const a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const d = R * c; // Distance in km
return d;
}
function deg2rad(deg: number) {
return deg * (Math.PI / 180);
}
function msToTime(duration: number) {
if (duration <= 0) return "0h 00m";
const minutes = Math.floor((duration / (1000 * 60)) % 60);
const hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
return `${hours}h ${minutes}m`;
}