Skip to content

Commit

Permalink
Merge pull request #12 from d610f20/stats-defaults
Browse files Browse the repository at this point in the history
[Statistics] Verify stat file and insert population and workhours if missing
  • Loading branch information
NicEastvillage authored Apr 2, 2020
2 parents 660f9e5 + 7c1b745 commit 22665da
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 22 deletions.
20 changes: 3 additions & 17 deletions in/example.stat.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
<city>
<general inhabitants="1000" households="500" childrenAgeLimit="19" retirementAgeLimit="66" carRate="0.58" unemploymentRate="0.05" footDistanceLimit="250" incomingTraffic="200" outgoingTraffic="50" />
<parameters carPreference="0.50" meanTimePerKmInCity="6" freeTimeActivityRate="0.15" uniformRandomTraffic="0.20" departureVariation="300" />

<population>
<bracket beginAge="0" endAge="30" peopleNbr="30" />
<bracket beginAge="30" endAge="60" peopleNbr="40" />
<bracket beginAge="60" endAge="90" peopleNbr="30" />
</population>

<workHours>
<opening hour="30600" proportion="0.30" />
<opening hour="32400" proportion="0.70" />
<closing hour="43200" proportion="0.20" />
<closing hour="63000" proportion="0.20" />
<closing hour="64800" proportion="0.60" />
</workHours>
</city>
<general inhabitants="3500" households="2000"/>
<parameters uniformRandomTraffic="0.2" />
</city>
8 changes: 4 additions & 4 deletions perlin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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
Expand All @@ -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
Expand Down
45 changes: 44 additions & 1 deletion randomActivityGen.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,53 @@ 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 <general>. 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
net = sumolib.net.readNet(args["--net-file"])

# 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)
Expand All @@ -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()

0 comments on commit 22665da

Please sign in to comment.