diff --git a/mapper/src/App.js b/mapper/src/App.js index 153308d..36305f1 100644 --- a/mapper/src/App.js +++ b/mapper/src/App.js @@ -305,7 +305,7 @@ function Map({data, loading, coords, mapState, setMapState, setSubmenu, showDire ); } -function Menu({data, duration, setDuration, end, setEnd, slider, setSlider, submenu, setSubmenu, showDirection, setShowDirection, showPoints, setShowPoints, setMapState, shareStart, shareEnd, drawnItems, coords}) { +function Menu({data, duration, setDuration, end, setEnd, slider, setSlider, submenu, setSubmenu, showDirection, setShowDirection, showPoints, setShowPoints, setMapState, shareStart, shareEnd, drawnItems, coords, pointsInRange}) { const [showRange, setShowRange] = useState(false); const [isSearching, setIsSearching] = useState(false); const [searchResults, setSearchResults] = useState(null); @@ -661,9 +661,9 @@ function Menu({data, duration, setDuration, end, setEnd, slider, setSlider, subm setSearchResults(newResults); }; - const totalDistance = useMemo(() => { + const { totalDistance, averagePace } = useMemo(() => { if (!coords || coords.length < 2) { - return null; + return { totalDistance: null, averagePace: null }; } let distance = 0; @@ -673,12 +673,30 @@ function Menu({data, duration, setDuration, end, setEnd, slider, setSlider, subm distance += p1.distanceTo(p2); } - if (distance < 1000) { - return `${distance.toFixed(0)} m`; - } else { - return `${(distance / 1000).toFixed(2)} km`; + const firstPointTime = moment(pointsInRange[0].time); + const lastPointTime = moment(pointsInRange[pointsInRange.length - 1].time); + const durationSeconds = lastPointTime.diff(firstPointTime, 'seconds'); + + let pace = null; + const distanceKm = distance / 1000; + if (distanceKm > 0.01 && durationSeconds > 0) { + const paceSecondsPerKm = durationSeconds / distanceKm; + if (paceSecondsPerKm < 3600) { // cap at 60min/km + const paceMinutes = Math.floor(paceSecondsPerKm / 60); + const paceSeconds = Math.round(paceSecondsPerKm % 60); + pace = `${paceMinutes}:${paceSeconds.toString().padStart(2, '0')} /km`; + } } - }, [coords]); + + let distanceStr; + if (distance < 1000) { + distanceStr = `${distance.toFixed(0)} m`; + } else { + distanceStr = `${distanceKm.toFixed(2)} km`; + } + + return { totalDistance: distanceStr, averagePace: pace }; + }, [coords, pointsInRange]); const range = parseSlider(end, duration, slider); const startDate = moment(end).subtract(...duration.delta); @@ -771,7 +789,7 @@ function Menu({data, duration, setDuration, end, setEnd, slider, setSlider, subm return (
{(showRange || !!submenu) &&
- {rangeTime(range[0])} - {rangeTime(range[1])} ({rangeDelta(range)}){totalDistance && ({totalDistance})} + {rangeTime(range[0])} - {rangeTime(range[1])} ({rangeDelta(range)}){totalDistance && ({totalDistance})}{averagePace && ({averagePace})}
}
@@ -1002,7 +1020,7 @@ function App() { const range = useMemo(() => parseSlider(end, duration, slider), [end, duration, slider]); - const coords = useMemo(() => { + const pointsInRange = useMemo(() => { // 1. Guard against invalid top-level data if (!Array.isArray(data)) { return []; @@ -1032,12 +1050,14 @@ function App() { // 6. Now that all data is known to be valid, filter by time if (time >= startTime && time <= endTime) { - result.push([lat, lon]); + result.push({ lat, lon, time }); } } return result; }, [data, range]); + const coords = useMemo(() => pointsInRange.map(p => [p.lat, p.lon]), [pointsInRange]); + const shareStart = shareStartParam ? moment.unix(shareStartParam) : null; const shareEnd = shareEndParam ? moment.unix(shareEndParam) : null; @@ -1097,6 +1117,7 @@ function App() { data={data} drawnItems={drawnItems} coords={coords} + pointsInRange={pointsInRange} />