Pierwszy wrzut promptstory
This commit is contained in:
100
utils/gpxUtils.ts
Normal file
100
utils/gpxUtils.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
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`;
|
||||
}
|
||||
Reference in New Issue
Block a user