Move fgtools module into it's own subfolder, add scripts to fix ICAO's of apt.dat files (mainly for those pulled from the XPlane gateway) and to generate apt.dat files from OurAirports and OSM data
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python
|
||||
#-*- coding:utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
import csv
|
||||
import requests
|
||||
import argparse
|
||||
import shutil
|
||||
|
||||
from fgtools.utils.files import find_input_files
|
||||
from fgtools.geo.coord import Coord
|
||||
from fgtools.utils import constants
|
||||
|
||||
def _get_ourairports_csv(what):
|
||||
path = os.path.join(constants.CACHEDIR, what + ".csv")
|
||||
if not os.path.isfile(path):
|
||||
with open(path, "w") as f:
|
||||
f.write(requests.get(f"https://davidmegginson.github.io/ourairports-data/{what}.csv").content.decode())
|
||||
f = open(path, "r", newline="")
|
||||
return list(csv.DictReader(f))[1:]
|
||||
|
||||
def get_ourairports_icao(airport, csv):
|
||||
c = Coord(airport["lon"], airport["lat"])
|
||||
matches = []
|
||||
for line in csv:
|
||||
d = c.distance_km(Coord(float(line["longitude_deg"]), float(line["latitude_deg"])))
|
||||
if d <= 10:
|
||||
matches.append({"distance": d, "icao": line["gps_code"] or line["local_code"] or line["ident"]})
|
||||
matches.sort(key=lambda m: m["distance"])
|
||||
if len(matches) and matches[0]["icao"]:
|
||||
if len(airport["icao"]) != 4:
|
||||
airport["newicao"] = matches[0]["icao"]
|
||||
else:
|
||||
print(f"No matching airport found for {airport['icao']} - skipping", end=" " * 100 + "\n")
|
||||
return airport
|
||||
|
||||
def process(files, output):
|
||||
csv = _get_ourairports_csv("airports")
|
||||
i = 0
|
||||
n = 0
|
||||
skipped = 0
|
||||
total = len(files)
|
||||
files_d = {}
|
||||
for p in files:
|
||||
print(f"Parsing files … {i / total * 100:.1f}% ({i} of {total} done, found {n} airports)", end="\r")
|
||||
i += 1
|
||||
file_d = {"lines": [], "airports": {}}
|
||||
with open(p, "r") as f:
|
||||
file_d["lines"] = list(map(lambda l: list(filter(None, l)), map(lambda s: s.split(" "), filter(None, map(str.strip, f.readlines())))))
|
||||
curicao = ""
|
||||
for line in file_d["lines"]:
|
||||
if line[0] in ("1", "16", "17"):
|
||||
curicao = line[4]
|
||||
skip = False
|
||||
file_d["airports"][curicao] = {"icao": curicao}
|
||||
n += 1
|
||||
elif line[0] == "1302":
|
||||
if line[1] == "datum_lon":
|
||||
if len(line) < 3:
|
||||
continue
|
||||
file_d["airports"][curicao]["lon"] = float(line[2])
|
||||
if line[1] == "datum_lat":
|
||||
if len(line) < 3:
|
||||
continue
|
||||
file_d["airports"][curicao]["lat"] = float(line[2])
|
||||
elif line[0] in ("100", "101", "102") and not ("lon" in file_d["airports"][curicao] and "lat" in file_d["airports"][curicao]):
|
||||
# no datum_lon / datum_lat found, approximate airport position from first runway / helipad found
|
||||
if line[0] == "100": # land runway
|
||||
lon = (float(line[10]) + float(line[19])) / 2
|
||||
lat = (float(line[9]) + float(line[18])) / 2
|
||||
elif line[0] == "101": # water runway
|
||||
lon = (float(line[5]) + float(line[8])) / 2
|
||||
lat = (float(line[4]) + float(line[7])) / 2
|
||||
else: # helipad
|
||||
lon = float(line[3])
|
||||
lat = float(line[2])
|
||||
file_d["airports"][curicao]["lon"] = lon
|
||||
file_d["airports"][curicao]["lat"] = lat
|
||||
|
||||
for icao in list(file_d["airports"].keys()):
|
||||
if not ("lon" in file_d["airports"][icao] and "lat" in file_d["airports"][icao]):
|
||||
print(f"Unable to get longitude / latitude of airport {curicao} in file {p} - skipping", end=" " * 100 + "\n")
|
||||
del file_d["airports"][icao]
|
||||
n -= 1
|
||||
skipped += 1
|
||||
|
||||
files_d[p] = file_d
|
||||
print(f"Parsing files … {i / total * 100:.1f}% ({i} of {total} done, found {n} airports, skipped {skipped})", end=" " * 100 + "\n")
|
||||
|
||||
i = 0
|
||||
total = n
|
||||
for p in files_d:
|
||||
for icao in files_d[p]["airports"]:
|
||||
print(f"Getting ICAOs for airports … {i / total * 100:.1f}% ({i} of {total} done)", end="\r")
|
||||
i += 1
|
||||
files_d[p]["airports"][icao] = get_ourairports_icao(files_d[p]["airports"][icao], csv)
|
||||
print(f"Getting ICAOs for airports … {i / total * 100:.1f}% ({i} of {total} done)", end=" " * 100 + "\n")
|
||||
|
||||
i = 0
|
||||
total = len(files_d)
|
||||
for p in files_d:
|
||||
print(f"Writing new apt.dat files … {i / total * 100:.1f}% ({i} of {total} done)", end="\r")
|
||||
i += 1
|
||||
if output == None:
|
||||
outp = p
|
||||
else:
|
||||
outp = os.path.join(output, os.path.split(p)[-1])
|
||||
|
||||
parts = os.path.split(outp)
|
||||
prefix, newname = os.path.join(*parts[:-1]), parts[-1]
|
||||
if len(files_d[p]["airports"]) > 0 and newname != "apt.dat":
|
||||
firsticao = list(files_d[p]["airports"].keys())[0]
|
||||
if "newicao" in files_d[p]["airports"][firsticao]:
|
||||
newname = files_d[p]["airports"][firsticao]["newicao"] + ".dat"
|
||||
newoutp = os.path.join(prefix, newname)
|
||||
|
||||
with open(outp, "w") as f:
|
||||
for line in files_d[p]["lines"]:
|
||||
if line[0] in ("1", "16", "17") and line[4] in files_d[p]["airports"] and "newicao" in files_d[p]["airports"][line[4]]:
|
||||
line[4] = files_d[p]["airports"][line[4]]["newicao"]
|
||||
f.write(" ".join(line) + "\n")
|
||||
|
||||
if outp != newoutp:
|
||||
print(f"Renaming file: {outp} -> {newoutp}", end=" " * 100 + "\n")
|
||||
shutil.move(outp, newoutp)
|
||||
print(f"Writing new apt.dat files … {i / total * 100:.1f}% ({i} of {total} done)", end=" " * 100 + "\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
argp = argparse.ArgumentParser(description="Fix apt.dat ICAO's - some apt.dat files from the XPlane gateway have them of the form XAY0016 - this script gets the right ICAO from OurAirports data (if the airport is found there)")
|
||||
|
||||
argp.add_argument(
|
||||
"-i", "--input",
|
||||
help="Input apt.dat file(s) / folder(s) containing apt.dat files",
|
||||
required=True,
|
||||
nargs="+"
|
||||
)
|
||||
|
||||
argp.add_argument(
|
||||
"-o", "--output",
|
||||
help="Output folder for the modified apt.dat files - omit to edit the files in-place",
|
||||
default=None
|
||||
)
|
||||
|
||||
args = argp.parse_args()
|
||||
|
||||
infiles = find_input_files(args.input)
|
||||
process(infiles, args.output)
|
||||
|
||||
Reference in New Issue
Block a user