예제 #1
0
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]
예제 #2
0
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
예제 #3
0
 def test_add_vertex(self):
     dg = Digraph()
     dg.add_vertex('a')
     self.assertTrue('a' in dg.vertex_names)
예제 #4
0
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)