aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2026-09-27 22:07:29 +0200
committerDaniel Schadt <kingdread@gmx.de>2026-09-27 22:07:29 +0200
commit2ac7a51cfb51e4a9597e10cf081e81cc19c3f642 (patch)
tree96e3bf8847d5ef72c137ff5abf617827217213be
parent77a12c168822ec62a1669103f9e94a8d5d30c0b3 (diff)
downloadfietsboek-2ac7a51cfb51e4a9597e10cf081e81cc19c3f642.tar.gz
fietsboek-2ac7a51cfb51e4a9597e10cf081e81cc19c3f642.tar.bz2
fietsboek-2ac7a51cfb51e4a9597e10cf081e81cc19c3f642.zip
shave trailing downhill off climbs
-rw-r--r--fietsboek/geo.py47
1 files changed, 44 insertions, 3 deletions
diff --git a/fietsboek/geo.py b/fietsboek/geo.py
index 190454b..e747f4d 100644
--- a/fietsboek/geo.py
+++ b/fietsboek/geo.py
@@ -24,7 +24,7 @@ CLIMB_MIN_LENGTH = 500
CLIMB_MIN_ASCENSION = 0.03
"""Minimum relative ascension for a segment to be considered a climb."""
-CLIMB_MERGE_THRESHOLD = 5
+CLIMB_MERGE_THRESHOLD = 50
"""Window for when climbs are merged (in m)."""
@@ -69,6 +69,15 @@ class MovementData:
downhill: float = 0.0
"""Downhill elevation, in meters."""
+ elevation_gain: float = 0.0
+ """Elevation gain, in meters."""
+
+ min_elevation: float = 0.0
+ """Minimum elevation, in meters."""
+
+ max_elevation: float = 0.0
+ """Maximum elevation, in meters."""
+
@dataclass(slots=True)
class Point:
@@ -157,6 +166,25 @@ class Path:
else:
speed = 0.0
elevation = b.elevation - a.elevation
+ movement_data.elevation_gain += elevation
+
+ if movement_data.min_elevation == 0.0:
+ movement_data.min_elevation = min(a.elevation, b.elevation)
+ else:
+ movement_data.min_elevation = min(
+ a.elevation,
+ b.elevation,
+ movement_data.min_elevation,
+ )
+
+ if movement_data.max_elevation == 0.0:
+ movement_data.max_elevation = max(a.elevation, b.elevation)
+ else:
+ movement_data.max_elevation = max(
+ a.elevation,
+ b.elevation,
+ movement_data.max_elevation,
+ )
movement_data.length += distance
if speed >= MOVING_THRESHOLD:
@@ -182,8 +210,21 @@ class Path:
:return: An iterator over the climbs, represented by the starting index
and ending index of points included in the climb.
"""
+ return self._trimmed_climbs()
+
+ def _trimmed_climbs(self):
+ for i, j in self._merged_climbs():
+ # We don't want a climb to end in a "downhill" part, even if that
+ # technically is still above the grade threshold. Therefore, we
+ # shave off everything downhill at the end.
+ max_elevation = max(point.elevation for point in self.points[i:j])
+ while self.points[j].elevation < max_elevation:
+ j -= 1
+ yield (i, j)
+
+ def _merged_climbs(self):
current_climb = None
- for climb in self._climbs():
+ for climb in self._raw_climbs():
if current_climb is None:
current_climb = climb
else:
@@ -196,7 +237,7 @@ class Path:
if current_climb is not None:
yield current_climb
- def _climbs(self):
+ def _raw_climbs(self):
i = 0
while i < len(self.points):