def digraph_factory(spec): graph1 = Digraph() graph1.add_vertex('a') graph1.add_vertex('b') graph1.add_vertex('c') graph2 = Digraph() graph2.add_vertex('a') graph2.add_vertex('b') graph2.add_vertex('c') graph2.add_edge('a', 'b') graph2.add_edge('b', 'c') spec_dict = {'no_edge': graph1, 'with_edges': graph2} return spec_dict[spec]
class Server: def __init__(self): # contents: { vertex_id: ( lat, long ) } # lat and long values are stored in 100,000th of degrees self.vertex_locations = {} # contents: { ( v1, v2 ): name } self.edges = {} self.graph = Digraph() def import_file(self, filename): """ Builds the graph and associated information from specified text file. """ print('Importing {}...'.format(filename)) scale = 100000 count = 0 for line in open(filename, 'r'): count += 1 split_line = line.rstrip().split(',') line_type = split_line[0] if line_type == 'V': v = split_line[1] self.graph.add_vertex(v) self.vertex_locations[v] = (float(split_line[2]) * scale, float(split_line[3]) * scale) if line_type == 'E': t = (split_line[1], split_line[2]) self.graph.add_edge(t) self.edges[t] = split_line[3] print('{} lines processed. Ready for input.'.format(count)) def cost_distance(self, e): """ cost_distance returns the straight-line distance between the two vertices at the endpoints of the edge e. >>> s = Server() >>> s.vertex_locations[29577354] = (400,-400) >>> s.vertex_locations[29770958] = (500,-500) >>> tcompare(100 * math.sqrt(2), s.cost_distance((29577354, 29770958))) True """ #print('cost_distance: e = {}'.format(e)) v1 = self.vertex_locations[e[0]] v2 = self.vertex_locations[e[1]] #print('cost_distance: v1 = {}; v2 = {}'.format(v1, v2)) return euclidean_distance(v1, v2) def closest_vertex(self, lat, long): """ Returns the id of the closest vertex to the specified lat and long >>> s = Server() >>> s.vertex_locations[29577354] = (5343099.6,-11349133.1) >>> s.vertex_locations[29770958] = (5357142.9,-11362729.9) >>> s.closest_vertex(5343099,-11349133) 29577354 """ coords = (lat, long) closest = min(self.vertex_locations.items(), key=lambda x: euclidean_distance(coords, x[1])) return closest[0] def find_shortest_path(self, lat1, long1, lat2, long2): """ Returns a least cost path of coordinates from (lat1, long1) to (lat1, long1) """ v1 = self.closest_vertex(lat1, long1) v2 = self.closest_vertex(lat2, long2) #print('Closest vertices: {} and {}'.format(v1, v2)) vertex_path = least_cost_path(self.graph, v1, v2, self.cost_distance) #print('vertex_path: {}'.format(vertex_path)) path = [] if vertex_path is not None: for v in vertex_path: path.append(self.vertex_locations[v]) return path
def test_add_vertex(self): dg = Digraph() dg.add_vertex('a') self.assertTrue('a' in dg.vertex_names)
def read_graph(input_file): """ Read in Digraph data from a file, and return it as a tuple containing a Digraph as well as a map of metadata with vertex positions and edge names. Returns: (digraph, { (node id or edge tuple): (position or edge name) }) """ # # graph and metadata to populate # graph = Digraph() # metadata = {} # # set of vertices that we have read in. Used to check that # # we aren't adding verts through |add_edge| that won't have any # # location metadata. # vert_set = set() # # process each line in the file # for line in input_file: # # strip all trailing whitespace # line = line.rstrip() # fields = line.split(",") # type = fields[0] # if type == 'V': # # got a vertex record # (id,lat,long) = fields[1:] # # vertex id's should be ints # id=int(id) # # lat and long are floats # lat=float(lat) # long=float(long) # vert_set.add(id) # graph.add_vertex(id) # metadata[id] = (lat,long) # elif type == 'E': # # got an edge record # (start,stop,name) = fields[1:] # # vertices are ints # start=int(start) # stop=int(stop) # e = (start,stop) # if start not in vert_set: # raise Exception("Vertex %d is an endpoint for an edge but has no metadata" % start) # if stop not in vert_set: # raise Exception("Vertex %d is an endpoint for an edge but has no metadata" % stop) # # get rid of leading and trailing quote " chars around name # name = name.strip('"') # graph.add_edge(e) # metadata[e] = name # else: # # weird input # raise Exception("Error: weird line |{}|".format(line)) vert_map = {} # vert_id => {at = (lat,lon), ine = set(), oute = set()} edge_map = {} # (id,id) => name for line in input_file: # strip all trailing whitespace line = line.rstrip() fields = line.split(",") type = fields[0] if type == "V": # got a vertex record (id, lat, long) = fields[1:] # vertex id's should be ints id = int(id) # lat and long are floats lat = float(lat) long = float(long) # todo vert_map[id] = {"at": (lat, long), "id": id, "ine": set(), "oute": set()} elif type == "E": # got an edge record (start, stop, name) = fields[1:] # vertices are ints start = int(start) stop = int(stop) e = (start, stop) # get rid of leading and trailing quote " chars around name name = name.strip('"') # todo edge_map[(start, stop)] = name else: # weird input raise Exception("Error: weird line |{}|".format(line)) graph = Digraph() cached_aux_verts = {} def get_aux_verts(e): if e not in cached_aux_verts: aux_vert = {} # first assign ine and oute on verts for e in edge_map: vert_map[e[0]]["oute"].add(e) vert_map[e[1]]["ine"].add(e) # metadata metadata = {} # now, make the aux vert set aux_vert_map = {} # {vert_id => {adjacent_id => aux_vert_id}} aux_vert_current_id = 1 for v_id in vert_map: my_aux_verts = {} vdat = vert_map[v_id] for e in vdat["ine"]: my_aux_verts[e[0]] = aux_vert_current_id aux_vert_current_id += 1 for e in vdat["oute"]: my_aux_verts[e[1]] = aux_vert_current_id aux_vert_current_id += 1 for id in my_aux_verts.values(): metadata[id] = vdat["at"] graph.add_vertex(id) aux_vert_map[v_id] = my_aux_verts # aux verts have been created, add the main edges for (a, b) in edge_map: graph.add_edge((aux_vert_map[a][b], aux_vert_map[b][a])) metadata[(aux_vert_map[a][b], aux_vert_map[b][a])] = 0 # and finally, we need to add the junction virtual edges for v_id in vert_map: vdat = vert_map[v_id] # for each incomming edge, join it to each outgoing edge for (a, b) in vdat["ine"]: in_aux_vert_id = aux_vert_map[v_id][a] for (b, c) in vdat["oute"]: out_aux_vert_id = aux_vert_map[v_id][c] # calculate cost cost = 0 if len(vdat["ine"]) == 1 and len(vdat["oute"]) == 1: # ignore turn cost for nodes that just represent curves cost = 0 else: # use the angle at_a = vert_map[a]["at"] at_b = vert_map[b]["at"] at_c = vert_map[c]["at"] dir_1 = (at_b[0] - at_a[0], at_b[1] - at_a[1]) dir_2 = (at_c[0] - at_b[0], at_c[1] - at_b[1]) theta = math.acos( (dir_1[0] * dir_2[0] + dir_1[1] * dir_2[1]) / ((dir_1[0] ** 2 + dir_1[1] ** 2) ** 0.5 * (dir_2[0] ** 2 + dir_2[1] ** 2) ** 0.5) * 0.9999 ) cost = theta * 10 # add graph.add_edge((in_aux_vert_id, out_aux_vert_id)) metadata[(in_aux_vert_id, out_aux_vert_id)] = cost # return the data return (graph, metadata)