Naprawa błędów awatara

This commit is contained in:
Arek Bykowski
2026-02-15 19:11:08 +01:00
parent 144e28e4c4
commit 9221287d1d
5 changed files with 76 additions and 45 deletions

View File

@@ -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`;