OurAirports โ
| Name | ourairports |
| Coverage | Global โ airport metadata, not aircraft (different domain from every other data runner) |
| Data source | https://davidmegginson.github.io/ourairports-data/airports.csv (+ companion regions.csv/countries.csv for name resolution, see below) |
| Format | CSV (fixed URL) |
| Run frequency | Weekly (Monday, 05:10 UTC) |
| Depends on Mictronics for ICAO hex | N/A โ this runner writes airport:{icao_code} records, not aircraft enrichment, and has no relationship to the Mictronics-provided icao_hex lookup used by the country aircraft-register runners. |
How it works โ
The OurAirports CSV is downloaded whole from a fixed URL (no index page or scraping involved) and parsed with csv.DictReader. Rows are filtered to those whose ident is exactly 4 characters (a valid ICAO airport code โ 3-character IATA-only idents and other lengths are dropped) and staged into a fresh local SQLite database (the file is deleted and recreated on every run) before being bulk-written to Redis in batches of 10,000. Each staged row also gets a computed phonic field โ a voice-friendly spoken name โ via compute_phonic(), which supports a host-provided JSON override file for exact per-airport overrides (see below).
Two small companion CSVs โ regions.csv and countries.csv, same host โ are also downloaded on every run and turned into {code: name} maps (build_code_name_map()). airports.csv's iso_region/iso_country are raw ISO codes with no name column of their own (e.g. AU-QLD, AU); these maps resolve them to display names (Queensland, Australia) at stage time. A code with no matching entry in either map (some iso_region values are unassigned placeholders, e.g. US-U-A) falls back to the raw code rather than going blank.
Columns โ
| Source column | Imported | Notes |
|---|---|---|
ident | โ | Filtered to 4-character ICAO codes; non-4-char rows dropped; used as icao_code and the Redis key |
iata_code | โ | โ iata_code; omitted from the record if blank |
name | โ | โ name; also used as input to phonic computation |
municipality | โ | โ city; also used as input to phonic computation |
iso_region | โ | โ region (resolved to a name via regions.csv, falling back to the raw code if unmatched) and โ region_code (the raw code, always) |
iso_country | โ | โ country (resolved to a name via countries.csv, falling back to the raw code if unmatched) and โ country_code (the raw code, always) |
latitude_deg | โ | โ latitude (float) |
longitude_deg | โ | โ longitude (float) |
id | โ | Present in source; not read by this runner |
type | โ | Present in source; not read by this runner |
elevation_ft | โ | Present in source; not read by this runner |
continent | โ | Present in source; not read by this runner |
scheduled_service | โ | Present in source; not read by this runner |
icao_code | โ | Present in source; not read by this runner โ the runner instead derives its own icao_code from the (4-character-filtered) ident column, not from this field |
gps_code | โ | Present in source; not read by this runner |
local_code | โ | Present in source; not read by this runner |
home_link | โ | Present in source; not read by this runner |
wikipedia_link | โ | Present in source; not read by this runner |
keywords | โ | Present in source; not read by this runner |
See specs/data-dictionary.yaml (ourairports entry) for full column semantics and cross-source schema notes.
Example Output โ
This runner writes airport:{icao_code} records, not aircraft data, so merge_aircraft.lua does not apply here. Read a record directly with JSON.GET:
docker run --rm --network host redis:latest redis-cli JSON.GET airport:KATL | python3 -m json.tool --sort-keys --no-ensure-ascii{
"icao_code": "KATL",
"name": "Hartsfield-Jackson Atlanta International Airport",
"city": "Atlanta",
"region": "Georgia",
"region_code": "US-GA",
"country": "United States",
"country_code": "US",
"phonic": "Atlanta Hartsfield-Jackson"
}TTL: 14 days (ENRICHMENT_TTL_SECONDS in shared/timing.py), not operator-configurable.
Configuration โ
See Data Runners for the full list of environment variables every runner reads. This runner writes airport:{icao_code} with a fixed 14-day TTL (ENRICHMENT_TTL_SECONDS in shared/timing.py).
MQTT โ
Published once, at the end of a run, to SkyFollower/runner/ourairports/statistic/{name} (all retained):
| Topic suffix | Value | Format |
|---|---|---|
records_imported | e.g. 271 | Integer as string |
last_run_at | e.g. 2026-07-07T14:32:01.123456+00:00 | ISO 8601 UTC |
last_run_status | Success or Failure | String |
Home Assistant autodiscovery configs are also published (retained) to homeassistant/sensor/SkyFollower_runner_ourairports_{name}/config for each of the three stats above.
Phonic Names โ
Each airport record includes a phonic field โ a voice-friendly spoken name used by downstream systems for overhead announcements. "International" and "Airport" are stripped from every computed phonic.
Override file โ
Copy config/runners/phonic_overrides.json.example to config/runners/phonic_overrides.json (relative to docker-compose.core.yaml) and edit it. That directory is mounted onto /app/config; the path defaults to /app/config/phonic_overrides.json and can be overridden with the OVERRIDES_PATH environment variable. Mounting the directory (rather than the file directly) means the override file can be added, edited, or removed without needing to recreate the container.
Format:
{
"KXXX": "Spoken name exactly as desired",
"KYYY": "Another override"
}If an airport's ICAO code has an entry in this file, that value is used verbatim โ no stripping or any other processing is applied. The general algorithm only runs for airports not in the file.
The file is read once each time the runner starts. Changes take effect on the next run. A missing file is silently ignored.
General algorithm (no override) โ
- If name starts with "Greater" or contains " of ": use name as-is.
- If city is not present in the airport name: prepend city.
- If phonic ends with city: move city to the front.
- Strip trailing
/or-artifacts (common in names like "City/Town Airport"). - Normalise
/and-to spaces; collapse extra spaces. - Strip "International" and "Airport" from the result.