Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ textalloc==1.1.6
shapely==2.0.6
types-shapely==2.0.0.20241221
orjson==3.10.7
requests==2.32.3
requests==2.32.4
39 changes: 39 additions & 0 deletions pretty_gpx/common/drawing/components/elevation_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,51 @@
from pretty_gpx.common.gpx.gpx_bounds import GpxBounds
from pretty_gpx.common.gpx.gpx_distance import get_pairwise_distance_m
from pretty_gpx.common.gpx.gpx_track import GpxTrack
from pretty_gpx.common.gpx.multi_gpx_track import MultiGpxTrack
from pretty_gpx.common.layout.paper_size import PaperSize
from pretty_gpx.common.utils.asserts import assert_in
from pretty_gpx.common.utils.asserts import assert_same_len
from pretty_gpx.common.utils.utils import get


def handle_flat_elevation_profile(track: GpxTrack | MultiGpxTrack,
bot_ratio: float,
ele_ratio: float) -> tuple[float, float]:
"""For nearly flat tracks with gradients below 1%, reduce vertical scaling accordingly.

1 ┌───────────────────────x────────────┐ ▲
│ xxxxxxx │ │ ele_ratio
│ xxxxxxxxxxx xxxx xxxxxxxxxx│ │
xxx xxxxxx xx ▼
x x
x x
x x
x x
x x
0 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1
"""
if isinstance(track, GpxTrack):
uphill_m = track.uphill_m
dist_km = track.dist_km
else:
uphill_m = sum(t.uphill_m for t in track.tracks)
dist_km = sum(t.dist_km for t in track.tracks)

avg_gradient = uphill_m*1e-3/dist_km
ref_gradient = 0.01 # 1%

if 0 <= avg_gradient < ref_gradient:
downscale = avg_gradient / ref_gradient

new_total_height = 1.0 - ele_ratio*(1.-downscale)
new_ele_height = ele_ratio * downscale

bot_ratio *= new_total_height
ele_ratio = new_ele_height / new_total_height

return bot_ratio, ele_ratio


def downsample(x: np.ndarray, y: np.ndarray, n: int) -> tuple[np.ndarray, np.ndarray]:
"""Downsample the signal Y evaluated at X to N points, applying a simple moving average smoothing beforehand."""
assert_same_len([x, y], msg="Downsampling arrays should be the same length")
Expand Down
10 changes: 7 additions & 3 deletions pretty_gpx/rendering_modes/city/drawing/city_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pretty_gpx.common.drawing.components.annotated_scatter import AnnotatedScatterAll
from pretty_gpx.common.drawing.components.centered_title import CenteredTitle
from pretty_gpx.common.drawing.components.elevation_profile import ElevationProfile
from pretty_gpx.common.drawing.components.elevation_profile import handle_flat_elevation_profile
from pretty_gpx.common.drawing.components.track_data import TrackData
from pretty_gpx.common.drawing.utils.drawer import DrawerSingleTrack
from pretty_gpx.common.drawing.utils.drawing_figure import DrawingFigure
Expand Down Expand Up @@ -53,9 +54,12 @@ class CityDrawer(DrawerSingleTrack):
def change_gpx(self, gpx_path: str | bytes, paper: PaperSize) -> None:
"""Load a single GPX file to create a City Poster."""
gpx_track = GpxTrack.load(gpx_path)
ele_ratio = 0.45
bot_ratio, ele_ratio = handle_flat_elevation_profile(gpx_track, self.bot_ratio, ele_ratio)

layouts = VerticalLayoutUnion.from_track(gpx_track,
top_ratio=self.top_ratio,
bot_ratio=self.bot_ratio,
bot_ratio=bot_ratio,
margin_ratio=self.margin_ratio)

total_query = OverpassQuery()
Expand All @@ -72,15 +76,15 @@ def change_gpx(self, gpx_path: str | bytes, paper: PaperSize) -> None:

layout = layouts.layouts[paper]
background.change_papersize(paper, layout.background_bounds)
ele_pofile = ElevationProfile.from_track(layout.bot_bounds, gpx_track, scatter_points, ele_ratio=0.45)
ele_profile = ElevationProfile.from_track(layout.bot_bounds, gpx_track, scatter_points, ele_ratio=ele_ratio)
title = CenteredTitle(bounds=layout.top_bounds)
scatter_all = AnnotatedScatterAll.from_scatter(paper, layout.background_bounds, layout.mid_bounds,
scatter_points, self.params)
track_data = TrackData.from_track(gpx_track)

self.data = CityLayout(layouts=layouts,
background=background,
bot=ele_pofile,
bot=ele_profile,
top=title,
mid_scatter=scatter_all,
mid_track=track_data,
Expand Down
10 changes: 7 additions & 3 deletions pretty_gpx/rendering_modes/mountain/drawing/mountain_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pretty_gpx.common.drawing.components.annotated_scatter import AnnotatedScatterAll
from pretty_gpx.common.drawing.components.centered_title import CenteredTitle
from pretty_gpx.common.drawing.components.elevation_profile import ElevationProfile
from pretty_gpx.common.drawing.components.elevation_profile import handle_flat_elevation_profile
from pretty_gpx.common.drawing.components.track_data import TrackData
from pretty_gpx.common.drawing.utils.drawer import DrawerSingleTrack
from pretty_gpx.common.drawing.utils.drawing_figure import DrawingFigure
Expand Down Expand Up @@ -51,9 +52,12 @@ class MountainDrawer(DrawerSingleTrack):
def change_gpx(self, gpx_path: str | bytes, paper: PaperSize) -> None:
"""Load a single GPX file to create a Mountain Poster."""
gpx_track = GpxTrack.load(gpx_path)
ele_ratio = 0.45
bot_ratio, ele_ratio = handle_flat_elevation_profile(gpx_track, self.bot_ratio, ele_ratio)

layouts = VerticalLayoutUnion.from_track(gpx_track,
top_ratio=self.top_ratio,
bot_ratio=self.bot_ratio,
bot_ratio=bot_ratio,
margin_ratio=self.margin_ratio)

total_query = OverpassQuery()
Expand All @@ -66,15 +70,15 @@ def change_gpx(self, gpx_path: str | bytes, paper: PaperSize) -> None:

layout = layouts.layouts[paper]
background.change_papersize(paper, layout.background_bounds)
ele_pofile = ElevationProfile.from_track(layout.bot_bounds, gpx_track, scatter_points, ele_ratio=0.45)
ele_profile = ElevationProfile.from_track(layout.bot_bounds, gpx_track, scatter_points, ele_ratio=ele_ratio)
title = CenteredTitle(bounds=layout.top_bounds)
scatter_all = AnnotatedScatterAll.from_scatter(paper, layout.background_bounds, layout.mid_bounds,
scatter_points, self.params)
track_data = TrackData.from_track(gpx_track)

self.data = MountainLayout(layouts=layouts,
background=background,
bot=ele_pofile,
bot=ele_profile,
top=title,
mid_scatter=scatter_all,
mid_track=track_data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pretty_gpx.common.drawing.components.annotated_scatter import AnnotatedScatterAll
from pretty_gpx.common.drawing.components.centered_title import CenteredTitle
from pretty_gpx.common.drawing.components.elevation_profile import ElevationProfile
from pretty_gpx.common.drawing.components.elevation_profile import handle_flat_elevation_profile
from pretty_gpx.common.drawing.components.track_data import TrackData
from pretty_gpx.common.drawing.utils.drawer import DrawerMultiTrack
from pretty_gpx.common.drawing.utils.drawing_figure import DrawingFigure
Expand Down Expand Up @@ -51,9 +52,12 @@ class MultiMountainDrawer(DrawerMultiTrack):
def change_gpx(self, gpx_paths: list[str] | list[bytes], paper: PaperSize) -> None:
"""Load several GPX file to create a Multi Mountain Poster."""
gpx_track = MultiGpxTrack.load(gpx_paths)
ele_ratio = 0.45
bot_ratio, ele_ratio = handle_flat_elevation_profile(gpx_track, self.bot_ratio, ele_ratio)

layouts = VerticalLayoutUnion.from_track(gpx_track,
top_ratio=self.top_ratio,
bot_ratio=self.bot_ratio,
bot_ratio=bot_ratio,
margin_ratio=self.margin_ratio)

total_query = OverpassQuery()
Expand All @@ -66,15 +70,16 @@ def change_gpx(self, gpx_paths: list[str] | list[bytes], paper: PaperSize) -> No

layout = layouts.layouts[paper]
background.change_papersize(paper, layout.background_bounds)
ele_pofile = ElevationProfile.from_track(layout.bot_bounds, gpx_track.merge(), scatter_points, ele_ratio=0.45)
ele_profile = ElevationProfile.from_track(layout.bot_bounds, gpx_track.merge(), scatter_points,
ele_ratio=ele_ratio)
title = CenteredTitle(bounds=layout.top_bounds)
scatter_all = AnnotatedScatterAll.from_scatter(paper, layout.background_bounds, layout.mid_bounds,
scatter_points, self.params)
track_data = TrackData.from_track(gpx_track)

self.data = MultiMountainLayout(layouts=layouts,
background=background,
bot=ele_pofile,
bot=ele_profile,
top=title,
mid_scatter=scatter_all,
mid_track=track_data,
Expand Down
Loading