diff options
-rw-r--r-- | fietsboek/transformers/__init__.py | 66 |
1 files changed, 53 insertions, 13 deletions
diff --git a/fietsboek/transformers/__init__.py b/fietsboek/transformers/__init__.py index f3afb57..f98a545 100644 --- a/fietsboek/transformers/__init__.py +++ b/fietsboek/transformers/__init__.py @@ -11,10 +11,10 @@ function to load and apply transformers. from abc import ABC, abstractmethod from collections.abc import Mapping -from itertools import chain, islice +from itertools import islice from typing import Literal, NamedTuple, TypeVar -from gpxpy.gpx import GPX +from gpxpy.gpx import GPX, GPXTrackPoint from pydantic import BaseModel from pyramid.i18n import TranslationString from pyramid.request import Request @@ -164,17 +164,57 @@ class FixNullElevation(Transformer): for point in segment.points ) - points = all_points() - previous_points = chain([None], all_points()) - next_points = chain(islice(all_points(), 1, None), [None]) - - for previous_point, point, next_point in zip(previous_points, points, next_points): - if point.elevation == 0.0: - if previous_point: - point.elevation += previous_point.elevation - if next_point: - point.elevation += next_point.elevation - point.elevation /= sum(1 for pt in [previous_point, next_point] if pt) + def rev_points(): + return ( + point + for track in reversed(gpx.tracks) + for segment in reversed(track.segments) + for point in reversed(segment.points) + ) + + max_slope = 1.0 + + # First, from the front, find the first point with non-zero elevation (or low enough slope) + bad_until = 0 + final_elevation = 0 + for i, (point, next_point) in enumerate(zip(all_points(), islice(all_points(), 1, None))): + if point.elevation != 0.0 and self.slope(point, next_point) < max_slope: + bad_until = i + final_elevation = point.elevation + break + + for point in islice(all_points(), None, bad_until): + point.elevation = final_elevation + + # Second, from the back + bad_until = 0 + final_elevation = 0 + for i, (point, prev_point) in enumerate(zip(rev_points(), islice(rev_points(), 1, None))): + if point.elevation != 0.0 and self.slope(point, prev_point) < max_slope: + bad_until = i + final_elevation = point.elevation + break + + for point in islice(rev_points(), None, bad_until): + point.elevation = final_elevation + + @staticmethod + def slope(point_a: GPXTrackPoint, point_b: GPXTrackPoint) -> float: + """Returns the slope between two GPX points. + + This is defined as delta_h / euclid_distance. + + :param point_a: First point. + :param point_b: Second point. + :return: The slope, as percentage. + """ + if point_a.elevation is None or point_b.elevation is None: + return 0.0 + delta_h = abs(point_a.elevation - point_b.elevation) + dist = point_a.distance_2d(point_b) + if dist == 0.0 or dist is None: + return 0.0 + return delta_h / dist def list_transformers() -> list[type[Transformer]]: |