Naprawa błędów awatara
This commit is contained in:
@@ -16,10 +16,15 @@ export const parseGpxFile = async (file: File): Promise<GpxAnalysisResult> => {
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(text, 'text/xml');
|
||||
|
||||
const trkpts = Array.from(xmlDoc.getElementsByTagName('trkpt'));
|
||||
// Obsługa różnych wariantów tagu 'trkpt' (z namespace lub bez)
|
||||
let trkpts = Array.from(xmlDoc.getElementsByTagName('trkpt'));
|
||||
if (trkpts.length === 0) {
|
||||
// Spróbuj znaleźć po localName, jeśli namespace'y przeszkadzają
|
||||
const allElements = xmlDoc.getElementsByTagName('*');
|
||||
trkpts = Array.from(allElements).filter(el => el.localName === 'trkpt');
|
||||
}
|
||||
|
||||
if (trkpts.length === 0) {
|
||||
// Fallback logic could be added here for routes without time, but for storytelling we focus on tracks
|
||||
throw new Error('Nie znaleziono punktów śledzenia (trkpt) w pliku GPX.');
|
||||
}
|
||||
|
||||
@@ -51,44 +56,62 @@ export const parseGpxFile = async (file: File): Promise<GpxAnalysisResult> => {
|
||||
let lastLat: number | null = null;
|
||||
let lastLon: number | null = null;
|
||||
let lastEle: number | null = null;
|
||||
let lastTime: number | null = null;
|
||||
|
||||
// Helper to get text content from child nodes loosely
|
||||
const getChildText = (parent: Element, tagName: string): string | null => {
|
||||
let el = parent.getElementsByTagName(tagName)[0];
|
||||
if (!el) {
|
||||
// Try finding by localName if strictly not found
|
||||
for(let i=0; i<parent.children.length; i++) {
|
||||
if(parent.children[i].localName === tagName) {
|
||||
el = parent.children[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return el ? el.textContent : null;
|
||||
};
|
||||
|
||||
// --- ITERACJA PO PUNKTACH ---
|
||||
for (let i = 0; i < trkpts.length; i++) {
|
||||
const pt = trkpts[i];
|
||||
const lat = parseFloat(pt.getAttribute('lat') || '0');
|
||||
const lon = parseFloat(pt.getAttribute('lon') || '0');
|
||||
const ele = parseFloat(pt.getElementsByTagName('ele')[0]?.textContent || '0');
|
||||
const timeStr = pt.getElementsByTagName('time')[0]?.textContent;
|
||||
|
||||
// Extensions (HR / Cadence)
|
||||
// Note: Namespaces can vary (ns3:hr, gpxtpx:hr), so we search loosely or by standard tag names inside extensions
|
||||
const extensions = pt.getElementsByTagName('extensions')[0];
|
||||
const eleStr = getChildText(pt, 'ele');
|
||||
const ele = eleStr ? parseFloat(eleStr) : NaN;
|
||||
|
||||
const timeStr = getChildText(pt, 'time');
|
||||
|
||||
// Extensions search for HR/Cadence
|
||||
// Przeszukujemy całe poddrzewo punktu w poszukiwaniu tagów hr/cad, bo struktura extensions bywa różna
|
||||
const allChilds = pt.getElementsByTagName('*');
|
||||
let hr = 0;
|
||||
let cad = 0;
|
||||
|
||||
if (extensions) {
|
||||
// Try standard Garmin/GPX schemes
|
||||
const hrNode = extensions.getElementsByTagName('gpxtpx:hr')[0] || extensions.getElementsByTagName('ns3:hr')[0] || extensions.getElementsByTagName('hr')[0];
|
||||
const cadNode = extensions.getElementsByTagName('gpxtpx:cad')[0] || extensions.getElementsByTagName('ns3:cad')[0] || extensions.getElementsByTagName('cad')[0];
|
||||
|
||||
if (hrNode?.textContent) {
|
||||
hr = parseInt(hrNode.textContent);
|
||||
if (!isNaN(hr) && hr > 0) {
|
||||
totalHr += hr;
|
||||
hrCount++;
|
||||
if (hr > maxHr) maxHr = hr;
|
||||
currentSplitHrSum += hr;
|
||||
currentSplitHrCount++;
|
||||
}
|
||||
}
|
||||
if (cadNode?.textContent) {
|
||||
cad = parseInt(cadNode.textContent);
|
||||
if (!isNaN(cad) && cad > 0) {
|
||||
totalCadence += cad;
|
||||
cadenceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (let j = 0; j < allChilds.length; j++) {
|
||||
const node = allChilds[j];
|
||||
const name = node.localName.toLowerCase();
|
||||
if (name === 'hr' || name === 'heartrate') {
|
||||
const val = parseInt(node.textContent || '0');
|
||||
if (!isNaN(val) && val > 0) hr = val;
|
||||
}
|
||||
if (name === 'cad' || name === 'cadence') {
|
||||
const val = parseInt(node.textContent || '0');
|
||||
if (!isNaN(val) && val > 0) cad = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (hr > 0) {
|
||||
totalHr += hr;
|
||||
hrCount++;
|
||||
if (hr > maxHr) maxHr = hr;
|
||||
currentSplitHrSum += hr;
|
||||
currentSplitHrCount++;
|
||||
}
|
||||
if (cad > 0) {
|
||||
totalCadence += cad;
|
||||
cadenceCount++;
|
||||
}
|
||||
|
||||
let currentTime = 0;
|
||||
@@ -117,8 +140,11 @@ export const parseGpxFile = async (file: File): Promise<GpxAnalysisResult> => {
|
||||
// Elevation Gain
|
||||
if (lastEle !== null && !isNaN(ele) && ele > lastEle) {
|
||||
const gain = ele - lastEle;
|
||||
totalElevationGain += gain;
|
||||
currentSplitElevGain += gain;
|
||||
// Ignorujemy mikro-zmiany (szum GPS) poniżej 0.5m
|
||||
if (gain > 0.2) {
|
||||
totalElevationGain += gain;
|
||||
currentSplitElevGain += gain;
|
||||
}
|
||||
}
|
||||
|
||||
// --- SPLIT LOGIC (Co 1 KM) ---
|
||||
@@ -133,7 +159,7 @@ export const parseGpxFile = async (file: File): Promise<GpxAnalysisResult> => {
|
||||
|
||||
// Reset Split
|
||||
splitIndex++;
|
||||
currentSplitDistance = 0; // or subtract 1.0 specifically for precision, but reset is safer for GPS drift
|
||||
currentSplitDistance = currentSplitDistance - 1.0;
|
||||
currentSplitTimeStart = currentTime;
|
||||
currentSplitHrSum = 0;
|
||||
currentSplitHrCount = 0;
|
||||
@@ -144,7 +170,6 @@ export const parseGpxFile = async (file: File): Promise<GpxAnalysisResult> => {
|
||||
lastLat = lat;
|
||||
lastLon = lon;
|
||||
lastEle = isNaN(ele) ? lastEle : ele;
|
||||
lastTime = currentTime;
|
||||
}
|
||||
|
||||
// --- PODSUMOWANIE ---
|
||||
@@ -157,7 +182,7 @@ export const parseGpxFile = async (file: File): Promise<GpxAnalysisResult> => {
|
||||
const uiStats: ActivityStats = {
|
||||
distance: `${totalDistanceKm.toFixed(2)} km`,
|
||||
duration: msToTime(totalDurationMs),
|
||||
elevation: `${Math.round(totalElevationGain)}m (Max: ${Math.round(maxElevation)}m)`
|
||||
elevation: `${Math.round(totalElevationGain)}m (Max: ${Math.round(maxElevation === -Infinity ? 0 : maxElevation)}m)`
|
||||
};
|
||||
|
||||
// --- BUDOWANIE BOGATEGO RAPORTU DLA AI ---
|
||||
@@ -166,7 +191,7 @@ export const parseGpxFile = async (file: File): Promise<GpxAnalysisResult> => {
|
||||
report += `- Dystans Całkowity: ${totalDistanceKm.toFixed(2)} km\n`;
|
||||
report += `- Czas Całkowity: ${msToTime(totalDurationMs)}\n`;
|
||||
report += `- Średnie Tempo: ${formatPace(avgPaceMs)} min/km\n`;
|
||||
report += `- Przewyższenia: +${Math.round(totalElevationGain)}m / Max Wysokość: ${Math.round(maxElevation)}m\n`;
|
||||
report += `- Przewyższenia: +${Math.round(totalElevationGain)}m / Max Wysokość: ${Math.round(maxElevation === -Infinity ? 0 : maxElevation)}m\n`;
|
||||
|
||||
if (avgHr > 0) {
|
||||
report += `- Tętno: Średnie ${avgHr} bpm / Max ${maxHr} bpm\n`;
|
||||
|
||||
Reference in New Issue
Block a user