Skip to content

SUMO

pip install "duckosm[sumo]"          # brings SUMO's netconvert
duckosm sumo monaco.duckdb           # -> sumo/monaco.net.xml

duckOSM writes the network in SUMO's plain-XML format and runs SUMO's own netconvert to build the .net.xml, in metres in the UTM zone of the data. Every SUMO edge id is the duckOSM edge_id, and each junction allows only the turns in edge_graph, so turn restrictions are kept. netconvert leaves out a few U-turns it can't build (Monaco: 37 of 1,628); it never adds a turn.

In a driving network each edge's lanes come from driving.lane_profile (design): one SUMO lane per lane, with its vehicle classes (a bus lane bus, a bike lane bicycle, a shared one bus bicycle, a car lane every road vehicle) and its width, numbered from the right as SUMO does. duckosm.sumo_compare.lane_diff(db, osm_file, work_dir) builds the same area with SUMO's own OSM import too and lists, per OSM way and direction, every lane difference with its cause (an override, the contraflow bus lane, a default where no lanes tag is ...).

Painted turn arrows (turn:lanes) are not in this network: SUMO's plain XML has no field for them. The GMNS export applies them (GMNS tables, lane_check): where a link's arrows fit its exits, read left to right by order, netconvert's OSM import connects that link's lanes by them, and those lane connections go into the plain XML.

A walking network is for pedestrians only (allow="pedestrian", with SUMO walking areas at the junctions), a cycling network for bicycles only. A driving network allows every vehicle class. A walking or cycling edge is one path lane, 2 m or 1.5 m wide (not the road's lane count at 3.2 m). A one-way road's lanes are centred on its line (spreadType="center"); the two directions of a two-way road lie either side of it.

Option Does
--no-netconvert only write the plain-XML inputs (.nod.xml, .edg.xml, .con.xml)
--no-connections let netconvert work out the turns itself
-c my.netccfg your own netconvert settings instead of duckOSM's (it still sets the input and output files)
-m walking another mode (default driving)
--out-dir, --name output folder (default sumo) and file name (default: the db's name)
import duckdb
from duckosm import to_sumo
con = duckdb.connect("monaco.duckdb", read_only=True)
out = to_sumo(con, "sumo/")                                  # out["net"] = "sumo/network.net.xml"
to_sumo(con, "sumo/", config={"junctions.join": "true"})     # change one netconvert option
to_sumo(con, "sumo/", edge_attrs={eid: {"numLanes": 2, "width": 3.1}})   # your own lanes and widths per edge (id as number or text)

Joining close junctions (junctions.join, off by default). A crossroads mapped as two or three close nodes becomes one junction. netconvert reads the turns (.con.xml) before it joins, and silently drops every turn onto an edge the join swallows (the short edge between two joined nodes): a road whose straight or left turn ran over that edge lost it. So when joining is on, duckOSM asks netconvert which nodes it joins (a first run with --junctions.join-output), merges those nodes itself (cluster_<ids>, as netconvert names them), writes every legal path through the swallowed edges as one turn (never back through a node it passed) and runs netconvert again without joining. Turn restrictions still hold: a path is legal only if each of its steps is in edge_graph.