feat(renderer): implement per-aircraft drawing — story 2-5
Implements draw_aircraft() with heading arrow (filled/outlined for MLAT), callsign+altitude label, and a 5-dot fade trail. Adds 5 tests covering all ACs; all 94 tests pass, ruff lint and format clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from planemapper.models import Aircraft
|
||||
from planemapper.renderer.aircraft import draw_aircraft
|
||||
|
||||
|
||||
def _white_image() -> Image.Image:
|
||||
return Image.new("RGB", (800, 480), color=(255, 255, 255))
|
||||
|
||||
|
||||
def _aircraft(**kwargs) -> Aircraft:
|
||||
defaults = {"icao": "ABC123", "lat": 53.0, "lon": -6.0}
|
||||
defaults.update(kwargs)
|
||||
return Aircraft(**defaults)
|
||||
|
||||
|
||||
def test_heading_east_changes_pixels() -> None:
|
||||
img = _white_image()
|
||||
ac = _aircraft(heading=90.0, altitude_ft=10000)
|
||||
draw_aircraft(img, ac, (400, 240), collections.deque())
|
||||
# Arrow should have painted at least one non-white pixel near centre
|
||||
changed = any(
|
||||
img.getpixel((x, y)) != (255, 255, 255) for x in range(388, 413) for y in range(228, 253)
|
||||
)
|
||||
assert changed
|
||||
|
||||
|
||||
def test_label_with_callsign_no_exception() -> None:
|
||||
img = _white_image()
|
||||
ac = _aircraft(callsign="BAW1", altitude_ft=28000)
|
||||
result = draw_aircraft(img, ac, (400, 240), collections.deque())
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_trail_drawn_no_exception() -> None:
|
||||
img = _white_image()
|
||||
ac = _aircraft(altitude_ft=5000)
|
||||
trail: collections.deque[tuple[int, int]] = collections.deque(
|
||||
[(390, 230), (380, 220), (370, 210)]
|
||||
)
|
||||
result = draw_aircraft(img, ac, (400, 240), trail)
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_mlat_aircraft_no_exception() -> None:
|
||||
img = _white_image()
|
||||
ac = _aircraft(is_mlat=True, altitude_ft=8000)
|
||||
result = draw_aircraft(img, ac, (400, 240), collections.deque())
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_empty_callsign_no_exception() -> None:
|
||||
img = _white_image()
|
||||
ac = _aircraft(callsign="", altitude_ft=15000)
|
||||
result = draw_aircraft(img, ac, (400, 240), collections.deque())
|
||||
assert result is None
|
||||
Reference in New Issue
Block a user