The project is in a healthy, maintained state
Every airport OurAirports lists with an IATA code, shipped inside the gem as a CSV. Turns the codes in an air waybill's route into names, cities, map positions and the ISO 3166-2 subdivision path a holiday calendar matches on. A scheduled job rebuilds the table and opens a pull request with what changed.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 3.3
 Project Readme

trackberry-airports

Every airport with an IATA code (about 9,000), with its name, city, position and ISO 3166-2 subdivision, as a table shipped inside the gem. No network calls.

require "trackberry/airports"

Trackberry::Airports.find("FRA")              # => #<data Trackberry::Airports::Airport iata="FRA", icao="EDDF", ...>
Trackberry::Airports.city("FRA")              # => "Frankfurt"
Trackberry::Airports.short_name("LIM")        # => "Lima Jorge Chávez"
Trackberry::Airports.country("FRA")           # => "DE"
Trackberry::Airports.coordinates("FRA")       # => { "lat" => 50.0267, "lng" => 8.5584 }
Trackberry::Airports.subdivision_path("SXB")  # => ["FR-67", "FR-6AE", "FR-GES"]

An unknown code answers nil (or [] for a path): carriers send city codes ("LON") and codes of airports that have closed, and that is ordinary, not an error.

Why not the airports gem

airports is the established gem for this, and it does the plain lookup well. This one exists for what it does not do:

  • Fresh data. airports is built from OpenFlights, which stopped updating in 2019, and adds the airports opened since by hand (BER, HYD). This one is built from OurAirports, which is still maintained, and rebuilt every month behind checks that stop a bad download from shipping.
  • Where the airport is, inside its country. airports gives the country as a name ("United Kingdom"). This one gives the ISO 3166-2 country code and subdivision path, which is what a regional holiday calendar or a customs rule keys on.
  • Numbers as numbers. Coordinates are floats, not strings.

airports has time zones; this one does not. If that is all you need, use it.

Subdivisions

subdivision_path is the ISO 3166-2 code of the area the airport is in and every code above it, most specific first, the same shape seaports gives for a port. Hand it to a holiday calendar that matches a holiday at any level of the path, such as Trackberry's own (not published):

Trackberry::Holidays.holiday?(Date.new(2027, 5, 27), Trackberry::Airports.subdivision_path("MUC"))  # => true, Corpus Christi in Bavaria
Trackberry::Holidays.holiday?(Date.new(2027, 5, 27), Trackberry::Airports.subdivision_path("HAM"))  # => false, not in Hamburg

Where the code comes from, best first (the subdivision_source column says which):

Source Airports Example
override: a reviewed list, data/sources/subdivision_overrides.csv 0
boundaries: the airport's position on Natural Earth's states-and-provinces map 831 Strasbourg FR-67, Beijing CN-BJ
ourairports: OurAirports' iso_region 7,903 Frankfurt DE-HE, Hamburg DE-HH

The map is used two ways:

  • To go below OurAirports. OurAirports stops at the top level: Grand Est for Strasbourg. When the map finds an area inside that code, the airport gets the longer path: Bas-Rhin, inside the European Collectivity of Alsace, inside Grand Est. Some rules are kept at those levels. Alsace and Moselle have two public holidays the rest of France does not, which a calendar would key on FR-6AE and FR-57; Nager.Date does not list them yet. When the map disagrees with OurAirports, OurAirports wins. The map answers with the area containing the airport, and only for an airport outside every area (built on reclaimed land, past the drawn coastline) with the nearest one within 10 km.
  • To fill a gap. OurAirports still writes some codes ISO has replaced, such as China's pre-2017 numeric ones, and XX-U-A for "unassigned".

8,734 of 9,051 airports are placed. The 317 left have no subdivision because both sources still use codes ISO has retired (Poland, Norway, South Africa, Czechia, Kazakhstan) or the territory has none (French Polynesia, Puerto Rico). None of them is in a country whose public holidays differ by region, so a holiday lookup by country is complete for them.

Every code is checked against ISO 3166-2 when the table is built. The ISO hierarchy and the map lookup are seaports' Seaports::Subdivisions and Seaports::Boundaries, used by the build only: the gem itself depends on neither.

Cities

city is the city the airport serves, the one a carrier, a courier or a person names: "Brussels", "Athens", "Leipzig". municipality is the town it stands in, as OurAirports gives it: "Zaventem", "Spata-Artemida", "Schkeuditz". They are the same for most airports.

The served city comes from OpenFlights for the 6,000 airports it lists. OpenFlights stopped updating in 2019, so its cities were extracted once into data/sources/served_cities.csv by script/extract_served_cities.rb, from a pinned commit. A city carries over only when OpenFlights puts the airport within 50 km of OurAirports' position, so a code IATA has since given to another airport does not bring the old city with it. Every other airport takes its municipality, without a trailing note in brackets: "Helsinki", not "Helsinki (Vantaa)".

The subdivision always follows where the airport is, not the city it serves: Cincinnati's airport is in Kentucky (US-KY), not Ohio.

Short names

short_name is the label to show beside a code when the city alone is not enough ("London" is five airports): "London Heathrow", "Lima Jorge Chávez", "Dubai". It is the airport's name without "Airport", "International" and their translations, with the city put in front when what is left does not already say it (compared without accents or case).

Where that reads badly (an official name that runs to eight words, a repeated word, a dotted İ), a reviewed label wins: data/sources/short_name_overrides.csv, as iata,short_name,note. Like the subdivision overrides it is a build input, not shipped; the label is built into the short_name column of the table, so a monthly refresh keeps it. Each refresh lists the rows whose airport has gone or that the rule now labels the same, so they can be removed. An unknown code answers nil.

Data

OurAirports (public domain), filtered to airports with an IATA code that are not closed. When two rows share a code, the one with scheduled flights wins, then the larger airport. Coordinates are rounded to four decimal places (about ten metres).

Served cities: OpenFlights, © OpenFlights contributors, available under the Open Database License.

License

The code is MIT. The table (data/airports.csv) is a derivative of OpenFlights' database, so it is under the ODbL 1.0: a database you build from it and use publicly has to credit OpenFlights and be offered under the ODbL too. Using the gem to look airports up does not. See LICENSE.txt.

Refresh

.github/workflows/refresh.yml runs on the 3rd of every month:

  1. rake airports:refresh downloads OurAirports, the ISO 3166-2 list and the Natural Earth map, and rebuilds data/airports.csv.
  2. Trackberry::Airports::Diff checks the result before anything ships:
    • at least 8,500 airports;
    • the table shrinks by no more than 2%;
    • airports with a subdivision fall by no more than 2%;
    • no more than 2% move to another principal subdivision;
    • two dozen major airports (FRA, LHR, JFK, LIM…) are all present;
    • fourteen of them, in countries whose holidays differ by region, keep their principal subdivision (FRA in DE-HE, MUC in DE-BY…), because one country's codes breaking stays under the percentage caps.
  3. If it passes, the job opens a pull request listing every added, removed, renamed, moved, re-citied and resubdivided airport, and starts CI on it. If it fails, or a download fails, it opens an issue instead.

Merging a refresh bumps the minor version, and .github/workflows/release.yml publishes it to rubygems.org.

To rebuild from files already on disk: bundle exec rake "airports:refresh[airports.csv,iso_3166-2.json,admin1.geojson]".

Install

gem "trackberry-airports", "~> 0.1", require: "trackberry/airports"