aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2026-09-24 22:26:24 +0200
committerDaniel Schadt <kingdread@gmx.de>2026-09-24 22:26:24 +0200
commit9a0a4fbfbd005c64c2db2c1606cc7dccbff22053 (patch)
tree443dd534d9e9d338b5c06da056a8f7c6ad0e671b
parente592c65a55c8523987c9aeec627d693be7487c82 (diff)
downloadfietsboek-9a0a4fbfbd005c64c2db2c1606cc7dccbff22053.tar.gz
fietsboek-9a0a4fbfbd005c64c2db2c1606cc7dccbff22053.tar.bz2
fietsboek-9a0a4fbfbd005c64c2db2c1606cc7dccbff22053.zip
fix climb detection
The old algorithm didn't work; it left large amounts of "non-climb" space before the climb. This is because we always kept the window at the minimum length, so even a large mountain at the end of the window could push the average above the ascension threshold and cause the whole window to be included.
-rw-r--r--fietsboek/geo.py81
1 files changed, 16 insertions, 65 deletions
diff --git a/fietsboek/geo.py b/fietsboek/geo.py
index e7a58a9..190454b 100644
--- a/fietsboek/geo.py
+++ b/fietsboek/geo.py
@@ -197,71 +197,22 @@ class Path:
yield current_climb
def _climbs(self):
- # Detection of climbs:
- # We use a single pass over the track with two pointers, the "leader"
- # and the "chaser". The chaser always points behind the leader. In a
- # loop, one of each is always advanced:
- #
- # If the distance between leader and chaser is less than the minimum
- # climb length, we advance the leader to enlarge the window. This
- # does not constitute a climb.
- #
- # If the current ascension is below the ascension threshold, we advance
- # the chaser. This moves the window to advance the search. If the
- # ascension was above the threshold before the last advancement, we emit
- # the climb (which is now over).
- #
- # If the current ascension is above the threshold, we advance the
- # leader. This enlarges the window while constituting a climb.
- try:
- front = enumerate(self.points)
- li, lead = next(front)
- back = enumerate(self.points)
- ci, chaser = next(back)
- except StopIteration:
- # Happens if there are no points
- return
-
- length = 0.0
- elevation_diff = 0.0
- climb_start = None
-
- try:
- while True:
- if length < CLIMB_MIN_LENGTH:
- old_point = lead
- li, lead = next(front)
- length += old_point.distance(lead)
- elevation_diff += lead.elevation - old_point.elevation
- continue
-
- ascension = elevation_diff / length
- if ascension < CLIMB_MIN_ASCENSION:
- if climb_start is not None:
- yield (climb_start, li)
- climb_start = None
- # Skip to the end of the just emitted climb
- while ci != li:
- old_point = chaser
- ci, chaser = next(back)
- length -= old_point.distance(chaser)
- elevation_diff -= chaser.elevation - old_point.elevation
- else:
- old_point = chaser
- ci, chaser = next(back)
- length -= old_point.distance(chaser)
- elevation_diff -= chaser.elevation - old_point.elevation
- else:
- if climb_start is None:
- climb_start = ci
- # See how much farther we can extend this climb
- old_point = lead
- li, lead = next(front)
- length += old_point.distance(lead)
- elevation_diff += lead.elevation - old_point.elevation
- except StopIteration:
- if climb_start is not None:
- yield (climb_start, li)
+ i = 0
+
+ while i < len(self.points):
+ left = self.points[i]
+ j = i
+ length = 0.0
+ elevation_diff = 0.0
+ while j + 1 < len(self.points) and (length == 0.0 or elevation_diff / length >= CLIMB_MIN_ASCENSION):
+ length += self.points[j].distance(self.points[j + 1])
+ elevation_diff += self.points[j + 1].elevation - self.points[j].elevation
+ j += 1
+ if length >= CLIMB_MIN_LENGTH:
+ yield (i, j)
+ i = j
+ else:
+ i += 1
def gpx_xml(