feat(story-2.3): implement home marker and airspace outline rendering

Adds draw_home_marker() in overlay.py (red cross at projected home
position) and draw_airspace() in airspace.py (GeoJSON Polygon
boundaries in blue, graceful FileNotFoundError handling). Includes
airspace fixture and three tests covering all acceptance criteria.
All 70 tests pass; ruff check and format clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Edholm
2026-04-22 23:16:03 -04:00
parent 5a18d0867a
commit e8ce0602a4
6 changed files with 144 additions and 29 deletions
+21 -1
View File
@@ -1 +1,21 @@
{"type": "FeatureCollection", "features": []}
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-6.5, 53.3],
[-5.5, 53.3],
[-5.5, 53.7],
[-6.5, 53.7],
[-6.5, 53.3]
]
]
},
"properties": {"name": "Test Airspace"}
}
]
}
+53
View File
@@ -0,0 +1,53 @@
from __future__ import annotations
import pathlib
import pytest
from PIL import Image
from planemapper.renderer.airspace import draw_airspace
from planemapper.renderer.overlay import draw_home_marker
from planemapper.renderer.projection import MapBounds
FIXTURE_DIR = pathlib.Path(__file__).parent / "fixtures"
@pytest.fixture
def white_image() -> Image.Image:
return Image.new("RGB", (800, 480), color=(255, 255, 255))
@pytest.fixture
def bounds() -> MapBounds:
return MapBounds(home_lat=53.0, home_lon=-6.0, radius_nm=100.0)
def test_home_marker_draws_red_cross(white_image: Image.Image, bounds: MapBounds) -> None:
draw_home_marker(white_image, bounds)
# Centre pixel should be red (COLOUR_HOME_MARKER)
assert white_image.getpixel((400, 240)) == (255, 0, 0)
def test_airspace_drawn_without_exception(
white_image: Image.Image,
bounds: MapBounds,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(
"planemapper.renderer.airspace.AIRSPACE_PATH",
FIXTURE_DIR / "airspace_sample.geojson",
)
draw_airspace(white_image, bounds) # should not raise
def test_airspace_missing_file_no_exception(
white_image: Image.Image,
bounds: MapBounds,
tmp_path: pathlib.Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(
"planemapper.renderer.airspace.AIRSPACE_PATH",
tmp_path / "nonexistent.geojson",
)
draw_airspace(white_image, bounds) # should not raise — logs WARNING instead