diff --git a/in/example.stat.xml b/in/example.stat.xml index ece9fec..f8302a6 100644 --- a/in/example.stat.xml +++ b/in/example.stat.xml @@ -1,18 +1,4 @@ - - - - - - - - - - - - - - - - - + + + \ No newline at end of file diff --git a/perlin.py b/perlin.py index b63fbea..4ee1173 100644 --- a/perlin.py +++ b/perlin.py @@ -20,9 +20,9 @@ # The 'noise' lib has good resolution until above 10 mil, but a SIGSEGV is had on values above [-100000, 100000] # FIXME: these values result in a noisemap which contains CRT-like lines and patterns. Find better, sane values. # POPULATION_BASE = random.randrange(-1000, 1000) -POPULATION_BASE: float = 0 +POPULATION_BASE = 4 # INDUSTRY_BASE = random.randrange(-1000, 1000) -INDUSTRY_BASE: float = 0 +INDUSTRY_BASE = 1 def get_edge_pair_centroid(coords: List[Tuple[float, float]]) -> (float, float): @@ -36,7 +36,7 @@ def get_edge_pair_centroid(coords: List[Tuple[float, float]]) -> (float, float): return x_avg, y_avg -def get_perlin_noise(x: float, y: float, base: float, scale: float = 0.005, octaves: int = 3) -> float: +def get_perlin_noise(x: float, y: float, base: int, scale: float = 0.005, octaves: int = 3) -> float: """ The 'noise' lib returns a value in the range of [-1:1]. The noise value is scaled to the range of [0:1]. :param base: offset into noisemap @@ -49,7 +49,7 @@ def get_perlin_noise(x: float, y: float, base: float, scale: float = 0.005, octa return (noise.pnoise2(x=x * scale, y=y * scale, octaves=octaves, base=base) + 1) / 2 -def get_population_number(edge: sumolib.net.edge.Edge, base: float, centre, +def get_population_number(edge: sumolib.net.edge.Edge, base: int, centre, radius, scale: float = 0.005, octaves: int = 3) -> float: """ Returns a Perlin simplex noise at centre of given street diff --git a/randomActivityGen.py b/randomActivityGen.py index 77f4ab1..3c28c08 100644 --- a/randomActivityGen.py +++ b/randomActivityGen.py @@ -100,7 +100,45 @@ def setup_city_gates(net: sumolib.net.Net, stats: ET.ElementTree, gate_count: in }) -if __name__ == "__main__": +def verify_stats(stats: ET.ElementTree): + """ + Do various verification on the stats file to ensure that it is usable. If population and work hours are missing, + some default values will be insert as these are required by ActivityGen. + + :param stats: stats file parsed with ElementTree + """ + city = stats.getroot() + assert city.tag == "city", "Stat file does not seem to be a valid stat file. The root element is not city" + # According to ActivityGen (https://github.com/eclipse/sumo/blob/master/src/activitygen/AGActivityGenHandler.cpp#L124-L161) + # only general::inhabitants and general::households are required. Everything else has default values. + general = stats.find("general") + # TODO Maybe guestimate the number of inhabitants and households based on the network's size + assert general is not None, "Stat file is missing . Inhabitants and households are required" + assert general.attrib["inhabitants"] is not None, "Number of inhabitants are required" + assert general.attrib["households"] is not None, "Number of households are required" + + # It is also required that there are at least one population bracket + population = city.find("population") + if population is None: + # Population is missing, so we add a default population + population = ET.SubElement(city, "population") + ET.SubElement(population, "bracket", {"beginAge": "0", "endAge": "30", "peopleNbr": "30"}) + ET.SubElement(population, "bracket", {"beginAge": "30", "endAge": "60", "peopleNbr": "40"}) + ET.SubElement(population, "bracket", {"beginAge": "60", "endAge": "90", "peopleNbr": "30"}) + + # Similarly at least and one opening and closing workhour is required + work_hours = city.find("workHours") + if work_hours is None: + # Work hours are missing, so we add some default work hours + work_hours = ET.SubElement(city, "workHours") + ET.SubElement(work_hours, "opening", {"hour": "28800", "proportion": "70"}) # 70% at 8.00 + ET.SubElement(work_hours, "opening", {"hour": "30600", "proportion": "30"}) # 30% at 8.30 + ET.SubElement(work_hours, "closing", {"hour": "43200", "proportion": "10"}) # 10% at 12.00 + ET.SubElement(work_hours, "closing", {"hour": "61200", "proportion": "30"}) # 30% at 17.00 + ET.SubElement(work_hours, "closing", {"hour": "63000", "proportion": "60"}) # 60% at 17.30 + + +def main(): args = docopt(__doc__, version="RandomActivityGen v0.1") # Read in SUMO network @@ -108,6 +146,7 @@ def setup_city_gates(net: sumolib.net.Net, stats: ET.ElementTree, gate_count: in # Parse statistics configuration stats = ET.parse(args["--stat-file"]) + verify_stats(stats) # Scale and octave seems like sane values for the moment apply_network_noise(net, stats, 0.005, 3) @@ -116,3 +155,7 @@ def setup_city_gates(net: sumolib.net.Net, stats: ET.ElementTree, gate_count: in # Write statistics back stats.write(args["--output-file"]) + + +if __name__ == "__main__": + main()