Skip to main content

MOSSTOOL Demos

To run the examples provided in the mosstool repository, you need to clone the repository and install the required dependencies.

git clone https://github.com/tsinghua-fib-lab/mosstool.git
cd mosstool
poetry install

Map-Building

Map-Builder

Build Map

python ./examples/build_map.py
Load net files

net is basic road net file for builder to build a map, either a GeoJSON file or a Map file is available. aois, pois and public_transport are optional input arguments for builder.

with open("data/geojson/net.geojson", "r") as f:
net = geojson.load(f)
Initialize the builder and build the map
builder = Builder(
net=net,
proj_str="+proj=tmerc +lat_0=33.9 +lon_0=116.4",
gen_sidewalk_speed_limit=50 / 3.6, # if the speed limit of one lane is lower than this, then sidewalks are available for thie lane
road_expand_mode="M", # in raw GeoJSON road net file, only road has a centerline geometry shape and lanes are expanded from road. "M" means that the lanes are expanded on both sides of the road centerline.
)
m = builder.build("test")
Visualize the generated map
pb = dict2pb(m, Map())
with open("data/temp/map.pb", "wb") as f: # dump as binary files
f.write(pb.SerializeToString())
vis_map = VisMap(pb) # visualization
fc = vis_map.feature_collection
with open("data/temp/map.geojson", "w") as f:
geojson.dump(fc, f, ensure_ascii=False, indent=2)
deck = vis_map.visualize()
deck.to_html("data/temp/map.html")

Add Public Transport to Map

python ./examples/add_pt_to_map.py
Load public transport JSON data
with open(PT_PATH, "r") as f:
pt = json.load(f)
Load Map file to add PTs

Adding PTs when generating a new map with a GeoJSON file provided is also supported, here we use generated map as an example.

with open(ORIG_MAP_PATH, "rb") as f:
net = Map()
net.ParseFromString(f.read())
Initialize the builder and build the map
builder = Builder(
net=net,
public_transport=pt,
proj_str=net.header.projection,
gen_sidewalk_speed_limit=50 / 3.6,
aoi_mode="append",
road_expand_mode="M",
)
m_dict = builder.build("test")
Save the dict and Map files
pickle.dump(m_dict, open(MAP_PKL_PATH, "wb"))
with open(MAP_PB_PATH, "wb") as f:
pb = dict2pb(m_dict, Map())
f.write(pb.SerializeToString())

Post Process of Adding Public Transport to Map

python ./examples/add_pt_post_process.py
Activate routing service
# pre-route
# run: ./routing -map ./data/temp/srt.raw_pt_map.pb
Load the dict and Map files from add_pt_to_map.py
LISTENING_HOST = "http://localhost:52101"
MAP_PKL_PATH = "./data/temp/srt.raw_pt_map.pkl"
PT_MAP_PATH = f"./data/temp/srt.map_with_pt.pb"
m_dict = pickle.load(open(MAP_PKL_PATH, "rb"))
Post process and save the Map file
new_m = public_transport_process(m_dict, LISTENING_HOST)
pb = dict2pb(new_m, Map())
with open(PT_MAP_PATH, "wb") as f:
f.write(pb.SerializeToString())

Split Map into Multiple Maps

split maps for more convenient manual adjustments with Web Tools/Map Editor.

python ./examples/split_map.py
Load the bounding boxes and the map
with open("data/geojson/split_box.geojson", "r") as f:
bbox = geojson.load(f)
with open("data/map/m.pb", "rb") as f:
pb = Map()
pb.ParseFromString(f.read())
Split the map into each boxes
split_map(
geo_data=bbox,
map=pb,
output_path="data/temp",
)

Merge Split Maps

python ./examples/merge_map.py
Load all maps
maps = []
for i in [0, 1]:
with open(f"data/temp/test_{i}.pb", "rb") as f:
pb = Map()
pb.ParseFromString(f.read())
maps.append(pb)
Merge and rebuild the map

rebuild map with basic lanes, roads, junctions, AOIs and POIs in merged_pb. Extra AOIs and POIs can be added with aoi_mode="append", or use aoi_mode="overwrite" to replace original AOIs and POIs with input AOIs and POIs.

merged_pb = merge_map(
partial_maps=maps,
output_path="data/temp/merged_m.pb",
)
if REBUILD_MAP:
builder = Builder(
net=merged_pb,
proj_str=merged_pb.header.projection,
gen_sidewalk_speed_limit=50 / 3.6,
aoi_mode="append", # keep AOIs in merged_pb
road_expand_mode="M",
)
rebuild_m = builder.build(merged_pb.header.name)
rebuild_pb = dict2pb(rebuild_m, Map())
with open("data/temp/rebuild_merged_m.pb", "wb") as f:
f.write(rebuild_pb.SerializeToString())

Visualization Map from MongoDB

python ./examples/vis_map.py
Connect to MongoDB and download the map
client = MongoClient(os.environ["MONGO_URI"])
coll = client[os.environ["MAP_DB"]][os.environ["MAP_COLL"]]
pb = Map()
pb = coll2pb(coll, pb)
Save visualization file
m = VisMap(pb)
print(m.lane_shapely_xys[0])
deck = m.visualize()
deck.to_html("map.html") # open with browser

From OpenStreetMap

Fetch RoadNet from OSM

python ./examples/map_osm2geojson.py
Set the bounding box
bbox = {
"max_lat": 40.1,
"min_lat": 39.9,
"min_lon": 116.5,
"max_lon": 116.6,
}
Fetch raw data and process

proj_str is set to calculate the position relations more precisely between map items.

# load configs
rn = RoadNet(
proj_str="+proj=tmerc +lat_0=39.90611 +lon_0=116.3911",
max_latitude=bbox["max_lat"],
min_latitude=bbox["min_lat"],
max_longitude=bbox["max_lon"],
min_longitude=bbox["min_lon"],
)

path = "cache/topo.geojson"
rn.create_road_net(path)

Fetch AOI from OSM

python ./examples/map_aoi_geojson.py
Set the bounding box
# Beijing
bbox = {
"max_lat": 40.20836867760951,
"min_lat": 39.69203625898142,
"min_lon": 116.12174658204533,
"max_lon": 116.65141646506795,
}
Fetch raw data and process

proj_str is set to calculate the position relations more precisely between map items.

building = Building(
proj_str="+proj=tmerc +lat_0=39.90611 +lon_0=116.3911",
max_latitude=bbox["max_lat"],
min_latitude=bbox["min_lat"],
max_longitude=bbox["max_lon"],
min_longitude=bbox["min_lon"],
)
path = "data/temp/aois.geojson"
aois = building.create_building(output_path=path)

Fetch POI from OSM

python ./examples/map_poi_geojson.py
Set the bounding box
# Beijing
bbox = {
"max_lat": 40.20836867760951,
"min_lat": 39.69203625898142,
"min_lon": 116.12174658204533,
"max_lon": 116.65141646506795,
}
Fetch raw data and process
pois = PointOfInterest(
max_latitude=bbox["max_lat"],
min_latitude=bbox["min_lat"],
max_longitude=bbox["max_lon"],
min_longitude=bbox["min_lon"],
)
path = "data/temp/pois.geojson"
pois = pois.create_pois(output_path=path)

From SUMO

Convert Map

python ./examples/sumo_map_convert.py
Initialize the MapConverter and convert the map
s2m = MapConverter(
net_path="data/sumo/shenzhen.net.xml",
traffic_light_path="data/sumo/trafficlight.xml",
poly_path="data/sumo/poly.xml",
)
m = s2m.convert_map()
Visualization
pb = dict2pb(m, Map())
with open("data/temp/sumo_map.pb", "wb") as f:
f.write(pb.SerializeToString())
vis_map = VisMap(pb)
fc = vis_map.feature_collection
deck = vis_map.visualize()
deck.to_html("data/temp/sumo_map.html")

Trip-Generation

Trip-Generators

Generate OD-Matrix with Gravity Model

python ./examples/gravity.py
Initialize the GravityGenerator
gravity_generator = GravityGenerator(
Lambda=0.2,
Alpha=0.5,
Beta=0.5,
Gamma=0.5
)
Load the area data
area = gpd.read_file("data/gravitygenerator/Beijing-shp/beijing.shp")
Generate the O-D matrix
od_matrix = gravity_generator.generate(pops)

Generate Random Trips

python ./examples/random_persons.py
Load the map and person template

template is a Person object with preset attributes, the generated trips are based on the template.

with open("data/temp/map.pb", "rb") as f:
m = Map()
m.ParseFromString(f.read())
template = Person()
template.CopyFrom(DEFAULT_PERSON)
template.vehicle_attribute.model = "normal"
template.pedestrian_attribute.model = "normal"
Generate random persons
persons = rg.uniform(
num=100,
first_departure_time_range=(8 * 3600, 9 * 3600),
schedule_interval_range=(5 * 60, 10 * 60),
start_id=0,
)
Fill schedules of persons with routing service
# pre-route
# run: ./routing -map data/temp/map.pb
client = RoutingClient("http://localhost:52101")
ok_persons = []
for p in persons:
p = await pre_route(client, p)
if len(p.schedules) > 0 and len(p.schedules[0].trips) > 0:
ok_persons.append(p)
print(ok_persons)
print("final length: ", len(ok_persons))
pb = Persons(persons=ok_persons)
with open("data/temp/persons.json", "w") as f:
f.write(pb2json(pb))

Generate Trips with O-D Matrix from AigcGenerator

python ./examples/trip_generate_aigc_od.py
Initialize the AigcGenerator and load the area data
# Initialize the generator
aigc_generator = AigcGenerator()
aigc_generator.set_satetoken(YOUR_ACCESS_TOKEN)
area = gpd.read_file("data/gravitygenerator/Beijing-shp/beijing.shp")
aigc_generator.load_area(area)
# Generate the OD matrix
od_matrix = aigc_generator.generate()
Initialize the TripGenerator
with open("data/gravitygenerator/beijing_map.pb", "rb") as f:
m = Map()
m.ParseFromString(f.read())
tg = TripGenerator(
m=m,
)
Generate persons with O-D matrix
od_persons = tg.generate_persons(
od_matrix=od_matrix,
departure_time_curve=[
1,1,1,1,2,3,
4,5,6,5,4,0.5,
1,1,0.5,1,1,0.5,
1,1,0.5,1,1,0.5,
],
areas=area,
agent_num=10000,
seed=0,
)
pb = Persons(persons=od_persons)
with open("data/temp/beijing_OD_person.pb", "wb") as f:
f.write(pb.SerializeToString())
Fill schedules of persons with routing service
# The generated trip of the person is not guaranteed to be reachable in the map. Preroute is required.
# pre-route
# run: ./routing -map data/gravitygenerator/beijing_map.pb
client = RoutingClient("http://localhost:52101")
ok_persons = []
for p in od_persons:
p = await pre_route(client, p)
if len(p.schedules) > 0 and len(p.schedules[0].trips) > 0:
ok_persons.append(p)
print(len(ok_persons))
pb = Persons(persons=ok_persons)
with open("data/temp/beijing_OD_ok_person.pb", "wb") as f:
f.write(pb.SerializeToString())

Generate Trips with O-D Matrix from GravityGenerator

python ./examples/trip_generate_gravity_od.py
Initialize the GravityGenerator and load the area and pop data
# Initialize the gravity generator
gravity_generator = GravityGenerator(Lambda=0.2, Alpha=0.5, Beta=0.5, Gamma=0.5)
# Load the area data
area = gpd.read_file("data/gravitygenerator/Beijing-shp/beijing.shp")
pops = np.load("data/gravitygenerator/worldpop.npy")[:, 0]
gravity_generator.load_area(area)
# Generate the OD matrix
od_matrix = gravity_generator.generate(pops)
Initialize the TripGenerator
with open("data/gravitygenerator/beijing_map.pb", "rb") as f:
m = Map()
m.ParseFromString(f.read())
tg = TripGenerator(
m=m,
)
Generate persons with O-D matrix
od_persons = tg.generate_persons(
od_matrix=od_matrix,
departure_time_curve=[
1,1,1,1,2,3,
4,5,6,5,4,0.5,
1,1,0.5,1,1,0.5,
1,1,0.5,1,1,0.5,
],
areas=area,
agent_num=10000,
seed=0,
)
pb = Persons(persons=od_persons)
with open("data/temp/beijing_OD_person.pb", "wb") as f:
f.write(pb.SerializeToString())
Fill schedules of persons with routing service
# The generated trip of the person is not guaranteed to be reachable in the map. Preroute is required.
# pre-route
# run: ./routing -map data/gravitygenerator/beijing_map.pb
client = RoutingClient("http://localhost:52101")
ok_persons = []
for p in od_persons:
p = await pre_route(client, p)
if len(p.schedules) > 0 and len(p.schedules[0].trips) > 0:
ok_persons.append(p)
print(len(ok_persons))
pb = Persons(persons=ok_persons)
with open("data/temp/beijing_OD_ok_person.pb", "wb") as f:
f.write(pb.SerializeToString())

Generate Public Transport drivers

python ./examples/gen_pt_drivers.py
Initialize the TripGenerator
# map from `./examples/add_pt_to_map.py` and `./examples/add_pt_post_process.py`
with open("data/temp/srt.map_with_pt.pb", "rb") as f:
m = Map()
m.ParseFromString(f.read())
tg = TripGenerator(
m=m,
)
Generate the drivers
bus_drivers = tg.generate_public_transport_drivers()
persons_output_path = "data/temp/bus_drivers.pb"
pb = Persons(persons=bus_drivers)
with open(persons_output_path, "wb") as f:
f.write(pb.SerializeToString())

Static Traffic Assignment

Extra conditions are required for STA, if you want to make all driving trips to be assigned to the routes generated by the STA, you need to make sure that the following conditions are met:

  1. All trips' departure_time should be set.
  2. There must be lane connections between any in_road and out_road within any junctions.

You should also check the statistics returned by the sta.run method to see if there are any invalid/ignored trips.

python ./examples/sta.py
Load map and trip data
from mosstool.trip.gmns import STA
from mosstool.type import Map, Persons

m = Map() # load your map
persons = Persons() # load your persons
Run STA
sta = STA(m, "data/temp/sta")
ps, stat = sta.run(persons, reset_routes=True)
print(f"STA finished, {stat}")
# Save the result
with open("data/temp/sta/sta_persons.pb", "wb") as f:
f.write(ps.SerializeToString())

From SUMO

Route Converter

python ./examples/sumo_route_convert.py
Initialize the MapConverter and convert the map
s2m = MapConverter(
net_path="data/sumo/shenzhen.net.xml",
traffic_light_path="data/sumo/trafficlight.xml",
poly_path="data/sumo/poly.xml",
)
m = s2m.convert_map()
id2uid = s2m.get_sumo_id_mappings()
map_pb = dict2pb(m, Map())
Initialize the RouteConverter and convert the route
s2r = RouteConverter(
converted_map=map_pb,
sumo_id_mappings=id2uid,
route_path="./data/sumo/trips.xml",
additional_path="./data/sumo/additional.xml",
)
r = s2r.convert_route()
pb = dict2pb(r, Persons())
with open("data/temp/sumo_persons.pb", "wb") as f:
f.write(pb.SerializeToString())