Skip to content

Instantly share code, notes, and snippets.

@pcchin
Last active July 30, 2019 01:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pcchin/5b9a99e7d6afdebe0bdd064437f59c9b to your computer and use it in GitHub Desktop.
Save pcchin/5b9a99e7d6afdebe0bdd064437f59c9b to your computer and use it in GitHub Desktop.
A simple Python program designed to calculate the distance between your hub airport and the destination airport and outputs them into the existing demand Excel file from am3.info. Openpyxl is required and you can use -h to see all the possible modifiers.
# -*- coding: utf-8 -*-
import argparse
import traceback
import math
from openpyxl import load_workbook
A_UPPERCASE = ord('A')
ALPHABET_SIZE = 26
def main():
parser = argparse.ArgumentParser(description="Adds a column to an AM3 database that calculates the distance between"
" your base airport and the target airport. It is designed to work "
"with the default Demand.xlsx file from am3.info and uses openpyxl.")
parser.add_argument("airports", help="Custom location for the a CSV file containing the info of airports.")
parser.add_argument("excel", help="The location of the demand excel file.")
parser.add_argument("base", help="The base airport for your operations.")
parser.add_argument("-s", "--sheet", help="The sheet name to use to read and write the info. "
"Defaults to first sheet.", default="")
parser.add_argument("-sp", "--separator", help="The separator used to separate the values in the txt file. "
"Defaults to comma.", default=",")
parser.add_argument("-c", "--col", help="The column number to insert into the file. Starts from 1, defaults to 11.",
default=11, type=int)
parser.add_argument("-ix", "--icao_xlsx", help="The column in the xlsx file that shows the ICAO of the airports. "
"Starts with 1, defaults to 4.", default=4, type=int)
parser.add_argument("-it", "--icao_txt", help="The column in the txt file that shows the ICAO of the airports. "
"Starts with 0, defaults to 5.", default=5, type=int)
parser.add_argument("-p", "--pos", help="The element number for the list for the longitude of the points, which "
"should be followed by its latitude. Starts with 0, defaults to 6.", default=6, type=int)
args = parser.parse_args()
try:
wb = load_workbook(args.excel)
wb.template = False
try:
txt = open(args.airports, "r", encoding="utf8")
base_coords = [None, None]
# Get coordinates of target airport
for line in txt:
airport_data = line.split(args.separator)
if len(airport_data) > args.pos + 1:
# The start and end of each ICAO has to be stripped of " as it was downloaded as this way from the txt file.
icao = airport_data[args.icao_txt].lstrip("\"").rstrip("\"")
if icao == args.base:
try:
base_coords.insert(0, float(airport_data[args.pos]))
base_coords.insert(1, float(airport_data[args.pos + 1]))
except ValueError:
base_coords.insert(0, float(airport_data[args.pos + 1]))
base_coords.insert(1, float(airport_data[args.pos + 2]))
break
# Starts dict
if base_coords[0] is None or base_coords[1] is None:
print("Error: The coordinates for the target airport could not be found.")
else:
print("Coordinates of " + args.base + " is " + str(base_coords[0]) + ", " + str(base_coords[1]))
distance_dict = {}
earth_radius = 6378.1
current_coords = [None, None]
coords_diff = [None, None]
txt.seek(0, 0)
for line in txt:
airport_data = line.split(",")
if len(airport_data) > args.pos + 1:
icao = airport_data[args.icao_txt].lstrip("\"").rstrip("\"")
print("Current airport in txt: " + icao, end="\r")
# Add each item to the dict
try:
current_coords.insert(0, float(airport_data[args.pos]))
current_coords.insert(1, float(airport_data[args.pos + 1]))
except ValueError:
current_coords.insert(0, float(airport_data[args.pos + 1]))
current_coords.insert(1, float(airport_data[args.pos + 2]))
# Calculates distance based on Haversine
coords_diff.insert(0, math.radians(current_coords[0] - base_coords[0]))
coords_diff.insert(1, math.radians(current_coords[1] - base_coords[1]))
a = (math.sin(coords_diff[0] / 2) ** 2) + ((math.sin(coords_diff[1] / 2) ** 2) *
math.cos(math.radians(current_coords[0])) *
math.cos(math.radians(base_coords[0])))
dist = 2 * earth_radius * math.atan2(math.sqrt(a), math.sqrt(1 - a))
distance_dict[icao] = dist
# Updates distance to xlsx file
if args.sheet == "":
target_ws = wb[wb.sheetnames[0]]
else:
target_ws = wb[args.sheet]
target_ws[base_10_to_alphabet(args.col) + "1"] = "Distance to " + args.base
row = 2
current_icao_ref = base_10_to_alphabet(args.icao_xlsx) + str(row)
while target_ws[current_icao_ref].value is not None and target_ws[current_icao_ref].value != "":
# Stores values to xlsx file
print("Current airport in xlsx: ", target_ws[current_icao_ref].value, end="\r")
if target_ws[current_icao_ref].value in distance_dict:
target_ws[base_10_to_alphabet(args.col) + str(row)] = \
distance_dict[target_ws[current_icao_ref].value]
row += 1
current_icao_ref = base_10_to_alphabet(args.icao_xlsx) + str(row)
print("\nSaving xlsx file...")
wb.save(args.excel)
txt.close()
except FileNotFoundError:
print("")
traceback.print_exc()
print("Error: The txt file is not found.")
except FileNotFoundError:
print("")
traceback.print_exc()
print("Error: The excel file is not found.")
except TypeError:
print("")
traceback.print_exc()
print("The txt file provided is in an invalid format.")
raise SystemExit
def _decompose(number):
"""Generate digits from `number` in base alphabet, most significants
bits first.
"""
number -= 1 # Account for A in base alphabet being 1 in decimal rather than 0
if number < ALPHABET_SIZE:
yield number
else:
number, remainder = divmod(number, ALPHABET_SIZE)
yield from _decompose(number)
yield remainder
# Base 10 to base 26 converter from StackOverflow
def base_10_to_alphabet(number):
"""Convert a decimal number to its base alphabet representation"""
return ''.join(
chr(A_UPPERCASE + part)
for part in _decompose(number)
)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment