2022-07-30 23:39:21 +08:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#-*- coding:utf-8 -*-
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import argparse
|
|
|
|
import json
|
|
|
|
import io
|
|
|
|
import zipfile
|
|
|
|
import base64
|
|
|
|
import requests
|
|
|
|
import appdirs
|
|
|
|
|
|
|
|
def get_airports_list():
|
|
|
|
os.makedirs(appdirs.user_cache_dir("fgtools"), exist_ok=True)
|
|
|
|
airports_json_path = os.path.join(appdirs.user_cache_dir("fgtools"), "airports.json")
|
|
|
|
if not os.path.isfile(airports_json_path):
|
|
|
|
airports_json = requests.get("https://gateway.x-plane.com/apiv1/airports").json()
|
|
|
|
with open(airports_json_path, "w") as f:
|
|
|
|
json.dump(airports_json, f)
|
|
|
|
else:
|
|
|
|
with open(airports_json_path, "r") as f:
|
|
|
|
airports_json = json.load(f)
|
|
|
|
|
|
|
|
return airports_json["total"], airports_json["airports"]
|
|
|
|
|
|
|
|
def filter_airports_list(airports_count, airports_list, icaos, bbox):
|
|
|
|
airports_list_filtered = []
|
|
|
|
i = 0
|
|
|
|
for airport in airports_list:
|
|
|
|
print(f"Filtering airports … {i / airports_count * 100:.1f}% ({i} of {airports_count})\r", end="")
|
2022-07-30 23:54:17 +08:00
|
|
|
i += 1
|
2022-07-30 23:39:21 +08:00
|
|
|
airport_metadata = airport.get("metadata", {}) or {}
|
|
|
|
if icaos and any(icao in (airport.get("AirportCode", None), airport_metadata.get("icao_code", None)) for icao in icaos):
|
|
|
|
airports_list_filtered.append(airport)
|
|
|
|
elif bbox:
|
2022-07-31 02:33:29 +08:00
|
|
|
if bbox[1] < airport["Latitude"] < bbox[3] and bbox[0] < airport["Longitude"] < bbox[2]:
|
2022-07-30 23:39:21 +08:00
|
|
|
airports_list_filtered.append(airport)
|
|
|
|
print("Filtering airports … done ")
|
|
|
|
|
|
|
|
return airports_list_filtered
|
|
|
|
|
2022-07-30 23:54:17 +08:00
|
|
|
def write_aptdat_files(airports_list, output, txt_output, overwrite):
|
2022-07-30 23:39:21 +08:00
|
|
|
airports_count = len(airports_list)
|
|
|
|
i = 0
|
|
|
|
for airport in airports_list:
|
|
|
|
print(f"Downloading and writing airports … {i / airports_count * 100:.1f}% ({i} of {airports_count})\r", end="")
|
2022-07-30 23:54:17 +08:00
|
|
|
i += 1
|
2022-07-30 23:39:21 +08:00
|
|
|
airport_metadata = airport.get("metadata", {}) or {}
|
|
|
|
icao_code = airport_metadata.get("icao_code", None) or airport["AirportCode"]
|
2022-07-30 23:54:17 +08:00
|
|
|
if os.path.isfile(os.path.join(output, icao_code + ".dat")) and not overwrite:
|
|
|
|
continue
|
2022-07-30 23:39:21 +08:00
|
|
|
|
|
|
|
scenery_id = airport.get("RecommendedSceneryId", None)
|
|
|
|
if not scenery_id:
|
|
|
|
print("Airport", icao_code, "has no scenery - skipping ")
|
|
|
|
continue
|
|
|
|
|
|
|
|
scenery_json = requests.get("https://gateway.x-plane.com/apiv1/scenery/" + str(scenery_id)).json()
|
|
|
|
scenery_blob = io.BytesIO(base64.b64decode(scenery_json["scenery"]["masterZipBlob"]))
|
|
|
|
with zipfile.ZipFile(scenery_blob, "r") as scenery_zip:
|
|
|
|
try:
|
|
|
|
scenery_dat = scenery_zip.open(icao_code + ".dat")
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
with open(os.path.join(output, icao_code + ".dat"), "wb") as aptdat:
|
|
|
|
aptdat.write(scenery_dat.read())
|
|
|
|
|
|
|
|
if txt_output and os.path.isdir(txt_output):
|
|
|
|
try:
|
|
|
|
scenery_txt = scenery_zip.open(icao_code + ".txt")
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
with open(os.path.join(txt_output, icao_code + ".txt"), "wb") as apttxt:
|
|
|
|
apttxt.write(scenery_txt.read())
|
|
|
|
|
|
|
|
print("Downloading and writing airports … done ")
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
argp = argparse.ArgumentParser(description="Pulls apt.dat files from the XPlane Gateway selected either by a bounding box or ICAO codes")
|
|
|
|
|
|
|
|
argp.add_argument(
|
|
|
|
"-i", "--icao",
|
|
|
|
help="ICAO code(s) of the apt.dat files to be pulled",
|
|
|
|
nargs="+"
|
|
|
|
)
|
|
|
|
|
|
|
|
argp.add_argument(
|
|
|
|
"-b", "--bbox",
|
|
|
|
help="All airports in the given bounding box will be pulled",
|
|
|
|
nargs=4,
|
|
|
|
type=float
|
|
|
|
)
|
|
|
|
|
|
|
|
argp.add_argument(
|
|
|
|
"-o", "--output",
|
|
|
|
help="Directory to put apt.dat files in",
|
|
|
|
required=True
|
|
|
|
)
|
|
|
|
|
|
|
|
argp.add_argument(
|
|
|
|
"-q", "--query",
|
|
|
|
help="Don't write apt.dat files, only output a list of found airports",
|
|
|
|
action="store_true"
|
|
|
|
)
|
|
|
|
|
|
|
|
argp.add_argument(
|
|
|
|
"-t", "--txt-output",
|
|
|
|
help="Where to write .txt files to (.txt files won't be written if this option is not specified or the directory doesn't exist)",
|
|
|
|
default=""
|
|
|
|
)
|
|
|
|
|
2022-07-30 23:54:17 +08:00
|
|
|
argp.add_argument(
|
|
|
|
"--overwrite",
|
|
|
|
help="Whether to redownload and overwrite apt.dat files that already exist in OUTPUT",
|
|
|
|
action="store_true"
|
|
|
|
)
|
|
|
|
|
2022-07-30 23:39:21 +08:00
|
|
|
args = argp.parse_args()
|
|
|
|
|
|
|
|
if not args.icao and not args.bbox:
|
|
|
|
argp.error("At least one of -i/--icao and -b/--bbox must be given !")
|
|
|
|
|
|
|
|
if not os.path.isdir(args.output):
|
|
|
|
argp.error(f"Output folder {args.output} does not exist - exiting")
|
|
|
|
|
|
|
|
|
|
|
|
airports_count, airports_list = get_airports_list()
|
|
|
|
airports_list_filtered = filter_airports_list(airports_count, airports_list, args.icao, args.bbox)
|
|
|
|
|
|
|
|
if args.query:
|
|
|
|
print("Airport code ICAO code")
|
|
|
|
for airport in airports_list_filtered:
|
|
|
|
print(airport["AirportCode"] + "\t", (airport.get("metadata", {}) or {}).get("icao_code", "\t"), sep="\t")
|
|
|
|
|
2022-07-30 23:54:17 +08:00
|
|
|
write_aptdat_files(airports_list_filtered, args.output, args.txt_output, args.overwrite)
|
2022-07-30 23:39:21 +08:00
|
|
|
|