Numbers outside of the map [still cluttered]

This commit is contained in:
Olaf committed 2026-10-21 10:51:50 +02:00
1 parent b692f94122
commit e39de24921
5 files changed
+274 -45

No files matched your search

+113 -42
View File
@@ -74,6 +74,68 @@ def label(group: ET.Element, text: str, x: float, y: float, height: float) -> No
add_line(group, line)
def outside_index(
group: ET.Element,
text: str,
curves: list[Cubic],
map_rectangle: BaseGeometry,
outer_rectangle: BaseGeometry,
occupied: list[BaseGeometry],
height: float,
pen_width: float,
) -> None:
west, top, east, bottom = map_rectangle.bounds
width = (len(text) - 1 + 2 / 3) * height
padding = pen_width / 2 + 0.2
anchors: list[tuple[float, float]] = []
for curve in curves:
for first, last in curve.inside_intervals(map_rectangle.bounds):
anchors.extend((curve.point(first), curve.point(last)))
candidates = []
for ax, ay in anchors:
candidates.extend(
[
(abs(ax - west), "left", ax, ay),
(abs(ax - east), "right", ax, ay),
(abs(ay - top), "top", ax, ay),
(abs(ay - bottom), "bottom", ax, ay),
]
)
for _, side, ax, ay in sorted(candidates):
for step in (0, 1, -1, 2, -2, 3, -3):
shift = step * (height + 2 * padding)
if side == "left":
x, y = west - width - 2 * padding, ay - height / 2 + shift
endpoint = (x + width + padding, y + height / 2)
elif side == "right":
x, y = east + 2 * padding, ay - height / 2 + shift
endpoint = (x - padding, y + height / 2)
elif side == "top":
x, y = ax - width / 2 + shift, top - height - 2 * padding
endpoint = (x + width / 2, y + height + padding)
else:
x, y = ax - width / 2 + shift, bottom + 2 * padding
endpoint = (x + width / 2, y - padding)
extent = box(x - padding, y - padding, x + width + padding, y + height + padding)
leader = LineString([(ax, ay), endpoint])
if (
outer_rectangle.covers(extent)
and extent.disjoint(map_rectangle)
and not any(
extent.intersects(previous) or leader.intersects(previous)
for previous in occupied
)
):
add_line(group, leader)
label(group, text, x, y, height)
occupied.append(extent)
return
raise ValueError(
"Cannot place track identifiers outside the map; select fewer tracks "
"or increase --extension."
)
def utc(timestamp: float) -> str:
return datetime.fromtimestamp(timestamp, UTC).strftime("%Y-%m-%d %H:%M:%S")
@@ -195,7 +257,8 @@ def render(
)
row_height = options.font_height * 1.8
terrain_attribution = terrain_source.startswith("PDOK")
legend_height = (len(tracks) + 5 + int(terrain_attribution)) * row_height
flight_rows = (len(tracks) + 1) // 2
legend_height = (flight_rows + 5 + int(terrain_attribution)) * row_height
available_width = options.width - 2 * (options.margin + options.extension)
available_height = options.height - 2 * (options.margin + options.extension) - legend_height
if available_height < 25:
@@ -284,60 +347,68 @@ def render(
},
)
add_curves(group, page_curves)
short_label = str(index)
placed = False
for fraction in (0.9, 0.1, 0.7, 0.3, 0.5):
curve_index = min(int(fraction * len(page_curves)), len(page_curves) - 1)
anchor_x, anchor_y = page_curves[curve_index].point(
fraction * len(page_curves) - curve_index
)
for dx, dy in ((1, 1), (1, -3), (-4, 1), (-4, -3)):
x, y = anchor_x + dx, anchor_y + dy
extent = box(
x - 0.3,
y - 0.3,
x + len(short_label) * options.font_height,
y + options.font_height + 0.3,
)
if track_rectangle.covers(extent) and not any(
extent.intersects(previous) for previous in occupied
):
label(text_group, short_label, x, y, options.font_height)
occupied.append(extent)
placed = True
break
if placed:
break
if not placed:
raise ValueError("Cannot place track identifiers legibly; select fewer tracks.")
outside_index(
text_group,
str(index),
page_curves,
map_rectangle,
track_rectangle,
occupied,
options.font_height,
options.pen_width,
)
legend_y = top + map_height + options.extension + row_height
ground_label = "N/A" if ground is None else f"{ground:.1f} M"
legend = [
"FLIGHT TIMES UTC - REPORTED BAROMETRIC ALTITUDE, NOT HEIGHT ABOVE GROUND",
"FLIGHT MIDPOINT TIMES UTC - ALT IS REPORTED PRESSURE ALTITUDE, NOT HEIGHT ABOVE GROUND",
f"MAP AVERAGE LAND ELEVATION: {ground_label} - N/A MEANS UNAVAILABLE",
f"LAND ELEVATION COVERAGE: {100 * land_coverage:.1f} PERCENT - MAPPED WATER EXCLUDED",
]
for index, track in enumerate(tracks, 1):
legend_width = options.width - 2 * options.margin
for index, text in enumerate(legend):
if len(text) * options.font_height > legend_width:
raise ValueError("Legend is too wide; reduce --font-height or increase page width.")
label(text_group, text, options.margin, legend_y + index * row_height, options.font_height)
column_gap = 2 * options.font_height
column_width = (legend_width - column_gap) / 2
for offset, track in enumerate(tracks):
first = track.observations[0]
altitude_label = (
"N/A" if track.average_altitude is None else f"{track.average_altitude:.0f} M"
)
legend.append(
f"{index}: {utc(track.start)} TO {utc(track.end)} "
f"{first.callsign or 'CALLSIGN N/A'} ({first.icao24.upper()}) BARO ALT {altitude_label}"
text = (
f"{offset + 1}: {utc((track.start + track.end) / 2)} "
f"{first.callsign or 'CALLSIGN N/A'} ({first.icao24.upper()}) Alt {altitude_label}"
)
legend.extend(
[
"MAP: OPENSTREETMAP CONTRIBUTORS (ODBL) - OPENSTREETMAP.ORG/COPYRIGHT",
"FLIGHTS: THE OPENSKY NETWORK - OPENSKY-NETWORK.ORG",
]
)
if len(text) * options.font_height > column_width:
raise ValueError(
"Flight legend entry is too wide for two columns; reduce --font-height "
"or increase page width."
)
column, row = divmod(offset, flight_rows)
label(
text_group,
text,
options.margin + column * (column_width + column_gap),
legend_y + (len(legend) + row) * row_height,
options.font_height,
)
attribution = [
"MAP: OPENSTREETMAP CONTRIBUTORS (ODBL) - OPENSTREETMAP.ORG/COPYRIGHT",
"FLIGHTS: THE OPENSKY NETWORK - OPENSKY-NETWORK.ORG",
]
if terrain_attribution:
legend.append("TERRAIN: AHN DTM VIA PDOK (CC0) - ELEVATIONS RELATIVE TO NAP")
for index, text in enumerate(legend):
if len(text) * options.font_height > options.width - 2 * options.margin:
attribution.append("TERRAIN: AHN DTM VIA PDOK (CC0) - ELEVATIONS RELATIVE TO NAP")
for index, text in enumerate(attribution):
if len(text) * options.font_height > legend_width:
raise ValueError("Legend is too wide; reduce --font-height or increase page width.")
label(text_group, text, options.margin, legend_y + index * row_height, options.font_height)
label(
text_group,
text,
options.margin,
legend_y + (len(legend) + flight_rows + index) * row_height,
options.font_height,
)
output.parent.mkdir(parents=True, exist_ok=True)
temp_path: Path | None = None
try: