Ask a service that knows which leg it is, and fetch a map worth the screen
A callsign is a flight number rather than a leg, and the free registers hold one route per number, so an aircraft over Arizona kept being handed a hop between two airports in Texas. Nothing on the air settles it: ADS-B carries no origin or destination. A commercial schedule service does know, because it holds the day's actual movements. Four are wired up and all four are optional: FlightAware AeroAPI, Flightradar24, OAG and Cirium. Each is asked before the free databases and each answers for the moment the aircraft was overhead rather than for the flight number in general, so the leg chosen is the one that was in the air. With no keys set nothing changes at all: a source with no key is skipped rather than asked and refused, and the free databases answer as before. Keys come from the environment and are never written to the settings file, because a settings file is meant to be copied between machines and pasted into a message asking for help, and an API key is not. There is a test that holds that line. None of the four has been run against its live service, since each wants a paid account. They were written from the published response shapes and are tested against those shapes, so each reader finds what it recognises and returns nothing otherwise: a service that has changed since costs a route rather than a scan. Cirium's plain departureTime is local and carries no offset, so the UTC field is preferred where it is there -- reading the local one as UTC is up to half a day out, which is exactly far enough to pick the wrong leg of the same number. Reading now happens inside the same guard as asking, as an answer shaped differently from the documented one is the failure most likely to actually happen. And the map. The zoom is now chosen from how wide the picture is rather than from the area alone, with half again over the width fetched and averaged down, since a downscaled tile is sharp and an upscaled one is not. The window fetches a little more world than it shows so panning does not leave the ground blank, and now fetches that bigger piece at the bigger piece's own size: rendering it into the window's own pixels and stretching it back was a fifth of an upscale over the whole map, which is what a sharp map looks like when it looks blurred. The comment in the fetcher said the opposite of what the code did, which is how it stayed hidden. At 1920 by 1080 over a hundred miles the tiles now hold about 1.6 times the pixels the window wants. At 3840 by 2160 the tile budget is reached, the zoom stops climbing and the map is enlarged after all; a smaller radius buys the detail back, and somebody else's tile server is not a thing to fetch a thousand tiles from for one picture. The README says so rather than implying otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
This commit is contained in:
parent
87b0954f0c
commit
8eb3bdbb86
20 changed files with 1758 additions and 66 deletions
|
|
@ -598,3 +598,191 @@ def test_a_route_written_now_is_kept(tmp_path, register):
|
|||
again = FlightBook(cache=book.cache_path)
|
||||
entry = again.get("4CA1FA", "RYR1234")
|
||||
assert entry.origin_country == "GB"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# A callsign is a flight number, not a leg
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _route(origin=(29.65, -95.28), destination=(29.53, -98.47)) -> Flight:
|
||||
"""Houston Hobby to San Antonio: a half-hour hop across Texas."""
|
||||
return Flight(icao="AC0FB6", callsign="SWA930",
|
||||
origin_code="KHOU", origin="Houston",
|
||||
origin_lat=origin[0], origin_lon=origin[1],
|
||||
destination_code="KSAT", destination="San Antonio",
|
||||
destination_lat=destination[0], destination_lon=destination[1])
|
||||
|
||||
|
||||
def test_an_aircraft_on_its_route_is_believed():
|
||||
from bandsaunter.flights import route_fits
|
||||
|
||||
# Halfway between the two, and at either end.
|
||||
assert route_fits(_route(), 29.6, -96.9)
|
||||
assert route_fits(_route(), 29.65, -95.28)
|
||||
assert route_fits(_route(), 29.53, -98.47)
|
||||
|
||||
|
||||
def test_an_aircraft_nowhere_near_its_route_is_not():
|
||||
"""The one that prompted this: a 737 over Arizona at cruise, given a
|
||||
thirty-minute hop between two airports in Texas. An airline runs the
|
||||
same flight number over several legs in a day and a register holds one
|
||||
route for it."""
|
||||
from bandsaunter.flights import route_fits
|
||||
|
||||
assert not route_fits(_route(), 32.7086, -110.4061)
|
||||
|
||||
|
||||
def test_a_long_route_is_given_more_room_than_a_short_one():
|
||||
"""An aircraft on a transcontinental leg wanders further from the great
|
||||
circle than one on a hop, and neither is a wrong route."""
|
||||
from bandsaunter.flights import route_fits
|
||||
|
||||
coast = _route(origin=(40.69, -74.17), destination=(33.43, -112.01))
|
||||
assert route_fits(coast, 32.7086, -110.4061) # Newark to Phoenix
|
||||
assert route_fits(coast, 39.0, -95.0)
|
||||
|
||||
|
||||
def test_a_route_with_no_positions_is_left_alone():
|
||||
"""Not knowing is not the same as knowing it is wrong."""
|
||||
from bandsaunter.flights import route_fits
|
||||
|
||||
bare = Flight(icao="A", origin_code="KHOU", destination_code="KSAT")
|
||||
assert route_fits(bare, 32.7, -110.4)
|
||||
assert route_fits(Flight(icao="A"), 0.0, 0.0)
|
||||
|
||||
|
||||
def test_a_route_that_cannot_be_flown_is_still_written_down(tmp_path):
|
||||
"""Said rather than hidden: it is what the register holds for that
|
||||
flight number, and worth having."""
|
||||
from bandsaunter.flightlog import report
|
||||
|
||||
log = _log(tmp_path)
|
||||
for i in range(3):
|
||||
log.append(_Frame(icao="AC0FB6", callsign="SWA930"),
|
||||
_Craft(32.7086, -110.4061 + i * 0.01, "SWA930"),
|
||||
when=1_000_000.0 + i)
|
||||
log.close()
|
||||
|
||||
class _Book:
|
||||
def get(self, icao, callsign=""):
|
||||
return _route()
|
||||
|
||||
text = "\n".join(report(read_logs(log.path), _Book()))
|
||||
assert "Houston" in text and "San Antonio" in text
|
||||
assert "nowhere near it" in text
|
||||
|
||||
|
||||
def test_a_route_that_fits_is_not_second_guessed(tmp_path):
|
||||
from bandsaunter.flightlog import report
|
||||
|
||||
log = _log(tmp_path)
|
||||
for i in range(3):
|
||||
log.append(_Frame(icao="AC0FB6", callsign="SWA930"),
|
||||
_Craft(29.6, -96.9 + i * 0.01, "SWA930"),
|
||||
when=1_000_000.0 + i)
|
||||
log.close()
|
||||
|
||||
class _Book:
|
||||
def get(self, icao, callsign=""):
|
||||
return _route()
|
||||
|
||||
text = "\n".join(report(read_logs(log.path), _Book()))
|
||||
assert "Houston" in text
|
||||
assert "nowhere near" not in text
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Picking the leg out of a day's work
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
HEXDB_DAY = {"flight": "AAL2465", "route": "KORD-KEWR-KORD"}
|
||||
HEXDB_LEG = {"flight": "BAW49", "route": "EGLL-KSEA"}
|
||||
|
||||
AIRPORTS = {
|
||||
"KORD": {"code": "KORD", "name": "Chicago O'Hare", "latitude": 41.978,
|
||||
"longitude": -87.905, "country": "US"},
|
||||
"KEWR": {"code": "KEWR", "name": "Newark Liberty", "latitude": 40.692,
|
||||
"longitude": -74.169, "country": "US"},
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def with_airports(register, monkeypatch):
|
||||
"""A book that knows where a handful of airports are, and no network."""
|
||||
book, asked, answers = register
|
||||
monkeypatch.setattr(type(book), "airport",
|
||||
lambda self, code: AIRPORTS.get(code.upper(), {}))
|
||||
return book, asked, answers
|
||||
|
||||
|
||||
def test_two_stops_are_a_route(register):
|
||||
book, asked, answers = register
|
||||
answers["hexdb.io/api/v1/route"] = HEXDB_LEG
|
||||
entry = book.get("400001", "BAW49")
|
||||
book.wait(5.0)
|
||||
assert (entry.origin_code, entry.destination_code) == ("EGLL", "KSEA")
|
||||
assert entry.stops == ("EGLL", "KSEA")
|
||||
|
||||
|
||||
def test_a_whole_day_of_stops_is_not_read_as_one_flight(register):
|
||||
""""KORD-KEWR-KORD" read from its ends is Chicago to Chicago, which is
|
||||
not a flight."""
|
||||
book, asked, answers = register
|
||||
answers["hexdb.io/api/v1/route"] = HEXDB_DAY
|
||||
entry = book.get("AD64CD", "AAL2465")
|
||||
book.wait(5.0)
|
||||
assert entry.stops == ("KORD", "KEWR", "KORD")
|
||||
assert entry.origin_code == "" and entry.destination_code == ""
|
||||
|
||||
|
||||
def test_the_leg_is_picked_out_by_where_the_aircraft_is(with_airports):
|
||||
book, asked, answers = with_airports
|
||||
answers["hexdb.io/api/v1/route"] = HEXDB_DAY
|
||||
entry = book.get("AD64CD", "AAL2465")
|
||||
book.wait(5.0)
|
||||
# Over Pennsylvania, which is on the way from Chicago to Newark.
|
||||
assert book.leg_for(entry, 40.8, -78.0) == ("KORD", "KEWR")
|
||||
|
||||
|
||||
def test_resolving_fills_the_leg_in(with_airports):
|
||||
book, asked, answers = with_airports
|
||||
answers["hexdb.io/api/v1/route"] = HEXDB_DAY
|
||||
entry = book.get("AD64CD", "AAL2465")
|
||||
book.wait(5.0)
|
||||
got = book.resolve(entry, 40.8, -78.0)
|
||||
assert got.origin_code == "KORD" and got.destination_code == "KEWR"
|
||||
assert "Chicago" in got.origin and got.origin_country == "US"
|
||||
assert got.origin_lat == pytest.approx(41.978)
|
||||
|
||||
|
||||
def test_an_aircraft_on_none_of_the_legs_is_given_none_of_them(with_airports):
|
||||
"""Which is an answer, and a better one than naming a leg it cannot
|
||||
be on."""
|
||||
book, asked, answers = with_airports
|
||||
answers["hexdb.io/api/v1/route"] = HEXDB_DAY
|
||||
entry = book.get("AD64CD", "AAL2465")
|
||||
book.wait(5.0)
|
||||
assert book.leg_for(entry, 32.7, -110.4) is None # over Arizona
|
||||
assert book.resolve(entry, 32.7, -110.4).origin_code == ""
|
||||
|
||||
|
||||
def test_resolving_leaves_an_ordinary_route_alone(with_airports):
|
||||
book, asked, answers = with_airports
|
||||
answers["hexdb.io/api/v1/route"] = HEXDB_LEG
|
||||
entry = book.get("400001", "BAW49")
|
||||
book.wait(5.0)
|
||||
assert book.resolve(entry, 51.0, -20.0) is entry
|
||||
|
||||
|
||||
def test_an_airport_nobody_can_place_costs_only_its_own_leg(register,
|
||||
monkeypatch):
|
||||
book, asked, answers = register
|
||||
monkeypatch.setattr(type(book), "airport",
|
||||
lambda self, code: AIRPORTS.get(code.upper(), {}))
|
||||
answers["hexdb.io/api/v1/route"] = {"flight": "X", "route":
|
||||
"KORD-ZZZZ-KEWR"}
|
||||
entry = book.get("AD64CD", "X")
|
||||
book.wait(5.0)
|
||||
# The middle stop cannot be placed, so neither leg touching it can be
|
||||
# tested; nothing is claimed.
|
||||
assert book.leg_for(entry, 40.8, -78.0) is None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue