Skip to content
Open
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
16 changes: 8 additions & 8 deletions pretty_gpx/common/drawing/components/annotated_scatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from pretty_gpx.common.utils.asserts import assert_eq
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 safe


@dataclass
Expand Down Expand Up @@ -166,13 +165,14 @@ def finalize_text_allocation(paper_size: PaperSize,
annot = None
if scatter.name is not None:
text, line = output.texts_xy[alloc_idx], output.lines_xy[alloc_idx]
text_x, text_y = safe(text)
line_x, line_y = safe(line)
annot = AnnotationArrow(text_s=input.list_text_s[alloc_idx],
text_lat=text_y,
text_lon=text_x,
arrow_begin_lat=line_y[0],
arrow_begin_lon=line_x[0])
if text is not None and line is not None:
text_x, text_y = text
line_x, line_y = line
annot = AnnotationArrow(text_s=input.list_text_s[alloc_idx],
text_lat=text_y,
text_lon=text_x,
arrow_begin_lat=line_y[0],
arrow_begin_lon=line_x[0])
alloc_idx += 1

res[scatter.category].list_lat.append(scatter.lat)
Expand Down
27 changes: 21 additions & 6 deletions pretty_gpx/common/drawing/utils/text_allocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,13 @@ class TextAllocationInput:

@dataclass(kw_only=True)
class TextAllocationOutput:
"""Output of textalloc."""
texts_xy: list[tuple[float, float]] = field(default_factory=list)
lines_xy: list[tuple[tuple[float, float], tuple[float, float]]] = field(default_factory=list)
"""Output of textalloc.

A `None` entry means textalloc couldn't find a non-overlapping spot for that text (e.g. too many
labels crammed in a small area) -- the corresponding label is dropped, its marker is still drawn.
"""
texts_xy: list[tuple[float, float] | None] = field(default_factory=list)
lines_xy: list[tuple[tuple[float, float], tuple[float, float]] | None] = field(default_factory=list)


@profile
Expand Down Expand Up @@ -90,12 +94,19 @@ def allocate_text(input: TextAllocationInput,
fontproperties=input.annot_fontproperties)
output = TextAllocationOutput()
assert_same_len((texts_xyz, lines_xyz, input.list_text_s))
for text, line in zip(texts_xyz, lines_xyz):
assert text is not None, "Failed to allocate text"
nbr_dropped = 0
for text, line, text_s in zip(texts_xyz, lines_xyz, input.list_text_s):
if text is None or line is None:
# Too many labels too close together for textalloc to find a non-overlapping spot.
# Drop this one label rather than failing the whole poster (its marker is still drawn).
nbr_dropped += 1
output.texts_xy.append(None)
output.lines_xy.append(None)
continue

assert_len(text, 3)
text_x, text_y, _ = text

assert line is not None
assert_len(line, 3)
line_x, line_y, _ = line
assert_len(line_x, 2)
Expand All @@ -104,6 +115,10 @@ def allocate_text(input: TextAllocationInput,
output.texts_xy.append((text_x, text_y))
output.lines_xy.append((line_x, line_y))

if nbr_dropped > 0:
logger.warning(f"Could not allocate {nbr_dropped}/{len(input.list_text_s)} text labels "
"(too many labels too close together): dropping them, keeping their markers.")

if DEBUG:
__debug_after(paper_size, background_bounds, mid_bounds, input, output)

Expand Down
5 changes: 4 additions & 1 deletion pretty_gpx/common/gpx/gpx_track.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ def append_track_to_gpx_track(gpx_track: GpxTrack, track_points: list[GPXTrackPo
gpx_track.list_ele_m.append(point.elevation)

if prev_point is not None:
prev_cumul_dist_km += safe(prev_point.distance_3d(point)) * 1e-3
# Match gpxpy's own `length()` call order (point.distance_3d(previous)): its flat-earth
# approximation uses cos(latitude) of the `self` point only, so swapping the operands
# yields a (tiny but non-zero) different distance and breaks coherence with gpx.length_3d().
prev_cumul_dist_km += safe(point.distance_3d(prev_point)) * 1e-3

gpx_track.list_cumul_dist_km.append(prev_cumul_dist_km)

Expand Down
10 changes: 7 additions & 3 deletions pretty_gpx/common/utils/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,13 @@ def profile_parallel(func: Callable[P, R]) -> Callable[P, tuple[R, list[Profilin
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> tuple[R, list[ProfilingEvent]]:
Profiling.set_bypass_queue("Parallel")
with Profiling.Scope(get_function_name(func)):
retval = func(*args, **kwargs)
events = Profiling.pop_bypass_queue()
try:
with Profiling.Scope(get_function_name(func)):
retval = func(*args, **kwargs)
finally:
# Always pop the bypass queue, even if `func` raised, so a failed call doesn't
# permanently block subsequent calls reusing the same worker process.
events = Profiling.pop_bypass_queue()
return retval, events
return wrapper

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from pretty_gpx.common.layout.vertical_layout import VerticalLayoutUnion
from pretty_gpx.common.request.overpass_request import OverpassQuery
from pretty_gpx.common.utils.profile import profile
from pretty_gpx.rendering_modes.mountain.data.mountain_passes import prepare_download_mountain_passes
from pretty_gpx.rendering_modes.mountain.data.mountain_passes import process_mountain_passes
from pretty_gpx.rendering_modes.mountain.drawing.mountain_background import MountainBackground
from pretty_gpx.rendering_modes.multi_mountain.data.mountain_huts import prepare_download_mountain_huts
from pretty_gpx.rendering_modes.multi_mountain.data.mountain_huts import process_mountain_huts
Expand Down Expand Up @@ -52,6 +54,7 @@ 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)
merged_track = gpx_track.merge()
ele_ratio = 0.45
bot_ratio, ele_ratio = handle_flat_elevation_profile(gpx_track, self.bot_ratio, ele_ratio)

Expand All @@ -62,15 +65,17 @@ def change_gpx(self, gpx_paths: list[str] | list[bytes], paper: PaperSize) -> No

total_query = OverpassQuery()
prepare_download_mountain_huts(total_query, gpx_track)
prepare_download_mountain_passes(total_query, merged_track)
total_query.launch_queries()

scatter_points = get_start_end_named_points(gpx_track)
scatter_points += process_mountain_huts(total_query, gpx_track)
scatter_points += process_mountain_passes(total_query, merged_track)
background = MountainBackground.from_union_bounds(layouts.union_bounds)

layout = layouts.layouts[paper]
background.change_papersize(paper, layout.background_bounds)
ele_profile = ElevationProfile.from_track(layout.bot_bounds, gpx_track.merge(), scatter_points,
ele_profile = ElevationProfile.from_track(layout.bot_bounds, merged_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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ def default() -> "MultiMountainParams":
ScatterPointCategory.MOUNTAIN_HUT: ScatterParams(markersize=A4Float(mm=3.5),
marker=MarkerType.HOUSE,
color="m"),
ScatterPointCategory.MOUNTAIN_PASS: ScatterParams(markersize=A4Float(mm=3.5),
marker=MarkerType.TRIANGLE,
color="m"),
ScatterPointCategory.START: ScatterParams(markersize=A4Float(mm=3.5),
marker=MarkerType.DISK,
color="m"),
Expand All @@ -66,6 +69,8 @@ def default() -> "MultiMountainParams":
annot_params={
ScatterPointCategory.MOUNTAIN_HUT: AnnotatedScatterParams(arrow_linewidth=A4Float(mm=0.5),
fontsize=A4Float(mm=3.0)),
ScatterPointCategory.MOUNTAIN_PASS: AnnotatedScatterParams(arrow_linewidth=A4Float(mm=0.5),
fontsize=A4Float(mm=3.0)),
ScatterPointCategory.START: AnnotatedScatterParams(arrow_linewidth=A4Float(mm=0.5),
fontsize=A4Float(mm=3.0)),
ScatterPointCategory.END: AnnotatedScatterParams(arrow_linewidth=A4Float(mm=0.5),
Expand All @@ -81,6 +86,9 @@ def default() -> "MultiMountainParams":
ScatterPointCategory.MOUNTAIN_HUT: ScatterParams(markersize=A4Float(mm=3.5),
marker=MarkerType.HOUSE,
color="m"),
ScatterPointCategory.MOUNTAIN_PASS: ScatterParams(markersize=A4Float(mm=3.5),
marker=MarkerType.TRIANGLE,
color="m"),
ScatterPointCategory.START: ScatterParams(markersize=A4Float(mm=3.5),
marker=MarkerType.DISK,
color="m"),
Expand Down
Loading