Package: xplane-airports
Tools for working with X-Plane airport dataxplane_airportsis a Python package for interacting withX-Plane's airport data (apt.dat) files.This includes the following major components:TheAptDatmodule: Used to parse & query rawapt.datfiles (e.g., the files stored on disk in your X-Plane installation)TheAptDatclass itself: A parser for X-Plane's airport data files (which may contain more than 35,000 airports); a collection ofAirportobjectsTheAirportclass: Represents an individual airport from anapt.datfile.Thegatewaymodule: Used to interact withthe X-Plane Scenery Gatewayto get information about what airports are available, and to download individual scenery packs contributed by the community.airports(): Queries for metadata on all 35,000+ airports on the Gateway.airport(): Queries the Gateway for information about the specified airport itself, as well as metadata on all scenery packs submitted for it. Unlikescenery_pack(), though, this doesnotinclude actualapt.dator DSF data.scenery_pack(): Downloads either the recommended pack for the specified airport, or the scenery pack with the specifiedintID. Includes both theapt.datdata and DSF, where applicable.recommended_scenery_packs(): A generator equivalent to callingscenery_pack()to download the recommended scenery pack for every airport (or only a preselected list of airports, at your discretion).Installation instructionsxplane_airportsrequires Python 3.6 or newer.Install via pip with:$ pip install xplane_airportsIf you're migrating from a pre-4.0 version of the library, see theMigration notessection below.Sample codeParsing the default apt.dat file in your local X-Plane installationfromxplane_airports.AptDatimportAptDat,AirportfrompathlibimportPathxplane_installation=Path(input("Path to your X-Plane installation: "))assertxplane_installation.is_dir(),f"{xplane_installation}does not exist or is not a directory"print("Reading 35,000+ airports from disk (this will take awhile)")default_xplane_apt_dat=AptDat(xplane_installation/'Resources/default scenery/default apt dat/Earth nav data/apt.dat')print(f"{len(default_xplane_apt_dat)}airports found in your default apt.dat\n")ksea=default_xplane_apt_dat['KSEA']""":type ksea: Airport"""print("KSEA's airport data on disk begins:")print(ksea.head())Getting metadata on airports from the Gatewayfromxplane_airports.gatewayimportairportsall_apts=airports()print("There are%dairports on the X-Plane Scenery Gateway"%len(all_apts))print("KSEA has the following metadata on the Gateway:")forkey,valueinall_apts['KSEA'].items():print('\t'+key+':',value)Downloaded the recommended scenery pack for an airport from the Gatewayfromxplane_airports.gatewayimportscenery_pack,GatewayAptksea_recommended_pack=scenery_pack('KSEA')""":type ksea_recommended_pack: GatewayApt"""print("KSEA downloaded from the Gateway begins:")print(ksea_recommended_pack.apt.head())More sample code is available in the doctests inthegatewaymoduledocs below.TheAptDatmoduleTools for reading, inspecting, and manipulating X-Plane’s airport (apt.dat) files.AptDat.AptDatclassAptDat.AptDat(path_to_file=None)A container class forAirportobjects. Parses X-Plane’s giganticapt.datfiles, which may have data on tens of thousands of airports.Fieldsairports(List[Airport])xplane_version(int): The version of the apt.dat spec used by the airports in this collectionStatic methodfrom_file_text(apt_dat_file_text,from_file) ->AptDatParameters:apt_dat_file_text(str): The contents of an apt.dat (or ICAO.dat) filefrom_file(os.PathLike): Path to the file from which this was readPropertyidsA generator containing the X-Plane IDs of all airports in the collection. Note that these IDs may or may not correspond to the airports’ ICAO identifiers.Type: collection.Iterable[str]PropertynamesA generator containing the names of all airports in the collectionType: collection.Iterable[str]Methodsearch_by_id(apt_id)Parameter:apt_id(str) – The X-Plane ID of the airport you want to queryReturns: The airport with the specified ID, orNoneif no matching airport exists in this collection.Return type: Optional[Airport]Methodsearch_by_name(apt_name)Parameter:apt_name(str) – The name of the airport you want to queryReturns: All airports that match the specified name, case-insensitive (an empty list if no airports match)
Return type: list[Airport]Methodsearch_by_predicate(predicate_fn)Parameter:predicate_fn((Airport)-> bool) – We will collect all airports for which this function returnsTrueReturn type: list[Airport]Methodsort(key='name')By default, we store the airport data in whatever order we read it from the apt.dat file. When you call sort, though, we’ll ensure that it’s in order (default to name order, just like it’s always been in the shipping versions of X-Plane).Parameter:key(str) – TheAirportkey to sort onMethodwrite_to_disk(path_to_write_to)Writes a complete apt.dat file containing this entire collection of airports.Parameter:path_to_write_to(Optional[os.PathLike]) – A complete file path (ending in .dat); ifNone, we'll use the path we read this apt.dat in from.AptDat.AirportA single airport from an apt.dat file.classxplane_airports.AptDat.Airport(name: str,id: str,from_file: str = '',has_atc: bool = False,elevation_ft_amsl: float = 0,text: List[AptDat.AptDatLine]=[])\Dataclass members:from_file(pathlib.Path; default empty): Path to theapt.datfile from which this airport was readraw_lines(List[str]; default empty): The complete text of the portion of the apt.dat file pertaining to this airportxplane_version(int; default 1100): The version of the X-Plane apt.dat spec this airport uses (e.g., 1050, 1100, 1130)tokenized_lines(List[Tuple]; default empty): An intermediate tokenization of the raw lines, used for speed of parsingStatic methodfrom_lines(apt_dat_lines,from_file_name) ->AirportParameters:\from_file_name(str) – The name of the apt.dat file you read this airport in fromapt_dat_lines(collections.Iterable[AptDatLine|str]) – The lines of the apt.dat file (either strings or parsed AptDatLine objects)Static methodfrom_str(file_text,from_file_name) ->AirportParameters:file_text(str) – The portion of the apt.dat file text that specifies this airportfrom_file_name(str) – The name of the apt.dat file you read this airport in fromMethodhead(num_lines=10)-> strReturns the firstnum_linesof theapt.dattext for this airportPropertytext(List[AptDatLine])The rawstrlines transformed into more usableAptDatLineobjects. Note that this will be measurably slow if you need to work with millions of lines.Propertyname(str)The name of the airport, like "Seattle-Tacoma Intl"Propertyid(str)The X-Plane identifier for the airport, which may or may not correspond to its ICAO IDPropertyhas_atc(bool)True if the airport header indicates the airport has air traffic controlPropertyelevation_ft_amsl(float)The elevation, in feet above mean sea level, indicated in the airport header linePropertymetadata(Dict[MetadataKey, str])Various key-value metadata pairs defined by X-Plane.Propertyhas_comm_freq(bool)True if this airport defines communication radio frequencies for interacting with ATC\Propertyhas_ground_routes(bool)\True if this airport defines any destinations for ground vehicles (like baggage cars, fuel trucks, etc.), ground truck parking locations, or taxi routes\Propertyhas_taxi_route(bool)True if this airport defines routing rules for ATC’s use of its taxiways.\Propertyhas_taxiway(bool)True if this airport defines any taxiway geometry\Propertyhas_taxiway_sign(bool)True if this airport defines any taxi signs\Propertyhas_traffic_flow(bool)True if this airport defines rules for when and under what conditions certain runways should be used by ATC\Propertylatitude(float)The latitude of the airport, which X-Plane calculates as the latitude of the center of the first runway.\Propertylongitude(float)The longitude of the airport, which X-Plane calculates as the longitude of the center of the first runway.\Methodhas_row_code(row_code_or_codes) -> boolTrue if the airport has any lines in its text that begin with the specified row code(s)Parameter:row_code_or_codes(Union_[int,_str_,_collections.Iterable_[int]__]_) – One or more “row codes” (the first token at the beginning of a line; almost always int)Methodwrite_to_disk(path_to_write_to)Writes a complete apt.dat file containing just this airport.Parameter:path_to_write_to(os.PathLike) – A complete file path (ending in .dat)AptDat.AptDatLineA single line from anapt.datfile.classxplane_airports.AptDat.AptDatLine(line_text)Methodis_airport_header() -> boolTrue if this line marks the beginning of an airport, seaport, or heliportMethodis_file_header() -> bool
True if this is part of an apt.dat file headerMethodis_ignorable() -> boolTrue if this line carries no semantic value for any airport in the apt.dat file.Methodis_runway() -> boolTrue if this line represents a land runway, waterway, or helipadMethodrunway_type->RunwayTypeThe type of runway represented by this linePropertytokens-> list[str]The tokens in this lineAptDat.RunwayTypeclassxplane_airports.AptDat.RunwayTypeEnum for row codes used to identify different types of runways:LAND_RUNWAYWATER_RUNWAYHELIPADThegatewaymoduleTools for interfacing with the X-Plane Scenery Gateway’s API.Docs at:https://gateway.x-plane.com/apigateway.GatewayAptclassxplane_airports.gateway.GatewayApt(apt:AptDat.Airport, txt: Optional[str], readme: str, copying: str, pack_metadata: dict, apt_metadata: Optional[dict])All the data we get back about an airport when we download a scenery pack viascenery_pack()Dataclass members:apt(Airport): Python object with the contents of theapt.datfiletxt(str orNone): Contents of the DSF .txt file; airports with no 3D will not include thisreadme(str): Contents of the README for this scenery packcopying(str): Contents of the COPYING instructions for this scenery packpack_metadata(dict): The JSON object received from the Gateway with metadata about this particular scenery packapt_metadata(dict orNone): The JSON object received from the Gateway with metadata about the airport this scenery pack represents; None if this hasn't been downloaded (yet)gateway.GatewayFeatureclassxplane_airports.gateway.GatewayFeatureEnum class representing the features that may be used to tag scenery packs on the Gateway. Note that these are subject to frequent addition/removal/change; only a few are guaranteed to be stable.HasATCFlow (guaranteed stable)HasTaxiRoute (guaranteed stable)HasNavaidConflictAlwaysFlattenHasLogTxtIssueLRInternalUse (guaranteed stable)ExcludeSubmissions (guaranteed stable)HasGroundRoutes (guaranteed stable)TerrainIncompatibleRunwayNumberingOrLengthFixAlwaysFlattenIneffectiveMajorAirportTerrainIncompatibleAtPerimeterRunwayNumberingFixIconicAirportFloatingRunwayGroundRoutesCertifiedFacadeInjectionScenicAirportMisusedGroundPolygonsTop30Top50RunwayInWaterRunwayUnusableTerrainMeshMissingLowResolutionTerrainPolygonsAPI wrapping functionsxplane_airports.gateway.airport(airport_id) -> dictQueries the Scenery Gateway for metadata on a single airport, plus metadata on all the scenery packs uploaded for that airport.API endpoint documented at:https://gateway.x-plane.com/api#get-a-single-airport\Returns: A dict with metadata about the airportParameter:airport_id(str) – The identifier of the airport on the Gateway (may or may not be an ICAO ID)>>>expected_keys={'icao','airportName','airportClass','latitude','longitude','elevation','acceptedSceneryCount','approvedSceneryCount','recommendedSceneryId','scenery'}>>>ksea=airport('KSEA')>>>all(keyinkseaforkeyinexpected_keys)TrueIncludes metadata of all scenery packs uploaded for this airport:>>>len(airport('KSEA')['scenery'])>=9True>>>all_scenery_metadata=airport('KSEA')['scenery']>>>first_scenery_pack_metadata=all_scenery_metadata[0]>>>expected_keys={'sceneryId','parentId','userId','userName','dateUploaded','dateAccepted','dateApproved','dateDeclined','type','features','artistComments','moderatorComments','Status'}>>>all(keyinfirst_scenery_pack_metadataforkeyinexpected_keys)Truexplane_airports.gateway.airports() -> dictQueries the Scenery Gateway for all the airports it knows about. Note that the download size is greater than 1 MB.API endpoint documented at:https://gateway.x-plane.com/api#get-all-airports\Returns a dict with metadata on all 35,000+ airports; keys are X-Plane identifiers (which may or may not correspond to ICAO identifiers), and values are various airport metadata.>>>airports()['KSEA']{'AirportCode':'KSEA','AirportName':'Seattle Tacoma Intl','AirportClass':None,'Latitude':47,'Longitude':-122,'Elevation':None,'Deprecated':None,'DeprecatedInFavorOf':None,'AcceptedSceneryCount':2,'ApprovedSceneryCount':2,'ExcludeSubmissions':0,'RecommendedSceneryId':45283,'Status':'Scenery Submitted','SceneryType':0,'SubmissionCount':2}>>>len(airports())>35000Truexplane_airports.gateway.recommended_scenery_packs(selective_apt_ids=None) -> collections.Iterable[GatewayApt]A generator to iterate over the recommended scenery packs for all (or just the selected) airports on the Gateway. Downloads and unzips all files into memory.Parameter:selective_apt_ids(Optional[collections.Iterable[str]]) – IfNone, we will download scenery for all 35,000+ airports; if a list of airport IDs (as returned byairports()), the airports whose recommended packs we should download.Returns a generator of the recommended scenery packs; each pack contains the same data as a call toscenery_pack()directlyEasily request a subset of airports:>>>packs=recommended_scenery_packs(['KSEA','KLAX','KBOS'])>>>len(list(packs))==3andall(isinstance(pack,GatewayApt)forpackinpacks)TrueAudit airports for specific features:>>>all_3d=True>>>all_have_atc_flow=True>>>all_have_taxi_route=True>>>forpackinrecommended_scenery_packs(['KATL','KORD','KDFW','KLAX']):...all_3d&=pack.pack_metadata['type']=='3D'andpack.txtisnotNone...all_have_atc_flow&=GatewayFeature.HasATCFlowinpack.pack_metadata['features']andpack.apt.has_traffic_flow()...all_have_taxi_route&=GatewayFeature.HasTaxiRouteinpack.pack_metadata['features']andpack.apt.has_taxi_route()>>>all_3dandall_have_atc_flowandall_have_taxi_routeTruexplane_airports.gateway.scenery_pack(pack_to_download) ->GatewayAptDownloads a single scenery pack, including its apt.dat and any associated DSF from the Gateway, and unzips it into memory.Parameter:pack_to_download(strorint) – Ifint, the scenery ID of the pack to be downloaded; ifstr, the airport whose recommended pack we should download.Returns the downloaded files and the metadata about the scenery pack>>>expected_keys={'sceneryId','parentId','icao','aptName','userId','userName','dateUploaded','dateAccepted','dateApproved','dateDeclined','type','features','artistComments','moderatorComments','additionalMetadata','masterZipBlob'}>>>ksea_pack_metadata=scenery_pack('KSEA').pack_metadata>>>all(keyinksea_pack_metadataforkeyinexpected_keys)True>>>scenery_pack('KORD').pack_metadata['type']in('3D','2D')True>>>all(isinstance(feature,GatewayFeature)forfeatureinscenery_pack('KMCI').pack_metadata['features'])TrueMigration notesVersion 4.0 of the library introduces a handful of important breaking changes:I've rewritten theAirportsclass with a few major changes for performance:We no longer useAptDatLineobjects by default. They're still available as a@propertywith the same name as their previous dataclass member (text), but we now use a tuple of tokenized lines as our primary data structure internally. This eliminated the single biggest bottleneck in parsing X-Plane's 35k+ airport default apt.dat, and cut the total parsing time down from > 140 seconds on my machine to under 32 seconds.A number of other former dataclass members have been moved to@propertymethods.Neither change should affect basically any sane usage of theAirportclassexceptfor construction (but even that you should probably be getting from theAptDatclass or one of theAirportclass's static methods!).Version 4 also utilizes@functools.cached_propertyto cache some potentially-expensive@propertymethods in theAirportclass. Sincefunctoolsintroduced this in Python 3.8, if you're on an earlier Python version, you won't get this caching behavior (you'll get normal properties instead). This is less than ideal, but it works.