Store the drawing order¶
pip install "duckosm[levels]"
duckosm levels monaco.duckdb # the level area monaco.levels/, solved, written into the file
duckosm levels-edit monaco.duckdb # optional: fix places by hand, with mapstyle's base map and every road; each Apply writes into the file
When roads cross or meet, a map must decide which is painted over which: a bridge over the road under it, a street over its sidewalk, a junction without a ring between the roads that meet. The map does it with two numbers for every road: a casing number (for the outline, in three parts: start, main, end) and a fill number (for the colour). The casings and fills are painted number by number, lowest first, and at each number all casings before all fills. 0 is the ground.
duckosm build hands the roads to roadstyle as its last step, which works them out exactly as for any roadstyle map, and stores the result in the schema
visualization of the file, so that a map or another tool reads it instead of computing it again. The file is always current: a rebuild keeps your edits (they are tables in the file) and solves again with them.
For a big area the step takes long: options.levels: false (build --no-levels) skips it, and duckosm levels makes it later.
The level area¶
The roads of all the modes together (edges and private_edges of driving, walking and cycling) go into one level area, a folder next to the file (monaco.levels/, or --area DIR):
| File | Whose | Holds |
|---|---|---|
roads.parquet, pairs.csv |
made by each run | the solver's input: one row per road, the pairs of roads that cross or meet |
edits.csv, heads.csv, caps.csv |
yours; a copy of the tables in the file | your changes: pairs added or switched off, head lengths and end shapes per road end. The tables are the master: each run writes these files from them |
levels.csv, levels_info.json |
made by each solve | the result, and what the solver says about it |
area.json |
made once | the file the area belongs to |
Your edits are in the file itself (visualization.level_*, below): a rebuild carries them over, and the folder is only a work area. A file of the folder that differs from the table is kept as *.csv.before when it is written again. A row that names a road that is not in the file any more is ignored with a warning and kept in visualization.level_unmatched; the next build tries it again. How the solver decides, and the editor:
Which road is on top.
What it writes¶
| Table | Holds |
|---|---|
visualization.edge_levels |
one row for every edge_id: casing_start, casing_level, casing_end, fill_level, and its ends as drawn: head_start_m, head_end_m, cap_start, cap_end |
visualization.edge_levels_meta |
one row: where it came from (the area), the number of edges and a hash of their ids, the roadstyle version, the time |
visualization.level_edits, level_heads, level_caps |
your edits.csv, heads.csv and caps.csv, copied at each solve, so the database holds everything. A new area folder gets them back from here (needs a roadstyle that has restore_area_inputs) |
Both tables are replaced at each solve, by duckosm levels and by the editor. A stack of roads that cannot all be kept is counted in the message, never hidden.
Read it¶
import duckdb, roadstyle as rs
from duckosm.levels import load_roads
con = duckdb.connect("monaco.duckdb", read_only=True)
levels = rs.load_area_levels(con, load_roads("monaco.duckdb"))
load_area_levels checks that the edges are those the numbers were solved for, and stops with a message if not. It never recomputes. After a rebuild of the file, run duckosm levels again.
Options: duckosm levels.