aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2026-09-28 23:09:01 +0200
committerDaniel Schadt <kingdread@gmx.de>2026-09-28 23:09:01 +0200
commit6258b36bab2044f802cb89f244fca388bad27987 (patch)
tree964fbe482d1dc6015412d241bcb2c02e9addf7e4
parent49c1309ebbbd059fec68bfaee74aba50f60ea34b (diff)
downloadfietsboek-6258b36bab2044f802cb89f244fca388bad27987.tar.gz
fietsboek-6258b36bab2044f802cb89f244fca388bad27987.tar.bz2
fietsboek-6258b36bab2044f802cb89f244fca388bad27987.zip
implement climb extraction actionclimbs
-rw-r--r--fietsboek/actions.py72
1 files changed, 70 insertions, 2 deletions
diff --git a/fietsboek/actions.py b/fietsboek/actions.py
index cdddaa2..bcde0e4 100644
--- a/fietsboek/actions.py
+++ b/fietsboek/actions.py
@@ -7,6 +7,7 @@ the test functions.
"""
import datetime
+import gzip
import io
import logging
import re
@@ -17,11 +18,12 @@ from pyramid.request import Request
from sqlalchemy import select
from sqlalchemy.orm.session import Session
-from . import convert, email, models, trackmap
+from . import convert, email, models, osm, trackmap
from . import transformers as mod_transformers
from . import util
from .config import TileLayerConfig
from .data import DataManager, TrackDataDir
+from .geo import Path
from .models.track import TrackType, Visibility
from .models.user import TokenType
from .views.tileproxy import TileRequester
@@ -278,4 +280,70 @@ def send_verification_token(request: Request, user: models.User):
)
-__all__ = ["add_track", "edit_images", "execute_transformers", "send_verification_token"]
+def _categorize_climb(length: float, avg_grade: float) -> int | None:
+ # https://bicycles.stackexchange.com/questions/1210/how-are-the-categories-for-climbs-decided
+ score = length * avg_grade * 100
+ if score > 80000:
+ return 0
+ if score > 64000:
+ return 1
+ if score > 32000:
+ return 2
+ if score > 16000:
+ return 3
+ if score > 8000:
+ return 4
+ # You call this a climb?...
+ return None
+
+
+def extract_climbs(
+ dbsession: Session,
+ track: models.Track,
+ tile_requester: TileRequester,
+ layer: TileLayerConfig,
+):
+ """(Re-)extracts the climbs from the given track.
+
+ If the track already has climbs assigned, they will be deleted.
+
+ :param dbsession: The database session.
+ :param track: The track for which to extract climbs.
+ """
+ track.climbs = []
+ dbsession.add(track)
+ path = track.path()
+ climbs = path.climbs()
+ for climb_start, climb_end in climbs:
+ subpath = Path(path.points[climb_start:climb_end])
+ movement_data = subpath.movement_data()
+
+ preview = trackmap.render(subpath, layer, tile_requester)
+ imageio = io.BytesIO()
+ preview.save(imageio, "png")
+ preview = imageio.getvalue()
+
+ profile = trackmap.height_profile(subpath, (300, 150))
+ profile_compressed = gzip.compress(profile.encode("ascii"))
+
+ avg_grade = movement_data.elevation_gain / movement_data.length
+
+ name = osm.find_name(subpath)
+
+ climb = models.Climb(
+ track=track,
+ start_index=climb_start,
+ end_index=climb_end,
+ image=preview,
+ profile=profile_compressed,
+ avg_grade=avg_grade,
+ length=movement_data.length,
+ lowest_elevation=movement_data.min_elevation,
+ highest_elevation=movement_data.max_elevation,
+ category=_categorize_climb(movement_data.length, avg_grade),
+ automatic_name=name,
+ )
+ dbsession.add(climb)
+
+
+__all__ = ["add_track", "edit_images", "execute_transformers", "send_verification_token", "extract_climbs"]