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:
TheFGFSEagle
2022-09-15 20:33:25 +02:00
parent eae92402bc
commit 875799b048
20 changed files with 1295 additions and 110 deletions
+149
View File
@@ -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)