TrackStats.build_profile now also computes elapsed_secs per point (relative to the first known timestamp), alongside the existing cumulative distance. Point x-positions stay distance-based either way — only the shared axis strip's 5 tick labels (and the tooltip) switch between the two, so toggling is a cheap re-render rather than rebuilding the charts. The toggle only appears when a track actually has elapsed-time data to switch to.
74 lines
2.9 KiB
Ruby
74 lines
2.9 KiB
Ruby
require_relative "test_helper"
|
|
require "maptool/track_stats"
|
|
|
|
class TrackStatsTest < Minitest::Test
|
|
def test_computes_distance_and_elevation_gain_ignoring_descents
|
|
# A right-angle path (equirectangular approximation is accurate enough
|
|
# at this scale to just assert "nonzero, roughly plausible") with a
|
|
# climb-then-descend elevation profile, so both distance and
|
|
# elevation-gain have a real value to check against.
|
|
points = [
|
|
[17.0, 48.0, 100],
|
|
[17.001, 48.0, 150], # +50m gain
|
|
[17.001, 48.001, 120], # descent — not subtracted
|
|
]
|
|
times = [Time.utc(2026, 1, 1, 0, 0, 0), nil, Time.utc(2026, 1, 1, 0, 10, 0)]
|
|
|
|
stats = Maptool::TrackStats.compute(points, times: times)
|
|
|
|
assert_operator stats["distance_meters"], :>, 100
|
|
assert_equal 50.0, stats["elevation_gain_meters"]
|
|
assert_equal 600, stats["duration_secs"]
|
|
assert_equal 3, stats["point_count"]
|
|
end
|
|
|
|
def test_nil_elevation_gain_and_duration_when_data_is_missing
|
|
stats = Maptool::TrackStats.compute([[17.0, 48.0], [17.0, 48.0]], times: [])
|
|
assert_nil stats["elevation_gain_meters"]
|
|
assert_nil stats["duration_secs"]
|
|
end
|
|
|
|
def test_single_point_has_zero_distance
|
|
stats = Maptool::TrackStats.compute([[17.0, 48.0]])
|
|
assert_equal 0.0, stats["distance_meters"]
|
|
end
|
|
|
|
def test_build_profile_has_cumulative_distance_elevation_and_extras
|
|
points = [[17.0, 48.0, 100], [17.001, 48.0, 150]]
|
|
extras = [{ heart_rate_bpm: 120 }, { heart_rate_bpm: 130, cadence_rpm: 80 }]
|
|
|
|
profile = Maptool::TrackStats.build_profile(points, extras: extras)
|
|
|
|
assert_equal 2, profile.size
|
|
assert_equal 0.0, profile[0]["distance_meters"]
|
|
assert_equal 100, profile[0]["elevation_meters"]
|
|
assert_equal 120, profile[0]["heart_rate_bpm"]
|
|
assert_nil profile[0]["cadence_rpm"]
|
|
assert_operator profile[1]["distance_meters"], :>, 0
|
|
assert_equal 80, profile[1]["cadence_rpm"]
|
|
assert_equal [17.001, 48.0], [profile[1]["lon"], profile[1]["lat"]]
|
|
end
|
|
|
|
def test_build_profile_extras_default_to_nil_when_shorter_than_coordinates
|
|
profile = Maptool::TrackStats.build_profile([[17.0, 48.0], [17.001, 48.0]], extras: [])
|
|
assert_nil profile[0]["heart_rate_bpm"]
|
|
assert_nil profile[1]["speed_mps"]
|
|
end
|
|
|
|
def test_build_profile_elapsed_secs_is_relative_to_the_first_known_timestamp
|
|
points = [[17.0, 48.0], [17.001, 48.0], [17.002, 48.0]]
|
|
times = [nil, Time.utc(2026, 1, 1, 0, 0, 0), Time.utc(2026, 1, 1, 0, 5, 0)]
|
|
|
|
profile = Maptool::TrackStats.build_profile(points, times: times)
|
|
|
|
assert_nil profile[0]["elapsed_secs"], "no timestamp yet at the first point"
|
|
assert_equal 0, profile[1]["elapsed_secs"], "first known timestamp is the zero point"
|
|
assert_equal 300, profile[2]["elapsed_secs"]
|
|
end
|
|
|
|
def test_build_profile_elapsed_secs_is_nil_without_any_times
|
|
profile = Maptool::TrackStats.build_profile([[17.0, 48.0]], times: [])
|
|
assert_nil profile[0]["elapsed_secs"]
|
|
end
|
|
end
|