feat(2-6): stateful Renderer, DisplayInterface, and pipeline smoke test
Implements story 2-6: Renderer class with per-aircraft trail history (deque capped at TRAIL_MAX_DOTS=5), NullDisplay with DEBUG logging, WaveshareDisplay stub, and end-to-end pipeline smoke test. All 96 tests pass; ruff check and format clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Protocol
|
||||
|
||||
from PIL import Image
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DisplayInterface(Protocol):
|
||||
def show(self, image: Image.Image) -> None: ...
|
||||
@@ -9,4 +14,9 @@ class DisplayInterface(Protocol):
|
||||
|
||||
class NullDisplay:
|
||||
def show(self, image: Image.Image) -> None:
|
||||
pass
|
||||
log.debug("NullDisplay.show: %dx%d", image.width, image.height)
|
||||
|
||||
|
||||
class WaveshareDisplay:
|
||||
def show(self, image: Image.Image) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -1 +1,33 @@
|
||||
# stub
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from planemapper.constants import TRAIL_MAX_DOTS
|
||||
from planemapper.models import Aircraft
|
||||
from planemapper.renderer.aircraft import draw_aircraft
|
||||
from planemapper.renderer.airspace import draw_airspace
|
||||
from planemapper.renderer.overlay import draw_home_marker
|
||||
from planemapper.renderer.projection import MapBounds, project
|
||||
|
||||
|
||||
class Renderer:
|
||||
def __init__(self, base_map: Image.Image, bounds: MapBounds) -> None:
|
||||
self._base_map = base_map
|
||||
self._bounds = bounds
|
||||
self._trails: dict[str, collections.deque[tuple[int, int]]] = {}
|
||||
|
||||
def render(self, aircraft_list: list[Aircraft]) -> Image.Image:
|
||||
image = self._base_map.copy()
|
||||
draw_airspace(image, self._bounds)
|
||||
draw_home_marker(image, self._bounds)
|
||||
for aircraft in aircraft_list:
|
||||
pos = project(aircraft.lat, aircraft.lon, self._bounds)
|
||||
trail = self._trails.get(aircraft.icao, collections.deque())
|
||||
draw_aircraft(image, aircraft, pos, trail)
|
||||
trail.appendleft(pos)
|
||||
while len(trail) > TRAIL_MAX_DOTS:
|
||||
trail.pop()
|
||||
self._trails[aircraft.icao] = trail
|
||||
return image
|
||||
|
||||
Reference in New Issue
Block a user