A sharper map, and aircraft that fade rather than blink out

Three things asked for, and a fourth found while doing them.

The map looked like a photograph of a map, and did so twice over.  The
window fetched at its own pixel size but for a box a third larger in each
direction -- the margin added to stop the ground blinking -- and then cut
the middle out, so every pixel was enlarged by two thirds.  It now asks
for enough pixels to cover the bigger box at the window's own detail.
Underneath that, both the window and the animation took the nearest source
pixel: the tile mosaic is commonly half again the size of the picture, so
most of every tile was thrown away and what survived was the aliasing.
Both now average the source pixels that fall in each output cell, done as
the difference of a running total rather than a loop.

An aircraft that goes quiet now fades instead of vanishing.  Taking it off
between one frame and the next says it stopped existing; fading says it
stopped talking, which is what happened.  It fades where it was last
actually seen and never along a reckoned track, because the reason for
giving up on it is that where it would be by now is a guess.  --fade sets
how long, and it is in the menu.  The window has alpha and fades smoothly;
an indexed picture cannot blend, so the animation gained a fourth ramp at
a seventh of full and fades in four steps, which at a second apart reads
as a fade.  A trail fades with the aircraft it belongs to, and the box
goes before the symbol does.

The aircraft's country of registration carries a flag now as well as the
two ends of its route, from the register where one answered and from the
address block otherwise.

And the fourth: the window's header counted an aircraft that had gone
quiet as overhead, which was saying more than had been heard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
This commit is contained in:
The Dust Council 2026-09-04 19:04:09 -07:00
parent df080f6571
commit 87b0954f0c
17 changed files with 1609 additions and 101 deletions

View file

@ -922,3 +922,267 @@ def test_labels_step_aside_rather_than_landing_on_each_other():
for i, one in enumerate(taken):
for two in taken[i + 1:]:
assert not fm._overlaps(one, two), f"{one} overlaps {two}"
# ---------------------------------------------------------------------------
# The country an aircraft is registered in
# ---------------------------------------------------------------------------
def test_the_registration_carries_the_country_it_is_registered_in():
track = straight()
entry = _Entry()
entry.owner_country = "United Kingdom"
rows = dict((text, flag) for text, flag in
fm.label_lines(track, track.fixes[0], "knots", entry))
assert rows["B739 N904DN"] == "GB"
def test_the_address_block_stands_in_when_a_register_says_nothing():
"""The 24-bit address says which country issued it, by treaty, with no
website involved at all."""
track = straight()
entry = _Entry()
entry.owner_country = ""
entry.country = "United States"
rows = dict(fm.label_lines(track, track.fixes[0], "knots", entry))
assert rows["B739 N904DN"] == "US"
def test_a_country_nobody_named_gets_no_flag():
track = straight()
entry = _Entry()
entry.owner_country = ""
entry.country = ""
rows = dict(fm.label_lines(track, track.fixes[0], "knots", entry))
assert rows["B739 N904DN"] == ""
# ---------------------------------------------------------------------------
# Airports on the map
# ---------------------------------------------------------------------------
def test_an_airport_a_route_gave_a_position_for_is_marked():
known = {"A": _Entry()}
found = fm._airports_from(known)
assert {code for code, _, _ in found} == {"KATL", "EGLL"}
def test_an_airport_with_only_a_code_is_looked_up_so_it_can_be_marked():
"""A route names two airports and often gives a position for neither; an
airport that cannot be placed cannot be drawn."""
asked = []
class _Book:
def airports(self, codes):
asked.extend(codes)
return [{"code": code, "latitude": 32.1, "longitude": -110.9}
for code in codes]
entry = _Entry(origin_lat=0.0, origin_lon=0.0,
destination_lat=0.0, destination_lon=0.0)
found = fm._airports_from({"A": entry}, _Book())
assert sorted(asked) == ["EGLL", "KATL"]
assert len(found) == 2
def test_an_airport_that_cannot_be_looked_up_is_simply_not_drawn():
class _Book:
def airports(self, codes):
raise OSError("no network")
entry = _Entry(origin_lat=0.0, origin_lon=0.0,
destination_lat=0.0, destination_lon=0.0)
assert fm._airports_from({"A": entry}, _Book()) == []
def test_an_airport_already_placed_is_not_looked_up_again():
asked = []
class _Book:
def airports(self, codes):
asked.extend(codes)
return []
fm._airports_from({"A": _Entry()}, _Book())
assert asked == [], "asked about airports it already had"
def test_an_airport_is_drawn_where_it_is_and_named():
view = fm.fit(two_aircraft(), width=600)
middle = ((view.south + view.north) / 2, (view.west + view.east) / 2)
base = fm.background(view, airports=[("EGLL", middle[0], middle[1])])
x, y = view.xy(*middle)
marker = (base[y - 4:y + 5, x - 4:x + 5] == fm.AIRPORT)
assert marker.any(), "no marker where the airport is"
# And its name beside it, in the same colour, to the right of the marker.
beside = (base[y - 6:y + 8, x + 5:x + 60] == fm.AIRPORT)
assert beside.sum() > 10, "the airport was marked but not named"
def test_an_airport_off_the_edge_is_not_drawn():
view = fm.fit(two_aircraft(), width=600)
plain = fm.background(view)
away = fm.background(view, airports=[("KSEA", 47.4, -122.3)])
assert np.array_equal(plain, away)
# ---------------------------------------------------------------------------
# How dark the ground is
# ---------------------------------------------------------------------------
def _brightest_drawn(brightness):
"""The lightest the map actually gets at one brightness setting."""
top = int(fm.dim_ground(np.array([[fm.GROUND_SHADES - 1]]),
brightness)[0, 0])
return fm.PALETTE[fm.GROUND + top].astype(float).mean()
def test_the_map_is_light_enough_to_read():
"""It was too dark to make out a coastline at all."""
assert _brightest_drawn(fm.GROUND_BRIGHTNESS) > 110
def test_the_aircraft_stay_brighter_than_the_ground_they_are_over():
"""Whatever else, the picture has to stay about the aeroplanes."""
ground = _brightest_drawn(fm.GROUND_BRIGHTNESS)
for feet in (0, 10_000, 25_000, 40_000):
marker = fm.PALETTE[fm.RAMP + fm.altitude_step(feet)]
assert marker.astype(float).mean() > ground, feet
def test_there_is_room_to_turn_it_up_and_down():
assert _brightest_drawn(1.0) > _brightest_drawn(fm.GROUND_BRIGHTNESS)
assert _brightest_drawn(0.3) < _brightest_drawn(fm.GROUND_BRIGHTNESS)
@pytest.mark.parametrize("brightness", [0.0, -1.0, 5.0, 1.0, 0.1])
def test_a_brightness_outside_the_range_is_brought_back_into_it(brightness):
shades = fm.dim_ground(np.arange(fm.GROUND_SHADES, dtype=np.uint8),
brightness)
assert shades.min() >= 0 and shades.max() <= fm.GROUND_SHADES - 1
def test_turning_it_down_really_does_darken_the_picture():
levels = np.full((20, 20), fm.GROUND_SHADES - 1, dtype=np.uint8)
assert fm.dim_ground(levels, 0.3).max() < fm.dim_ground(levels, 0.9).max()
def test_the_darkest_ground_is_no_darker_than_the_background():
darkest = fm.PALETTE[fm.GROUND].astype(float).mean()
background = fm.PALETTE[fm.BG].astype(float).mean()
assert darkest >= background
# ---------------------------------------------------------------------------
# Fading out rather than blinking out
# ---------------------------------------------------------------------------
def test_an_aircraft_still_being_heard_is_at_full_strength():
track = straight(seconds=120)
seen = fm.showing(track, track.fixes[-1].at, stale=300.0, fade=20.0)
assert seen is not None and seen[1] == 1.0
def test_one_that_has_gone_quiet_fades_instead_of_disappearing():
track = straight(seconds=120)
last = track.fixes[-1].at
strengths = []
for gone in (301, 305, 310, 315, 319):
seen = fm.showing(track, last + gone, stale=300.0, fade=20.0)
assert seen is not None, gone
strengths.append(seen[1])
assert strengths == sorted(strengths, reverse=True)
assert strengths[0] > 0.9 and strengths[-1] < 0.1
def test_it_is_gone_once_the_fade_is_over():
track = straight(seconds=120)
last = track.fixes[-1].at
assert fm.showing(track, last + 321, stale=300.0, fade=20.0) is None
def test_no_fade_takes_it_away_the_moment_it_is_given_up_on():
track = straight(seconds=120)
last = track.fixes[-1].at
assert fm.showing(track, last + 301, stale=300.0, fade=0.0) is None
def test_it_fades_where_it_was_last_seen_and_not_where_it_might_be():
"""The whole reason for giving up on an aircraft is that where it would
be by now is a guess; fading it along that guess would be inventing an
aeroplane slowly instead of quickly."""
track = straight(seconds=120)
last = track.fixes[-1]
seen = fm.showing(track, last.at + 310, stale=300.0, fade=20.0)
assert (seen[0].latitude, seen[0].longitude) == (last.latitude,
last.longitude)
def test_before_it_was_ever_heard_it_is_still_not_drawn():
track = straight(seconds=120)
assert fm.showing(track, track.fixes[0].at - 60, fade=20.0) is None
def test_the_fading_aircraft_is_drawn_in_fainter_colours():
track = straight(seconds=120)
view = fm.fit([track], width=500)
base = fm.background(view)
last = track.fixes[-1].at
def drawn(when):
"""The aircraft's own pixels: what changed, not what was already
there. The altitude key along the bottom is painted in the same
colours and belongs to the background."""
frame = fm.render_frame(base, view, [track], when, stale=300.0,
fade=20.0, labels=False)
return frame[frame != base]
fresh = drawn(last)
faint = drawn(last + 318)
assert fresh.size and faint.size
# A live aircraft has its marker in the full colours; its trail behind it
# is dimmer on purpose, so only the brightest pixels are the test.
assert (fresh < fm.TRAIL).any(), "a live aircraft was drawn faintly"
# A nearly gone one has nothing in them at all.
assert not (faint < fm.FAINT).any(), "a fading aircraft was drawn brightly"
def test_a_faded_aircraft_keeps_its_symbol_and_loses_its_label():
track = straight(seconds=120)
view = fm.fit([track], width=500)
base = fm.background(view)
last = track.fixes[-1].at
known = {track.icao: _Entry()}
faint = fm.render_frame(base, view, [track], last + 318, stale=300.0,
fade=20.0, known=known)
assert (faint != base).any(), "nothing was drawn at all"
assert not _has_text(faint, "B739"), "the label survived the fade"
def test_the_trail_fades_with_the_aircraft_it_belongs_to():
"""The two are one thing on the picture, and half of it lingering would
be worse than either."""
track = straight(seconds=120)
view = fm.fit([track], width=500)
base = fm.background(view)
last = track.fixes[-1].at
faint = fm.render_frame(base, view, [track], last + 318, stale=300.0,
fade=20.0, labels=False)
drawn = faint[faint != base]
assert drawn.size
assert drawn.min() >= fm.FAINT, "the trail stayed behind when it faded"
def test_the_faint_colours_are_the_same_hues_only_dimmer():
for step in (0, 15, 31):
bright = fm.PALETTE[fm.RAMP + step].astype(float)
faint = fm.PALETTE[fm.FAINT + step].astype(float)
assert faint.sum() < bright.sum()
# The same colour, turned down: the ratios between the channels hold.
assert np.allclose(faint / max(1e-9, faint.sum()),
bright / bright.sum(), atol=0.03)
def test_the_palette_still_has_room_after_the_fade_colours():
assert fm.FAINT + fm.RAMP_STEPS < fm.TRANSPARENT
assert fm.PALETTE.shape == (256, 3)