from CSAir_helpers import *
import json
from Location import Location
from Graph import DiGraph
import re
import requests
def load_initial_json(prompt=True):
"""Refactored initial prompt for query to this helper function"""
filename_to_load = 'map_data.json'
if prompt is False:
graph = parse_json(filename_to_load)
return graph
graph = False
while graph is False:
filename_to_load = update_data("Enter the filename to load.\n"
"If left blank, will load the default file", filename_to_load)
graph = parse_json(filename_to_load)
if graph is False:
print("Could not load file. Try again.")
return graph
def parse_json(filename):
"""
Parses a correctly formatted JSON file and returns a graph.
:param filename: The path to the JSON file
:return: Graph directed graph, False on failure
"""
graph = DiGraph.DiGraph()
try:
json_data = open(filename)
except FileNotFoundError:
return False
try:
data = json.load(json_data)
except ValueError:
return False
json_data.close()
try: # check if the JSON is directed or undirected
directed = data['directed']
except KeyError:
directed = False
for city_node in data["metros"]: # add cities
first_cardinal = list(city_node['coordinates'])[0] # get cardinal directions
second_cardinal = list(city_node['coordinates'])[1]
first_coordinate = city_node['coordinates'][first_cardinal] # get lat/lon number values
second_coordinate = city_node['coordinates'][second_cardinal]
if first_cardinal == 'W' or first_cardinal == 'E': # assign lat/lon
latitude = str(second_coordinate) + second_cardinal
longitude = str(first_coordinate) + first_cardinal
else:
latitude = str(first_coordinate) + first_cardinal
longitude = str(second_coordinate) + second_cardinal
loc = Location(city_node['code'], city_node['name'], city_node['country'], city_node['continent'],
city_node['timezone'], latitude, longitude, city_node['population'], city_node['region'])
graph.add_node(loc, city_node['code'])
for route in data["routes"]: # add routes
source_node = route['ports'][0]
dest_node = route['ports'][1]
graph.add_edge(source_node, dest_node, route['distance']) # Add first directed edge
if directed is False:
graph.add_edge(dest_node, source_node, route['distance']) # Add second directed edge
return graph
def save_json(graph, filename):
"""
Saves the graph to a JSON file
:param graph: The graph
:param filename: Name of the file to write to
"""
edges = graph.get_edges()
directed = check_directed(graph) # Check if the JSON needs to be saved as directed.
if not directed:
edges = graph.get_undirected_edges()
data = {'directed': directed, 'data sources': [], 'metros': [], 'routes': []} # begin building the JSON data object
for edge in edges: # add edges
new_route = {'ports': [edge.get_source(), edge.get_destination()], 'distance': edge}
data['routes'].append(new_route)
nodes = graph.get_nodes()
for node in nodes: # add cities
city = node.get_object() # get Location object from node
lat_letter = re.sub(r'\d+', "", city.latitude) # get the cardinal letter
lat_num = re.findall(r'\d+', city.latitude) # get the coordinate number
lon_letter = re.sub(r'\d+', "", city.longitude)
lon_num = re.findall(r'\d+', city.longitude)
coordinates = {lat_letter: lat_num, lon_letter: lon_num} # build coordinates
new_metro = {'code': city.code, 'name': city.name,
'country': city.country, 'continent': city.continent,
'timezone': city.timezone, 'coordinates': coordinates,
'population': city.population, 'region': city.region} # build city
data['metros'].append(new_metro) # add city
try: # save JSON file
file = open(filename, "w")
except OSError:
return False
file.write(json.dumps(data, sort_keys=True, indent=4, separators=(',', ': ')))
file.close()
print("Data saved to " + filename)
return True
def json_api_request(graph, code):
"""
Make an API request to Wunderground to obtain data for a city given the airport code.
:param graph: The graph
:param code: Airport/city code
"""
#make request, load JSON, then parse JSON
url = 'http://api.wunderground.com/api/82385a8fea1e0a46/geolookup/q/' + code + '.json'
print(url)
resp = requests.get(url=url) # using HTTP Requests for humans: http://docs.python-requests.org/en/latest/
data = json.loads(resp.text)
try:
country = data['location']['country_name']
except KeyError:
return "Error"
latitude = int(float(data['location']['lat']))
longitude = int(float(data['location']['lon']))
if latitude < 0:
latitude = str(abs(latitude)) + "S"
else:
latitude = str(abs(latitude)) + "N"
if longitude < 0:
longitude = str(abs(longitude)) + "W"
else:
longitude = str(abs(longitude)) + "E"
timezone = data['location']['tz_long']
name = data['location']['city']
continent = country
population = 0
region = 0
if country == "USA":
region = 1
continent = "North America"
print("Note: The API does not have population, continent, or region data.\n"
"You will need to edit the city to add it.\n")
if "Error" not in add_city_helper(graph, code, name, country, continent, timezone, latitude, longitude, population, region):
return True
else:
return False
def check_directed(graph):
"""
Check if it is necessary that the graph is represented as a directed graph when saved to a JSON file.
:param graph: The graph
:return: True if the graph must be represented as directed, false otherwise.
"""
edges = graph.get_edges()
#check that each edge has an identical edge in the opposite direction
for edge in edges:
source = edge.get_source()
dest = edge.get_destination()
dest_edges = graph.get_edges_for_node(dest)
found = False
for d_edge in dest_edges:
if d_edge.get_destination() == source:
found = True
if not found:
print("Graph needs to be saved as directed.")
return True
return False
def merge_json(graph, filename):
"""
Merge a new JSON file into the graph
:param graph: The graph
:param filename: new JSON file.
:return: True on success, false otherwise
"""
other_graph = parse_json(filename)
if other_graph is False:
print("Could not load data, " + filename + " does not exist.")
return False
graph.merge(other_graph)
print("Merged " + filename + " into current data.")
return True