Journey: stacked header, bus/train icons, drop gray text
- Origin/destination now on their own lines instead of one truncated "A -> B" line, which cut off the destination entirely on the small panel. - Each leg gets a colored dot (blue = train, green = bus), using the panel's exact palette colors so they render solid, not dithered. Mode is read from EFA's product.name (confirmed live: "StadtBus"/ "RegioBus"/"SchnellBus" vs "S-Bahn"/"Regionalzug"/"Zug"). - Detail-line text switched from dithered gray to solid black -- the gray was hard to read at the small font size the panel renders at.
This commit is contained in:
1 parent
06b90ffe6c
commit
080cb11501
1 file changed
+67
-22
+67
-22
@@ -56,7 +56,12 @@ REQUEST_HEADERS = {
|
|||||||
|
|
||||||
RED = (200, 0, 0)
|
RED = (200, 0, 0)
|
||||||
BLACK = (0, 0, 0)
|
BLACK = (0, 0, 0)
|
||||||
MUTED = (110, 110, 110)
|
# Pure panel colors (from the epd4in0e's 6-color quantization palette in
|
||||||
|
# display/waveshare_epd/epd4in0e.py) -- used for the train/bus mode dots so
|
||||||
|
# they render as solid color on the real hardware instead of dithering like
|
||||||
|
# an off-palette RGB value would.
|
||||||
|
BLUE = (0, 0, 255)
|
||||||
|
GREEN = (0, 255, 0)
|
||||||
|
|
||||||
# EFA's inclMOT_N flags select which transport modes a trip request is
|
# EFA's inclMOT_N flags select which transport modes a trip request is
|
||||||
# allowed to use -- N is an index into EFA's internal mode table, not the
|
# allowed to use -- N is an index into EFA's internal mode table, not the
|
||||||
@@ -228,8 +233,16 @@ class JourneyScreen(Screen):
|
|||||||
)
|
)
|
||||||
if is_walk:
|
if is_walk:
|
||||||
continue
|
continue
|
||||||
|
# EFA's product.name is a reliable human-readable mode string
|
||||||
|
# ("StadtBus", "RegioBus", "SchnellBus", "Niederflurbus" for
|
||||||
|
# buses; "S-Bahn", "Regionalzug", "Zug" for trains) -- more
|
||||||
|
# robust than product.class, whose numeric values aren't a
|
||||||
|
# clean bus/train split (bus classes 5-7 sit right next to
|
||||||
|
# S-Bahn's class 1). Confirmed live against VRR.
|
||||||
|
is_bus = "bus" in (product.get("name") or "").lower()
|
||||||
legs.append({
|
legs.append({
|
||||||
"line": transportation.get("name") or "?",
|
"line": transportation.get("name") or "?",
|
||||||
|
"mode": "bus" if is_bus else "train",
|
||||||
"origin": leg.get("origin") or {},
|
"origin": leg.get("origin") or {},
|
||||||
"destination": leg.get("destination") or {},
|
"destination": leg.get("destination") or {},
|
||||||
"infos": leg.get("infos") or [],
|
"infos": leg.get("infos") or [],
|
||||||
@@ -270,7 +283,7 @@ class JourneyScreen(Screen):
|
|||||||
"departure": dep_actual.astimezone().strftime("%H:%M"),
|
"departure": dep_actual.astimezone().strftime("%H:%M"),
|
||||||
"delay_min": max(0, delay_min),
|
"delay_min": max(0, delay_min),
|
||||||
"arrival": arr_actual.astimezone().strftime("%H:%M") if arr_actual else "--:--",
|
"arrival": arr_actual.astimezone().strftime("%H:%M") if arr_actual else "--:--",
|
||||||
"lines": [leg["line"] for leg in legs],
|
"legs": [{"name": leg["line"], "mode": leg["mode"]} for leg in legs],
|
||||||
"transfers": len(legs) - 1,
|
"transfers": len(legs) - 1,
|
||||||
"platform": (first["origin"].get("properties") or {}).get("platformName") or "",
|
"platform": (first["origin"].get("properties") or {}).get("platformName") or "",
|
||||||
"notice": "; ".join(notice_titles),
|
"notice": "; ".join(notice_titles),
|
||||||
@@ -297,11 +310,18 @@ class JourneyScreen(Screen):
|
|||||||
pad = width * 0.04
|
pad = width * 0.04
|
||||||
origin = settings.get("origin_label") or settings.get("origin_query") or "?"
|
origin = settings.get("origin_label") or settings.get("origin_query") or "?"
|
||||||
destination = settings.get("destination_label") or settings.get("destination_query") or "?"
|
destination = settings.get("destination_label") or settings.get("destination_query") or "?"
|
||||||
header_font = load_font(size=int(scale * 0.05), bold=True)
|
# Origin/destination stacked on their own lines rather than one
|
||||||
header = truncate_to_width(draw, f"{origin} → {destination}", header_font, width - 2 * pad)
|
# "A -> B" line -- on the small panel a single combined line
|
||||||
draw.text((pad, height * 0.045), header, font=header_font, fill=BLACK)
|
# truncated too early to show the destination at all.
|
||||||
|
header_font = load_font(size=int(scale * 0.045), bold=True)
|
||||||
|
origin_line = truncate_to_width(draw, origin, header_font, width - 2 * pad)
|
||||||
|
dest_line = truncate_to_width(draw, f"→ {destination}", header_font, width - 2 * pad)
|
||||||
|
line_gap = header_font.size * 1.22
|
||||||
|
header_top = height * 0.035
|
||||||
|
draw.text((pad, header_top), origin_line, font=header_font, fill=BLACK)
|
||||||
|
draw.text((pad, header_top + line_gap), dest_line, font=header_font, fill=BLACK)
|
||||||
|
|
||||||
header_bottom = height * 0.045 + header_font.size * 1.35
|
header_bottom = header_top + line_gap * 2
|
||||||
draw.rectangle((pad, header_bottom, width - pad, header_bottom + 2), fill=BLACK)
|
draw.rectangle((pad, header_bottom, width - pad, header_bottom + 2), fill=BLACK)
|
||||||
|
|
||||||
row_top = header_bottom + height * 0.03
|
row_top = header_bottom + height * 0.03
|
||||||
@@ -325,31 +345,56 @@ class JourneyScreen(Screen):
|
|||||||
dep_w = text_width(draw, dep_text, time_font)
|
dep_w = text_width(draw, dep_text, time_font)
|
||||||
|
|
||||||
arrow_x = pad + dep_w + scale * 0.02
|
arrow_x = pad + dep_w + scale * 0.02
|
||||||
draw.text((arrow_x, top_line_y + (time_font.size - arrow_font.size) / 2), "→", font=arrow_font, fill=MUTED)
|
draw.text((arrow_x, top_line_y + (time_font.size - arrow_font.size) / 2), "→", font=arrow_font, fill=BLACK)
|
||||||
arrow_w = text_width(draw, "→", arrow_font)
|
arrow_w = text_width(draw, "→", arrow_font)
|
||||||
|
|
||||||
draw.text((arrow_x + arrow_w + scale * 0.02, top_line_y), journey["arrival"], font=time_font, fill=BLACK)
|
draw.text((arrow_x + arrow_w + scale * 0.02, top_line_y), journey["arrival"], font=time_font, fill=BLACK)
|
||||||
|
|
||||||
detail_parts = [" → ".join(journey["lines"])]
|
# Detail line: a colored dot per leg (blue = train, green = bus --
|
||||||
if journey["transfers"] > 0:
|
# pure panel colors, no dithering) followed by its line name, then
|
||||||
detail_parts.append(f"{journey['transfers']}x umsteigen")
|
# transfer/platform/notice info. Drawn as manually-placed segments
|
||||||
if journey["platform"]:
|
# instead of one joined string so each leg can carry its own dot.
|
||||||
detail_parts.append(f"Gl. {journey['platform']}")
|
|
||||||
if journey["notice"]:
|
|
||||||
detail_parts.append(journey["notice"])
|
|
||||||
|
|
||||||
detail_y = top_line_y + time_font.size * 1.25
|
detail_y = top_line_y + time_font.size * 1.25
|
||||||
detail_x = pad
|
right_edge = width - pad
|
||||||
if journey["notice"]:
|
icon_d = detail_font.size * 0.55
|
||||||
draw.text((detail_x, detail_y), "⚠", font=detail_font, fill=RED)
|
cursor_x = pad
|
||||||
detail_x += text_width(draw, "⚠", detail_font) + scale * 0.015
|
|
||||||
|
|
||||||
detail_line = truncate_to_width(draw, " · ".join(detail_parts), detail_font, width - pad - detail_x)
|
def draw_segment(text, color=BLACK, avail=None):
|
||||||
draw.text((detail_x, detail_y), detail_line, font=detail_font, fill=MUTED)
|
nonlocal cursor_x
|
||||||
|
if avail is not None:
|
||||||
|
text = truncate_to_width(draw, text, detail_font, avail)
|
||||||
|
draw.text((cursor_x, detail_y), text, font=detail_font, fill=color)
|
||||||
|
cursor_x += text_width(draw, text, detail_font)
|
||||||
|
return text
|
||||||
|
|
||||||
|
for idx, leg in enumerate(journey["legs"]):
|
||||||
|
if cursor_x >= right_edge - icon_d:
|
||||||
|
break
|
||||||
|
dot_color = BLUE if leg["mode"] == "train" else GREEN
|
||||||
|
dot_top = detail_y + (detail_font.size - icon_d) / 2
|
||||||
|
draw.ellipse((cursor_x, dot_top, cursor_x + icon_d, dot_top + icon_d), fill=dot_color)
|
||||||
|
cursor_x += icon_d + scale * 0.01
|
||||||
|
|
||||||
|
shown = draw_segment(leg["name"], avail=right_edge - cursor_x)
|
||||||
|
if idx < len(journey["legs"]) - 1 and cursor_x < right_edge and shown == leg["name"]:
|
||||||
|
draw_segment(" → ", avail=right_edge - cursor_x)
|
||||||
|
|
||||||
|
trailing_parts = []
|
||||||
|
if journey["transfers"] > 0:
|
||||||
|
trailing_parts.append(f"{journey['transfers']}x umsteigen")
|
||||||
|
if journey["platform"]:
|
||||||
|
trailing_parts.append(f"Gl. {journey['platform']}")
|
||||||
|
if trailing_parts and cursor_x < right_edge:
|
||||||
|
draw_segment(" · " + " · ".join(trailing_parts), avail=right_edge - cursor_x)
|
||||||
|
|
||||||
|
if journey["notice"] and cursor_x < right_edge - icon_d:
|
||||||
|
cursor_x += scale * 0.01
|
||||||
|
draw_segment("⚠ ", color=RED)
|
||||||
|
draw_segment(journey["notice"], avail=right_edge - cursor_x)
|
||||||
|
|
||||||
return image
|
return image
|
||||||
|
|
||||||
def _render_message(self, draw, width, height, scale, msg):
|
def _render_message(self, draw, width, height, scale, msg):
|
||||||
font = load_font(size=int(scale * 0.06))
|
font = load_font(size=int(scale * 0.06))
|
||||||
mw = text_width(draw, msg, font)
|
mw = text_width(draw, msg, font)
|
||||||
draw.text(((width - mw) / 2, height / 2), msg, font=font, fill=MUTED)
|
draw.text(((width - mw) / 2, height / 2), msg, font=font, fill=BLACK)
|
||||||
Reference in new issue
Block a user