diff --git a/bincio/extract/parsers/fit.py b/bincio/extract/parsers/fit.py index 6fa596a..438fa21 100644 --- a/bincio/extract/parsers/fit.py +++ b/bincio/extract/parsers/fit.py @@ -57,12 +57,17 @@ class FitParser: lat = _semicircles_to_deg(_get(frame, "position_lat")) lon = _semicircles_to_deg(_get(frame, "position_long")) speed_raw = _get(frame, "speed") # m/s + # enhanced_altitude is written by barometric altimeters (most + # modern Garmins). Fall back to GPS-derived altitude if absent. + _alt = _get(frame, "enhanced_altitude") + if _alt is None: + _alt = _get(frame, "altitude") dp = DataPoint( timestamp=ts, lat=lat, lon=lon, - elevation_m=_get(frame, "altitude"), + elevation_m=_alt, hr_bpm=_get(frame, "heart_rate"), cadence_rpm=_get(frame, "cadence"), speed_kmh=speed_raw * 3.6 if speed_raw is not None else None, diff --git a/site/src/components/ActivityDetail.svelte b/site/src/components/ActivityDetail.svelte index 18bae3d..615a90b 100644 --- a/site/src/components/ActivityDetail.svelte +++ b/site/src/components/ActivityDetail.svelte @@ -244,6 +244,7 @@ {trackUrl} {timeseries} bbox={detail?.bbox ?? null} + initialCoords={activity.preview_coords} accentColor={color} bind:hoveredIdx /> diff --git a/site/src/components/ActivityMap.svelte b/site/src/components/ActivityMap.svelte index d094210..7bcb14b 100644 --- a/site/src/components/ActivityMap.svelte +++ b/site/src/components/ActivityMap.svelte @@ -7,6 +7,10 @@ export let trackUrl: string; export let timeseries: Timeseries | null = null; export let bbox: [number, number, number, number] | null = null; + /** ~20 [lat, lon] preview coords from the activity summary — used to position + * the map immediately on mount so there's no flash of world view before the + * detail JSON loads and bbox arrives. */ + export let initialCoords: [number, number][] | null = null; export let accentColor: string = '#00c8ff'; export let hoveredIdx: number | null = null; @@ -19,11 +23,25 @@ const TILE_STYLE = 'https://tiles.openfreemap.org/styles/positron'; onMount(() => { + // Derive initial center and zoom from preview_coords so the map starts at + // the right location without waiting for the async detail JSON / bbox load. + let initCenter: [number, number] = [0, 0]; + let initZoom = 1; + if (initialCoords && initialCoords.length > 0) { + const lats = initialCoords.map(c => c[0]); + const lons = initialCoords.map(c => c[1]); + initCenter = [ + (Math.min(...lons) + Math.max(...lons)) / 2, + (Math.min(...lats) + Math.max(...lats)) / 2, + ]; + initZoom = 10; // rough default; fitBounds will correct this when bbox arrives + } + map = new maplibregl.Map({ container: mapEl, style: TILE_STYLE, - center: [0, 0], - zoom: 1, + center: initCenter, + zoom: initZoom, attributionControl: false, });