mirror of
https://github.com/Ahp06/SUMO_Emissions.git
synced 2024-11-24 12:36:30 +00:00
Merge pull request #10 from Ahp06/sumo-config-generator
An urban traffic generator to generate SUMO scenarios from an OpenStreetMap map with the following options: different vehicle classes (passenger, bus, …) with a given density in veh/km/h a random seed, for reproducibility a given length
This commit is contained in:
commit
1d5c0ec576
116
.gitignore
vendored
Normal file
116
.gitignore
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
284
sumo_project/configurator.py
Normal file
284
sumo_project/configurator.py
Normal file
@ -0,0 +1,284 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
|
||||
if 'SUMO_HOME' in os.environ:
|
||||
TOOLSDIR = os.path.join(os.environ['SUMO_HOME'], 'tools')
|
||||
sys.path.append(TOOLSDIR)
|
||||
else:
|
||||
sys.exit("Please declare environment variable 'SUMO_HOME'")
|
||||
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
from sys import argv
|
||||
from types import SimpleNamespace
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import randomTrips
|
||||
import sumolib
|
||||
|
||||
# Absolute path of the directory the script is in
|
||||
SCRIPTDIR = os.path.dirname(__file__)
|
||||
TEMPLATEDIR = os.path.join(SCRIPTDIR, 'templates')
|
||||
SUMOBIN = os.path.join(os.environ['SUMO_HOME'], 'bin')
|
||||
|
||||
# Init logger
|
||||
logfile = os.path.join(SCRIPTDIR, f'files/logs/configurator_{datetime.datetime.utcnow().isoformat()}.log')
|
||||
logging.basicConfig(
|
||||
filename=logfile,
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
"""
|
||||
Definition of vehicle classes.
|
||||
See http://sumo.dlr.de/wiki/Definition_of_Vehicles,_Vehicle_Types,_and_Routes#Abstract_Vehicle_Class
|
||||
"""
|
||||
vehicle_classes = {
|
||||
'passenger': {
|
||||
'--vehicle-class': 'passenger',
|
||||
'--vclass': 'passenger',
|
||||
'--prefix': 'veh',
|
||||
'--min-distance': 300,
|
||||
'--trip-attributes': 'departLane="best"',
|
||||
},
|
||||
'bus': {
|
||||
'--vehicle-class': 'bus',
|
||||
'--vclass': 'bus',
|
||||
'--prefix': 'bus',
|
||||
},
|
||||
'truck': {
|
||||
'--vehicle-class': 'truck',
|
||||
'--vclass': 'truck',
|
||||
'--prefix': 'truck',
|
||||
'--min-distance': 600,
|
||||
'--trip-attributes': 'departLane="best"',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RandomTripsGenerator:
|
||||
def __init__(self, netpath, routepath, output, vclass, density, *flags, **opts):
|
||||
self.vclass = vclass
|
||||
self.density = density
|
||||
self.options = {
|
||||
# Default options
|
||||
'--net-file': netpath,
|
||||
'--output-trip-file': output,
|
||||
'--route-file': routepath,
|
||||
**opts
|
||||
}
|
||||
self.flags = [*flags]
|
||||
edges = sumolib.net.readNet(netpath).getEdges()
|
||||
self._init_trips(edges, vclass, density)
|
||||
self.options.update(vehicle_classes[self.vclass])
|
||||
|
||||
def generate(self):
|
||||
logging.info(f'Generating trips for vehicle class {self.vclass} with density of {self.density} veh/km/h')
|
||||
randomTrips.main(randomTrips.get_options(dict_to_list(self.options) + self.flags))
|
||||
|
||||
def _init_trips(self, edges, vclass, density):
|
||||
"""
|
||||
:param edges: foo.rou.xml
|
||||
:param density: vehicle/km/h
|
||||
"""
|
||||
# calculate the total length of the available lanes
|
||||
length = 0.
|
||||
for edge in edges:
|
||||
if edge.allows(vclass):
|
||||
length += edge.getLaneNumber() * edge.getLength()
|
||||
|
||||
logging.debug(f'density = {density}')
|
||||
period = 3600 / (length / 1000) / density
|
||||
logging.debug(f'Period computed for network : {period}, vclass={self.vclass}')
|
||||
self.options.update({'-p': period})
|
||||
|
||||
|
||||
class StoreDictKeyPair(argparse.Action):
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
pairs = {}
|
||||
for kv in values:
|
||||
k, v = kv.split("=")
|
||||
pairs[k] = v
|
||||
setattr(namespace, self.dest, pairs)
|
||||
|
||||
|
||||
def load_netconvert_template(osm_input, out_name):
|
||||
netconfig = ElementTree.parse(os.path.join(TEMPLATEDIR, 'simul.netcfg'))
|
||||
root = netconfig.getroot()
|
||||
root.find('input/osm-files').set('value', osm_input)
|
||||
root.find('output/output-file').set('value', f'{out_name}.net.xml')
|
||||
root.find('report/log').set('value', f'{out_name}.netconvert.log')
|
||||
return netconfig
|
||||
|
||||
|
||||
def load_polyconvert_template(osm_file, type_file, scenario_name):
|
||||
polyconfig = ElementTree.parse(os.path.join(TEMPLATEDIR, 'simul.polycfg'))
|
||||
root = polyconfig.getroot()
|
||||
root.find('input/osm-files').set('value', osm_file)
|
||||
root.find('input/net-file').set('value', f'{scenario_name}.net.xml')
|
||||
root.find('input/type-file').set('value', type_file)
|
||||
root.find('output/output-file').set('value', f'{scenario_name}.poly.xml')
|
||||
root.find('report/log').set('value', f'{scenario_name}.polyconvert.log')
|
||||
return polyconfig
|
||||
|
||||
|
||||
def load_sumoconfig_template(simulation_name, routefiles=(), generate_polygons=False, seed=None):
|
||||
routefiles = routefiles or (f'{simulation_name}.rou.xml',)
|
||||
sumoconfig = ElementTree.parse(os.path.join(TEMPLATEDIR, 'simul.sumocfg'))
|
||||
root = sumoconfig.getroot()
|
||||
root.find('input/net-file').set('value', f'{simulation_name}.net.xml')
|
||||
root.find('input/route-files').set('value', ','.join(routefiles))
|
||||
additional = root.find('input/additional-files')
|
||||
if generate_polygons:
|
||||
additional.set('value', f'{simulation_name}.poly.xml')
|
||||
else:
|
||||
root.find('input').remove(additional)
|
||||
root.find('report/log').set('value', f'{simulation_name}.log')
|
||||
# Set the seed for the random number generator. By default, use the current time
|
||||
root.find('random_number/seed').set('value', seed or str(int(time.time())))
|
||||
return sumoconfig
|
||||
|
||||
|
||||
def generate_scenario(osm_file, out_path, scenario_name, generate_polygons=False):
|
||||
net_template = load_netconvert_template(osm_file, scenario_name)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
# Generate NETCONVERT configuration
|
||||
netconfig = os.path.join(tmpdirname, f'{scenario_name}.netcfg')
|
||||
net_template.write(netconfig)
|
||||
# Copy typemaps to tempdir
|
||||
shutil.copytree(os.path.join(TEMPLATEDIR, 'typemap'), os.path.join(tmpdirname, 'typemap'))
|
||||
# Call NETCONVERT
|
||||
logging.info("Generating network…")
|
||||
netconvertcmd = [os.path.join(SUMOBIN, 'netconvert'), '-c', netconfig]
|
||||
logging.debug(f'Calling {" ".join(netconvertcmd)}')
|
||||
subprocess.run(netconvertcmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
# Optionaly generate polygons
|
||||
if generate_polygons:
|
||||
generate_polygons_(osm_file, scenario_name, tmpdirname)
|
||||
# Move files to destination
|
||||
ignore_patterns = shutil.ignore_patterns('*.polycfg', '*.netcfg', 'typemap')
|
||||
shutil.copytree(tmpdirname, out_path, ignore=ignore_patterns)
|
||||
|
||||
|
||||
def generate_polygons_(osm_file, scenario_name, dest):
|
||||
polyconfig = os.path.join(dest, f'{scenario_name}.polycfg')
|
||||
poly_template = load_polyconvert_template(osm_file, 'typemap/osmPolyconvert.typ.xml', scenario_name)
|
||||
poly_template.write(polyconfig)
|
||||
# Call POLYCONVERT
|
||||
logging.info('Generating polygons…')
|
||||
polyconvert_cmd = [os.path.join(SUMOBIN, 'polyconvert'), '-c', polyconfig]
|
||||
logging.debug(f'Calling {" ".join(polyconvert_cmd)}')
|
||||
subprocess.run(polyconvert_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
|
||||
def generate_mobility(out_path, name, vclasses, end_time):
|
||||
netfile = f'{name}.net.xml'
|
||||
netpath = os.path.join(out_path, netfile)
|
||||
output = os.path.join(out_path, f'{name}.trips.xml')
|
||||
routefiles = []
|
||||
for vclass, density in vclasses.items():
|
||||
# simname.bus.rou.xml, simname.passenger.rou.xml, ...
|
||||
routefile = f'{name}.{vclass}.rou.xml'
|
||||
routepath = os.path.join(out_path, routefile)
|
||||
routefiles.append(routefile)
|
||||
logging.debug(routefile)
|
||||
generator = RandomTripsGenerator(netpath, routepath, output, vclass, float(density))
|
||||
generator.flags.append('-l')
|
||||
generator.flags.append('--validate')
|
||||
generator.options.update(**{'--end': end_time})
|
||||
generator.generate()
|
||||
return routefiles
|
||||
|
||||
|
||||
def generate_sumo_configuration(routefiles, path, scenario_name, generate_polygons):
|
||||
sumo_template = load_sumoconfig_template(scenario_name, routefiles, generate_polygons)
|
||||
sumo_template.write(os.path.join(path, f'{scenario_name}.sumocfg'))
|
||||
|
||||
|
||||
def generate_all(args):
|
||||
simulation_name = args.name
|
||||
simulation_dir = os.path.join(args.path, simulation_name)
|
||||
try:
|
||||
generate_polygons = args.generate_polygons
|
||||
except AttributeError:
|
||||
generate_polygons = False
|
||||
osm_file = args.osmfile
|
||||
logs_dir = os.path.join(simulation_dir, 'log')
|
||||
|
||||
generate_scenario(osm_file, simulation_dir, simulation_name, generate_polygons)
|
||||
routefiles = generate_mobility(simulation_dir, simulation_name, args.vclasses, args.end)
|
||||
generate_sumo_configuration(routefiles, simulation_dir, simulation_name, generate_polygons)
|
||||
# Move all logs to logdir
|
||||
move_logs(simulation_dir, logs_dir)
|
||||
|
||||
|
||||
def move_logs(simulation_dir, logs_dir):
|
||||
for f in os.listdir(simulation_dir):
|
||||
if os.path.splitext(f)[1] == '.log':
|
||||
shutil.move(os.path.join(simulation_dir, f), logs_dir)
|
||||
|
||||
|
||||
def dict_to_list(d):
|
||||
return [item for k in d for item in (k, d[k])]
|
||||
|
||||
|
||||
def parse_command_line(args=None):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('osmfile', help='Path to the .osm file to convert to a SUMO simulation')
|
||||
parser.add_argument('--path', help='Where to generate the files')
|
||||
parser.add_argument('--name', required=True, help='Name of the SUMO scenario to generate')
|
||||
parser.add_argument('--generate-polygons', default=False, action='store_true',
|
||||
help='Whether to generate polygons and POIs (defaults to false).')
|
||||
parser.add_argument('--vclass', dest='vclasses', action=StoreDictKeyPair,
|
||||
nargs="+", metavar="VCLASS=DENSITY",
|
||||
help='Generate this vclass with given density, in pair form vclass=density. The density is '
|
||||
'given in vehicles per hour per kilometer. For now, the following vehicle classes are '
|
||||
'available: passenger, truck, bus.')
|
||||
parser.add_argument('--seed', help='Initializes the random number generator.')
|
||||
parser.add_argument('-e', '--end', type=int, default=200, help='end time (default 200)')
|
||||
return parser.parse_args(args=args)
|
||||
|
||||
|
||||
def handle_args(options):
|
||||
# If no vehicle classes are specified, use 'passenger' as a default with a density of 10 cars/km/h.
|
||||
options.vclasses = options.vclasses or {'passenger': 10}
|
||||
# Delete simul_dir if it already exists
|
||||
simul_dir = os.path.join(options.path, options.name)
|
||||
if os.path.isdir(simul_dir):
|
||||
input(f'{simul_dir} already exists ! Press Enter to delete...')
|
||||
shutil.rmtree(simul_dir)
|
||||
logging.debug(f'Options : {options}')
|
||||
generate_all(options)
|
||||
|
||||
|
||||
def parse_json(json_file):
|
||||
logging.info(f'Loading config from {json_file}')
|
||||
config = SimpleNamespace(**json.load(json_file))
|
||||
logging.debug(f'Config {config}')
|
||||
return config
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Try to load the config file
|
||||
if len(argv) > 2 and argv[1] == '-c' or argv[1] == '--config' or argv[1] == '-config':
|
||||
try:
|
||||
with open(argv[2]) as jsonfile:
|
||||
config = parse_json(jsonfile)
|
||||
handle_args(config)
|
||||
except FileNotFoundError:
|
||||
msg = f'The config file {argv[2]} does not exist!'
|
||||
logging.fatal(msg)
|
||||
raise FileNotFoundError(msg)
|
||||
else:
|
||||
# Run with command line arguments
|
||||
config = parse_command_line()
|
||||
handle_args(config)
|
59
sumo_project/templates/simul.netcfg
Normal file
59
sumo_project/templates/simul.netcfg
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/netconvertConfiguration.xsd">
|
||||
|
||||
<input>
|
||||
<type-files value="typemap/osmNetconvert.typ.xml,typemap/osmNetconvertUrbanDe.typ.xml,typemap/osmNetconvertPedestrians.typ.xml,typemap/osmNetconvertBicycle.typ.xml,typemap/osmBidiRailNetconvert.typ.xml" />
|
||||
<osm-files value="simul.raw.osm" />
|
||||
</input>
|
||||
|
||||
<output>
|
||||
<!--<output-prefix value="simul." />-->
|
||||
<output-file value="simul.net.xml"/>
|
||||
</output>
|
||||
|
||||
<tls_building>
|
||||
<tls.join value="true"/>
|
||||
<tls.guess-signals value="true"/>
|
||||
<tls.discard-simple value="true" />
|
||||
<tls.default-type value="actuated"/>
|
||||
<tls.min-dur value="5"/>
|
||||
<tls.max-dur value="30"/>
|
||||
</tls_building>
|
||||
|
||||
<ramp_guessing>
|
||||
<ramps.guess value="true"/>
|
||||
</ramp_guessing>
|
||||
|
||||
<junctions>
|
||||
<junctions.join value="true"/>
|
||||
</junctions>
|
||||
|
||||
<processing>
|
||||
<geometry.remove value="true"/>
|
||||
<roundabouts.guess value="true"/>
|
||||
|
||||
<!--
|
||||
<junctions.limit-turn-speed value="1"/>
|
||||
<crossings.guess value="true"/>
|
||||
<walkingareas value="false"/>
|
||||
<osm.elevation value="true"/>
|
||||
<no-turnarounds value="false"/>
|
||||
<no-turnarounds.tls value="true"/>
|
||||
<no-turnarounds.except-deadend value="true"/>
|
||||
<no-internal-links value="false"/>
|
||||
<ignore-errors value="false"/>
|
||||
<ignore-errors.connections value="false"/>
|
||||
<show-errors.connections-first-try value="true"/>-->
|
||||
</processing>
|
||||
|
||||
<building_defaults>
|
||||
<default.crossing-width value="2.0"/>
|
||||
</building_defaults>
|
||||
|
||||
<report>
|
||||
<verbose value="true"/>
|
||||
<log value="netconvert.log"/>
|
||||
</report>
|
||||
|
||||
</configuration>
|
24
sumo_project/templates/simul.polycfg
Normal file
24
sumo_project/templates/simul.polycfg
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/polyconvertConfiguration.xsd">
|
||||
|
||||
<input>
|
||||
<net-file value="simul.net.xml"/>
|
||||
<osm-files value="simul.raw.osm"/>
|
||||
<type-file value="typemap/osmPolyconvert.typ.xml"/>
|
||||
</input>
|
||||
|
||||
<output>
|
||||
<output-file value="simul.poly.xml"/>
|
||||
</output>
|
||||
|
||||
<processing>
|
||||
<ignore-errors value="true"/>
|
||||
</processing>
|
||||
|
||||
<report>
|
||||
<verbose value="true"/>
|
||||
<log value="polyconvert.log"/>
|
||||
</report>
|
||||
|
||||
</configuration>
|
30
sumo_project/templates/simul.sumocfg
Normal file
30
sumo_project/templates/simul.sumocfg
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/sumoConfiguration.xsd">
|
||||
|
||||
<input>
|
||||
<net-file value="simul.net.xml"/>
|
||||
<route-files value="simul.rou.xml"/>
|
||||
<additional-files value="simul.poly.xml"/>
|
||||
</input>
|
||||
|
||||
<processing>
|
||||
<lateral-resolution value="0.3"/>
|
||||
<ignore-junction-blocker value="60"/>
|
||||
<collision.action value="teleport"/>
|
||||
<time-to-teleport value="180"/>
|
||||
<max-depart-delay value="900"/>
|
||||
<time-to-impatience value="30"/>
|
||||
</processing>
|
||||
|
||||
<report>
|
||||
<verbose value="true" synonymes="v"/>
|
||||
<log value="sim.log" synonymes="l log-file"/>
|
||||
<duration-log.statistics value="true"/>
|
||||
</report>
|
||||
|
||||
<random_number>
|
||||
<seed value="1"/>
|
||||
</random_number>
|
||||
|
||||
</configuration>
|
@ -0,0 +1,7 @@
|
||||
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/types_file.xsd">
|
||||
<type id="railway.rail" oneway="false"/>
|
||||
<type id="railway.tram" oneway="false"/>
|
||||
<type id="railway.light_rail" oneway="false"/>
|
||||
<type id="railway.subway" oneway="false"/>
|
||||
<type id="railway.preserved" oneway="false"/>
|
||||
</types>
|
52
sumo_project/templates/typemap/osmNetconvert.typ.xml
Normal file
52
sumo_project/templates/typemap/osmNetconvert.typ.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/types_file.xsd">
|
||||
<type id="highway.motorway" numLanes="3" speed="44.44" priority="13" oneway="true" disallow="pedestrian bicycle moped rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.motorway_bridge" numLanes="3" speed="44.44" priority="13" oneway="true" disallow="pedestrian bicycle moped rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.motorway_tunnel" numLanes="3" speed="44.44" priority="13" oneway="true" disallow="pedestrian bicycle moped rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.motorway_link" numLanes="1" speed="22.22" priority="12" oneway="true" disallow="pedestrian bicycle moped rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.trunk" numLanes="2" speed="27.78" priority="11" oneway="false" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.trunk_bridge" numLanes="2" speed="27.78" priority="11" oneway="false" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.trunk_tunnel" numLanes="2" speed="27.78" priority="11" oneway="false" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.trunk_link" numLanes="1" speed="22.22" priority="10" oneway="false" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.primary" numLanes="2" speed="27.78" priority="9" oneway="false" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.primary_bridge" numLanes="2" speed="27.78" priority="9" oneway="false" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.primary_tunnel" numLanes="2" speed="27.78" priority="9" oneway="false" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.primary_link" numLanes="1" speed="22.22" priority="8" oneway="false" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.secondary" numLanes="2" speed="27.78" priority="7" oneway="false" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.secondary_bridge" numLanes="2" speed="27.78" priority="7" oneway="false" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.secondary_tunnel" numLanes="2" speed="27.78" priority="7" oneway="false" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.secondary_link" numLanes="1" speed="22.22" priority="6" oneway="false" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.tertiary" numLanes="1" speed="22.22" priority="6" oneway="false" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.tertiary_bridge" numLanes="1" speed="22.22" priority="6" oneway="false" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.tertiary_tunnel" numLanes="1" speed="22.22" priority="6" oneway="false" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.tertiary_link" numLanes="1" speed="22.22" priority="5" oneway="false" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.unclassified" numLanes="1" speed="13.89" priority="5" oneway="false" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.residential" numLanes="1" speed="13.89" priority="4" oneway="false" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.residential_bridge" numLanes="1" speed="13.89" priority="4" oneway="false" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.residential_tunnel" numLanes="1" speed="13.89" priority="4" oneway="false" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.living_street" numLanes="1" speed="2.78" priority="3" oneway="false" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.track" numLanes="1" speed="5.56" priority="1" oneway="false" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.unsurfaced" numLanes="1" speed="8.33" priority="1" oneway="false" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.services" numLanes="1" speed="8.33" priority="1" oneway="false" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.service" numLanes="1" speed="5.56" priority="1" oneway="false" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
|
||||
<!-- everything which serves mainly pedestrians is oneway because all current pedestrian models do not care about direction -->
|
||||
<type id="highway.footway" numLanes="1" speed="2.78" priority="1" oneway="true" width="2" allow="pedestrian"/>
|
||||
<type id="highway.pedestrian" numLanes="1" speed="2.78" priority="1" oneway="true" width="2" allow="pedestrian bicycle"/>
|
||||
<type id="highway.path" numLanes="1" speed="2.78" priority="1" oneway="true" width="2" allow="pedestrian bicycle"/>
|
||||
<type id="highway.bridleway" numLanes="1" speed="2.78" priority="1" oneway="true" width="2" allow="pedestrian bicycle"/>
|
||||
<type id="highway.cycleway" numLanes="1" speed="5.56" priority="1" oneway="false" width="2" allow="bicycle"/>
|
||||
<type id="highway.step" numLanes="1" speed="1.39" priority="1" oneway="true" width="2" allow="pedestrian"/>
|
||||
<type id="highway.steps" numLanes="1" speed="1.39" priority="1" oneway="true" width="2" allow="pedestrian"/>
|
||||
<type id="highway.stairs" numLanes="1" speed="1.39" priority="1" oneway="true" width="2" allow="pedestrian"/>
|
||||
|
||||
<type id="highway.bus_guideway" numLanes="1" speed="8.33" priority="1" oneway="true" allow="bus"/>
|
||||
<type id="highway.raceway" numLanes="2" speed="83.33" priority="14" oneway="false" allow="vip"/>
|
||||
<type id="highway.ford" numLanes="1" speed="2.78" priority="1" oneway="false" allow="army"/>
|
||||
|
||||
<type id="railway.rail" numLanes="1" speed="83.33" priority="15" oneway="true" allow="rail rail_electric"/>
|
||||
<type id="railway.tram" numLanes="1" speed="13.89" priority="15" oneway="true" allow="tram"/>
|
||||
<type id="railway.light_rail" numLanes="1" speed="27.78" priority="15" oneway="true" allow="rail_urban"/>
|
||||
<type id="railway.subway" numLanes="1" speed="27.78" priority="15" oneway="true" allow="rail_urban"/>
|
||||
<type id="railway.preserved" numLanes="1" speed="27.78" priority="15" oneway="true" allow="rail"/>
|
||||
|
||||
</types>
|
@ -0,0 +1,6 @@
|
||||
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/types_file.xsd">
|
||||
<type id="cycleway.lane" bikeLaneWidth="1.0" allow="bicycle"/>
|
||||
<type id="cycleway.opposite_lane" bikeLaneWidth="1.0" allow="bicycle"/>
|
||||
<type id="cycleway.track" bikeLaneWidth="1.5" allow="bicycle"/>
|
||||
<type id="cycleway.opposite_track" bikeLaneWidth="1.5" allow="bicycle"/>
|
||||
</types>
|
@ -0,0 +1,18 @@
|
||||
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/types_file.xsd">
|
||||
<type id="highway.primary" sidewalkWidth="2" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.primary_link" sidewalkWidth="2" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.primary_bridge" sidewalkWidth="2" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.primary_tunnel" disallow="pedestrian bicycle rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.secondary" sidewalkWidth="1.5" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.secondary_link" sidewalkWidth="1.5" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.secondary_bridge" sidewalkWidth="1.5" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.secondary_tunnel" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.tertiary" sidewalkWidth="1.5" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.tertiary_link" sidewalkWidth="1.5" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.tertiary_bridge" sidewalkWidth="1.5" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.tertiary_tunnel" disallow="pedestrian rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.unclassified" sidewalkWidth="1.5" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.residential" sidewalkWidth="1.5" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.residential_bridge" sidewalkWidth="1.5" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
<type id="highway.residential_tunnel" sidewalkWidth="1.5" disallow="rail rail_urban rail_electric tram ship"/>
|
||||
</types>
|
@ -0,0 +1,5 @@
|
||||
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/types_file.xsd">
|
||||
<type id="waterway.river" width="7" numLanes="1" speed="10" priority="13" oneway="false" allow="ship"/>
|
||||
<type id="waterway.canal" width="5" numLanes="1" speed="10" priority="13" oneway="false" allow="ship"/>
|
||||
<type id="route.ferry" width="5" numLanes="1" speed="10" priority="13" oneway="false" allow="ship"/>
|
||||
</types>
|
12
sumo_project/templates/typemap/osmNetconvertUrbanDe.typ.xml
Normal file
12
sumo_project/templates/typemap/osmNetconvertUrbanDe.typ.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<types xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://sumo.dlr.de/xsd/types_file.xsd">
|
||||
<type id="highway.motorway" speed="27.78"/>
|
||||
<type id="highway.motorway_link" speed="16.67"/>
|
||||
<type id="highway.trunk" speed="13.89"/>
|
||||
<type id="highway.trunk_link" speed="13.89"/>
|
||||
<type id="highway.primary" speed="13.89"/>
|
||||
<type id="highway.primary_link" speed="13.89"/>
|
||||
<type id="highway.secondary" speed="13.89"/>
|
||||
<type id="highway.secondary_link" speed="13.89"/>
|
||||
<type id="highway.tertiary" speed="13.89"/>
|
||||
<type id="highway.tertiary_link" speed="13.89"/>
|
||||
</types>
|
@ -0,0 +1,3 @@
|
||||
<polygonTypes>
|
||||
<polygonType id="boundary" name="boundary" color="1,0,0" layer="100" fill="false"/>
|
||||
</polygonTypes>
|
40
sumo_project/templates/typemap/osmPolyconvert.typ.xml
Normal file
40
sumo_project/templates/typemap/osmPolyconvert.typ.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<polygonTypes>
|
||||
<polygonType id="waterway" name="water" color=".71,.82,.82" layer="4" discard="true"/>
|
||||
<polygonType id="natural" name="natural" color=".55,.77,.42" layer="4" discard="true"/>
|
||||
<polygonType id="natural.water" name="water" color=".71,.82,.82" layer="4" discard="true"/>
|
||||
<polygonType id="natural.wetland" name="water" color=".71,.82,.82" layer="4" discard="true"/>
|
||||
<polygonType id="natural.wood" name="forest" color=".55,.77,.42" layer="4" discard="true"/>
|
||||
<polygonType id="natural.land" name="land" color=".98,.87,.46" layer="4" discard="true"/>
|
||||
|
||||
<polygonType id="landuse" name="landuse" color=".76,.76,.51" layer="3"/>
|
||||
<polygonType id="landuse.forest" name="forest" color=".55,.77,.42" layer="3"/>
|
||||
<polygonType id="landuse.park" name="park" color=".81,.96,.79" layer="3"/>
|
||||
<polygonType id="landuse.residential" name="residential" color=".92,.92,.89" layer="3"/>
|
||||
<polygonType id="landuse.commercial" name="commercial" color=".82,.82,.80" layer="3"/>
|
||||
<polygonType id="landuse.industrial" name="industrial" color=".82,.82,.80" layer="3"/>
|
||||
<polygonType id="landuse.military" name="military" color=".60,.60,.36" layer="3"/>
|
||||
<polygonType id="landuse.farm" name="farm" color=".95,.95,.80" layer="3"/>
|
||||
<polygonType id="landuse.greenfield" name="farm" color=".95,.95,.80" layer="3"/>
|
||||
<polygonType id="landuse.village_green" name="farm" color=".95,.95,.80" layer="3"/>
|
||||
|
||||
<polygonType id="tourism" name="tourism" color=".81,.96,.79" layer="2"/>
|
||||
<polygonType id="military" name="military" color=".60,.60,.36" layer="2"/>
|
||||
<polygonType id="sport" name="sport" color=".31,.90,.49" layer="2"/>
|
||||
<polygonType id="leisure" name="leisure" color=".81,.96,.79" layer="2"/>
|
||||
<polygonType id="leisure.park" name="tourism" color=".81,.96,.79" layer="2"/>
|
||||
<polygonType id="aeroway" name="aeroway" color=".50,.50,.50" layer="2"/>
|
||||
<polygonType id="aerialway" name="aerialway" color=".20,.20,.20" layer="2"/>
|
||||
|
||||
<polygonType id="shop" name="shop" color=".93,.78,1.0" layer="1"/>
|
||||
<polygonType id="historic" name="historic" color=".50,1.0,.50" layer="1"/>
|
||||
<polygonType id="man_made" name="building" color="1.0,.90,.90" layer="1"/>
|
||||
<polygonType id="building" name="building" color="1.0,.50,.50" layer="1"/>
|
||||
<polygonType id="amenity" name="amenity" color=".93,.78,.78" layer="1"/>
|
||||
<polygonType id="amenity.parking" name="parking" color=".72,.72,.70" layer="1"/>
|
||||
<polygonType id="power" name="power" color=".10,.10,.30" layer="1" discard="true"/>
|
||||
<polygonType id="highway" name="highway" color=".10,.10,.10" layer="1" discard="true"/>
|
||||
|
||||
<polygonType id="boundary" name="boundary" color="1.0,.10,.10" layer="0" fill="false"/>
|
||||
<polygonType id="admin_level" name="admin_level" color="1.0,.33,.33" layer="0" fill="false" discard="true"/>
|
||||
<polygonType id="place" name="admin_level" color="1.0,.33,.33" layer="0" fill="false" discard="true"/>
|
||||
</polygonTypes>
|
143
sumo_project/tests/configurator_tests.py
Normal file
143
sumo_project/tests/configurator_tests.py
Normal file
@ -0,0 +1,143 @@
|
||||
import io
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import configurator
|
||||
|
||||
# Absolute path of the directory the script is in
|
||||
SCRIPTDIR = os.path.dirname(__file__)
|
||||
|
||||
|
||||
class TemplateTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.sim_name = 'test_simulation'
|
||||
self.sim_path = '/test_simulation'
|
||||
self.log_path = '/test_simulation/log'
|
||||
|
||||
def test_load_netconvert_template(self):
|
||||
tree = configurator.load_netconvert_template('test.osm', 'test_simulation')
|
||||
self.assertEqual(tree.find('input/osm-files').get('value'), 'test.osm')
|
||||
self.assertEqual(tree.find('output/output-file').get('value'), f'{self.sim_name}.net.xml')
|
||||
self.assertEqual(tree.find('report/log').get('value'), f'{self.sim_name}.netconvert.log')
|
||||
|
||||
def test_load_sumoconfig_template_default(self):
|
||||
tree = configurator.load_sumoconfig_template(self.sim_name)
|
||||
self.assertEqual(tree.find('input/net-file').get('value'), f'{self.sim_name}.net.xml')
|
||||
self.assertEqual(tree.find('input/route-files').get('value'), f'{self.sim_name}.rou.xml')
|
||||
self.assertEqual(tree.find('report/log').get('value'), f'{self.sim_name}.log')
|
||||
|
||||
def test_load_sumoconfig_template_with_polygons(self):
|
||||
tree = configurator.load_sumoconfig_template(self.sim_name, generate_polygons=True)
|
||||
self.assertEqual(tree.find('input/net-file').get('value'), f'{self.sim_name}.net.xml')
|
||||
self.assertEqual(tree.find('input/route-files').get('value'), f'{self.sim_name}.rou.xml')
|
||||
self.assertEqual(tree.find('report/log').get('value'), f'{self.sim_name}.log')
|
||||
self.assertEqual(tree.find('input/additional-files').get('value'), f'{self.sim_name}.poly.xml')
|
||||
|
||||
def test_load_sumoconfig_template_with_routefiles(self):
|
||||
routefiles = (f'{self.sim_name}.bus.rou.xml', f'{self.sim_name}.passenger.rou.xml')
|
||||
tree = configurator.load_sumoconfig_template(self.sim_name, routefiles)
|
||||
self.assertEqual(tree.find('input/net-file').get('value'), f'{self.sim_name}.net.xml')
|
||||
self.assertEqual(tree.find('input/route-files').get('value'), ','.join(routefiles))
|
||||
self.assertEqual(tree.find('report/log').get('value'), f'{self.sim_name}.log')
|
||||
|
||||
def test_load_sumoconfig_template_with_seed(self):
|
||||
routefiles = (f'{self.sim_name}.bus.rou.xml', f'{self.sim_name}.passenger.rou.xml')
|
||||
tree = configurator.load_sumoconfig_template(self.sim_name, routefiles, seed=42)
|
||||
self.assertEqual(tree.find('random_number/seed').get('value'), 42)
|
||||
|
||||
def test_load_polyconvert_template(self):
|
||||
tree = configurator.load_polyconvert_template(
|
||||
osm_file=f'{self.sim_name}.osm',
|
||||
type_file='typemap/test.typ.xml',
|
||||
scenario_name=f'{self.sim_name}'
|
||||
)
|
||||
self.assertEqual(tree.find('input/osm-files').get('value'), f'{self.sim_name}.osm')
|
||||
self.assertEqual(tree.find('input/net-file').get('value'), f'{self.sim_name}.net.xml')
|
||||
self.assertEqual(tree.find('input/type-file').get('value'), 'typemap/test.typ.xml')
|
||||
self.assertEqual(tree.find('output/output-file').get('value'), f'{self.sim_name}.poly.xml')
|
||||
self.assertEqual(tree.find('report/log').get('value'), f'{self.sim_name}.polyconvert.log')
|
||||
|
||||
|
||||
class GenerationTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.base_path = tempfile.mkdtemp()
|
||||
self.sim_name = 'test_simulation'
|
||||
self.sim_path = os.path.join(self.base_path, self.sim_name)
|
||||
self.log_path = os.path.join(self.sim_name, 'log')
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.base_path)
|
||||
|
||||
def test_generate_scenario(self):
|
||||
osm_file = os.path.join(SCRIPTDIR, 'sample.osm')
|
||||
configurator.generate_scenario(osm_file, self.sim_path, self.sim_name, generate_polygons=False)
|
||||
self.assert_is_file(os.path.join(self.sim_path, f'{self.sim_name}.net.xml'))
|
||||
|
||||
def test_generate_scenario_with_polygons(self):
|
||||
osm_file = os.path.join(SCRIPTDIR, 'sample.osm')
|
||||
configurator.generate_scenario(osm_file, self.sim_path, self.sim_name, generate_polygons=True)
|
||||
self.assert_is_dir(self.sim_path)
|
||||
generated_files = [
|
||||
f'{self.sim_name}.poly.xml',
|
||||
f'{self.sim_name}.net.xml'
|
||||
]
|
||||
for f in generated_files:
|
||||
self.assert_is_file(os.path.join(self.sim_path, f))
|
||||
|
||||
def test_generate_mobility(self):
|
||||
# The scenario must be generated before the mobility
|
||||
osm_file = os.path.join(SCRIPTDIR, 'sample.osm')
|
||||
trips_file = os.path.join(self.sim_path, f'{self.sim_name}.trips.xml')
|
||||
configurator.generate_scenario(osm_file, self.sim_path, self.sim_name)
|
||||
classes = {'passenger': 10, 'truck': 1}
|
||||
routefiles = configurator.generate_mobility(self.sim_path, self.sim_name, vclasses=classes, end_time=200)
|
||||
|
||||
self.assert_is_file(trips_file)
|
||||
for f in routefiles:
|
||||
self.assert_is_file(os.path.join(self.sim_path, f))
|
||||
|
||||
def assert_exists(self, path):
|
||||
self.assertTrue(os.path.exists(path), msg=f'{path} does not exist')
|
||||
|
||||
def assert_is_file(self, path):
|
||||
self.assert_exists(path)
|
||||
self.assertTrue(os.path.isfile(path), msg=f'{path} is not a file')
|
||||
|
||||
def assert_is_dir(self, path):
|
||||
self.assert_exists(path)
|
||||
self.assertTrue(os.path.isdir(path), msg=f'{path} is not a directory')
|
||||
|
||||
|
||||
class InputTests(unittest.TestCase):
|
||||
def test_commandline(self):
|
||||
options = ['--name', 'test-config', '--path', '/some/path', '--vclass', 'passenger=10', 'truck=1', '--', 'test.osm']
|
||||
actual_conf = configurator.parse_command_line(options)
|
||||
self.assertEqual(actual_conf.name, 'test-config')
|
||||
self.assertEqual(actual_conf.osmfile, 'test.osm')
|
||||
self.assertEqual(actual_conf.path, '/some/path')
|
||||
self.assertEqual(actual_conf.vclasses, {'passenger': '10', 'truck': '1'})
|
||||
|
||||
def test_from_config_file(self):
|
||||
options = """
|
||||
{
|
||||
"name": "test-config",
|
||||
"path": "/some/path",
|
||||
"vclasses": {
|
||||
"passenger": 10,
|
||||
"truck": 1
|
||||
},
|
||||
"osmfile": "test.osm"
|
||||
}
|
||||
"""
|
||||
actual_conf = configurator.parse_json(io.StringIO(options))
|
||||
self.assertEqual(actual_conf.name, 'test-config')
|
||||
self.assertEqual(actual_conf.osmfile, 'test.osm')
|
||||
self.assertEqual(actual_conf.path, '/some/path')
|
||||
self.assertEqual(actual_conf.vclasses, {'passenger': 10, 'truck': 1})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
49966
sumo_project/tests/sample.osm
Normal file
49966
sumo_project/tests/sample.osm
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user