コード例 #1
0
def _road_point_generator(roads):
    """
    Picks a random point on on a road from roads
    """
    assert len(roads) > 0

    # Length of all roads combined
    total_length = sum(map(lambda road: road.getLength(), roads))

    while True:
        # Select a point on the combined stretch of road
        distance = random.uniform(0, total_length)

        # Find the selected road
        length_sum = 0.0
        found_road = False
        for road in roads:
            if (length_sum + road.getLength()) >= distance:
                # Compute the exact point on the selected road

                # Distance along the road segment
                remaining = distance - length_sum
                pos = position_on_edge(road, remaining)

                yield [pos[0], pos[1], road, remaining]
                found_road = True
                break
            else:
                length_sum += road.getLength()

        if not found_road:
            raise AssertionError(
                "Failed to pick a road. A distance beyond the last road must have been erroneously "
                "picked: {} (length sum: {}) (total length: {})".format(
                    distance, length_sum, total_length))
コード例 #2
0
def calc_school_divergence(test: TestInstance, plot: bool) -> List[float]:
    """
    Calculate the divergence between generated and real schools by solving the assignment problem on them
     and plot these schools and assignments on the network if enabled.
    :param test: the given TestInstance to test
    :param plot: whether to plot the city, schools, and assignments
    :return: the divergence for each assigned school
    """
    net: sumolib.net.Net = sumolib.net.readNet(test.net_file)

    # Get mean school coordinates for real and generated statistics
    gen_coords = np.array([
        position_on_edge(net.getEdge((xml_school.get("edge"))),
                         float(xml_school.get("pos"))) for xml_school in
        ET.parse(test.gen_stats_out_file).find("schools").findall("school")
    ])

    real_coords = np.array([
        position_on_edge(net.getEdge((xml_school.get("edge"))),
                         float(xml_school.get("pos"))) for xml_school in
        ET.parse(test.real_stats_file).find("schools").findall("school")
    ])

    # Get euclidean distance between all points in both sets as a cost matrix.
    # Note that the ordering is seemingly important for linear_sum_assignment to work.
    #  Not ordering causes points in the larger set, larger than the max size of the smaller set to be ignored.
    if len(real_coords) > len(gen_coords):
        dist = cdist(gen_coords, real_coords)
    else:
        dist = cdist(real_coords, gen_coords)

    # Solve the assignment problem
    _, assignment = linear_sum_assignment(dist)

    if plot:
        plot_school_assignment(net, test.name, gen_coords, real_coords,
                               assignment)

    # return list of assigned schools divergence
    return [
        dist[i, assignment[i]]
        for i in range(0, min(len(gen_coords), len(real_coords)))
    ]
コード例 #3
0
def write_school_coords(net: sumolib.net.Net, stats: ET.ElementTree, filename):
    """
    Writes all schools' positions found in stats file to a csv called 'filename'. These coordinates can be used for
    testing, e.g 2d KS tests between generated schools, and real school positions in the city
    :param net: network file that the schools in stats file is placed on
    :param stats: stats file parsed with ElementTree containing schools :param filename: name of csv to be written
    """
    # Ensure that output directory exists
    directory = "school_coordinates"
    if not os.path.exists(directory):
        os.mkdir(directory)

    xml_schools = [
        xml_school for xml_school in stats.find("schools").findall("school")
    ]

    if xml_schools is None:
        print(
            f"Cannot write schools to CSV: no schools found in the generated stats file for {filename}"
        )
        return

    positions = []

    # find all generated school positions, append to positions
    for gen_school_edge in xml_schools:
        pos = float(gen_school_edge.get("pos"))
        edge = net.getEdge(gen_school_edge.get("edge"))
        positions.append(position_on_edge(edge, pos))

    # workaround to append columns to csv file. read old_csv, make a new csv and write to this, rename to old csv when done
    old_csv = f'{directory}/{filename}-school-coords.csv'
    new_csv = f'{directory}/{filename}-school-coords-new.csv'

    # if the old_csv already exists, do as mentioned above
    if os.path.exists(old_csv):
        with open(old_csv, 'r') as read_obj, \
                open(new_csv, 'w', newline='') as write_obj:
            csv_reader = csv.reader(read_obj)
            csv_writer = csv.writer(write_obj)
            for i, row in enumerate(csv_reader):
                row.append(positions[i][0])
                row.append(positions[i][1])
                csv_writer.writerow(row)
        try:
            os.rename(new_csv, old_csv)
        except WindowsError:
            os.remove(old_csv)
            os.rename(new_csv, old_csv)
    else:
        # if file does not exist, simply write to it
        file = open(old_csv, 'w', newline='')
        with file:
            writer = csv.writer(file)
            writer.writerows(positions)
コード例 #4
0
def setup_bus_stops(net: sumolib.net.Net, stats: ET.ElementTree, min_distance,
                    k):
    """
    Generates bus stops from net, and writes them into stats.
    """
    logging.debug(
        f"[bus-stops] Using min_distance: {min_distance}, and k (attempts): {k}"
    )
    edges = net.getEdges()

    city = stats.getroot()
    bus_stations = city.find("busStations")
    seed_bus_stops = []
    if bus_stations is None:
        bus_stations = ET.SubElement(city, "busStations")
    else:
        for station in bus_stations.findall("busStation"):
            assert "edge" in station.attrib, "BusStation isn't placed on an edge"
            edge_id = station.attrib["edge"]
            assert "pos" in station.attrib, "BusStation doesn't have a position along the edge"
            along = float(station.attrib["pos"])

            edge = net.getEdge(edge_id)
            if edge is None:
                logging.warning(
                    "BusStation in stat file reference edge (id=\"{}\") that doesn't exist in the road "
                    "network".format(edge_id))
                continue

            pos = position_on_edge(edge, along)

            seed_bus_stops.append([pos[0], pos[1], edge, along])

    for i, busstop in enumerate(
            bus_stop_generator(edges,
                               min_distance,
                               min_distance * 2,
                               k,
                               seeds=seed_bus_stops)):
        edge = busstop[2]
        dist_along = busstop[3]
        ET.SubElement(bus_stations,
                      "busStation",
                      attrib={
                          "id": str(i),
                          "edge": edge.getID(),
                          "pos": str(dist_along),
                      })
コード例 #5
0
def display_network(net: sumolib.net.Net, stats: ET.ElementTree, max_size: int, centre: Tuple[float, float],
                    network_name: str):
    """
    :param net: the network to display noisemap for
    :param stats: the stats file describing the network
    :param max_size: maximum width/height of the resulting image
    :param centre: the centre of the network for drawing dot
    :param network_name: the name of the network for drawing in upper-left corner
    :return:
    """
    # Basics about the city and its size
    boundary = net.getBoundary()
    city_size = (boundary[2] - boundary[0], boundary[3] - boundary[1])

    # Determine the size of the picture and scalars for scaling the city to the correct size
    # We might have a very wide city. In this case we want to produce a wide image
    width_height_relation = city_size[1] / city_size[0]
    if city_size[0] > city_size[1]:
        width = max_size
        height = int(max_size * width_height_relation)
    else:
        width = int(max_size / width_height_relation)
        height = max_size
    width_scale = width / city_size[0]
    height_scale = height / city_size[1]

    def to_png_space(xy: Tuple[float, float]) -> Tuple[float, float]:
        """ Translate the given city position to a png position """
        return (xy[0] - boundary[0]) * width_scale, (xy[1] - boundary[1]) * height_scale

    # Load pretty fonts for Linux and Windows, falling back to defaults
    fontsize = max(max_size // 90, 10)
    try:
        font = ImageFont.truetype("LiberationMono-Regular.ttf", size=fontsize)
    except IOError:
        try:
            font = ImageFont.truetype("arial.ttf", size=fontsize)
        except IOError:
            logging.warning("[render] Could not load font, falling back to default")
            font = ImageFont.load_default()

    assert font is not None, "[render] No font loaded, cannot continue"

    # Make image and prepare for drawing
    img = Image.new("RGB", (width, height), (255, 255, 255))
    draw = ImageDraw.Draw(img, "RGBA")

    # Draw streets
    if stats.find("streets") is not None:
        for street_xml in stats.find("streets").findall("street"):
            edge = net.getEdge(street_xml.attrib["edge"])
            population = float(street_xml.attrib["population"])
            industry = float(street_xml.attrib["workPosition"])
            green = int(10 + 245 * (1 - industry))
            blue = int(10 + 245 * (1 - population))
            for pos1, pos2 in [edge.getShape()[i:i + 2] for i in range(0, int(len(edge.getShape()) - 1))]:
                draw.line((to_png_space(pos1), to_png_space(pos2)), (0, green, blue),
                          int(1.5 + 3.5 * population ** 1.5))
    else:
        logging.warning(f"[render] Could not find any streets in statistics")

    # Draw city gates
    if stats.find("cityGates") is not None:
        for gate_xml in stats.find("cityGates").findall("entrance"):
            edge = net.getEdge(gate_xml.attrib["edge"])
            traffic = max(float(gate_xml.attrib["incoming"]), float(gate_xml.attrib["outgoing"]))
            x, y = to_png_space(position_on_edge(edge, float(gate_xml.attrib["pos"])))
            r = int(max_size / 600 + traffic / 1.3)
            draw.ellipse((x - r, y - r, x + r, y + r), fill=COLOUR_CITY_GATE)
    else:
        logging.warning(f"[render] Could not find any city-gates in statistics")

    # Draw bus stops
    if stats.find("busStations") is not None:
        for stop_xml in stats.find("busStations").findall("busStation"):
            edge = net.getEdge(stop_xml.attrib["edge"])
            x, y = to_png_space(position_on_edge(edge, float(stop_xml.attrib["pos"])))
            r = max_size / 600
            draw.ellipse((x - r, y - r, x + r, y + r), fill=COLOUR_BUS_STOP)
    else:
        logging.warning(f"[render] Could not find any bus-stations in statistics")

    # Draw schools
    if stats.find("schools") is not None:
        for school_xml in stats.find("schools").findall("school"):
            edge = net.getEdge(school_xml.attrib["edge"])
            capacity = int(school_xml.get('capacity'))
            x, y = to_png_space(position_on_edge(edge, float(school_xml.get('pos'))))
            r = int((max_size / 275 * (capacity / 500) ** 0.4) * 1.1)
            draw.ellipse((x - r, y - r, x + r, y + r), fill=COLOUR_SCHOOL)
    else:
        logging.warning(f"[render] Could not find any schools in statistics")

    if not any([stats.find(x) for x in {"streets", "cityGates", "busStations", "schools"}]):
        logging.error("[render] No elements found in statistics, cannot display network and features")
        exit(1)

    # Draw city centre
    x, y = to_png_space(centre)
    r = max_size / 100
    draw.ellipse((x - r, y - r, x + r, y + r), fill=COLOUR_CENTRE)

    # Flip image on the horizontal axis and update draw-pointer
    img = img.transpose(FLIP_TOP_BOTTOM)
    draw = ImageDraw.Draw(img, "RGBA")

    Legend(max_size, height, draw, font) \
        .draw_network_name(network_name) \
        .draw_scale_legend(city_size, width_scale) \
        .draw_gradient("Pop, work gradient") \
        .draw_icon_legend(COLOUR_CENTRE, "Centre") \
        .draw_icon_legend(COLOUR_SCHOOL, "School") \
        .draw_icon_legend(COLOUR_BUS_STOP, "Bus stop") \
        .draw_icon_legend(COLOUR_CITY_GATE, "City gate")

    img.show()
コード例 #6
0
# base of file name, e.g. "vejen.trips.rou.xml" -> "vejen"
fname = os.path.basename(args["--trips-file"])
while "." in fname:
    fname = os.path.splitext(fname)[0]

data = []
with open(os.path.dirname(args["--trips-file"]) + f"/{fname}-trip-starts.csv", "w", newline="") as csv_starts:
    writer_starts = csv.writer(csv_starts)

    for trip_xml in trips.findall("trip"):
        edge = net.getEdge(trip_xml.get("from"))
        departTime = float(trip_xml.get("depart"))
        departPos = float(trip_xml.get("departPos") or edge.getLength() * random.random())

        x, y = position_on_edge(edge, departPos)
        x -= offset_x
        y -= offset_y
        datapoint = (x, y, departTime)
        writer_starts.writerow(datapoint)
        data.append(datapoint)

if args["--png"] or args["--gif"]:
    # Calculate dimensions and scaling
    max_size = 800
    width_height_relation = net_height / net_width
    if net_width > net_height:
        width = max_size
        height = int(max_size * width_height_relation)
    else:
        width = int(max_size / width_height_relation)