import re
from Location import Location
cruise_speed = 208.333333 # 750km/h in m/s
def add_city_helper(graph, code, name, country, continent, timezone, latitude, longitude, population, region):
"""
Helper function for add_city and edit_city
:return: True if city added, false otherwise
"""
failed = False # flag to indicate a failure and exit after error messages are printed.
lat_letter = re.sub(r'\d+', "", latitude) # get the cardinal letter
lat_num = re.findall(r'\d+', latitude) # get the coordinate number
lon_letter = re.sub(r'\d+', "", longitude)
lon_num = re.findall(r'\d+', longitude)
return_string = ""
try:
population = int(population)
if population < 0: # population sanity check
return_string += "Error: Unable to add city, Population can not be negative." + '\n'
failed = True
except ValueError:
return_string += "Error: Unable to add city, population must be a number." + '\n'
failed = True
#check to make sure latitude is correctly N or S, and has a number
if (lat_letter != "N" and lat_letter != "S") or not lat_num:
return_string += "Error: Invalid latitude letter (" + lat_letter + ") or number (" + str(lat_num) + ")" + '\n'
failed = True
#check to make sure longitude is correctly W or E, and has a number
if (lon_letter != "W" and lon_letter != "E") or not lon_num:
return_string += "Error: Invalid longitude letter (" + lon_letter + ") or number (" + str(lon_num) + ")" + '\n'
failed = True
if failed:
return return_string
#add the new city
new_city = Location(code, name, country, continent, timezone, latitude, longitude, population, region)
if graph.add_node(new_city, code):
return_string += "New city added successfully: " + code + '\n'
#new_city.return_string += )
return return_string
else:
return_string += "Error: Unable to add city, a city with the same code already exists." + '\n'
return return_string
def add_route_helper(graph, source, dest, distance, direction):
"""
Helper function for add_route
:return: True if added, false otherwise.
"""
if graph.find_node(source) is None or graph.find_node(dest) is None:
print("Unable to add route. One or both cities not found.")
return False
graph.add_edge(source, dest, distance) # add route
print("Route added ", end="")
if direction == "y":
graph.add_edge(dest, source, distance) # add route in second direction if user specified
print("in both directions.")
print("")
return True
def remove_route_helper(graph, source, dest, direction):
"""
Helper function for remove_route
:return: True if route was removed, false otherwise.
"""
edges = graph.get_edges() # list of edges
edges_to_remove = [] # list of edges to remove
for edge in edges: # check if edge matches source/dest. Also checks for edge in second direction.
if (edge.get_destination() == dest and edge.get_source() == source) \
or (edge.get_destination() == source and edge.get_source() == dest):
edges_to_remove.append(edge) # add to edges to remove
for edge in edges_to_remove: # remove edges from graph.
graph.remove_edge(edge.get_source(), edge.get_destination())
if len(edges_to_remove) > 0:
if direction == "n":
graph.add_edge(dest, source, edges_to_remove[0])
return True
print("Unable to remove route. Route does not exist.")
return False
def time_for_acceleration(distance):
"""
Calculates time to accelerate to 750km/h over given distance.
Using: time = 2*distance(m) / final velocity (m/s)
:param distance: distance (in meters)
:return: time (in seconds)
"""
return (2*distance)/cruise_speed
def seconds_to_formatted_time(seconds):
"""
Convert seconds to a formatted time string.
:param seconds: int of seconds
:return: formatted string.
"""
hours = seconds/60/60
minutes = (seconds/60) % 60
hour_str = "%d" % hours
hour_str += ":%d" % minutes
if minutes < 10:
hour_str += "0"
return hour_str
def update_routes(graph, new_code, old_code):
"""
Helper function for edit_city
Updates routes for a renamed city to the new city code
:param new_code: new code of city.
:param old_code: old code of city.
"""
edges_to_update_dest = [] # lists of edges where the renamed node is source or destination
edges_to_update_source = []
edges = graph.get_edges()
for edge in edges: # add edges to update to the update lists
if edge.get_destination() == old_code:
edges_to_update_dest.append(edge)
elif edge.get_source() == old_code:
edges_to_update_source.append(edge)
graph.remove_edges_containing_node(old_code) # remove edges containing the old node.
for edge in edges_to_update_dest: # update edges to the new code
graph.add_edge(edge.get_source(), new_code, edge) # add updated edge
for edge in edges_to_update_source:
graph.add_edge(new_code, edge.get_destination(), edge)
def update_data(message_str, data):
"""
Takes input from the user to update data. If the user enters a blank string, data remains the same.
:param message_str: Message to display to the user
:param data: object to update.
:return: the updated data
"""
result = input(message_str + " (" + data + "): ").rstrip('\n')
if result == "":
result = data
return result
def generate_route_list(route_str):
"""
Generate list of nodes in the route.
:param route_str: String of city codes separated by hyphens '-'
:return: List containing the route
"""
if route_str is not None:
route_list = route_str.split("-")
else:
route_list = None
return route_list
def generate_route_string(route_list):
"""
Generate a string containing the route in the input list.
:param route_list: List containing the route
:return: String of city codes separated by hyphens '-'
"""
route_str = ""
for node in route_list[0:-1]:
route_str += node + "-"
return route_str + route_list[-1] # append dest node and return
def route_helper(graph, route):
"""
Helper function for route_shortest and route_info.
Calculates and displays cost, time, and distance for the route.
:param graph: The graph.
:param route: List containing nodes for the route
:return: True on success, False on failure.
"""
return_string = ""
total_distance = 0
cost = 0 # total cost
cost_multiplier = 0.35 # starting cost multiplier for leg
time = 0 # total time
for i, _ in enumerate(route[0:-1]): # check all nodes in the route
edge = graph.get_edge_from_nodes(route[i], route[i+1]) # get edge between two nodes on route
if edge is None: # verify that connection exists
return_string += "Error: no flight from: " + route[i] + " to " + route[i+1] + '\n'
return return_string
leg_distance = int(edge) # get distance of edge
total_distance += leg_distance # sum distances
leg_cost = leg_distance*cost_multiplier # leg base cost
cost += leg_cost # sum costs
if cost_multiplier > 0: # reduce leg cost multiplier
cost_multiplier -= 0.05
cost_multiplier = round(cost_multiplier, 3)
leg_time = calc_leg_time(leg_distance) # calculate leg flight time
return_string += "\nLeg " + route[i] + " to " + route[i+1] + '\n' # print leg information
return_string += "Leg distance: " + str(leg_distance) + "km" + '\n'
return_string += "Leg cost: $%.2f" % leg_cost + '\n'
return_string += "Leg flight time: " + seconds_to_formatted_time(leg_time)+ '\n'
if i > 0: # calculate layover time
leg_time += calc_layover_time(graph, route[i])
time += leg_time # sum time
return_string += "\n Route: " + generate_route_string(route) + '\n' # print route information
return_string += "Total distance: " + str(total_distance) + "km" + '\n'
return_string += "Total cost: $%.2f" % cost + '\n'
return_string += "Total time: " + seconds_to_formatted_time(time) + '\n'
return return_string
def calc_leg_time(leg_distance):
"""
Calculate time for a leg given it's distance.
:param leg_distance: distance (km)
:return: time in seconds
"""
leg_time = 0
#if dist > 400km, accelerate to 750km/h for first 200km, fly at 750km/h, then decelerate to 0 for last 200km
if leg_distance >= 400:
leg_time += time_for_acceleration(200000)*2 # time for acceleration from 0 to 750km/h and back to 0
leg_time += ((leg_distance-400)*1000)*(1/cruise_speed) # cruise time
else: # if dist < 400km, accelerate to 750km/h for first half then decelerate to 0
leg_time += time_for_acceleration((leg_distance/2)*1000)*2
return leg_time
def calc_layover_time(graph, node):
"""
Calculate layover time for a node.
:return: time in seconds
"""
num_outbound = len(graph.get_edges_for_node(node))
minutes_for_layover = 120 - 10*(num_outbound-1)
print("Layover time at: " + node + ": " + seconds_to_formatted_time(minutes_for_layover*60))
return minutes_for_layover*60