From 87b0954f0c753d5bff8d65ff23df7005a2e8137a Mon Sep 17 00:00:00 2001 From: The Dust Council Date: Fri, 4 Sep 2026 19:04:09 -0700 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg --- README.md | 44 ++++- bandsaunter/__init__.py | 2 +- bandsaunter/aircraft.py | 45 ++++- bandsaunter/basemap.py | 174 +++++++++++++++++-- bandsaunter/cli.py | 15 ++ bandsaunter/flags.py | 76 +++++++- bandsaunter/flightmap.py | 192 +++++++++++++++++---- bandsaunter/flights.py | 58 ++++++- bandsaunter/livemap.py | 220 +++++++++++++++++++----- docs/realtime.png | Bin 103636 -> 105496 bytes packaging/bandsaunter.1 | 31 +++- packaging/make-man.py | 29 +++- packaging/saunterbrowse.1 | 2 +- tests/test_aircraft_menu.py | 3 +- tests/test_basemap.py | 222 +++++++++++++++++++++++- tests/test_flightmap.py | 264 ++++++++++++++++++++++++++++ tests/test_livemap.py | 333 +++++++++++++++++++++++++++++++++++- 17 files changed, 1609 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index b0b0b52..efe6a3c 100644 --- a/README.md +++ b/README.md @@ -1014,8 +1014,19 @@ the arrow first, so the two ends of the flight stay whole and sit under one another where they read as a pair. `d` cycles the detail — full box, just height and speed, or symbols alone — -for when the sky is busy. `t` toggles trails, `g` the map underneath, `+`/`-` -the range, `q` closes it. +for when the sky is busy. `t` toggles trails, `g` the map underneath, `[`/`]` +its brightness, `+`/`-` the range, `q` closes it. + +Aircraft fade out here too, over `--fade` seconds, keeping their symbol and +losing their box as they go — a box at a tenth of its colour is something in +the way of the aircraft still flying. One that has gone quiet stops being +counted as overhead, since saying it is would be saying more than was heard. + +The map holds still. It is fetched a third larger than the window in every +direction, and the middle of the view settles once and then stays put rather +than being recomputed as aircraft come and go — otherwise the view shifts by a +fraction of a mile every few seconds, throws away the tiles fetched for the +old one, and the ground blinks out while new ones arrive. **Closing the window leaves exactly the files a passive capture does**: the same log, the same report, the same KML and animation, because it is the same @@ -1138,6 +1149,14 @@ the twenty minutes the data says it took. Nothing moves at a constant speed for the look of the thing, and an aircraft not heard from for five minutes stops being drawn rather than being flown on by guesswork. +**An aircraft that goes quiet fades rather than vanishing.** Taking it off the +picture between one frame and the next says it stopped existing; fading it +says it stopped talking, which is what happened. It fades where it was last +actually seen and never along a reckoned track — the reason for giving up on +it in the first place is that where it would be by now is a guess. `--fade +SECONDS` (20 by default, and in the options menu) sets how long that takes; +zero takes it away at once, as before. + Time runs at `--speed` seconds of flying per second of animation, or give `--seconds` and let it work the speed out. Altitude is the colour, low warm to high cold, with the key along the bottom; the trail behind each aircraft is the @@ -1165,6 +1184,16 @@ at all. Where a route arrives as nothing but a pair of airport codes, the country comes from the code itself: the first letter or two of an ICAO code is a region, so `EGLL` is British and `KSEA` American with nothing else to go on. +The aircraft's own **country of registration** gets a flag too, beside the +type and registration — from the register where one answered, and otherwise +from the 24-bit address, which says by treaty who issued it. + +**Every aerodrome under the picture is marked**, not only the ones being flown +between: a receiver hears aircraft over its own county, and the county's +airports are what say where on the map you are looking. They come from the +same OpenStreetMap data the tiles are drawn from, asked as a question rather +than a picture, once per area and kept for a month — a runway does not move. +`--no-airports` turns it off. The GIF is written here from first principles — a palette, an LZW stream, frame differencing with a transparent index — in the same spirit as the PNGs @@ -1255,6 +1284,17 @@ on their own, `--tiles URL` points at another server (your own, if you run one), and when there is no network and nothing cached the picture falls back to the plain grid it drew before. +The map is drawn at the resolution it was fetched at, not stretched: tiles are +averaged down to the picture rather than point-sampled, so lettering and roads +stay whole instead of breaking up, and the window fetches enough pixels to +cover its margin at full detail rather than enlarging what it has. + +`--map-brightness PERCENT` (70 by default) is how far up its range the map is +drawn. The ground has to stay dark enough that the aircraft are the brightest +thing on the picture and light enough that a coastline can be made out at all, +and which way to err depends on the screen you are looking at. In the window, +`[` and `]` change it while it runs. + ## Meters and weather sensors Two things on the ISM bands are worth naming rather than reporting as hex. diff --git a/bandsaunter/__init__.py b/bandsaunter/__init__.py index fba1582..35611de 100755 --- a/bandsaunter/__init__.py +++ b/bandsaunter/__init__.py @@ -9,7 +9,7 @@ and transcribing speech. # 2026-08-21_02 is the second build made on the 21st. The revision is padded # to two digits so versions sort as text. VERSION_DATE = "2026-09-04" -VERSION_REVISION = 4 +VERSION_REVISION = 6 __version__ = f"{VERSION_DATE}_{VERSION_REVISION:02d}" diff --git a/bandsaunter/aircraft.py b/bandsaunter/aircraft.py index 3ef762d..089adbb 100644 --- a/bandsaunter/aircraft.py +++ b/bandsaunter/aircraft.py @@ -110,11 +110,14 @@ class AircraftOptions: width: int = 960 trail: float = 0.0 stale: float = 300.0 + fade: float = 20.0 labels: bool = True radius: float = 100.0 location: str = "" recheck: bool = False basemap: bool = True + airports: bool = True + map_brightness: int = 70 tile_url: str = "" def validate(self) -> list[str]: @@ -268,6 +271,19 @@ OPTIONS: tuple[Setting, ...] = ( "leaves a comet tail instead, which is easier to follow when many " "aircraft cross the same piece of sky.", unit="s", minimum=0.0, flags=("--trail",), example="0"), + O("fade", "Fade out over", "Drawing", "float", + "how long an aircraft takes to fade away once it has gone quiet", + "An aircraft that stops transmitting has not stopped existing, and " + "taking it off the picture between one frame and the next says that it " + "did. Instead it is left where it was last actually seen and fades from " + "there, which reads as an aircraft going quiet rather than as a blink. " + "Nothing is invented by it: the fading happens at the last known " + "position, never at a reckoned one, because the reason for giving up on " + "an aircraft in the first place is that where it would be by now is a " + "guess. Zero takes it away the moment it is given up on.", + unit="s", minimum=0.0, flags=("--fade",), example="20", + guidance="Long enough to notice, short enough that a busy sky is not " + "half ghosts."), O("stale", "Forget after", "Drawing", "float", "stop drawing an aircraft this long after its last report", "Between reports an aircraft is dead-reckoned from the speed and " @@ -311,6 +327,28 @@ OPTIONS: tuple[Setting, ...] = ( guidance="Worth turning on for anything recorded before this version. " "Newer logs have the check applied as they are written, so it " "finds almost nothing."), + O("airports", "Mark the airports", "Drawing", "bool", + "mark every aerodrome on the map, not only the ones flown between", + "A route names the two airports its aircraft is flying between, and " + "those are almost never the ones underneath: a receiver hears aircraft " + "over its own county, and the county's airports are what say where on " + "the map you are looking. They are asked for once per area from the " + "same map data the tiles are drawn from, and kept on disk for a month " + "afterwards, since a runway does not move.", + flags=("--airports",), off_flags=("--no-airports",), + guidance="Turn it off for a picture with nothing but the aircraft on " + "it, or where there is no network and nothing cached."), + O("map_brightness", "Map brightness", "Drawing", "int", + "how bright the map under the aircraft is drawn, as a percentage", + "The map is the ground, not the subject, so it is drawn dark enough " + "that the aircraft and their trails stay the brightest things on the " + "picture. Too dark and a coastline cannot be made out at all; too " + "bright and a city washes out the aircraft crossing it. This is which " + "way to err on the screen you are actually looking at.", + unit="%", minimum=10, maximum=100, flags=("--map-brightness",), + metavar="PERCENT", example="70", + guidance="Turn it up until the coast and the roads are readable, and " + "no further."), O("basemap", "Map underneath", "Drawing", "bool", "draw a real map under the flight paths", "A flight path over a black rectangle says how the aircraft moved and " @@ -641,7 +679,9 @@ def watch(console, options: AircraftOptions, output_dir: str, book = FlightBook(online=options.lookup) sky = livemap.Sky(unit=options.speed_unit, hold=options.hold, home=read_position(options.location), - radius_nm=radius_in_nm(options) or 100.0) + radius_nm=radius_in_nm(options) or 100.0, + brightness=max(10, options.map_brightness) / 100.0, + fade=max(0.0, options.fade)) sky.started = started sky.log_name = log.path.name if log is not None else "" if options.simulate: @@ -826,8 +866,11 @@ def draw(console, options: AircraftOptions, tracks, out_path, book=None): seconds=options.length, speed=options.speed, width=options.width, trail_seconds=options.trail, stale=options.stale, labels=options.labels, + fade=max(0.0, options.fade), unit=options.speed_unit, ground=options.basemap, tile_url=options.tile_url, + brightness=max(10, options.map_brightness) / 100.0, + airports=options.airports, radius_nm=radius_in_nm(options), centre=read_position(options.location)) except (OSError, RuntimeError, ValueError) as exc: diff --git a/bandsaunter/basemap.py b/bandsaunter/basemap.py index dc885d4..1d7b87a 100644 --- a/bandsaunter/basemap.py +++ b/bandsaunter/basemap.py @@ -22,10 +22,12 @@ numpy, which is all this needs. from __future__ import annotations +import json import math import os import struct import time +import urllib.parse import urllib.request import zlib from pathlib import Path @@ -36,7 +38,7 @@ from . import __version__ __all__ = ["decode_png", "tile_of", "choose_zoom", "fetch_tile", "mosaic", "ground_under", "TILE_URL", "ATTRIBUTION", "MAX_TILES", "MAX_ZOOM", - "cache_dir", "PNGError"] + "cache_dir", "PNGError", "airports_in", "AIRPORTS_URL"] # The standard OpenStreetMap tiles. Any {z}/{x}/{y} server can be put here # instead; nothing below knows anything about this one in particular. @@ -48,8 +50,8 @@ ATTRIBUTION = "MAP DATA (C) OPENSTREETMAP CONTRIBUTORS" # A drawing is worth a few dozen tiles and no more. Past that the zoom is # reduced instead: a coarser map still says where the coastline is. -MAX_TILES = 40 -MAX_ZOOM = 12 +MAX_TILES = 90 +MAX_ZOOM = 13 MIN_ZOOM = 2 TILE_PIXELS = 256 @@ -61,6 +63,24 @@ USER_AGENT = (f"bandsaunter/{__version__} " FETCH_PAUSE = 0.12 +# Where to ask what aerodromes are in a piece of the world. The same OSM +# data the tiles are drawn from, asked as a question rather than a picture. +AIRPORTS_URL = "https://overpass-api.de/api/interpreter" + +# One question covers a whole view and is kept for a month, because runways +# do not move. The same politeness as the tiles: cache it, say who is +# asking, and do not ask twice for the same thing. +AIRPORT_CACHE_DAYS = 30 + +# Bumped when the question changes, so that answers to the old one are asked +# again rather than believed. +AIRPORT_CACHE_VERSION = 2 + +# Enough to name what is under an aircraft and not so many that the map is a +# list of airstrips. +MOST_AIRPORTS = 40 + + class PNGError(ValueError): """A PNG this decoder cannot read.""" @@ -294,6 +314,137 @@ def mosaic(south: float, west: float, north: float, east: float, zoom: int, return canvas, left * TILE_PIXELS, top * TILE_PIXELS +# --------------------------------------------------------------------------- +# What is on the ground +# --------------------------------------------------------------------------- + +def _resample(values: np.ndarray, edges: np.ndarray, axis: int) -> np.ndarray: + """Average each output cell over the source pixels that fall in it. + + A box filter, done as the difference of a running total so the whole + axis is two passes rather than a loop. Where the source is coarser than + the output -- a map zoomed in further than the tiles go -- a cell covers + less than one source pixel, and it takes that one. + """ + length = values.shape[axis] + starts = np.clip(np.floor(edges[:-1]).astype(np.int64), 0, length - 1) + ends = np.clip(np.ceil(edges[1:]).astype(np.int64), 1, length) + ends = np.maximum(ends, starts + 1) + running = np.cumsum(values, axis=axis, dtype=np.float64) + pad = np.zeros_like(np.take(running, [0], axis=axis)) + running = np.concatenate([pad, running], axis=axis) + total = (np.take(running, ends, axis=axis) + - np.take(running, starts, axis=axis)) + counts = (ends - starts).astype(np.float64) + shape = [1] * values.ndim + shape[axis] = counts.size + return (total / counts.reshape(shape)).astype(np.float32) + + +def _airport_cache(box) -> Path: + root = os.environ.get("XDG_CACHE_HOME") or "~/.cache" + name = "_".join(f"{round(v, 1):+06.1f}" for v in box) + return Path(root).expanduser() / "bandsaunter" / "airports" / f"{name}.json" + + +def airports_in(south: float, west: float, north: float, east: float, + url: str = AIRPORTS_URL, timeout: float = 45.0, + cache: Path | None = None, ask=None) -> list[dict]: + """Every aerodrome in a piece of the world, with a code and a position. + + Asked of the same OpenStreetMap data the tiles are drawn from, as a + question rather than a picture, and kept on disk afterwards: a runway + does not move, so one question covers a view for a month. + + An aerodrome with no code is left out. A map wants to say *which* + airport an aircraft is over, and there are a great many landing strips + with a name and nothing else; the ones worth marking have a code. + """ + box = (round(south, 1), round(west, 1), round(north, 1), round(east, 1)) + path = cache if cache is not None else _airport_cache(box) + try: + body = json.loads(path.read_text()) + if int(body.get("version") or 0) >= AIRPORT_CACHE_VERSION and \ + time.time() - float(body.get("fetched_at") or 0) < \ + AIRPORT_CACHE_DAYS * 86_400: + return body.get("airports") or [] + except (OSError, ValueError): + pass + + found: list[dict] = [] + try: + raw = (ask or _ask_overpass)(box, url, timeout) + for element in (raw or {}).get("elements", []): + tags = element.get("tags") or {} + code = _airport_code(tags) + lat = element.get("lat") or (element.get("center") or {}).get("lat") + lon = element.get("lon") or (element.get("center") or {}).get("lon") + if not code or lat is None or lon is None: + continue + found.append({"code": code, + "name": (tags.get("name") or "").strip(), + "icao": bool(tags.get("icao")), + "latitude": float(lat), "longitude": float(lon)}) + except Exception: + return [] # a map with no airports on it, not a crash + + # The ones with a real ICAO code first, since those are the ones an + # aircraft is likely to be flying to or from. + found.sort(key=lambda a: (0 if a.get("icao") else 1, a["code"])) + # One airport is often tagged twice -- a point for the terminal and an + # outline for the field -- and marking it twice writes its name over + # itself. + once: dict[str, dict] = {} + for one in found: + once.setdefault(one["code"], one) + found = list(once.values())[:MOST_AIRPORTS] + try: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps({"fetched_at": time.time(), + "version": AIRPORT_CACHE_VERSION, + "airports": found})) + except OSError: + pass + return found + + +def _airport_code(tags: dict) -> str: + """What to call an aerodrome, or nothing if it has no code at all. + + The ICAO code where it has one. Otherwise a reference, and only when it + is four letters: a great many landing strips carry a local identifier + like "14AZ" or "MX-0492", which names nothing anybody would recognise + and turns a map into a list of airstrips. + """ + icao = (tags.get("icao") or "").strip().upper() + if len(icao) == 4 and icao.isalpha(): + return icao + ref = (tags.get("ref") or "").strip().upper() + if len(ref) == 4 and ref.isalpha(): + return ref + return "" + + +def _ask_overpass(box, url: str, timeout: float) -> dict: + """The one question this program asks Overpass.""" + south, west, north, east = box + where = f"({south},{west},{north},{east})" + # Relations as well as nodes and ways: a big airport is a relation more + # often than not -- Tucson International and Davis-Monthan both are -- + # so asking only for the other two finds every airstrip in the county + # and misses the two the county is known for. + query = ("[out:json][timeout:25];" + f'(node["aeroway"="aerodrome"]{where};' + f' way["aeroway"="aerodrome"]{where};' + f' relation["aeroway"="aerodrome"]{where};);' + "out center tags;") + request = urllib.request.Request( + url, data=urllib.parse.urlencode({"data": query}).encode(), + headers={"User-Agent": USER_AGENT}) + with urllib.request.urlopen(request, timeout=timeout) as answer: + return json.loads(answer.read(4_000_000).decode("utf8", "replace")) + + # --------------------------------------------------------------------------- # Putting it under the picture # --------------------------------------------------------------------------- @@ -323,16 +474,18 @@ def ground_under(south: float, west: float, north: float, east: float, if tiles is None: return None - lons = west + (east - west) * (np.arange(width) + 0.5) / width - lats = north - (north - south) * (np.arange(height) + 0.5) / height + # The edges of each output pixel rather than its middle, so that what + # lands in it can be averaged. Taking the nearest source pixel instead + # throws away most of a tile -- the mosaic is commonly half again the + # size of the picture -- and what survives is the aliasing: hard, broken + # lettering and roads that come and go along their length. + lons = west + (east - west) * np.arange(width + 1) / width + lats = north - (north - south) * np.arange(height + 1) / height span = float(2 ** zoom) * TILE_PIXELS xs = (lons + 180.0) / 360.0 * span - origin_x clipped = np.clip(lats, -85.05112878, 85.05112878) ys = (1.0 - np.arcsinh(np.tan(np.radians(clipped))) / math.pi) / 2.0 \ * span - origin_y - cols = np.clip(np.rint(xs).astype(np.int64), 0, tiles.shape[1] - 1) - rows = np.clip(np.rint(ys).astype(np.int64), 0, tiles.shape[0] - 1) - picture = tiles[rows[:, None], cols[None, :]] # Brightness only, inverted, and dimmed. Inverted because a printed map # is ink on white paper and this picture is the other way round: the @@ -341,8 +494,9 @@ def ground_under(south: float, west: float, north: float, east: float, # night background. Dimmed because the map is the ground under the # aircraft rather than the subject: anything drawn on top has to stay the # brightest thing on the picture. - luma = (0.299 * picture[:, :, 0] + 0.587 * picture[:, :, 1] - + 0.114 * picture[:, :, 2]) + whole = (0.299 * tiles[:, :, 0] + 0.587 * tiles[:, :, 1] + + 0.114 * tiles[:, :, 2]).astype(np.float32) + luma = _resample(_resample(whole, ys, axis=0), xs, axis=1) low, high = float(luma.min()), float(luma.max()) if high - low < 1.0: levels = np.zeros_like(luma) diff --git a/bandsaunter/cli.py b/bandsaunter/cli.py index 11a1e36..b34fdab 100755 --- a/bandsaunter/cli.py +++ b/bandsaunter/cli.py @@ -215,6 +215,9 @@ examples: "(default: all of it)") fl.add_argument("--stale", type=float, default=300.0, metavar="SECONDS", help="drop an aircraft this long after its last report") + fl.add_argument("--fade", type=float, default=None, metavar="SECONDS", + help="how long an aircraft takes to fade away once it " + "has gone quiet (0 to remove it at once)") fl.add_argument("--no-labels", dest="labels", action="store_false", help="draw the aircraft without callsigns beside them") fl.add_argument("--no-map", dest="draw", action="store_false", @@ -234,6 +237,12 @@ examples: help="draw the tracks on their own, with no map under them") fl.add_argument("--tiles", default=None, metavar="URL", help="where map tiles come from ({z}/{x}/{y}.png)") + fl.add_argument("--map-brightness", type=int, default=None, + metavar="PERCENT", + help="how bright the map under the aircraft is (10-100)") + fl.add_argument("--no-airports", dest="airports", action="store_false", + default=None, + help="do not mark the aerodromes under the flight paths") fl.add_argument("--radius", type=float, default=None, metavar="MILES", help="how far around the receiver the map reaches, in the " "same unit as the speeds (0 = fit what was heard)") @@ -1163,10 +1172,16 @@ def cmd_flights(args) -> int: options.trail = args.trail options.stale = args.stale options.labels = args.labels + if args.fade is not None: + options.fade = args.fade if args.basemap is not None: options.basemap = args.basemap if args.tiles: options.tile_url = args.tiles + if args.map_brightness is not None: + options.map_brightness = args.map_brightness + if args.airports is not None: + options.airports = args.airports if args.radius is not None: options.radius = args.radius if args.at: diff --git a/bandsaunter/flags.py b/bandsaunter/flags.py index 47ba4b9..5a7f1a0 100644 --- a/bandsaunter/flags.py +++ b/bandsaunter/flags.py @@ -22,7 +22,8 @@ from __future__ import annotations import numpy as np __all__ = ["FLAG_W", "FLAG_H", "COLOURS", "COLOUR_ORDER", "FLAGS", - "flag_for", "pixels_for", "known", "country_of_icao"] + "flag_for", "pixels_for", "known", "country_of_icao", + "COUNTRY_ISO", "iso_for"] FLAG_W, FLAG_H = 12, 8 @@ -169,6 +170,79 @@ FLAGS: dict[str, tuple[str, ...]] = { } +# The names this program writes for countries, and the codes they answer to. +# Two sources spell a country in words rather than letters -- the treaty +# table of address blocks, and the registers -- and a flag needs the letters. +COUNTRY_ISO: dict[str, str] = { + "Afghanistan": "AF", "Albania": "AL", "Algeria": "DZ", "Angola": "AO", + "Argentina": "AR", "Armenia": "AM", "Australia": "AU", "Austria": "AT", + "Azerbaijan": "AZ", "Bahrain": "BH", "Bangladesh": "BD", "Belarus": "BY", + "Belgium": "BE", "Bhutan": "BT", "Bolivia": "BO", + "Bosnia and Herzegovina": "BA", "Botswana": "BW", "Brazil": "BR", + "Brunei": "BN", "Bulgaria": "BG", "Burundi": "BI", "Cambodia": "KH", + "Cameroon": "CM", "Canada": "CA", "Central African Republic": "CF", + "Chad": "TD", "Chile": "CL", "China": "CN", "Colombia": "CO", + "Congo": "CG", "Costa Rica": "CR", "Croatia": "HR", "Cuba": "CU", + "Cyprus": "CY", "Czechia": "CZ", "Czech Republic": "CZ", + "Côte d'Ivoire": "CI", "Democratic Republic of the Congo": "CD", + "Denmark": "DK", "Dominican Republic": "DO", "Ecuador": "EC", + "Egypt": "EG", "Equatorial Guinea": "GQ", "Eritrea": "ER", + "Estonia": "EE", "Ethiopia": "ET", "Fiji": "FJ", "Finland": "FI", + "France": "FR", "Gabon": "GA", "Georgia": "GE", "Germany": "DE", + "Ghana": "GH", "Greece": "GR", "Guinea": "GN", "Hungary": "HU", + "Iceland": "IS", "India": "IN", "Indonesia": "ID", "Iran": "IR", + "Iraq": "IQ", "Ireland": "IE", "Israel": "IL", "Italy": "IT", + "Jamaica": "JM", "Japan": "JP", "Jordan": "JO", "Kazakhstan": "KZ", + "Kenya": "KE", "Kuwait": "KW", "Kyrgyzstan": "KG", "Laos": "LA", + "Latvia": "LV", "Lebanon": "LB", "Liberia": "LR", "Libya": "LY", + "Lithuania": "LT", "Luxembourg": "LU", "Madagascar": "MG", + "Malawi": "MW", "Malaysia": "MY", "Mali": "ML", "Malta": "MT", + "Marshall Islands": "MH", "Mauritius": "MU", "Mexico": "MX", + "Moldova": "MD", "Monaco": "MC", "Mongolia": "MN", "Montenegro": "ME", + "Morocco": "MA", "Mozambique": "MZ", "Myanmar": "MM", "Namibia": "NA", + "Nepal": "NP", "Netherlands": "NL", "New Zealand": "NZ", "Niger": "NE", + "Nigeria": "NG", "North Korea": "KP", "North Macedonia": "MK", + "Norway": "NO", "Oman": "OM", "Pakistan": "PK", "Panama": "PA", + "Papua New Guinea": "PG", "Paraguay": "PY", "Peru": "PE", + "Philippines": "PH", "Poland": "PL", "Portugal": "PT", "Qatar": "QA", + "Romania": "RO", "Russia": "RU", "Russian Federation": "RU", + "Rwanda": "RW", "San Marino": "SM", "Saudi Arabia": "SA", + "Senegal": "SN", "Serbia": "RS", "Seychelles": "SC", + "Sierra Leone": "SL", "Singapore": "SG", "Slovakia": "SK", + "Slovenia": "SI", "Somalia": "SO", "South Africa": "ZA", + "South Korea": "KR", "Korea, Republic of": "KR", "Spain": "ES", + "Sri Lanka": "LK", "Sudan": "SD", "Sweden": "SE", "Switzerland": "CH", + "Syria": "SY", "Taiwan": "TW", "Tajikistan": "TJ", "Tanzania": "TZ", + "Thailand": "TH", "Togo": "TG", "Tonga": "TO", "Tunisia": "TN", + "Turkey": "TR", "Turkmenistan": "TM", "Uganda": "UG", "Ukraine": "UA", + "United Arab Emirates": "AE", "United Kingdom": "GB", + "United States": "US", "United States of America": "US", + "Uruguay": "UY", "Uzbekistan": "UZ", "Vanuatu": "VU", "Venezuela": "VE", + "Viet Nam": "VN", "Vietnam": "VN", "Yemen": "YE", "Zambia": "ZM", + "Zimbabwe": "ZW", +} + + +def iso_for(name: str) -> str: + """The two letters for a country written out in words. + + A military block is named for the country whose it is -- "United States + military" -- and flies the same flag, so the suffix is dropped rather + than treated as somewhere else. + """ + name = (name or "").strip() + if not name: + return "" + if len(name) == 2 and name.isalpha(): + return name.upper() # already a code + if name in COUNTRY_ISO: + return COUNTRY_ISO[name] + for suffix in (" military", " Military"): + if name.endswith(suffix): + return COUNTRY_ISO.get(name[:-len(suffix)], "") + return "" + + def known(code: str) -> bool: return (code or "").strip().upper() in FLAGS diff --git a/bandsaunter/flightmap.py b/bandsaunter/flightmap.py index ca7de0f..ec8a6b9 100644 --- a/bandsaunter/flightmap.py +++ b/bandsaunter/flightmap.py @@ -41,7 +41,9 @@ from .images import GLYPH_H, draw_text, text_width, write_png __all__ = ["Animation", "Projection", "animate", "render_frame", "write_gif", "write_mp4", "fit", "PALETTE", "ffmpeg_available", "ground_for", "background", "label_lines", "draw_flag", - "FLAG", "flag_index"] + "FLAG", "FAINT", "flag_index", "dim_ground", "local_airports", + "showing", "fade_ramp", + "GROUND_BRIGHTNESS"] # --------------------------------------------------------------------------- @@ -58,6 +60,7 @@ TRAIL = RAMP + 32 # the same 32, dimmed, for the path just flown OLD = TRAIL + 32 # and dimmer still, for the path flown earlier GROUND = OLD + 32 # 32 shades of the map underneath FLAG = GROUND + 32 # the dozen colours the little flags are made of +FAINT = FLAG + 12 # the same 32 again, fainter still, for fading RAMP_STEPS = 32 GROUND_SHADES = 32 TRANSPARENT = 255 # never drawn with: it means "as the frame before" @@ -114,10 +117,18 @@ def _dimmed(colours, factor: float) -> list[tuple[int, int, int]]: return [tuple(int(round(c * factor)) for c in rgb) for rgb in colours] -# The map under the aircraft: night-blue, dark at the bottom of the range so -# that a trail crossing a city is still the brightest thing there. -GROUND_LOW = (12, 14, 20) -GROUND_HIGH = (74, 82, 96) +# The map under the aircraft: night-blue, running from nearly the background +# to about as bright as it can go before the aircraft stop standing out. How +# far up that range a drawing actually goes is the brightness setting below, +# so there is room to turn it up on a screen that needs it and down on one +# that does not. +GROUND_LOW = (16, 19, 26) +GROUND_HIGH = (150, 164, 186) + +# What fraction of that range the map is drawn over unless told otherwise. +# Enough to read a coastline and the name of a town, and not so much that a +# city washes out the aircraft crossing it. +GROUND_BRIGHTNESS = 0.70 def _ground_shades(steps: int = GROUND_SHADES) -> list[tuple[int, int, int]]: @@ -146,7 +157,7 @@ def flag_index(letter: str) -> int: def _palette() -> np.ndarray: ramp = _ramp() table = (list(_FIXED) + ramp + _dimmed(ramp, 0.55) + _dimmed(ramp, 0.30) - + _ground_shades() + _flag_colours()) + + _ground_shades() + _flag_colours() + _dimmed(ramp, 0.14)) table += [(0, 0, 0)] * (256 - len(table)) return np.array(table[:256], dtype=np.uint8) @@ -154,6 +165,25 @@ def _palette() -> np.ndarray: PALETTE = _palette() +# How faint an aircraft is drawn as it fades, in the four strengths the +# palette holds. An indexed picture cannot blend, so a fade is a handful of +# steps rather than a slope -- which at a second or two apart reads as a +# fade all the same. +FADE_STEPS = ((0.66, RAMP), (0.40, TRAIL), (0.18, OLD), (0.0, FAINT)) + +# Below this the label goes: a name at a tenth of its colour is not +# information, it is something in the way of the aircraft still flying. +LABEL_WHILE = 0.40 + + +def fade_ramp(strength: float) -> int: + """Which set of colours an aircraft at this strength is drawn from.""" + for above, base in FADE_STEPS: + if strength > above: + return base + return FAINT + + def altitude_step(feet: float) -> int: """Which of the 32 altitude colours a height falls in.""" if feet <= 0: @@ -356,9 +386,25 @@ def _degrees(value: float, axis: str) -> str: return f"{abs(value):.2f}{hemisphere}" +def dim_ground(levels, brightness: float = GROUND_BRIGHTNESS): + """Squeeze the map into the part of its range being used. + + The palette holds the whole range; a drawing uses as much of it as the + brightness asks for. Scaling the shades rather than rebuilding the + palette means the animation's colour table stays the same table from one + frame to the next, which is the whole basis of the frame differencing. + """ + part = max(0.05, min(1.0, float(brightness))) + top = max(1, int(round((GROUND_SHADES - 1) * part))) + return np.clip((np.asarray(levels, dtype=np.float64) + * top / (GROUND_SHADES - 1)).round(), + 0, GROUND_SHADES - 1).astype(np.uint8) + + def background(view: Projection, title: str = "", airports=(), unit: str = DEFAULT_SPEED_UNIT, ground=None, - attribution: str = "") -> np.ndarray: + attribution: str = "", + brightness: float = GROUND_BRIGHTNESS) -> np.ndarray: """The map without anything flying on it: ground, grid, scale, key, title. ``ground`` is the real map underneath, as brightness levels covering the @@ -369,7 +415,7 @@ def background(view: Projection, title: str = "", airports=(), width, height = canvas_size(view) img = np.full((height, width), BG, dtype=np.uint8) if ground is not None: - patch = np.asarray(ground, dtype=np.uint8) + patch = dim_ground(ground, brightness) rows = min(view.height, patch.shape[0]) cols = min(view.width, patch.shape[1]) img[view.top:view.top + rows, view.left:view.left + cols] = \ @@ -399,6 +445,7 @@ def background(view: Projection, title: str = "", airports=(), continue x, y = view.xy(lat, lon) _box(img, x - 3, y - 3, x + 3, y + 3, AIRPORT) + _box(img, x - 1, y - 1, x + 1, y + 1, AIRPORT, fill=True) draw_text(img, x + 6, y - 3, name, AIRPORT) if title: @@ -461,7 +508,8 @@ def render_frame(base: np.ndarray, view: Projection, tracks: list[Track], when: float, *, trail_seconds: float = 0.0, stale: float = 300.0, labels: bool = True, clock: str = "", unit: str = DEFAULT_SPEED_UNIT, - project: bool = True, known=None) -> np.ndarray: + project: bool = True, known=None, + fade: float = 0.0) -> np.ndarray: """The map at one moment: where everything was, and where it had been. ``project`` is what makes an animation an animation: between reports an @@ -479,24 +527,31 @@ def render_frame(base: np.ndarray, view: Projection, tracks: list[Track], if track.at(when, stale=stale) is not None) > CROWDED taken: list[tuple[int, int, int, int]] = [] for track in tracks: - now = track.at(when, stale=stale) if project else \ - (track.fixes[-1] if when >= track.fixes[-1].at - else track.at(when, stale=stale)) - if now is None: + if project: + seen = showing(track, when, stale, fade) + else: + seen = ((track.fixes[-1], 1.0) if when >= track.fixes[-1].at + else showing(track, when, stale, fade)) + if seen is None: continue + now, strength = seen flying += 1 trail = track.trail(when, trail_seconds) recent = when - (trail_seconds or 120.0) / 2.0 + faded = fade_ramp(strength) for a, b in zip(trail, trail[1:]): - shade = (TRAIL if b.at >= recent else OLD) + \ - altitude_step(b.altitude_ft) + base_shade = TRAIL if b.at >= recent else OLD + # A fading aircraft takes its trail with it: the two are one + # thing on the picture and half of it lingering would be worse + # than either. + shade = max(base_shade, faded) + altitude_step(b.altitude_ft) x0, y0 = view.xy(a.latitude, a.longitude) x1, y1 = view.xy(b.latitude, b.longitude) _line(img, x0, y0, x1, y1, shade) - colour = RAMP + altitude_step(now.altitude_ft) + colour = faded + altitude_step(now.altitude_ft) x, y = view.xy(now.latitude, now.longitude) _marker(img, x, y, now.track_deg, colour) - if labels: + if labels and strength > LABEL_WHILE: _label(img, x, y, track, now, colour, taken, unit, entry=(known.get(track.icao) if known and not crowded else None)) @@ -505,6 +560,33 @@ def render_frame(base: np.ndarray, view: Projection, tracks: list[Track], return img +def showing(track: Track, when: float, stale: float = 300.0, + fade: float = 0.0): + """Where an aircraft is at a moment, and how strongly to draw it. + + An aircraft that stops transmitting has not stopped existing, and taking + it off the picture between one frame and the next says it did. So past + the point where its position can still be believed it is left where it + was last actually seen, fading, until it is gone -- which is what the + watching eye reads as "that one has gone quiet", rather than as a + blink. + + Fading happens at the last known position and never at a reckoned one: + the whole reason for giving up on an aircraft is that where it would be + by now is a guess. + """ + now = track.at(when, stale=stale) + if now is not None: + return now, 1.0 + if fade <= 0 or not track.fixes: + return None + last = track.fixes[-1] + gone = when - last.at - stale + if gone < 0 or gone > fade: + return None + return last, max(0.0, 1.0 - gone / fade) + + def _marker(img: np.ndarray, x: int, y: int, heading: float, colour: int) -> None: """A little arrowhead, pointing the way the aircraft is going. @@ -556,11 +638,15 @@ def label_lines(track: Track, now, unit: str = DEFAULT_SPEED_UNIT, rows.append((line, "")) if entry is None: return rows + from .flags import iso_for + + home = iso_for(getattr(entry, "owner_country", "") + or getattr(entry, "country", "")) kind = entry.type_code or entry.model if kind and entry.registration: - rows.append((f"{kind} {entry.registration}", "")) + rows.append((f"{kind} {entry.registration}", home)) elif kind or entry.registration: - rows.append((kind or entry.registration, "")) + rows.append((kind or entry.registration, home)) if entry.origin_code or entry.origin: rows.append((_short_place(entry.origin_code, entry.origin), entry.origin_country)) @@ -886,6 +972,25 @@ def _span(seconds: float) -> str: return f"{hours} h {minutes:02d} min" +def local_airports(view: Projection, ask=None) -> list: + """Every aerodrome inside the picture, from the map data itself. + + A route only names the airports its aircraft are flying between, and + those are almost never the ones underneath: a receiver hears aircraft + over its own county, and the county's airports are exactly what says + where on the map you are looking. + """ + from . import basemap + + try: + extra = {"ask": ask} if ask is not None else {} + found = basemap.airports_in(view.south, view.west, view.north, + view.east, **extra) + except Exception: + return [] + return [(a["code"], a["latitude"], a["longitude"]) for a in found] + + def _known_from(book, tracks: list[Track]) -> dict: """What a register says about each aircraft, looked up once. @@ -899,14 +1004,16 @@ def _known_from(book, tracks: list[Track]) -> dict: for track in tracks} -def _airports_from(known: dict): - """Every airport a route lookup gave a position for. +def _airports_from(known: dict, book=None): + """Every airport named by a route, wherever its position can be had. - Taken from what has already been looked up rather than asking again: the - book is thread-safe rather than free, and one question per aircraft is - the whole budget. + A route names two airports and often gives a position for neither, so + the codes are looked up as well -- once each, and remembered. An + airport that cannot be placed cannot be drawn, and a map of an evening's + flying with no airports on it is missing the two ends of every flight. """ seen: dict[str, tuple[str, float, float]] = {} + wanted: set[str] = set() for entry in known.values(): for code, lat, lon in ((getattr(entry, "origin_code", ""), getattr(entry, "origin_lat", 0.0), @@ -914,8 +1021,19 @@ def _airports_from(known: dict): (getattr(entry, "destination_code", ""), getattr(entry, "destination_lat", 0.0), getattr(entry, "destination_lon", 0.0))): - if code and (lat or lon): + if not code: + continue + if lat or lon: seen[code] = (code, lat, lon) + else: + wanted.add(code) + if book is not None and hasattr(book, "airports"): + try: + for found in book.airports(sorted(wanted - set(seen))): + seen[found["code"]] = (found["code"], found["latitude"], + found["longitude"]) + except Exception: + pass # a map with fewer airports on it return list(seen.values()) @@ -946,13 +1064,20 @@ def animate(tracks: list[Track], out_path, *, fps: float = 12.0, title: str = "", book=None, labels: bool = True, kind: str = "", unit: str = DEFAULT_SPEED_UNIT, ground: bool = False, fetch=None, tile_url: str = "", - radius_nm: float = 0.0, centre=None) -> Animation | None: + radius_nm: float = 0.0, centre=None, + brightness: float = GROUND_BRIGHTNESS, + airports: bool = False, ask=None, + fade: float = 0.0) -> Animation | None: """Draw the whole log as a moving map. ``speed`` is how many seconds of real flying go by in one second of animation; given ``seconds`` instead, it is worked out so the whole log plays in about that long. The clock in the corner is the real time of day, so a fast animation is still readable as an evening. + + ``ground`` and ``airports`` both reach for a network and are therefore + both off here, and both on in the settings the program itself uses: a + library call should not go out to the world unless it was asked to. """ located = [t for t in tracks if t.located] if not located: @@ -993,8 +1118,16 @@ def animate(tracks: list[Track], out_path, *, fps: float = 12.0, known = _known_from(book, located) levels, credit = ground_for(view, fetch, tile_url) if ground \ else (None, "") - base = background(view, title=heading, airports=_airports_from(known), - unit=unit, ground=levels, attribution=credit) + marked = _airports_from(known, book) + if airports: + # The ones underneath as well as the ones flown between, and the + # nearer ones last so they win where two share a name. + seen = {code for code, _, _ in marked} + marked += [one for one in local_airports(view, ask) + if one[0] not in seen] + base = background(view, title=heading, airports=marked, + unit=unit, ground=levels, attribution=credit, + brightness=brightness) canvas_w, canvas_h = canvas_size(view) base = _pad_to(base, canvas_w, canvas_h) @@ -1005,7 +1138,8 @@ def animate(tracks: list[Track], out_path, *, fps: float = 12.0, yield _pad_to(render_frame(base, view, located, when, trail_seconds=trail_seconds, stale=stale, labels=labels, - clock=clock, unit=unit, known=known), + clock=clock, unit=unit, known=known, + fade=fade), canvas_w, canvas_h) path = Path(out_path) diff --git a/bandsaunter/flights.py b/bandsaunter/flights.py index d672101..e81dcb7 100644 --- a/bandsaunter/flights.py +++ b/bandsaunter/flights.py @@ -37,6 +37,7 @@ AIRCRAFT_URL = "https://api.adsbdb.com/v0/aircraft/{icao}" ROUTE_URL = "https://api.adsbdb.com/v0/callsign/{call}" BACKUP_AIRCRAFT_URL = "https://hexdb.io/api/v1/aircraft/{icao}" BACKUP_ROUTE_URL = "https://hexdb.io/api/v1/route/icao/{call}" +AIRPORT_URL = "https://hexdb.io/api/v1/airport/icao/{code}" # Bumped when the shape of a cached record changes, so a cache written by an # older version is ignored rather than misread. Version 2 added the country @@ -418,7 +419,8 @@ class FlightBook: aircraft_url: str = AIRCRAFT_URL, route_url: str = ROUTE_URL, backup_aircraft_url: str = BACKUP_AIRCRAFT_URL, - backup_route_url: str = BACKUP_ROUTE_URL): + backup_route_url: str = BACKUP_ROUTE_URL, + airport_url: str = AIRPORT_URL): self.online = online self.timeout = timeout self.max_age = max_age @@ -426,10 +428,15 @@ class FlightBook: self.route_url = route_url self.backup_aircraft_url = backup_aircraft_url self.backup_route_url = backup_route_url + self.airport_url = airport_url self.cache_path = Path(cache) if cache is not None else _cache_path() self._lock = threading.Lock() self._entries: dict[str, Flight] = {} self._routes: dict[str, dict] = {} + # Airports, by ICAO code. A route often names two the map has never + # heard of and gives no position for either, and an airport that + # cannot be placed cannot be drawn. + self._airports: dict[str, dict] = {} # Callsigns already asked about. A route nobody holds would # otherwise be asked for again by every caller that looks at the same # aircraft -- the table, the report and the map all do -- and an @@ -455,6 +462,10 @@ class FlightBook: entry.version >= CACHE_VERSION and \ now - entry.fetched_at < self.max_age: self._entries[key] = entry + for key, body in (raw.get("airports") or {}).items(): + if isinstance(body, dict) and \ + now - float(body.get("fetched_at") or 0) < self.max_age: + self._airports[key] = body for key, body in (raw.get("routes") or {}).items(): if not isinstance(body, dict): continue @@ -472,6 +483,7 @@ class FlightBook: "aircraft": {k: e.__dict__ for k, e in self._entries.items() if e.status in ("found", "unlisted")}, "routes": dict(self._routes), + "airports": dict(self._airports), } self._dirty = False try: @@ -519,6 +531,50 @@ class FlightBook: thread.start() return entry + def airport(self, code: str) -> dict: + """Where an airport is, asked once and remembered. + + An empty answer is remembered too, and on purpose: a code no + register holds would otherwise be asked about on every drawing for + the rest of the month. + """ + code = (code or "").strip().upper() + if not code: + return {} + with self._lock: + got = self._airports.get(code) + if got is not None: + return got + found: dict = {} + if self.online and self.airport_url: + try: + body = self._request(self.airport_url.format( + code=urllib.parse.quote(code))) + found = {"code": code, + "name": str(body.get("airport") or "").strip(), + "latitude": float(body.get("latitude") or 0.0), + "longitude": float(body.get("longitude") or 0.0), + "country": str(body.get("country_code") + or "").strip().upper()} + if not (found["latitude"] or found["longitude"]): + found = {} + except Exception: + found = {} + with self._lock: + found["fetched_at"] = time.time() + self._airports[code] = found + self._dirty = True + return found + + def airports(self, codes) -> list[dict]: + """Every airport in a list of codes that can be placed on a map.""" + out: dict[str, dict] = {} + for code in codes: + found = self.airport(code) + if found.get("latitude") or found.get("longitude"): + out[found["code"]] = found + return list(out.values()) + def get_all(self, pairs) -> list[Flight]: return [self.get(icao, call) for icao, call in pairs] diff --git a/bandsaunter/livemap.py b/bandsaunter/livemap.py index e9953e1..7c55f35 100644 --- a/bandsaunter/livemap.py +++ b/bandsaunter/livemap.py @@ -23,6 +23,7 @@ from dataclasses import dataclass import numpy as np +from .flags import iso_for from .flightlog import (DEFAULT_SPEED_UNIT, distance_label, distance_nm, in_distance, in_speed, speed_label) from .flightmap import Projection, altitude_step @@ -56,6 +57,17 @@ REDRAW_MS = 200 # it is drawn every frame and wants a hard bound. TRAIL_POINTS = 400 +# How far the worked-out middle of the map has to drift before it is moved. +# Below this it is noise from aircraft arriving and leaving, and moving the +# map for it costs the tiles that were fetched for the old view. +RESETTLE_NM = 12.0 + +# How much map to fetch beyond what is being shown. A window that fetched +# exactly what it needed would fetch again on every resize and every small +# drift; a third again in each direction covers both, at the cost of about +# twice as many tiles the first time. +GROUND_MARGIN = 0.33 + # The widest a line in a box is allowed to get before it is folded. An # airport's full name and the town it is in run to forty characters on their # own and a route is two of them, so one flight from Los Angeles to Dallas @@ -66,6 +78,14 @@ WRAP_CHARS = 30 # tall, so twelve by eight sits on a line without crowding it. FLAG_PIXELS = 12 +# Below this an aircraft keeps its symbol and loses its box. +LABEL_WHILE = 0.40 + + +def _alpha(strength: float) -> int: + """How opaque something at this strength is drawn.""" + return max(0, min(255, int(round(255 * strength)))) + def _qt(): """The first Qt binding that imports, or None. @@ -137,6 +157,7 @@ class Blip: manufacturer: str = "" operator: str = "" country: str = "" + country_code: str = "" # two letters, for the flag beside it origin: str = "" origin_country: str = "" # two letters, for the flag beside it destination: str = "" @@ -199,7 +220,7 @@ class Blip: age = max(0.0, time.time() - self.last_seen) out.append(("", f"{self.messages:,} frames {age:.0f}s ago", "")) if self.country: - out.append(("", self.country, "")) + out.append(("reg'd", self.country, self.country_code)) return out def _bearing(self, home) -> float: @@ -265,6 +286,7 @@ def blip_for(craft, entry=None) -> Blip: blip.manufacturer = entry.manufacturer blip.operator = entry.operator or entry.airline blip.country = entry.owner_country or entry.country + blip.country_code = iso_for(blip.country) blip.origin = entry.origin or entry.origin_code blip.origin_country = entry.origin_country blip.destination = entry.destination or entry.destination_code @@ -285,13 +307,16 @@ class Sky: """ def __init__(self, unit: str = DEFAULT_SPEED_UNIT, hold: float = 45.0, - home=None, radius_nm: float = 100.0): + home=None, radius_nm: float = 100.0, + brightness: float = 0.70, fade: float = 20.0): import threading self.unit = unit self.hold = hold self.home = home self.radius_nm = radius_nm + self.brightness = brightness + self.fade = fade self.frames = 0 self.aircraft_seen = 0 self.started = time.time() @@ -308,7 +333,9 @@ class Sky: self._trails: dict[str, deque] = {} self._ground = None self._ground_for = None + self._ground_box = None self._wanted = None + self._settled = None # -- written by the listener ------------------------------------------ def update(self, blips, frames: int, seen: int) -> None: @@ -324,9 +351,15 @@ class Sky: if not trail or trail[-1][:2] != here[:2]: trail.append(here) - def set_ground(self, levels, key) -> None: + def set_ground(self, levels, key, box=None) -> None: + """Keep the map that was fetched, and the piece of world it covers. + + A failed fetch is kept too, as nothing: otherwise a machine with no + network asks for the same tiles five times a second all night. + """ with self._lock: self._ground, self._ground_for = levels, key + self._ground_box = box if self._wanted is not None and self._wanted[0] == key: self._wanted = None @@ -340,20 +373,52 @@ class Sky: with self._lock: return self._wanted + def ground_covering(self, south, west, north, east): + """The map already fetched, if it covers this view, and its box. + + What is asked for is a slightly different piece of the world every + time the middle shifts or the window is resized, and refetching for + each of those would leave the ground blank while it happened. The + fetched piece is bigger than the piece being shown for exactly this + reason, and anything inside it is taken from what is already here. + """ + with self._lock: + levels, box = self._ground, self._ground_box + if levels is None or box is None: + return None, None + if (south >= box[0] and west >= box[1] + and north <= box[2] and east <= box[3]): + return levels, box + return None, None + # -- read by the window ----------------------------------------------- def flying(self, now: float | None = None) -> list[Blip]: """What is overhead, oldest first heard at the top. An aircraft nothing has been heard from for a while has gone out of - range; it stays in the log and leaves the picture. + range; it stays in the log and fades off the picture. """ now = time.time() if now is None else now + limit = self.hold + max(0.0, self.fade) with self._lock: out = [b for b in self._blips.values() - if now - b.last_seen <= self.hold and b.located] + if now - b.last_seen <= limit and b.located] out.sort(key=lambda b: (b.first_seen, b.icao)) return out + def strength(self, blip: Blip, now: float | None = None) -> float: + """How strongly to draw one aircraft: full, then fading, then gone. + + Taking an aircraft off the picture between one frame and the next + says it stopped existing. Fading it says it stopped talking, which + is what actually happened. + """ + now = time.time() if now is None else now + age = max(0.0, now - blip.last_seen) + if age <= self.hold or self.fade <= 0: + return 1.0 if age <= self.hold else 0.0 + return max(0.0, 1.0 - (age - self.hold) / self.fade) + def trail(self, icao: str) -> list: with self._lock: return list(self._trails.get(icao, ())) @@ -367,17 +432,31 @@ class Sky: """Where to put the middle of the map. Whatever the receiver was told, or the middle of everything heard so - far, which settles within the first few aircraft and is a median, so - one bad position cannot move it. + far -- a median, so one bad position cannot move it. + + Once settled it is left alone. Recomputing it every time the picture + is painted moves the map by a fraction of a mile whenever an aircraft + appears or leaves, which throws away the tiles fetched for the old + view and leaves the ground blank while new ones are fetched: the map + blinks out several times a minute for no reason anybody watching + could see. It only moves again if it has drifted far enough to + matter, which happens once, early, and then never. """ if self.home is not None: return self.home with self._lock: + settled = self._settled lats = sorted(b.latitude for b in self._blips.values() if b.located) lons = sorted(b.longitude for b in self._blips.values() if b.located) if not lats: - return None - return lats[len(lats) // 2], lons[len(lons) // 2] + return settled + middle = lats[len(lats) // 2], lons[len(lons) // 2] + if settled is not None and \ + distance_nm(settled[0], settled[1], *middle) < RESETTLE_NM: + return settled + with self._lock: + self._settled = middle + return middle # --------------------------------------------------------------------------- @@ -509,12 +588,20 @@ def _build(): return Projection(south=south, west=west, north=north, east=east, left=0, top=0, width=width, height=height) + @staticmethod + def ground_box(view: Projection): + """The piece of world to fetch: what is shown, and some around it.""" + down = (view.north - view.south) * GROUND_MARGIN + across = (view.east - view.west) * GROUND_MARGIN + return (max(-85.0, view.south - down), view.west - across, + min(85.0, view.north + down), view.east + across) + def ground_key(self, view: Projection): """What the map underneath was fetched for, rounded so that a pixel of drift does not throw it away.""" - return (round(view.south, 3), round(view.west, 3), - round(view.north, 3), round(view.east, 3), - view.width, view.height) + box = self.ground_box(view) + return (round(box[0], 2), round(box[1], 2), round(box[2], 2), + round(box[3], 2), view.width, view.height) # -- painting ----------------------------------------------------- def paintEvent(self, event) -> None: @@ -533,22 +620,29 @@ def _build(): flying = self.sky.flying() if self.trails: for blip in flying: - self._draw_trail(painter, view, blip) + self._draw_trail(painter, view, blip, + self.sky.strength(blip)) # The symbols go down first and their own space is spoken for, # so that one aircraft's box cannot be placed on top of another # aircraft -- which is the one thing on the picture that has to # stay visible. - placed = [(blip, view.xy(blip.latitude, blip.longitude)) - for blip in flying] + now = time.time() + placed = [(blip, view.xy(blip.latitude, blip.longitude), + self.sky.strength(blip, now)) for blip in flying] taken: list[tuple[int, int, int, int]] = [] - for blip, (x, y) in placed: + for blip, (x, y), strength in placed: self._draw_symbol(painter, x, y, blip.track_deg, - craft_colour(blip.altitude_ft)) + craft_colour(blip.altitude_ft, + _alpha(strength)), strength) taken.append((x - 11, y - 11, 22, 22)) - for blip, (x, y) in placed: - self._draw_aircraft(painter, view, blip, x, y, taken) + for blip, (x, y), strength in placed: + self._draw_aircraft(painter, view, blip, x, y, taken, strength) self._draw_scale(painter, view) - self._draw_header(painter, len(flying)) + # Counted as overhead only while it is still being heard: one + # that has gone quiet is on the picture, fading, and saying it + # is overhead would be saying more than was heard. + self._draw_header(painter, + sum(1 for _b, _xy, s in placed if s >= 1.0)) painter.end() def _draw_waiting(self, painter) -> None: @@ -562,16 +656,26 @@ def _build(): "both arrived") def _draw_ground(self, painter, view) -> None: - key = self.ground_key(view) - levels = self.sky.ground(key) + levels, box = self.sky.ground_covering(view.south, view.west, + view.north, view.east) if levels is None: # Ask for it and carry on drawing. The map arrives when it # arrives; the aircraft are the part that cannot wait. - self.sky.want_ground( - key, (view.south, view.west, view.north, view.east), - (view.width, view.height)) + # Asked for at the size the bigger box needs to hold the + # same detail as the window, rather than at the window's + # own size: fetching a third more world into the same + # pixels and then cutting the middle out of it is how a + # sharp map ends up looking like a photograph of a map. + scale = 1.0 + 2.0 * GROUND_MARGIN + self.sky.want_ground(self.ground_key(view), + self.ground_box(view), + (int(view.width * scale), + int(view.height * scale))) return - shades = np.clip(np.asarray(levels), 0, GROUND_SHADES - 1) + from .flightmap import dim_ground + + shades = dim_ground(self._crop(levels, box, view), + self.sky.brightness) pixels = np.ascontiguousarray( PALETTE[GROUND + shades].astype(np.uint8)) self._ground_pixels = pixels # QImage does not copy it @@ -579,6 +683,25 @@ def _build(): image = QImage(pixels.data, width, height, 3 * width, RGB888) painter.drawImage(0, 0, image) + @staticmethod + def _crop(levels, box, view: Projection): + """The part of the fetched map this view is looking at. + + Both are laid out the same way -- latitude down, longitude across, + evenly -- so this is arithmetic rather than another projection. + """ + south, west, north, east = box + rows, columns = levels.shape[0], levels.shape[1] + lats = np.linspace(view.north, view.south, view.height) + lons = np.linspace(view.west, view.east, view.width) + y = np.clip(((north - lats) / max(1e-9, north - south) + * (rows - 1)).round().astype(np.int64), 0, rows - 1) + x = np.clip(((lons - west) / max(1e-9, east - west) + * (columns - 1)).round().astype(np.int64), + 0, columns - 1) + return np.clip(levels[y[:, None], x[None, :]], 0, + GROUND_SHADES - 1) + def _draw_graticule(self, painter, view) -> None: pen = QPen(rgb(GRID)) pen.setStyle(DOTTED) @@ -611,20 +734,24 @@ def _build(): _degrees(lon, "lon")) lon += step - def _draw_trail(self, painter, view, blip: Blip) -> None: + def _draw_trail(self, painter, view, blip: Blip, + strength: float = 1.0) -> None: points = self.sky.trail(blip.icao) if len(points) < 2: return - painter.setPen(QPen(craft_colour(blip.altitude_ft, 90), 1.4)) + painter.setPen(QPen(craft_colour(blip.altitude_ft, + int(90 * strength)), 1.4)) path = QPolygonF([QPointF(*view.xy(lat, lon)) for lat, lon, _ in points]) painter.drawPolyline(path) def _draw_aircraft(self, painter, view, blip: Blip, x, y, - taken) -> None: - if self.detail <= 0: + taken, strength: float = 1.0) -> None: + # A box at a tenth of its colour is not information, it is + # something in the way of the aircraft still flying. + if self.detail <= 0 or strength < LABEL_WHILE: return - colour = craft_colour(blip.altitude_ft) + colour = craft_colour(blip.altitude_ft, _alpha(strength)) lines = self._wrapped(self._box_lines(blip)) box = self._box_size(lines) bx, by = place_box(x, y, box[0], box[1], taken, @@ -642,7 +769,8 @@ def _build(): else blip.icao self._draw_box(painter, bx, by, box, lines, colour, title) - def _draw_symbol(self, painter, x, y, heading, colour) -> None: + def _draw_symbol(self, painter, x, y, heading, colour, + strength: float = 1.0) -> None: angle = math.radians(heading % 360.0) sin, cos = math.sin(angle), math.cos(angle) @@ -654,7 +782,7 @@ def _build(): painter.setBrush(colour) painter.drawPolygon(QPolygonF([point(9, 0), point(-6, 5), point(-3, 0), point(-6, -5)])) - painter.setBrush(QColor(255, 255, 255, 200)) + painter.setBrush(QColor(255, 255, 255, int(200 * strength))) painter.drawEllipse(QPointF(x, y), 1.6, 1.6) def _box_lines(self, blip: Blip) -> list[tuple[str, str]]: @@ -703,9 +831,10 @@ def _build(): title: str) -> None: width, height = box painter.setPen(QPen(colour, 1.2)) - painter.setBrush(QColor(10, 12, 18, 215)) + painter.setBrush(QColor(10, 12, 18, int(215 * (colour.alpha() / 255)))) painter.drawRoundedRect(QRectF(bx, by, width, height), 3.0, 3.0) painter.setFont(self.head_font) + faded = colour.alpha() painter.setPen(colour.lighter(135)) head = QtGui.QFontMetrics(self.head_font) painter.drawText(bx + 7, by + head.ascent() + 4, title) @@ -716,7 +845,7 @@ def _build(): y = by + head.height() + 6 + metrics.ascent() for label, value, country in lines: if label: - painter.setPen(rgb(GRID)) + painter.setPen(rgb(GRID, faded)) painter.drawText(bx + 7, y, f"{label:>5s}") left = bx + 7 + gutter if flagged: @@ -724,7 +853,7 @@ def _build(): self._draw_flag(painter, left, y - metrics.ascent() + 2, country) left += FLAG_PIXELS + 4 - painter.setPen(rgb(INK)) + painter.setPen(rgb(INK, faded)) painter.drawText(left, y, value) y += metrics.height() @@ -767,8 +896,8 @@ def _build(): painter.drawLine(12 + pixels, y - 4, 12 + pixels, y + 4) painter.drawText(18 + pixels, y + 4, f"{miles} {distance_label(self.sky.unit)}") - if self.show_ground and self.sky.ground( - self.ground_key(view)) is not None: + if self.show_ground and self.sky.ground_covering( + view.south, view.west, view.north, view.east)[0] is not None: painter.setPen(rgb(GRID)) painter.drawText(self.width() - 8 - metrics.horizontalAdvance(ATTRIBUTION), @@ -796,7 +925,8 @@ def _build(): painter.setFont(self.head_font) painter.setPen(rgb(INK)) painter.drawText(10, metrics.ascent() + 4, told) - keys = "d detail t trails g map +/- range q quit" + keys = ("d detail t trails g map [ ] bright " + "+/- range q quit") painter.setPen(rgb(GRID)) painter.drawText(self.width() - 8 - metrics.horizontalAdvance(keys), @@ -837,6 +967,10 @@ def _build(): self.sky.radius_nm = max(5.0, self.sky.radius_nm / 1.5) elif text == "-": self.sky.radius_nm = min(3000.0, self.sky.radius_nm * 1.5) + elif text == "[": + self.sky.brightness = max(0.1, self.sky.brightness - 0.1) + elif text == "]": + self.sky.brightness = min(1.0, self.sky.brightness + 0.1) self.view.update() def closeEvent(self, event) -> None: @@ -883,11 +1017,15 @@ def fetch_ground(sky: Sky, url: str = "", fetch=None) -> None: if wanted is None: time.sleep(0.2) continue - key, (south, west, north, east), (width, height) = wanted + key, box, (width, height) = wanted + south, west, north, east = box try: extra = {"fetch": fetch} if fetch is not None else {} if url: extra["url"] = url + # Fetched at the size of the window rather than the bigger box, + # so a wider view costs no more pixels; it is stretched back over + # the box when it is drawn. levels = basemap.ground_under(south, west, north, east, width, height, shades=_ground_shades(), **extra) @@ -895,7 +1033,7 @@ def fetch_ground(sky: Sky, url: str = "", fetch=None) -> None: levels = None # Remembered either way: a map that could not be fetched must not be # asked for again every fifth of a second for the rest of the night. - sky.set_ground(levels, key) + sky.set_ground(levels, key, box if levels is not None else None) def _ground_shades() -> int: diff --git a/docs/realtime.png b/docs/realtime.png index 24b80b61e5968ef33773af3d22f73d73d5e90def..6ddb5836a6c41ad256a627a51c74d366eabb483e 100644 GIT binary patch delta 87799 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

{<3?p@lES*ow#90Lc2-Nj|Y=( z=OeM__)a$5A8ZPlq^|*PZbZfdM@qO)yhk?eT^+5!+mf^%YeK{+o-s4YtbVQ4M*%A< z_l3Sl$kn6iDk;bs4sBptgLbVcRq29PTuyaP+~n4&MvI_O2tKd%)cS zYOuc<&sC}mjE(L5(b_sIAO=aMHRgxS=eSc-QxXOxu^ZxR7ET*UbM9A39G}d)0zT2Q zO}7e{x!jKB_Xf#NGX(?$R6Krs=yaphg9VxY|E^TYlE(fRB0VKu89c=lOf9 zYQ$j^39`QEe^_`i4g#Il_4R{ongK|nr21d~26NE%bfgjd0=`R5>0^$FAGeF_;OTgj z9;WR)kf=Hj7 zSB4*&;1}3G8pc1n`LU#GWok%EpPTjYd&{;(Bp(VAM!${$@!)aOJ6BP%7)l~UP`*H3 z@uLozMB#j$0(JR!6KbZV#jDXCB~zvK!dV1o6lONewe?lY>6*Qv&(|3h<>lqm81`h8 zT(}Q}tRn>P89<`IOgImiY1GclB(B0Bg-5)A`7TGg9%tuv3Hxg}b_4pvO<3Y>zp#92 z*EBLxsTSnb>wcT5|9$;uL1Jrq(1)+@2zTDzNGx~~>vO=qm77SsNVfnwo zC`xfbL$z^Lefl@(U9{Wrfd#p_j`5ifRNPa)c*2}5vl@6<~-cI0cE`EFC_= znEx(z4Wg(pXf0S*L&kHZ!7j2)^i-Cw~sW{_DRjgJ%MUK`fw+F$ONnw~)y1M}W-VD?imnMlT1K(z=%?wf9qbnuZVl($_iVcj)h?H?3L9&4KO9r; zN;6%fMyZ*Z^*X<&*x8mq36*@+QYvCzHVRI3;N{b{$S3KLS>Z_(8xJqQ?dCM#x~5M` znm+K83z1k+&8ZrE7|@WY40qID@RpU$0}_yOs#$25IY{Yv`HDF2l?&u^3bh*!^g`!) zrynEZN+&UnJMIPT$NR8q%OG}u@&^LRq z2B13^PG(n9%FO(`uT57=PHrbbMLvg?rgY0FZ4j#nZ2$7+O7}zfXHy1e4>p+?Qhn4p zkXj}!L@B3-tBnlg3#2C<5r1g{jJ_O9uD3!WB^JSiHeD3D_W_P zJJ^_U+Xi7te8VZYx(NG>7D@m#{knB^cf%s&bYbJ-GJ~dpRNtq(nEv=$>!nCF#v!!z z{>1zpqEG_fDyPYKSm$4by9w#(q`CSXXoA^fg8Is9x88{Eld=A`H}B8|Srb8sUi?hf zlMy!VVog8AYJ@~us`!ApC5=^$LN81@wNE!2DQKaDis$_A`&R$8ICl*XA1Jxe!yuk7 zKpb_$OBIJkE%)Lomu%awivn~cH$$a!)c1704}$!DmjHCwgwV)Lrn1%KQZ>=e>uNf@ zp_BgYLpTUMc+IItsW|;r$)wZbvFAHz(sKK5K8Jzd1X@rbH8dtKbqZ;{8!fjBB6uH@ z9v@Y$t2@tKr|900=nuZ}fh6RIOOxI14E7l-+W3VelS_oBy#``=^aOPUxq*|tvcPit%NkyH z1So1n2tcvExt|W8NLK|Uv=&S|(Vm^hu^41Ruue);g*r!ZZLZ2hzm;^&LRYXZBTC68 zT<@dFDH(;lRqfZgKacg#T7Y&qYs61Wp~4(8N)p8^xfrF6g{5&tbK(W0Kzns>ikAU1 zf+D;LeT*?vsE{Wm_bo*!yK`J;Uklu@*;7?o&CM9%TwLm z5$?%pN=u-iBkRKNFHb?hza$~7s znZPoV4|lShx}>>-D3f1@-qT=TLmZ`#E|TWz-MCRDKTTaVYi&9jK6iA;#5ONl15Mpi zqQoA^SL%x3aM2Irbe*fJDf%A~MVi1a9Huob$W z9yya@sbq4~vgi*rbHp$_3MF6cR`2aoD z!=B|QioUk-nZ`IH)Z_};$~1kg(Y;7f^4h+$(g`dSbaW#6rWH+~NuyUXNPxI>cp*by zI7}JJ%EUB>dC?I8#%5nsvBUBB$)s@J_2tE+x62&Fv$#X%T81SZA74u5Cakl7rKN+1 zfS34d-EL9>9>BBXIpGbjEuO;B!lD+W@eUc6l$Q4OkyBAgU=SYff|fIP>!J~cbfTe2 zFEMe_MvVbaGPOD0J1~js1wKB^gOpnBaG9o_ewV373Eq?EeR+1JmZJ#UH2=%!9)(vU zO#2Rnce=pUYVDJ*o7-`8!PZEQ<5{l+B_);~>2f&M1+hDGKC0*!#E0=mJ}}T;q~c@x z?deQ_!Vc0`od?i{lQ)zrEv&0hY=B|OK}YupI6Ag1(ySTG4x<6|4Gew!z8JH{*UcoZ z52Sz-VFlDfAn=r*Nc)0e-oc(!>12&^e|uDxL(Ibc+$4Tz|#N z?&28;dp34wW|TIrQ zkjWa&9c7V*0dzjlf-H<#CIOE-yCidR8+ysn?>(bnz0gGD7E>!jZ!Qr4%{uEjNR2$x zx}LNua@+gm|^53?{g%LVCCyF=jdx)s-_RC`5B%ZkQ%%~i44 zPW!zRf6G}{SWn2l=<9MufAI(?(&mx9DZ?m}Gr;~kg+!a{`s{%Wv0}zhS`NvnsD8so zyZVJRdh!oF%XQ*0@Fp^jr$B6O?&hT-EzTCdUSwmPfJUgR{qFbS&0JR^@qJD3FBxTz(l5Ksc@cHMIlDK$vui38*^NpQ1%F z4S5@2#wr}Dt2uh$Fv%z>VVgAPJ=gj)aImq@H^#hj#;VQE1(I8Wf_)(yZn~n-^<;q; zK!s;C?S+2_nk5_?$v)6m`P;Pk=MM>yKdd!H_{)1%bdD;I?uU2KBRG8WTjZDr;RI)E zGNy0wZpI1#XwMSXIljk0G^;er)xhwFn3#y7L+Lv#h7SZf3RF;v+3ECZbx`M8v(eTj zXk0(@pjpSL&PbYGjgFn&ac(E4@oXQv3UrUqX_911KnCadUHb1FAhDE9Sap#5#~B>w$tTPQCMX#-N`kP-gG^9HPwGDxkHd@M0+Z)*A! z_(qGF(6?rDNzS)Bx19A3>oZjVb$LP2JrjM5MrE>l=WYU>Mg_0yT5FsFr8*R=@3i}x z9?|2&*h0+}u{_l9dEU(mCC2qq_h7>&0?Pi%WVt%s7fPMo6X@75+@Ktj{KNf3j$pls zpp2~-=1cwAu zCg(lIF@Qba`8bYI8ando5834k zyNxE`v&}BiN{@g3KuN7B9=sd5kiqqTgt=s}+aZ?@^e|D+>AXKDmkuwBn+RxI0sw(< zTDFAW8vuWSGOVXe-s-C-FrFPRAU44wJ;ai&Bnc29hWB8_qUyd_JLir!w0k=hT4P(J zSZXA-aC|v;d^25ky|%TAB=wOi6y?dGO>Z!x0afP!v|;-D`uZ6<*%=uxPr_cm44`@; zO5P53{PLsFhtd&H1O*(kXIP^Xk@M1Ece?`K_x){J5c!~9Nj-pX_pIiX3E5ujTr}xn zdw&CTsXn?gb!i7fC`>--@#y`HD>rge9gUT=Z|{k4JpFBVv24~~uCb6a;bLndF`AaYqquJ(i6m?J@%d!tZ6?|5w ziTua?8eCVb`(S^Uw;?;Pj_eQ7M8;tTJo=QrwP+>j0_CG%A9#UpWPu%603fY^6D9we z$%y&@UQ~&dEUq`rlE)Q*#D-li2T4g6E-O6~6L5ED>1-f4Pia(6#Al{vvf9iM*qADJ zRo$>il2I{by}m-RTW3&TVXwSVqc|bYAtnxL_yCPbQ-(wuv5n))vPbFjDnd)_d<;Fk zGtHe7K551U$_mP?QS$vSUa6^$k8hT#m&eD|clFD#F;|w9Tn12lB`HuZpv?~_#>Pfk z2QM!C+tA~`WM_BO!d1Rm<-3{KPQg`CqE<3sfBWeQueRnBU4aD@b63cVgz?~YuI9in zceTFPCY*1BxK3YxzdF=tQK)jdoce-{Yg07?#XRS#Y@9oykz2lmhIP|M7tM4k#xlFw zy6q%%hrGa!{!>X1{n|^*tBC}ud*Bs(vr7o{j{_~0735(3$1W?F>O+SY7-kVb#Z*3P zdsj%Tcw8-mNc7~WtHO7b2i1of;Sc$fB@x;>#4({|6x4-Ys0x@glgCXe7uSFCF?p(aFoozWRVsT z<<@L;sg^9*nkl~~8u-}W-h5LxW1%t5<6_-7QbCJpcEkc&L0)`0PM_~WDb#L)@#Nt3 zfT96e$~iC}=971^7!naYMS~NK`VUu;;AQpFpfKH!A!7(_y)iM|cGwLFu%BitKz@#N z*^0l(w^0efdR2{SnYcGw5>hV#af1>L-atHD4lxnlcy?E61!;ZLwPoB77G^OC38ma0 z1k?iGgFX6~%=PZ%=sD`?bH$(ZT9*+3>x~Vyhb%0t=Oi-)8AU4`KNN6${2X5fRTx4b zekeP6PFUIU58QY0K`>0s&B}soF@zzOdix=_0hszPE@uXIRea~T08m|BeQYRuTMiC1 z?Iu8dnUcwjd;jBWdcKLN$zX!J?MU!RI+T-zEc0GWM>m9Fa* z#GkLA#OniH;fE?wcu9#KgfmwDjRR6vIw*d}{#@vn8(!xp66DNJGvrS@=9+dj#-j4s zoHo9y6zQPcmJgiLV0Ac2)0>$!hT@&CNStIZHqs*9y7JBaXiQ<1ygpO6XruqXe zh%^JJFYt*`UonEu$>+J!FBWM{T7uGv-Dr=dv?lS+aWa3K8$3U#~oZl#<-h`+a9&f?Y1ndV8;y zRqcjXGc~~ofo;^E%qko{rI8TA@$2e(r~UjH7Gah%st#5jLiM7~{_fw#BgDk_Wyc%u z59!}$jI5xF>O6k9HV*`2)7Ixi0IXbknYVbDr$}AG6#g=RTfq(+_`BXEsi`hW8`GmF zkE53wC#n>ijGhpEB&sAL3rkmF+ZX-9h#tyvLn-ZO+Wo15j$7A{Pn`vF3g+rO7U#ep z95Fo@@n2^WSR%yrr7p8=B2e%yQ=p9>*9!~$U{Z5%I2$jRBDgqKG&KAoYzhOQ`_s~J z=|M!2$Mw9gxA&VQUx&g4qBm+_26faCxLz3R-xeujL{EQvz6P}}DsF060T$zl&fm@Q zB<{6lX6$}gi9&1+rwFnc_)nOC(qAz;Dvr^%Y<#EMBMuu|K28KW08#QSkn#ot zr3oPgDeA3Y7&Hl-n|tE#XJl{X@}9<}a3`Yy)=scd@-jmZS?U&i6hjFBZ{*(JkEEY zuwcD&*x{W32dzR0`QGP60dFjR$1XOWChV~0eryrFeeA--`o#OK!^r}~YY>+jB{~B@ zdoR>nxj$)1$aqp*MGR-L7|G9mU-;Fva6H7+>4vFs_5ObTbpCc#H6zmKN4sg6>5H(c z3!Dn-AO9z1wj}=j%W~-M+NAsSCUR;wt!HIq0{o&bI3k;6IDOUkNya0c*Z|WXK^FWk zMV1*K`r0r#x(ReX<#Qq>4dUW9tsE1GdfU)ZVl?iI{g4Su_ZJS`5kjZO9kVZZf3qY- zMG=RI>ohn~u%LyJ#YJdct?+AG_Ll(jP$+-_>7cx#_}x3{N1kP6FM-a1o_gC&!F18W z-a-ko-!qAcK_@IZtsoOKY5kJccgdVZXtM(U-1c1C_EWq`(dIg%P~(8#W@4<|9*;yR zdS%N+p8)FRP>qfRF`Nj|E9`x`GAxkLm+@LMr&FcVj$=o*Ub->%|3w|X?(hBpg#{uq zdui2l4Gdbq$V#*M!>Q4C!25d|1PEHCDZ~(J*F)|jG&M}9s4S)`C=r*g3ulEaXW>1k zD+?WO8fX&wEH^gvO-%`2VEi)wHn2{}VbkDzT+~D804X^02(xt&;#@oF1|lyOp0P|usl!i z7OG0X@jI!04b;$!^UWwO{%LV-C1^QMn}u{jW$J{O&H&I82Sa4d^q}`gGbi$j%|Q84 z-4?++Ry{kY^m9rT-`yqk7`h2L-`3UCr{O&1GhQpMz&%jj|Ap}u27^Jqc5XMVx=*Bq z6i1dJu@m?(alj}a*LDv9C>s*$4hKL%(X)+NBPeUx)^`i5{FPi@Bvp4emz-l# z4Lak|p&7p9(k&1JS7FfURn(|-+03s-qLhilc!6)T*DC>3*G?ZiWM~#=WE>S zjz%(KMRF($R3##&^-;h*Z0ts|C3Ozgne6teTx#LBV=Qld5-pX!yA24*WDMgOLaV zzYM>H=3V_iA?o*0|3WOj_4uJzjfmJR&p!UJ`d8wR?N~Yz?(buDRy(@DN-5R(r*ZV) zXhX5qV%}>g_(5{@{EBX4&_8foFiC5sJ6DgEMXe0fA6;Bte@?)`W3g7Zv_4_KJ+~do zC%ebz)(sUs-i=wU4=pa04@MVEMbDo;=R2E&T=QuB>sW()v#J2R*FN98{zQ?WRN5eA zZo9?my)J#*+%u{P$L9d zz`Uwi--%M3zh*&<^KvOpl?29f%z`^Dous6+L_GZ2lgliqRI(<(sMjIO>GWYB)+VBR za-lMb^Bmln9WRur-z;@!F&({P?SZ;N|Jf2MNEpYWQ!~&d`HhJo!d=ryV@)Hn8It_W z<7UGniSslqomAaz)@71p-e@2hitiF|7{aCus8C1Yj?qJ}f7SXe^E&O%A{FT5Kwad= zmh(SC;h+j_YErE|4M_OxTRz&L`u4RelBQC62H7m8KKVYjx(HD&`~uc1i5Do zmZJ_U#thzM!}1a1`Reg83CI3=ce+s5#~ljNLfUkO(m3>pyPy1#d^M|E2>~*BHD$y0 z&LM3kq&$@(ZOB!ENL85&I5#EDzw_)B2VtuL}~wy=*AB$ zZ@-=m2XxjL)Bh{^D~?(1u^TXHhsEuVe!Fy83JF>0FJ2frUAqj##)c)Yt@3lio$o(cGTs6j3szPh`7KVHo|G=wJgL;$rT zgkZ(GYTj}4F#t7^PA2?$=Z1MRVjvYGj2LF?~9@OOO>k^$g6eMkt6E1o2UHnxghoPjt%b( zvZthsfnFl;O%iHz{kGWIicNSoWiX}99w_ItKJnOCGrqFtu1WNZ5yGM*s+ zK!V0B0a}i6oTvCWCp)w3G|Z(?`4^~iJnx@UdT00FcS=`2=lgPc_>|K_EpBiTJ*jZR zU~*dJ^~zntTzZErIu4Qq+x1afBUFr?oC3$<-a1nNsAR=9vyxX4Mxt|k@A2pdj23mh z`1#RSW)HXqQuqokwDCA!v2=7;K^n7c8Y$%SL+N(O4E5p>u%8>m42%j-ta4cmm&H4X zfq#%NiJ3~X_ZXo^!vqgcY&rAAKtJ65UX)ckJJaf4gLf7#M;93fWW!l!HCQYA9fKs4 z64GZl%u+DCsgD_L#%7LtWj4P>AUA5AgbKZq2n(bU{6M~)iy*{pm%;4|+O zFtfW>jv6eJ<>G)3f zvcEC>6d#uZa>T#@=h-TgW2j~r#Z2~Bf2dyJ?V#N&zm}W@nNRyeuHE+gCAS93pLJydCpRIuw zw{j3+rGal=#ZidF26!3J%zJ^3AEIKngaYr$eLwfV|Q6q@-`mJVzs;)F#fgDZG z{&>S?Y&c(0L{vY=^?#;pfj5J8stFDRY-|p2*LR(Y+o!_}_>I@vM5wfC#mQ;*t`3$^ z%Q~`KKO(a~TGD__VsE~Z2I?{Z7Y*;ue+hko%-Ae4O6n+skooR*Hb!C;sQ-@BH%vKX z@M-Zs8n-)3TfDj_PLKe{##V8Q;4)X`unI+I@KB7P3X7#>@T7S^w3p8ic3}Y;8Z#?G zkUplzu=qLtjN|guYBvF%&mh*5e-xxBA=S!{(%NroYL;S%y3$B2JS@2(2Y~pcKj@YD zTS3>0NFURIyBzxKD2y*Oeq^+61(Y!b)`&!AR#~KyG_GW}qUS*$qQOUjobjJgisZ~3 zhK6g{*N#(kIYd#kfC7e7CD^2YMwJkzc^T^My`OfyZ8vKut zfeX7BP0@tRzOBq1`R;GX28d}(vT`ql`B7i$Kc5t`Cc%Y=pClr}#fBxJCL&TjGu`xt z&9D#sAw5h{{r|A_mSI_S;kNJsL`p(L8tEYtm(!V8 zdc)x5ys(9$Hp~`}&l#1N_}FKeIA)42N~(I7|IM+JrRmEvApdPns#$G6$V%WeSWNNx z1xE(vc7Tv z=NBijof$ud^YlfX4I`EWkwwLIpJOLuEoPS^KFIok(Zdq}Qx;8t`RWedJ-cchV(2l6 zb3p9(_7OZfQDx(pAqevD@GJ<`wIX^UE74xH`8Gbl??#C82My<;p`^t868}$IUJEk| zR)WIS)%NlReHl9I`7d!IOn*+7B%#+v#EP5_Rh@?|{+AmYR?wS`1sGwC8tZ!Yb4OR< z%Hs=ccNtkT?|T}4bDSz*Cl)er2e~Dnhr6M{2jbUElFJ$Tb3E0NQf0TG%tpwmTT2L; zme>mk`PM9Ynwp+{AIwGWdLh~AYUJVwLESE6H~xr~ z>l6vojp2dZS-%AQ5SuB5J9?S-R!KHz&*0N@3)ZL6CS^M4ev362e&4j%5WF@XTu4DH zQqfGRk>>-ogBEhefAYjttiu^?%+BqK#xAzoR8^uQC8+Gezr3Y~Iqq0}AQ`ES2Fk68mx^_$$6?B&!#4IQMa z!@o}4++5y1SE{NpgQ?dsX2qq=2O1s=Y~>H1Z?xv$1#nq_6H)&#bJ?_tbn*~ZOGS1a zhOs@_3x2tuk0L>=He+UVy6kV`dF6N1Q>t{n5+(<#Ja}biULM>!(yNn^CKvBU$H+JF z&FOsEBQze1nT7e_puA%t&K)>Q(wp4_MVQ_?$AF>2%T4RE)KGeExU*1iGvB)Bd`}={ z;)eMC{pz;|vj-XS3k`OqB2@5)PoL^tu5?`E?GHP74v9)fH!8(47fBqetR{@WLU&I* zB|JCqu!)0MgT*N%ZKfuS^m*B(jo>|jT4`y;tv|lPCtK|W+&2>y6G z$Ct^jrKBVHM{aF)6rlr8A04vymEE^#`npd72Itl<0v3m9+kT$d$BlQWC5KFB-RMLm ziZ7?f-&3gx@kjfo5=8(8D8_gt6v!?Txubj<`l$ah6DT38*zBc*L2p5&`6J z;wpa@4pDI)G!>)V_Kyt+ZdaI|RT)z*8IXHDZrfzz|98gHoM7F@-c>XRSW!wM%ABG z$1A;@43ZlR33cp@+fMq=zGBzUHajA1ZI);(YDIUe7RYqq#q54vBDphEUMFMlyWqlC zd6)ID`_Yz$ZkL}@?LYa2!pFfkPLy*49&abc%O)v2{Bt+Q^*gj`f>IN;Q{o-3QiV<; zz(dHV0R+KMEYPY>P5P>VqCH2|HSp%!4bAGWZ&+D{z()-3kD@GCXQ)BkK!w~_G8E87 z60Lz~KzUb7z(!o$Olhkve z1}dw!-RdWuknd#L&WkQC=QjHOlu3c@D6qlcQ>%J&3u~-}v_-_D>SZWhP`GdHem|iY z^3{enU&#gHNO>QkU2=-yVAnq`wALyK`j14ELgjz|?)HDwvi^-KFa}q@|Njr+pO5PH z7D-Vh|0_j>=c5RlW0lK2^r;{@xP%}ir*g`2O9W4B?RWoG0)bNTLJ|`}?doQ<_PFuZ z8@xhvi`~|I&wpQT$GdD;iY~xpvD-}EC9{_N$b|Rl)|1&HUsf3!?oDpun3i!zYBijn zvw_OTKLl=*CL*(_Kk#+hmCPZ={3xc|6VJY>9vE4uP|mMv6=92uqr=o*z^mhAfT-iR z+c*xUQa~^EWoc1s-a-wmjL(OJAv%u?eNZUV(SA%)j!*Mb2=nqe^>V5J=ghbfjT+bO zK>jym(5Hx~Utus~(Boq<-KURMij`uDMh~RP)RI%iN234o()c6(C7PS{M24VW0GOGkCr@DqQ>hK3*{AU2W3k{Mq$ zcCz&`VQ2d+y}^I}3Q6LZVy)x`z{;eR4;C zWxOfP(B?QBLyZ#?mrK%VYt(=4PaPKWB!wN^27^uJs2?v-BK;wLCcO2Ut+K-wJYsM= zl)^^+gwwW}1B5}KYi1Tt$5{_+-!&M7*3)FaIX!@!?BeEfTJ8dB;^5is(X;*LhfXz}c82!}4oSpsraCG1vY*(&h*=cLerido@ zcjg7j<1!s$-34m3`}j${&%d#o^h<}~bRdDXkmc5fQEagJ9^h>#j(qYvcra~iBu9961Xm5yZy#$@N_q~zbx?;mrZ@{M z;9dc1*7+CtHhi$nm`!gc>Ylk!M5PNG1ko`=P+sARByVQn!&!@yC==OBvlo~(E^Iu( z+!J+7K4j8!qtZi}iAz*nYyJAf>bUQfxOwTYN^3N2C0$J?!1f=V!O8n zPk5y@#CiPH_`RnY&Aa`4qWr24aeboinc5Ind-E3PAE5oMz+j)d5iI=HE1c_H{aQa9 z`c@xZDoSQF#Pu|L7s%%gpxp?1KJlH(rJZj=S_`*WU3E~L??o8*C?aGtBa{D*^|sy2 zE*y95;0iq&+)F*{IQ8yF45s+pMG?t@8QA%@pPzzXk-<4j6(xoA`pV3rfBIXXetU~1 zS#9vU1Y zltb1hIyHNQo+kkvt(Cj`m{2you!eQR0_D?-)nd8YdX?QwDMe}M9pO%0z}w4(jON|n zpHw_9n0O5GU|~F0!K2>hyyQwtPM4@S_gC>%goAGgSmft|T5dr8&*twE%gHl(2>qUY zRDATC-=D-KPk=MSHO-*Uq1RFKMsVPUjydH(y` z2!|8?-AA`_!i~O%k|(x>m0rP-hfn@g{X9I75($}Y+%(G*`o>jxJI0;()rll{0oE!f zDsn@(JUxn%fmbT5Ti!yT_P9HW)!@5fYx~wcCDI;>j7De`{QXKc8BnF8Te&3{(r(2Z zVjN;RoWMHHdC4W4(vr~?nbuKm3qH)ExR=X5{oqdKaIA@Xkp_hUHTrVGj&Qk#O%eE} z&x5^cUKe-n)>tm&pg~N6jpy;%B5(G$^{=}I^b6pVBP59O=9w;ze!raTkUPj`Ib2_v zI5$UfK|xv^5b{^IF4a$IDh;R)0?;janiEMxL+0y01RnhFMnap3xy>eLLJm&z26bhX zzi3d@A&K;-ktr`tBv;?NAJS1E{VC46)H6KU;MtAlj6x}rh>lF_55>Gqt`V`pbold2 z8t9Fq3pc6rse|s=RWa_#^}|dxFQj)ba?X@j zCE2)Uq#t!~nWVqx{hbkRHkA~{@#NsYv;l)f&2F$hqVHOqu@j`KP7Db?Y;r&sU}R)|t9*{i zj3C{>H+jn+=5GxXGH$eX2Z2K3(^jWeL*V(yz z4O2bieU`$bnJ%5aI<>cHcf-}w=e=sa-V=1A`L3NZG_npikLDg;CGOih9Ql$HJ>MT; zCZKXmK#B{=uWUPod$Dgw9jODGWW{oc^pw31U?XNI3kJ zDiSNsH#2xnDm=bc3`DgO>a6^%$JFNh`4WW(`ovS@)TjHEF88{%-;z|mTbp!#Za(!4 zFQ8C&q9s(y??tmsHJGCjqEga`6c>{)fsmohACA>Hpu{4_{pYDgpL*c8Z~coDhEz;S zO>T>~hyJ&92$*nSqNDS;TQTG~&=#aTV?U`2d@==|W1$T8Zfm!~Vk)S}Wc2jG#*1fd z&Dgkxf&) z+i~oN4pA!n(LYrhd%Ly!=j&QhCj(H}MXnTjRB<5GQnUXkrGmc|9oTB^o(Er8Zg@xf zY|J`a?VMoy;HY3{W#UIyX_Ly1INlxL9miMPn-;ppZoJy;nI-V%E^8m_%Zo4+q2+T^ zr|%qTcvaN=Q_u5O-I=dia~;tbj3iP##HL#d@BRs?z>5FMW?67>r1Q;m?L95LJMEUO zeMku5O)^=oeM$jd`0K-T@y5#|R;Qm6o((KLpQ}1w^BD z`%1T$JT-}yGD8Kspma}28M8F*JF0UddmJj+l;qSmoPzWQyP5bQ+0YLiOFQ$APc-41 z?L*DadIOPJaQ(z(;3;uqlAD}RqDm~TB@SPQ2a(n@46>m?EEu6y5 z>#G>FXxJo#qu((WxIpyYw;8_YuA4e%Z+tDJx_+*G#HP+uiLusy!-C@^##4Yrdl>*W z9YcJgnub~1&^W@-9K@s0KqLtAgCRc%A0nQ9Il=&a(_sg>xl{xYWMV`--QME-aawy^ zXZ~Z=>MGrPrA0&5rs2R6UMtE)K&j;LOpQalG!n&oR;&C$0TfbePeEqyf($PLvz?{%CtyS(?w}qSSK^m zNiz^Z%Csp+#{5;8Q?g{@73mztCU zA{5CWDV5JBU*po^?0MrHi1Un#ik{p%+&kx*+&n=z?y!F=38LzaRQ;@=V73)6skzS=h76@zrA1+M7+pEsNO?=Pd zT1fjn)&0T)YmrPWg$~i5U+=y(GF=B-h^x%za(q&H_d2^wRq4x3^VM<;zsko?MI%*+ z_(B8k0z1rD`Zb_A5-swpCnG2?kGrtpnXsJLPQeH`HX&suUwI`lF+W#6sUle|x%r2l8>1D4~{Rb5UmTk$w-oi|HY-DsUW+{(GlMqZoNbfSI`mjRAuZ9~P!=e{g((fao$0^}cbx#-h3qn(h^rR#2SePAaZ=hy855+@g zuF`~VJfh5|`bXU%D(UyZ!@5-2TI0z%@}R_w(kdK1Gc(Iggv0KB#P`Ve8HrgbMCj98 z{xbW89B(qXlV}DQu|j)&G)@|fzlf5lYMeV$sBwmot)Xt7{TjVC!H@;_nD}ZwOSW=>qUl_cY!Q{ZZUZ#Ns zJvM(J8YvQMBFY=0X5QZiersX^ZT$Cr#!ApQ!c)oNPzY2y&Y#=_OtaVGXIXA$Z_>Xc z3g*VQ@Vh&nyb_BRO{F!JJf~BUF8Y_biG2iIvhQ0^G;7N8Wgn_c`$SkTu7 zJwXUB8+vzmRBWi!*jdxhZ7QojCto`icdmiQlJ~I~Y?I|^5n8=_G!yyOs1`Xw?f1~A zCBa5k78|*0Ir0F*@2#e%baqK+1G#UW6rD>{kBgObPTGemo#B`jt)jRuFEOh!0yE+B z^l_6?Y9qAjH&Xmc{Bm^2GuatnLJUxHd+kV z`DOk^T#cT5)&XLxKgX^f#}=MD=)<+)op8WA3+r@p`{Wbs%UND=^WzgR{**h9jL(7Y zjPQz@&m}IYkdrNmzh1S|&Y&00!zqF5iVcU;P1R0rs#QGluj8mhVG@6u$-jape3U^K zOn@FJh6!wmk#*f{gm)n(nbpjr-d?HS>%#<}U*gw_sqU*#WuyD8!iqG6vLr!wp_nI{ zn>1)pK~c(7tfqU1A6V0_HPNRq`*iwr%R0-)74IHXJUjD`fv<(E``axl`*~LTlLTtJ ze1MzW=c;hLk4$^~rwT`H z_sbDRY+b>Kap{U5JcNEgUW7#}d6Y4zODF1?-+=xe@103MgShKm=zFg^@_VJsgSDAI z!!c*Uk1?5}LPPaRW!FzbU*hw*J(PsLrBXA78d^QmEIn58wDI2jrXd^8TeV;Hl@Lpu zEHL&v={%iS+)w#k^QUXV_ulrO^@zRyG0`0VlEw$m%k3Dq?*hg}d>8Lh09~`I%Z82` zkM+6J`li2yYAk8Z?(}^+@N^{y7fpkFeI&R|w7aOE$j%Q|&LqA2cz48vD4qnFY5P$T z_FvIZFFgVAfyo_x8kZB8XDjVx6f?XDgzEJ;Y4-CC@#)Fr!)1MKXs1XbnQ8;)o6m)t z-|0X1z1Xq5hS5J2_1mq_x1qsPBVWwOl8Ux7;Gg+po9e9b`_1&yEu78iXW#Z_N}Goc z(+qsHB6yPIwtV#*_w0{;w1}J8{|yHc?st(9`tw^O4!=5$=>M2Q~T>^ia~ZxXsC^+&L(JgsezT*daW&a^b3bZHQ8f(v>b86p@PG?1F-g64*EB&Ju7 z-1jAe)MUmiVl1R`IFR217={MDo$i0WRP)Sb!}D@{APC#P>GHxEawn%;-&hkgTCRM) zWR$>pb?N6onaSl8J1Bt+%dGCIb3Q+QAD4McXS%j4*FAR9J9l=%{O!AR*Iw;dqD2pu zoMn1*J)0yEPTDuSiYRq*#vZgpgb8-=RohQ}H(la0uQ=#)AadCpeh5kVedChzC5xby z{?guNXgwvtl-+b=bd_7|1!SNblgA_zT%_R|MOCi{=MwWlb_l#sPd3K?U3;ZdelH+6 zDti%p(e6~OU1#h8)^A_nBwIL}91?dp*}l3U6I9GVq?rjoQi_UX*FC|S9EP@Y)mI?z zsc>ho^*-NrHtw>7i+w(VOg6jxYpmAu5H!9wD12|)qZ2Pv!z+I&A2yN&^JFf6Ab@d`bK>BWdR`P?GT8EEiLD=~LK_hrBWaQLb-tT3Lu-5oCeytb1v0nB52LRSjp!kv4{ zg=)wuZ_bCCL6*S5@$pK=d8m^5gOD85o9HQaP*j${1zBB2TE^R+LHA3mx1zisO z9Iyp0ny%11zSldw~$Ma2=(XFi}Cc4JF=E6sBl{5is?s5Y?UX8S(@ zkvb(c{ei=H_>3QCj(BElHaL*q+OEVR2X7>)-4; zjf*Z81Lwcvc1xSPNq~&T(1N39PVg*2QZe=T@Q`zY#9-sPuK8x*_T`b_(k)R~B=NXDUnU4rx=setn{`yZk>eR_1y+Xzg1QgBndg zrq;vMoGzEXOS^E9`N6=#3^rucjKg-#>dUzBSz>eg%B5LHJyA2Z`~L(4sE185^3o-B zTf8{-@N_Oa$TYXcU=FeHAMZ>J3p;!B589#=JrX(T?NHxkImE;4z>LG~Dt~78F)$ND zWpPVWm{F3Ius)J5DJja-$db=3uBeC{3d?P)dO-Z|LxQz?o%Rd@wp4y7AsF1wz$0BAp*NJ9mMG#pepK?#shU zPoG5fLD__{+KwnXJ)Ol}g?4>?UD)Ho7Wl`x?F<@?dm8J9=pX5xq@0)!8UQdR9&OsX za<(SBP8QzT*&^TN%d5_W2RMTz|4W687x!ngr@q}c*&Hq2+uAx6y?GC!f`nk#RjU67 zIUF+THCuHBVp-@`+S1~O_&)diQz{&ROZ;@TYGQu%UA^y4Z>LLjtJT&?XI7k z&4;^}Q?T|kMNZ00rL(C?MKqj87sIl}&d%6QwTR&y7brf^W3tX^kU8$I5}R+7^*P||EUu3z?#h?OtCOg1vX8Ij&eGUt9w@uhpi6R@ zN{#SjEKphgmI6`rsJ&>=YXk$^&htx0E5SC*C^c+sbfkR%iQoYNk4v0KHof5LJ5{eS zC&xJMuq2Zy@CJRWNOKuFdLCNK&^#eK{Ajpri6DhO*i_wxA_%9o-4)l@JErO#U==EI z0ozEig6Z#O7Lz+<8&<$&=*Vb$IUbH84Ehu`UN@BvfUblFV9UF z<zuN?UcrX~@({V-xRqZ#F$LJG-)2M-Ad^deUw z5hoRudK_v951!55SPQ-3x)+l(5*R$U{K<}d))vUNkQTuWSr3TS&gz#Aa`@U>b%r6d zGC&YISj|x1Rqs5vej!N2ri}s7MDgz~`P{P54;Tjb|DGT%>X~uHNwgV$`A)`HI{Z|A zq*9|uok^{yCj@aF1E($W<3Muesw=qz_Iiz`PhnD|MdLr`i{_kZ=a<=Hnb9BUxrJ_i z@2SFx{kb3ZXXw*2zU1@T&6J)SB^hMw>>0}DXjqt=#-5BDf1zEb>^LA=I}He)GuDnb zoBHtFsHJ6q&e$pp)cCO>gG7( zr?oPjDY0N7fq3^ieZ*ur)jNLY&DC-CEzCXg|678%`Sb`a;^HV6BgtyKEU_Y)ghA7SW+l_qed-%@We;Byoi@ zjSKayO|T#|jF+s>#XV%iA-#-B)DajtINiMBsL^#ud-qT2pu&kih$7 z%6EL=YoTW9Ub9<@ECQlxxrT?GoQ~Eon?4k2iq$vIi|1&dywlq?00bixS?~3t0mg+k z`OuF}PS`C|Z63S3yT_j5E4#JMxc*sOUY1c-1B;#H90tm}{e4~kySR-6o-Nj9$-EBA z(^3j>mx4Yxazw~hs@X{VqFBF?2C%tK`gL|QIQV7l>c3A^LV1Dn1WYFaOtZNQZ0PeA zYblVN3a)7RQ+LgYE0^?9Us@2!axVeCi=~$X-2M>Q`UQ9^rV?$sPyQC5KoY9<1|{kE zA${)K+=RqTT9H4RT>))V+N3Qqat`}HdeG(LdG@18TGhgCzB*y$`>_qYXfyV)-upld zBz6;WT(u-x$PG%ZxRn@`nf^TzXq>!g>GxeOGw-EQ)f% z0reeN90t<_v7!4$H9@}5EF1YG7k(yR6^l?wDadG5n_hFb zHW7Gcu0L;twJOe)XZ_b?iQw3S0h%Mde-!>U;s1tzy%8}YQ;h1S-S*hzFVyJp9cIes z$KR{z{f|aVfq<>S762({cU?vqIxP_Em7oIp8_zRbSZ#s5;_#iYCr~ z{-@3Bp5K`-8-vHGsnpN`iwXpo!N5&(t7?e;+B%kVUrb$+LG$k#$JnQgk1>Q^6o3g< zGvWmVlQigs{1<>zFUAsjh?Xhy9>_Uvg<#gm0E~#1vY#MQ*YQ~Dh^Cz5! z7MD5{1O9~K*|!=!#)ZZkV(9z#x=7^rq8TU<7&po3=^7<5YVjb+mN`x)DMc@D?2+K# zzIlD&gz@rjihN9Z1F_xRGOMlKYoB?~gG2aWz;C*0i;jW8`>-R7NLbelw)(>{l*CTL z5L%2jG2n7xI_~fMQ zT&yQch+?#IN>(*@kObz=>y!w1ypI9^ z`HdAg3JUn`$tDtrK|To-r_6RnUCa5Q%r{-BBF^WD~zbTn1qq5@B(~O<<+GT@{-H3 zxBmhg8N+X~-m7aPh7sVzn;itbj-9!=XPp$?-;;whetUk=NDznO;vB_TKQ$8bXImIu!{q z?=)Rkfp}2=$$Gd5)^a^+pYp$So50N9*;*GqEz$-YI|oO}tJbXiXwD&cv5VrF>)+#J zPU0SBUvO?;ec*ycZeA$;GPqhk2QB9{C(8rbNxLDFk5N;nBG0*`xbxp{E{vvPE0Zuj zve32pC3(1cW19_XDG)rBU;f(D|DQRbjKt|}5ILM?Sp1`Z^iI6Vc^YP%n}LCXeS`u{ zb>sH=e~Aek)eC)8qaVe(y4OlL?RL$}x|83GL|i3s1_l}Mdu?^PKxdzCsIcTeksV`5 zWB%qenE#@_rOQ{cH&rFb!$SgHU=zQ1{VNjN#+To9brBfjac^9$fA&iw@?4=rUf9Yu zqX`9!S?nzh!4<)H_Yki6jP~|`jR#$c{R|;5%U!zO7bS=vUu@sc>PnG6`m;poNKT|w zj{}k|XEF%TC2`2Il?r+7r$}5c??S}Rhq-dZE`Rb5&_|5jh|HBk=S;=N| z<$ci(h@u+X5!d0-!JCVfvt0_^nd&_z!q9|bJuHYzlaG?JS@%t&bVWk$;0272{>JT8 z${lfrx}Xq}z_d~ZB86L5Net$NpwuF{++oY3i*4SWXMk%?%4j9HVZwp@Ql1K*ukS&h zN6b6hU!tO-3j30mPHYK8^gUTMHM2Sv5O7MGwKiMWxe^709)HB~aq-&Dj%o>C^!{0t zK?XL|BOPKb-}8yuex@l4?i_J$iH-vrl#rE2^PFd6|IOX4!=D~GGTwutW1^>B(2?E| zVU(UxEXJgM`0yNxOij%na&KCsFEmtn)OhLpoAJFh9(qWp9WvAuXP7e9YBA`ulbc_e zXj@T?;!QoKz4hfUmdY8=Fn#Br=soqeH9V)gfB^vW02as(@>Gcvhm>_#vi0udZeENDOaYnyGZsI+-1XNz?$qESj-xlA^8 zu6t-@WqCQCBN+>taiyTbz=zPkWP7Rn`GVMH5V{v{CJ=!V@c{jf;Ppg>LE}Y5t~~sj z*u3{>>W+33&mVR|*_LzZ@>O9XVa?Vg!c3HAUlV_l`Xg(SCBY zJEB5c_$NB3)eKxaF(N!e-?4{s$tbgre&!gx-vmG0(gIsq!-|X>+Ff6bimC@CFf)R@ zgJP11^#wIwc9Z;D1BAd)ns;E0pE8)~=G$RYf0kKLAkbB+dYm(m{J~~yqgFU@rrwK5 zr^>#|0gcBVl;D_Z9E>36XH^aonUyJRxAYy2rKzQ^YiD$ z#6S7bgA8sk`OOC-;sUklI{C};;DdXwc7KeqX3iPzwKPlC&NdqO2bl@0TP?-8$Ar4K zJdaF^%V9RN?X3=a>irj%mB5TjQaRHsJ*(UCs+wPOBWtEuH1h8z4*-gd!iTAG7kIZ4 zd)SE$2qCSJ#j7=THZ07v8#xASc{jOK30NqTQli(i>?BmIB;(xZf~j8r`0*)k01<88 zkNAiT?e{h5w@F0kH(Q~J;!RzA&B6K&Cc|8_n_J8BLxH>g3JC=&Hkd!Z!FwehSj~e^ zR&5P#OkTbuhV(ESe;uaH3xAzLFzt%Az|od)`q|FRc^^1d?|33)0q4=ADgxiEnfw37 zkmqSBd|yepeTO?UTv2{3a9v5*}z)&(MRCKZsA8oErnoU=vMEAHWD?ynV&Gb_r0)j zd5bqQgb{k9Jb_*mh_bjlzYISt;?tKKXpA-go@nkKD?anIAG0t>C&3Rji+j1PXqN?O zv499hJsghonp~;*lrOQ#Wr@^Rw(i>hPf=V`;!hNH$HRAG+q(Wsp4!>qOt@fsofmLj=L>Eg~ZCC}q#-;P_v9++teH=-(oGK1vnboBkrHn_xx7GNoZ+h6bhBx4oIl4QhR=^-J zsAX{MrpTxL<*kHnuqu7wQAJd+8w%7d$K-K^5%4Rt?dYyL&#ihWz?u~Kb6OsJ4=Twf zA$RhzOu<*8aVzzlD!~03CeG1w>p!@?urNAQ?+%^Xd)Mw~fKc@CxLRk91dW$#*HWQ4 zA{}MSoXujl`J%eREYjREvBD_y(%j#q#XP)SQ9W_qwX!*?=X`hHeT|(gE>)2VX{83W?jS`)z;MvnC2s9E zDd^Q$!7-f?H@2Qp@c?iE!U|w!YH1ZFg5I^=bc6XAVY0ZioD|-}zMIDLp8&f2Z%mb8 zNLPtoQ@*eN59SdI#gwPj<@3)Aqf)I|6xKKgUg8;G>!65OZ@Vm7Bd)}|J3$Dj+87u$ z0;QO-MK0T;@bP9`=u>&gSqz&Ap;Z!gfe69l2@L49c6_cC^Z`D4{9G0ds=3H(K8g*l-#MW9ro4D(mw`2kLW7z6cJX1SB2 zquun^z=WDT#x{xCUemk&OZ^LsvNpC)NsHm}8&f4e#!zzfJ7Rv_urY?_e&CaXxAkXb z=z<9`avcRUK&d`l8pZqMF8EfUct8Qt;syBbgX|hg(R3)EDpswfHDLD9=~g1Vjj3W)2~R-xwu4o>$9c~ zy13(ZGcH@#)Bn-&P+49Ax$opc*4`o_Y4uN{Ko=C{-56`UT)I%+L_KAqT<)vGX3x$X zU>}lX>irfg%voxv+pAm8JOCt>VZOORETgga4%(#v`+w-p)~!j7WK5 zlnoSy)k7}(G|^Opd&&9_8F5b{@33aC$6yaKsillHq9V1oHalp%f^g*Z>uUj&1A;y_ zHF5OJ}3OWGfJ2HE2sfHWJ!{3+w}fdoga z+V5YD4L)g$U(hdK0?y1kigge2)hVOJm&NdVn&On2(fH73<*d#Tb06j?nJijbfUGXJgWoG8UwD9y<-OV=$P} zNoQ@Y1%GCqjf91OekN&YYZV77mV`xnu%`Y0d?W(AF+-8*qz+~u&5?~^5Z>%kJZs}f zUh{gF2-L~krmsO;%d^x0^pulER%VOon&5A`vfb1 zLZvb(>8TwKw%|h9NFVg8;II$QK=|W_C|tRGfx##KwXP@nWfk#(B|np=iMsyg4LDxSWo4Es z25#D`k^3e*>GP+13x~(YwCG|5YAJG^hVXMi^HscYYjvhL>u`ypG0;Tfa_LNUXi6`Vow=ojxFSYw`xF4d;Eg)c6ZCISmJtT6znQt5*8lYjSv1tE44g|?qFj^}Tk~dBSM~F3_UR!V_KYP24 z3{i+ZGqW)BnLJIqpW)5#y~+>vwqYEQs+Vw85A)h-$>S+D`wR{Y0Di$;3_(lv!Oxt} z!5~FohkLo_Q2^$Pw2xVBFRH!&lja2~0bp1S1|0xmG3vDvB@1)9oV?Dh3f3jE&zPkp z=3GhFLdRXYf?h}>U~n2@3J#W!S60TN;qLO-&HFishrGnhDWp>Fky2DG7}bWK>3ixF z>a4{@rFS~&nVZ+iCzwVc6jkDk>1)gdNR98wr5B$WU*^w-Qy$L3MuT-6O&I2Ja2SqOs)Y=+xxYS?X+VQM}R< z`u##pyY6WPGcF|OaW|>IZKR&JEzu>3IS&6_Xp!xBsza>I!>*RJ=QTC9dMJ=CZ*j^q zlb=A4gXYKYZ3(xhJ#H#1Ej4`kGO;K2CWJ*cZ>I9P3&3H#J06HXnU`g5NUtX%ik+K4 zyy(r7Vwl&tWtQ_So;995@%nsXg;C*C82aVTpUl5^YyYp>K`W=h(G>R$i$dTn*Nx{S z7BHJNnlyl}Uc%u|_>J7lzKcy4E&5ZbdeP)xltCW1>tU43QvmOjA2Lef^zm$araikCpHp#WI$D`lbB0$mlb^txjTR{4DV@ zC2Zxt z1Kl>Bm;&ytOPi&-ZaOha9Dqu{guPRW&x1cNJb%6gT%-P_FBr5_0bK`6HTJ2(m#1I$ zG}WEj*Cxsi``*K%q8ax#b!!a#QA0IxHWxyvK3H)#6F>WIsH4*>NeMqQ{nH(|;G?}< z{|2DAh0Ku#hjqC`{()u>m=K{q#Y} zvETd;7eVA)D>%6R-$Z@kmXoJOQ=vuTN9*KV0snP67NgElx&&#*P*m1nf8SVw`|F(8 zBF7&g9V*du9rzgXBX`AdpqG-8LZ?5TxopAD;Si0Kq@;8X{E!A8Z^j5R`=_Kg{8xK> z%@hawN_BO0zRjk-Iy#vDPg^}fut6cMKJD_7fEllSKD4KInAaX2W@BJ!?JdJ6VuqyX zRqP}@t@iw=J|IrJ@6&}Go))S<2&{as=PhZ`UcjHjyi*Z1Ii|lcBbDHddyCTsA2*7s zV+(($>SBoMZ8uIBUFEm)#HS}be6DZysFInJU4VnO`1MhE8=aKD^iq@sMFc8;!Pp)IUU0- z;@dr6tNG2Y3$*rSm2OcfHXx{Kc*2<|4;ZF|KYGEP>d#zvt4;QduDd2CnzxrS@p+V` z$h@S!l*APrD( z>oQa$SN(i3BQ}daAOOZR6iCp?U*%zL4U3FM46iP=8QOZnW4R=d_2o*4oZyr0-3psS z2AM%l8E{JaibN+Ss-47Qk`we^f~i|+iZOX$PKVO%C(#UP;=?`9tv&h07TMS$_jT`q zndTJQ&})~4F*}wAu1KB33^HNT~2hydzIxvm01xh!YQ@2EiqAoFNIt>!UD;wnJDZv>xKv~dA0 ztumd+&hlIC4-t%L`F(IQE`t=vznUW*5LZ!!BajPUckF^lAI45%yWyY zC594Wi0a|n)fn9hGB-j9{9RbbbetIaV0mzHjFQ5}U$@2gH@;81((!9B4&0y!RAKYc zTQE06Uxvy8s-K-c5+VQ8_vXQyH}9128$!1QI-Oh{%~!{tGhhVP5N_3M(x2$a_UeRF z1S(ZY_@h1~f97#|Nl9vpg@J+0Uf1eRtNzjPqfb~hKgX{9wlG401fF=o&AHSnCjRkb zU>74}cQ+IC-36P_!~gjrLs$`m1kp8nvG=Ol2)eJWSY7r;Ejflax$-?3RZ&xJgh-2C zp$wC`17C<3VxU)EzlcW*MeyxIDlaOIlJZK!q`a0F+&Y!+PlB3hFeo^IT@DXMs{iCs zwbFGgbT(>IUx2+T2e-Z!N3P!shmY3zU+FQbmD+kpN*;7&FLv0=ADeIP^4^b)q1COj zwTWZ!++L3qXVR>^ak%mp%fVHv(#jd8p{3rLEw&iG$o&i^?|d`IQq!JNj!Jg(?`f4= z?KS0hw?6GA8LcgzEV(a9q^)urx9M#wMkyiVlacc*`tIW6I9SDjENtS3&nkmA)l<^r zoUy_8L}C$Ce)<_J$Lw$3YFKplh&e3wM9#eA+psrI8%UAN3^%jJf$@3j%8_y1zxg~k zl>hlFVj%n6g~z}vxAyV}yR6PKs!9l&N{E6E+V!8u!yB9{yrpuuAC@DX zw;b5IZ)d%TwGRhr7_#wB8@axaY41?a%0e5L6IX-HXRNY{L$*qMOcoAluZW`;-d%{H0RnwB<@K= z*CuS5Uvy*9%(ey6qofGW=kYs!awS~cBeK0%k_U;tn-||g%sQt-l%EqIG9M+ZR`MjL zwbb73^^Ce*czqJ3m5wX?(4YlL$h>{ZZ)!dNzV04rd?b_h)^5{BJ7hU+_p3cmFt|qw z-Yk7zxCXj>I#sP+3Clh*HEu(Ne(xHcf8RqFHs!>hA&OdU)XUWziS-@We zL5$f&kI#S}7RjWt)XtC|38uveeIpF{6WhcC&K6!Av zx7kb!rULj^zeL}J}))Lt8BCo%!pwbAf?Hmf9*2j7KEm36-H@UpP@ zH*q0DG(nwu1%rx<#joi(#csN^Kh-NFKkt?ZAwd|(E>Wlj_XLJ+?6csxrkZ_t% zk?{};Hv1b-C_j0@ed}k)NKkBi92u{t+PxR_(1# zkeF$Dnu>6bhPCT0&(7f%XkD@h7DMZ?(yuO?Y>yN z=C3$5hM;yb>Z)&3x*XTU{;jcfRbOkVqWy#__VcW(%u!mZvX0VEmr3864VDO|Qle&% z@{22i-k+`INI9h9yj>mFd}Z;;eQZ9I`Z@YsZN5?Ly~_Q$jvSMmO7-yrq*uvlS}(UJ z8s%3vClX-BvFUq|vAx!}v|t?BU-diBgrqBGy&t4U^-16=ScIjEBp>dIK5TtU`H9z8 z1O>urrB`A|9YOMvL_koIn5cmrl2?Ns{ox0yw_#LSQCQ$H5_>*8)Dx&&p=~r#$QS8+l*KWd7Sr zz^d4JaP`D&u=!-Zq-@)b%Xkr%ztvW%1dYhux42(>A8HWO9V&GQ+=U9%I1Q6&Xcz;U zQy-va4?es|UhCZxq-n}X1sN?rs?U>+#Je0>FlK7&+}iH_>oyNrA|FP38$oq`j*#pu+|A`l-$WYDY$vs@x*Y_)fgMZ&6o8jZOyGeEo$J%!>Af+d65$<3)Flo#o z%*G>d6x??|s1IO<@fnii!|p=3xw&Inl|Dg;aIfg(L=?zEz`S^1dGxDz(On|kTt@63 zFa5DE>N^z*%Zr;<$Kvi~$AV=1-9=B~_7uYBX}uXL=nf=tpQkU+XxM8)T36esSj`u7 zY17|W+-v$0Y!UQN?`0lamDB-<$Hq(pAGzOlrFwvX$eZkJXdUluJ&Ut#VUvS%;c!kC5chPb_FB{L%POIqVQ&s2LFftra3p4yJX_ORs z$K%A@6}elz$R$F=w6)m&M}bW778xokQV2d`i2?&MH^_Kf-wm7v5D`b&^)8;qx}cSJekAa<7By>{(aZE@xiu7 ze!CWdH;XU$LxrAwpjMjaTSS;Hja+i%=`M0IX#ib9S3PUjP<68Bo_OZ)$G`}N>PWKL zy3KyXHX(e+$zt}m*=2^+2nYy$3TTd+A)#SfSLW6XJ~Z1RMD5HV7FmVcWy|#Bi&{FG z4vEvlWceMHu)yJ{UekJm*HZY#2Bb@uB|BPHZA08nEVT;DCgxkgih_nHuQc8}dfb`M zA*6TMI=gQ9Eg{!sGT2N25d`zageP8JI>+qzkit9yVxvg7i-h)-j$4Ru%*@sMnW_)6 z<4lUwH(g)*Qe{U?%(j+AtY)zfE<=4DVFpR)?vU}0rKnNgj&@*49*gnX2g~;@%X_ge z{duTsJA?1CD4*ctG--ibui|jN{--|i}d~U%Sw$n=N+$Y>>Vw>y{R9`@~wg=_zcuHCC1(#%eCnlVp!n7V_1lyZV`*Vj1Vh`>n#66C)eyZj92=5yXWYeoS2|Ewf zxjpL{PP_3E(lp_!Y@7SzrTzla8(nY{YuR$)zg$|*a%8C?d=RClZ}4Fd4AMSy1c^a! zl2=YD#wzck1)VG#MI4PENi|M(wT{k4Re|fsZ^k>k!*4%WI;ZlUghhXY-6P?L_d+j} z9mMZ1iEpx-T6jD?9B)TcE;-Ijf&Dqq$2O{1ex^Gtx#1jIybgOkw^9Cc2gCcyIQcMs zj>z-<_9pmNfim?E3c%jcL4o@CI7P#iKLf47K;ir8rGMXjx6~o#wtHSEE$MSqm~oO8 z%BRbj-bFdjBtri6EF(+gC93L~$csF+E|-FMc#08JKPYs~an;gbCHku4H7@%zV_g4) zNT%r$JHZ^q)A1+vQ3gO-p;cK_^p12ITu)P!ItuwYJ|9d-^E5kYK#VZ?9JqO9K z-fUjD=;fgCk>E8tP;jK++#O1c1kuALOgPNlazp$fuHchqB-ImG%G(5EZ?0PSN!uMICf2fv^ zG6Pg2+#zSk90sY;eM5dC?YFM)Pq}9UG(P zn`*poBQ6>U5=BxKvT^wEpk!ac?luV&ud!GOf}q^lup=|eohLKZq1f0SSP+H0pI91_ zmz$H-`sr{$Kma~QFjD>0)Iisiz|y(_agyiih{Ng06Qu9jmp%7(W?JZ@oUC-EV1&9% zJYRHe?;F=+25zQpp>@BdqN%nR7NwuJTjE1`9<5bnmVIkSlP~hCN-e}w-4{zXhA&~@ zh@i!wc=3XEZC^A=Hd8)lw#I{rl^Y!zE!_r+aSYt#5hK%fxt z!J@wO<>gnIr1k4&W9if6x|uqsJ7Co}0;*!fVk$JL98~hbGSWzJ{g_&89Q8!GgItOy z%Afb)0qtMgzI@S-eWmw9iGf+C#(?VneZD8U?*{HjGDJ`0uI7UwKuqpo&F?Fm){fS0 zv>{gR2Ok69z@aJZPBRY+B|cDaGMV9V1$okkhLS6z$`5;-^S0X-^#ubGr=sJOAebEC z4~_$_V9B`5TT^403Kb&uK`6ChL_pwFOYG4DY=RPlZ+JZp1pZTamNSOimBbMZHbRf? z#7K>3!%9M^Z1?3c*%F7Fq)kaIjtYrt89ko3krYBo<$(y4Y{*?j2(#*r(vLYh_Y#&yKp$sCTycfH$5& z01da5zV=4yw$_!GhDHHKZ7Sf%L^0uX9o>2P_w)Rr8<;kf%0{~6zd({uJcbUmi!dw~ z%TlG+Mg_q#zm8^`ZM1S#0tfJn?K27k(EQ{j$d}noEFEc}zBTFEdYmR!h*6W_XO>Vh z+?}Y2Zaz`*zWoYp>5<|R&00RarM?@|yoM>4pssGlEY1lKvoDK&g9eEx7OT%3Zjo@w z(?^@BFoy;PLtZY}F=giKjUVk?Pq(s!Qd4dtM)GzT5Lgf{*^~!Jw1 k4rTW1M3*j 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 A761SM delta 85925 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 95.0 + + +def test_averaging_takes_the_whole_cell_and_no_more(): + """Each output cell is the mean of the source pixels under it.""" + values = np.arange(100, dtype=np.float32)[None, :] + out = bm._resample(values, np.linspace(0, 100, 11), axis=1) + assert out.shape == (1, 10) + for i in range(10): + assert abs(float(out[0, i]) - (i * 10 + 4.5)) < 0.01 + + +def test_averaging_works_the_other_way_up_too(): + values = np.arange(60, dtype=np.float32)[:, None] + out = bm._resample(values, np.linspace(0, 60, 7), axis=0) + assert out.shape == (6, 1) + assert float(out[0, 0]) < float(out[-1, 0]) + + +def test_a_cell_smaller_than_a_source_pixel_takes_that_pixel(): + """Zoomed in past the tiles, a cell covers less than one of them.""" + values = np.array([[10.0, 20.0, 30.0]], dtype=np.float32) + out = bm._resample(values, np.linspace(0, 3, 10), axis=1) + assert out.shape == (1, 9) + assert set(np.round(out[0]).astype(int)) <= {10, 20, 30} + + +def test_a_gradient_still_comes_out_as_a_gradient(): + south, west, north, east = tile_bounds(9, 81, 178) + levels = bm.ground_under(south, west, north, east, 64, 64, shades=32, + fetch=gradient_tile, zoom=9, pause=0) + rows = levels.mean(axis=1) + assert (np.diff(rows) <= 0.51).all(), "the gradient came out lumpy" + + +def test_averaging_is_the_same_shape_as_the_picture_asked_for(): + south, west, north, east = tile_bounds(9, 81, 178) + for width, height in ((40, 40), (137, 91), (300, 200), (17, 5)): + levels = bm.ground_under(south, west, north, east, width, height, + shades=32, fetch=gradient_tile, zoom=9, + pause=0) + assert levels.shape == (height, width) + + +def test_a_map_asked_for_at_more_detail_than_the_tiles_hold_still_works(): + """Zoomed in past the tiles, a cell covers less than one source pixel.""" + south, west, north, east = tile_bounds(9, 81, 178) + levels = bm.ground_under(south, west, north, east, 2000, 2000, shades=32, + fetch=gradient_tile, zoom=9, pause=0) + assert levels.shape == (2000, 2000) + assert levels[0].mean() != levels[-1].mean() diff --git a/tests/test_flightmap.py b/tests/test_flightmap.py index a5ede39..4efa7ae 100644 --- a/tests/test_flightmap.py +++ b/tests/test_flightmap.py @@ -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) diff --git a/tests/test_livemap.py b/tests/test_livemap.py index cc10b44..8d35133 100644 --- a/tests/test_livemap.py +++ b/tests/test_livemap.py @@ -55,7 +55,7 @@ def a_blip(icao="A76154", callsign="DAL538", lat=32.95, lon=-110.95, def a_sky(*blips, **over) -> Sky: settings = dict(unit="knots", hold=45.0, home=(32.4325, -111.0841), - radius_nm=100.0) + radius_nm=100.0, brightness=0.70) settings.update(over) sky = Sky(**settings) sky.started = now() - 600 @@ -297,11 +297,60 @@ def test_nothing_heard_yet_has_no_middle(): assert a_sky(home=None).centre() is None -def test_a_map_is_only_used_for_the_view_it_was_fetched_for(): +def test_a_map_is_used_for_any_view_it_covers(): + """The whole point of fetching more than is shown: a view that has + drifted a little is still inside what was fetched.""" sky = a_sky() - sky.set_ground(np.zeros((4, 4), dtype=np.uint8), "a") - assert sky.ground("a") is not None - assert sky.ground("b") is None + sky.set_ground(np.zeros((40, 40), dtype=np.uint8), "k", + (31.0, -113.0, 34.0, -109.0)) + levels, box = sky.ground_covering(32.0, -112.0, 33.0, -110.0) + assert levels is not None and box == (31.0, -113.0, 34.0, -109.0) + + +def test_a_map_is_not_used_for_a_view_that_has_left_it(): + sky = a_sky() + sky.set_ground(np.zeros((40, 40), dtype=np.uint8), "k", + (31.0, -113.0, 34.0, -109.0)) + assert sky.ground_covering(40.0, -113.0, 42.0, -109.0)[0] is None + assert sky.ground_covering(31.0, -120.0, 34.0, -109.0)[0] is None + + +@qt +def test_the_map_is_fetched_at_the_size_the_bigger_box_needs(app): + """Fetching a third more world into the same pixels and then cutting the + middle out of it is how a sharp map ends up looking like a photograph of + a map.""" + from bandsaunter.livemap import GROUND_MARGIN, SkyView + + sky = a_sky(a_blip()) + view = SkyView(sky) + view.resize(900, 650) + view._draw_ground(_NoPainter(), view.projection()) + wanted = sky.wanted_ground() + assert wanted is not None + _key, _box, (width, height) = wanted + assert width >= 900 * (1 + 2 * GROUND_MARGIN) - 2 + assert height >= 650 * (1 + 2 * GROUND_MARGIN) - 2 + + +class _NoPainter: + """Stands in for a painter for the one call that only asks for a map.""" + + def drawImage(self, *a): + raise AssertionError("nothing should be drawn without a map") + + +def test_more_is_fetched_than_is_shown(app): + """A window that fetched exactly what it needed would fetch again on + every resize and every small drift of the middle.""" + from bandsaunter.livemap import SkyView + + view = SkyView(a_sky(a_blip())) + view.resize(900, 650) + projection = view.projection() + box = view.ground_box(projection) + assert box[0] < projection.south and box[1] < projection.west + assert box[2] > projection.north and box[3] > projection.east def test_a_map_that_could_not_be_fetched_is_not_asked_for_again(): @@ -314,6 +363,84 @@ def test_a_map_that_could_not_be_fetched_is_not_asked_for_again(): assert sky.wanted_ground() is None +# --------------------------------------------------------------------------- +# The map staying still +# --------------------------------------------------------------------------- + +def test_the_middle_settles_and_then_stays_put(): + """Recomputing it on every paint moves the map a fraction of a mile + whenever an aircraft appears or leaves, which throws away the tiles + fetched for the old view and blinks the ground out several times a + minute.""" + sky = a_sky(a_blip(icao="1", lat=32.4, lon=-111.0), + a_blip(icao="2", lat=32.5, lon=-111.1), + a_blip(icao="3", lat=32.6, lon=-111.2), home=None) + settled = sky.centre() + assert settled is not None + # One more aircraft, a little to one side: the middle must not follow it. + sky.update([a_blip(icao="4", lat=32.9, lon=-110.7)], 1, 4) + assert sky.centre() == settled + + +def test_the_middle_does_move_if_it_is_really_somewhere_else(): + """Settling must not mean stuck: the first few aircraft can be anywhere.""" + sky = a_sky(a_blip(icao="1", lat=32.4, lon=-111.0), home=None) + first = sky.centre() + for i in range(6): + sky.update([a_blip(icao=f"far{i}", lat=40.0 + i * 0.1, lon=-90.0)], + 1, i) + assert sky.centre() != first + assert abs(sky.centre()[0] - 40.0) < 2 + + +def test_a_receiver_that_was_told_where_it_is_never_drifts_at_all(): + sky = a_sky(a_blip(icao="1", lat=32.4, lon=-111.0)) + for i in range(5): + sky.update([a_blip(icao=f"x{i}", lat=35.0 + i, lon=-100.0)], 1, i) + assert sky.centre() == (32.4325, -111.0841) + + +@qt +def test_a_small_drift_does_not_throw_the_map_away(app): + """The failure this is here for: the ground blinking out because the + view moved by less than a mile.""" + from bandsaunter.livemap import SkyView + + sky = a_sky(a_blip(icao="1", lat=32.40, lon=-111.00), + a_blip(icao="2", lat=32.50, lon=-111.10), home=None) + view = SkyView(sky) + view.resize(900, 650) + first = view.projection() + sky.set_ground(np.full((650, 900), 20, dtype=np.uint8), + view.ground_key(first), view.ground_box(first)) + # Another aircraft arrives a few miles away. + sky.update([a_blip(icao="3", lat=32.44, lon=-111.06)], 1, 3) + moved = view.projection() + assert sky.ground_covering(moved.south, moved.west, + moved.north, moved.east)[0] is not None + + +@qt +def test_the_crop_lines_the_map_up_with_the_view(app): + """A map fetched for a bigger box has to be cut to the right piece, or + the coast is drawn in the wrong place and the aircraft with it.""" + from bandsaunter.livemap import SkyView + + view = SkyView(a_sky(a_blip())) + view.resize(100, 80) + projection = view.projection() + box = (projection.south - 1.0, projection.west - 1.0, + projection.north + 1.0, projection.east + 1.0) + # A map that is dark in the north and bright in the south. + levels = np.repeat(np.linspace(0, 31, 200).astype(np.uint8)[:, None], + 200, axis=1) + cropped = view._crop(levels, box, projection) + assert cropped.shape == (projection.height, projection.width) + assert cropped[0].mean() < cropped[-1].mean() # north still on top + # And the piece taken is the middle of the fetched one, not all of it. + assert cropped.min() > levels.min() and cropped.max() < levels.max() + + def test_the_map_is_fetched_off_the_painting_thread(): asked = [] @@ -477,12 +604,18 @@ def test_the_map_underneath_is_drawn_when_there_is_one(app): sky = a_sky(a_blip()) view = SkyView(sky) view.resize(900, 650) - key = view.ground_key(view.projection()) - sky.set_ground(np.full((650, 900), 31, dtype=np.uint8), key) + projection = view.projection() + sky.set_ground(np.full((650, 900), 31, dtype=np.uint8), + view.ground_key(projection), + view.ground_box(projection)) lit = _rendered(view)[:, :, :3] - from bandsaunter.flightmap import GROUND, GROUND_SHADES, PALETTE + from bandsaunter.flightmap import (GROUND, GROUND_SHADES, PALETTE, + dim_ground) - r, g, b = (int(v) for v in PALETTE[GROUND + GROUND_SHADES - 1]) + # The brightest shade a drawing reaches is what the brightness allows, + # not the top of the palette. + top = int(dim_ground(np.array([[GROUND_SHADES - 1]]), sky.brightness)[0, 0]) + r, g, b = (int(v) for v in PALETTE[GROUND + top]) covered = ((lit[:, :, 2] == r) & (lit[:, :, 1] == g) & (lit[:, :, 0] == b)) assert covered.mean() > 0.5, "the map was not painted under the aircraft" @@ -652,3 +785,185 @@ def test_the_widest_box_stays_within_reason(app): width, _height = view._box_size( view._wrapped(blip.lines("mph", home=(32.4325, -111.0841)))) assert width < 320, width + + +# --------------------------------------------------------------------------- +# How bright the map is +# --------------------------------------------------------------------------- + +def test_the_map_brightness_reaches_the_window(): + from bandsaunter import aircraft as air + + sky = Sky(brightness=0.9) + assert sky.brightness == 0.9 + assert air.AircraftOptions().map_brightness == 70 + + +@qt +def test_turning_the_brightness_down_darkens_the_map(app): + from bandsaunter.livemap import SkyView + + def painted(brightness): + sky = a_sky(a_blip(), brightness=brightness) + view = SkyView(sky) + view.resize(400, 300) + projection = view.projection() + sky.set_ground(np.full((300, 400), 31, dtype=np.uint8), + view.ground_key(projection), + view.ground_box(projection)) + return _rendered(view, 400, 300)[:, :, :3].astype(float).mean() + + assert painted(0.3) < painted(0.7) < painted(1.0) + + +@qt +def test_the_keys_change_the_brightness(app): + from bandsaunter.livemap import Window, _qt + + _name, core, gui, _widgets, _signal = _qt() + window = Window(a_sky(a_blip())) + try: + def press(letter): + window.keyPressEvent(gui.QKeyEvent( + _get(core.QEvent, "Type", "KeyPress"), ord(letter), + _get(core.Qt, "KeyboardModifier", "NoModifier"), letter)) + + was = window.sky.brightness + press("]") + assert window.sky.brightness > was + press("[") + press("[") + assert window.sky.brightness < was + for _ in range(20): # and never off either end + press("[") + assert 0.0 < window.sky.brightness <= 1.0 + finally: + window.close() + + +def test_the_country_of_registration_carries_a_flag(): + from bandsaunter.adsb import Aircraft + from bandsaunter.flights import Flight + + craft = Aircraft(icao="4CA1FA", callsign="RYR1234") + blip = blip_for(craft, Flight(icao="4CA1FA", owner_country="Ireland")) + assert blip.country == "Ireland" and blip.country_code == "IE" + rows = {label: (value, flag) for label, value, flag in blip.lines("knots")} + assert rows["reg'd"] == ("Ireland", "IE") + + +def test_a_country_with_no_code_gets_no_flag_rather_than_a_wrong_one(): + from bandsaunter.adsb import Aircraft + from bandsaunter.flights import Flight + + blip = blip_for(Aircraft(icao="4CA1FA"), + Flight(icao="4CA1FA", owner_country="Atlantis")) + assert blip.country_code == "" + + +# --------------------------------------------------------------------------- +# Fading out rather than blinking out +# --------------------------------------------------------------------------- + +def test_an_aircraft_still_being_heard_is_drawn_in_full(): + sky = a_sky(hold=10.0, fade=20.0) + assert sky.strength(a_blip(last_seen=now() - 1)) == 1.0 + assert sky.strength(a_blip(last_seen=now() - 9)) == 1.0 + + +def test_one_that_has_gone_quiet_fades_rather_than_vanishing(): + """Taking it off between one frame and the next says it stopped + existing; fading says it stopped talking, which is what happened.""" + sky = a_sky(hold=10.0, fade=20.0) + at = now() + strengths = [sky.strength(a_blip(last_seen=at - age), at) + for age in (5, 15, 20, 25, 29)] + assert strengths == sorted(strengths, reverse=True) + assert strengths[0] == 1.0 + assert 0.0 < strengths[-1] < 0.1 + + +def test_it_is_gone_once_it_has_finished_fading(): + sky = a_sky(hold=10.0, fade=20.0) + old = a_blip(last_seen=now() - 40) + assert sky.strength(old) == 0.0 + sky.update([old], 1, 1) + assert old.icao not in [b.icao for b in sky.flying()] + + +def test_it_stays_on_the_picture_for_the_whole_fade(): + sky = a_sky(hold=10.0, fade=20.0) + quiet = a_blip(last_seen=now() - 25) + sky.update([quiet], 1, 1) + assert [b.icao for b in sky.flying()] == [quiet.icao] + + +def test_no_fade_at_all_takes_it_away_at_once(): + sky = a_sky(hold=10.0, fade=0.0) + assert sky.strength(a_blip(last_seen=now() - 9)) == 1.0 + assert sky.strength(a_blip(last_seen=now() - 11)) == 0.0 + + +def test_how_long_the_fade_takes_is_a_setting(): + from bandsaunter import aircraft as air + + assert air.AircraftOptions().fade == 20.0 + quick = a_sky(hold=10.0, fade=4.0) + slow = a_sky(hold=10.0, fade=60.0) + faded = a_blip(last_seen=now() - 20) + assert quick.strength(faded) == 0.0 + assert slow.strength(faded) > 0.5 + + +def test_the_setting_reaches_the_window(): + assert Sky(fade=7.5).fade == 7.5 + + +@qt +def test_a_fading_aircraft_is_drawn_fainter(app): + from bandsaunter.livemap import SkyView + + def brightness(age): + sky = a_sky(hold=5.0, fade=20.0) + sky.update([a_blip(last_seen=now() - age)], 1, 1) + view = SkyView(sky) + view.detail = 0 + picture = _rendered(view, 500, 400) + lit = _lit(picture) + if not len(lit): + return 0.0 + return float(picture[lit[:, 0], lit[:, 1], :3].mean()) + + assert brightness(1) > brightness(20) + + +@qt +def test_a_faded_aircraft_keeps_its_symbol_and_loses_its_box(app): + """A box at a tenth of its colour is something in the way of the + aircraft still flying.""" + from bandsaunter.livemap import SkyView + + sky = a_sky(hold=5.0, fade=20.0) + sky.update([a_blip(last_seen=now() - 22)], 1, 1) # strength about 0.15 + view = SkyView(sky) + fresh = _painted(_rendered(view, 600, 450)) + sky2 = a_sky(hold=5.0, fade=20.0) + sky2.update([a_blip(last_seen=now() - 1)], 1, 1) + view2 = SkyView(sky2) + full = _painted(_rendered(view2, 600, 450)) + assert fresh < full, "the box was still drawn on a nearly faded aircraft" + + +@qt +def test_one_that_has_gone_quiet_is_not_counted_as_overhead(app): + """It is on the picture, fading; saying it is overhead says more than + was heard.""" + from bandsaunter.livemap import SkyView + + sky = a_sky(hold=5.0, fade=30.0) + sky.update([a_blip(icao="AAA111", last_seen=now() - 1), + a_blip(icao="BBB222", lat=32.6, last_seen=now() - 20)], 9, 2) + picture = _rendered(SkyView(sky), 900, 650) + assert _painted(picture) > 100 # both are drawn + assert len(sky.flying()) == 2 + assert sum(1 for b in sky.flying() if sky.strength(b) >= 1.0) == 1