roadstyle 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. roadstyle-0.2.0/LICENSE +21 -0
  2. roadstyle-0.2.0/PKG-INFO +406 -0
  3. roadstyle-0.2.0/README.md +348 -0
  4. roadstyle-0.2.0/pyproject.toml +86 -0
  5. roadstyle-0.2.0/setup.cfg +4 -0
  6. roadstyle-0.2.0/src/roadstyle/__init__.py +124 -0
  7. roadstyle-0.2.0/src/roadstyle/_settings.py +182 -0
  8. roadstyle-0.2.0/src/roadstyle/basemaps.py +113 -0
  9. roadstyle-0.2.0/src/roadstyle/cli.py +169 -0
  10. roadstyle-0.2.0/src/roadstyle/colors.py +117 -0
  11. roadstyle-0.2.0/src/roadstyle/config.py +99 -0
  12. roadstyle-0.2.0/src/roadstyle/controls.py +126 -0
  13. roadstyle-0.2.0/src/roadstyle/data/defaults.json +629 -0
  14. roadstyle-0.2.0/src/roadstyle/edges.py +295 -0
  15. roadstyle-0.2.0/src/roadstyle/emit.py +247 -0
  16. roadstyle-0.2.0/src/roadstyle/filters.py +54 -0
  17. roadstyle-0.2.0/src/roadstyle/interactive.py +186 -0
  18. roadstyle-0.2.0/src/roadstyle/legend.py +110 -0
  19. roadstyle-0.2.0/src/roadstyle/overlays.py +82 -0
  20. roadstyle-0.2.0/src/roadstyle/palettes.py +172 -0
  21. roadstyle-0.2.0/src/roadstyle/py.typed +0 -0
  22. roadstyle-0.2.0/src/roadstyle/render.py +141 -0
  23. roadstyle-0.2.0/src/roadstyle/render_folium.py +116 -0
  24. roadstyle-0.2.0/src/roadstyle/render_lonboard.py +115 -0
  25. roadstyle-0.2.0/src/roadstyle/render_web.py +1277 -0
  26. roadstyle-0.2.0/src/roadstyle/snapshot.py +102 -0
  27. roadstyle-0.2.0/src/roadstyle/static/roadstyle.css +147 -0
  28. roadstyle-0.2.0/src/roadstyle/static/roadstyle.js +608 -0
  29. roadstyle-0.2.0/src/roadstyle/static/web_template.html +594 -0
  30. roadstyle-0.2.0/src/roadstyle/style.py +96 -0
  31. roadstyle-0.2.0/src/roadstyle/stylers.py +472 -0
  32. roadstyle-0.2.0/src/roadstyle/tiles.py +200 -0
  33. roadstyle-0.2.0/src/roadstyle/validate.py +62 -0
  34. roadstyle-0.2.0/src/roadstyle/vendor/maplibre-gl.css +1 -0
  35. roadstyle-0.2.0/src/roadstyle/vendor/maplibre-gl.js +59 -0
  36. roadstyle-0.2.0/src/roadstyle/vendor/pmtiles.js +1738 -0
  37. roadstyle-0.2.0/src/roadstyle.egg-info/PKG-INFO +406 -0
  38. roadstyle-0.2.0/src/roadstyle.egg-info/SOURCES.txt +54 -0
  39. roadstyle-0.2.0/src/roadstyle.egg-info/dependency_links.txt +1 -0
  40. roadstyle-0.2.0/src/roadstyle.egg-info/entry_points.txt +2 -0
  41. roadstyle-0.2.0/src/roadstyle.egg-info/requires.txt +40 -0
  42. roadstyle-0.2.0/src/roadstyle.egg-info/top_level.txt +1 -0
  43. roadstyle-0.2.0/tests/test_cli.py +69 -0
  44. roadstyle-0.2.0/tests/test_color_table.py +59 -0
  45. roadstyle-0.2.0/tests/test_edges.py +143 -0
  46. roadstyle-0.2.0/tests/test_emit.py +225 -0
  47. roadstyle-0.2.0/tests/test_legend.py +62 -0
  48. roadstyle-0.2.0/tests/test_palette_io.py +65 -0
  49. roadstyle-0.2.0/tests/test_render.py +69 -0
  50. roadstyle-0.2.0/tests/test_render_datadriven.py +90 -0
  51. roadstyle-0.2.0/tests/test_render_web.py +687 -0
  52. roadstyle-0.2.0/tests/test_settings_overrides.py +121 -0
  53. roadstyle-0.2.0/tests/test_style.py +88 -0
  54. roadstyle-0.2.0/tests/test_stylers.py +82 -0
  55. roadstyle-0.2.0/tests/test_tiles.py +148 -0
  56. roadstyle-0.2.0/tests/test_web_smoke.py +103 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kaveh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,406 @@
1
+ Metadata-Version: 2.4
2
+ Name: roadstyle
3
+ Version: 0.2.0
4
+ Summary: Opinionated OSM-theme road/edge map styling for folium, lonboard & the web (data-driven palettes, casing, themes)
5
+ Author: Kaveh
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Khoshkhah/roadstyle
8
+ Project-URL: Documentation, https://khoshkhah.github.io/roadstyle/
9
+ Project-URL: Source, https://github.com/Khoshkhah/roadstyle
10
+ Project-URL: Changelog, https://github.com/Khoshkhah/roadstyle/blob/main/CHANGELOG.md
11
+ Keywords: openstreetmap,osm,roads,cartography,map,visualization,folium,lonboard,geopandas,leaflet,maplibre
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: GIS
21
+ Classifier: Topic :: Scientific/Engineering :: Visualization
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: geopandas
26
+ Requires-Dist: shapely
27
+ Requires-Dist: folium
28
+ Requires-Dist: branca
29
+ Provides-Extra: numeric
30
+ Requires-Dist: mapclassify; extra == "numeric"
31
+ Requires-Dist: matplotlib; extra == "numeric"
32
+ Provides-Extra: basemaps
33
+ Requires-Dist: xyzservices; extra == "basemaps"
34
+ Provides-Extra: lonboard
35
+ Requires-Dist: lonboard; extra == "lonboard"
36
+ Provides-Extra: duckdb
37
+ Requires-Dist: duckdb; extra == "duckdb"
38
+ Provides-Extra: arrow
39
+ Requires-Dist: pyarrow; extra == "arrow"
40
+ Provides-Extra: tiles
41
+ Requires-Dist: mapbox-vector-tile; extra == "tiles"
42
+ Requires-Dist: pmtiles; extra == "tiles"
43
+ Provides-Extra: docs
44
+ Requires-Dist: mkdocs-material; extra == "docs"
45
+ Provides-Extra: dev
46
+ Requires-Dist: pytest; extra == "dev"
47
+ Requires-Dist: ruff; extra == "dev"
48
+ Requires-Dist: mypy; extra == "dev"
49
+ Requires-Dist: lonboard; extra == "dev"
50
+ Requires-Dist: mapclassify; extra == "dev"
51
+ Requires-Dist: matplotlib; extra == "dev"
52
+ Requires-Dist: xyzservices; extra == "dev"
53
+ Requires-Dist: duckdb; extra == "dev"
54
+ Requires-Dist: pyarrow; extra == "dev"
55
+ Requires-Dist: mapbox-vector-tile; extra == "dev"
56
+ Requires-Dist: pmtiles; extra == "dev"
57
+ Dynamic: license-file
58
+
59
+ # roadstyle
60
+
61
+ [![Tests](https://github.com/Khoshkhah/roadstyle/actions/workflows/test.yml/badge.svg)](https://github.com/Khoshkhah/roadstyle/actions/workflows/test.yml)
62
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
63
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
64
+
65
+ Turn a GeoDataFrame of road edges into a **styled, interactive, self-contained map** — proper
66
+ road cartography (the casing + fill "geometry sandwich", per-zoom widths, street names, one-way
67
+ arrows, tunnel/bridge grade separation, optional 3D bridge decks) in one offline HTML file, with
68
+ a scriptable JavaScript API.
69
+
70
+ ![3D bridges over Södermalm](docs/img/gallery/bridges_3d.png)
71
+
72
+ **Contents:**
73
+ [Features](#features) ·
74
+ [Installation](#installation) ·
75
+ [Quickstart](#quickstart) ·
76
+ [The studio (no code)](#the-studio--the-library-behind-knobs-no-code) ·
77
+ [Rendering parameters](#rendering-parameters) ·
78
+ [Data contract](#data-contract--which-column-powers-what) ·
79
+ [Recipes](#recipes) ·
80
+ [JavaScript API](#drive-the-map-from-javascript) ·
81
+ [Settings](#settings--one-defaults-file-your-overrides-on-top) ·
82
+ [Command line](#command-line) ·
83
+ [Documentation](#documentation)
84
+
85
+ ## Features
86
+
87
+ - **Real road cartography** — casing + fill sandwich, importance-ordered junctions, per-zoom
88
+ widths (openstreetmap-carto model), two-way lanes, curved street names, one-way arrows.
89
+ - **Grade separation** — tunnels faded + dashed underneath, bridges on decks on top (stacked
90
+ structures ordered by their OSM `layer`), and an optional **3D view** with extruded bridge decks.
91
+ - **One offline file** — MapLibre and the data are bundled into the saved HTML; it opens by
92
+ double-click, no server, no internet (with the `blank` basemap: zero network requests).
93
+ - **Data-driven styling** — colour/width by any column (categorical or numeric ramps), per-edge
94
+ colour tables, and multiple colour layers switchable client-side.
95
+ - **Big networks** — `tiles=True` packs the roads as an embedded vector tileset (PMTiles):
96
+ ~10⁵-edge maps open in seconds and stay responsive, still one offline file.
97
+ - **A JavaScript API** — every control is scriptable (`rsQuery`, `rsFilter`, `rsColor`,
98
+ `rsSelect`, …) with `rs:*` events, so the saved map can power your own dashboard.
99
+ - **Three backends** — `web` (MapLibre, the flagship), `folium` (Leaflet, legends), `lonboard`
100
+ (GPU, millions of edges).
101
+
102
+ ## Installation
103
+
104
+ Python ≥ 3.10. Core dependencies (`geopandas`, `shapely`, `folium`, `branca`) install
105
+ automatically.
106
+
107
+ ```bash
108
+ pip install roadstyle # from PyPI (v0.2.0+)
109
+
110
+ # or the latest development state straight from GitHub:
111
+ pip install "roadstyle @ git+https://github.com/Khoshkhah/roadstyle.git"
112
+ ```
113
+
114
+ Optional features are extras — install only what you need:
115
+
116
+ | Extra | Enables | Pulls in |
117
+ |---|---|---|
118
+ | `numeric` | continuous colour ramps + classification (`color_by` on numbers) | mapclassify, matplotlib |
119
+ | `tiles` | `tiles=True` — embedded vector tiles for big networks | mapbox-vector-tile, pmtiles |
120
+ | `lonboard` | the GPU backend for very large edge sets | lonboard |
121
+ | `duckdb` | `from_duckdb()` — read edges straight from DuckDB | duckdb |
122
+ | `arrow` | read edges from a pyarrow Table | pyarrow |
123
+ | `basemaps` | any XYZ provider from the xyzservices registry | xyzservices |
124
+
125
+ ```bash
126
+ pip install "roadstyle[numeric,tiles]"
127
+ ```
128
+
129
+ **Development install** (clone + editable + test tools):
130
+
131
+ ```bash
132
+ git clone https://github.com/Khoshkhah/roadstyle.git && cd roadstyle
133
+ pip install -e ".[dev]"
134
+ # or with conda: conda env create -f environment.yml && conda activate roadstyle && pip install -e ".[dev]"
135
+ pytest # 160 tests; browser tests need `pip install playwright`
136
+ ```
137
+
138
+ **Uninstall:** `pip uninstall roadstyle`. Your personal settings overrides
139
+ (`~/.config/roadstyle/roadstyle.json`, project-local `roadstyle.json`) are your files — pip
140
+ leaves them in place; delete them yourself if you want a clean slate.
141
+
142
+ ## Quickstart
143
+
144
+ ```python
145
+ import geopandas as gpd
146
+ import roadstyle as rs
147
+
148
+ edges = gpd.read_file("edges.gpkg") # any CRS; needs a `highway` class column
149
+
150
+ rs.render_edges(edges).save("map.html") # done — open map.html
151
+ ```
152
+
153
+ That one line gives you the full treatment: per-zoom widths, two-way lanes, arrows, street
154
+ names, hover/select with popups, a base-map switcher, a class filter panel, grade separation.
155
+ Common variations:
156
+
157
+ ```python
158
+ rs.render_edges(edges, basemap="dark_matter", view_3d=True).save("map3d.html") # dark + 3D bridges
159
+ rs.render_edges(edges, palette="carto", basemap="positron").save("carto.html") # the classic OSM look
160
+ rs.render_edges(edges, include=["motorway", "trunk", "primary"]).save("major.html")
161
+ rs.render_edges(edges, color_by="aadt", cmap="viridis").save("traffic.html") # colour by your data
162
+ rs.render_edges(edges, tiles=True).save("big.html") # 10⁵-edge networks
163
+ ```
164
+
165
+ Palettes: **`highsat`** (high-saturation, maximum legibility), **`carto`** (the muted
166
+ openstreetmap-carto look), **`mono`** (grayscale — quiet backdrop for data overlays).
167
+
168
+ ## The studio — the library behind knobs, no code
169
+
170
+ The gentlest way in. The studio is a small Streamlit app bundled in the repo (not part of the
171
+ pip package):
172
+
173
+ ```bash
174
+ git clone https://github.com/Khoshkhah/roadstyle.git && cd roadstyle
175
+ pip install roadstyle streamlit
176
+ streamlit run ui/studio/app.py
177
+ ```
178
+
179
+ ![roadstyle studio](docs/img/gallery/studio.png)
180
+
181
+ Two pages, same idea — every knob updates the live map **and** the exact Python code that
182
+ reproduces it:
183
+
184
+ - **Map** — upload a road file (`.gpkg` / `.geojson`) or pick a bundled Södermalm sample, then
185
+ click through palette, base map, 3D, vector tiles, colour-by-data, class filter, minzoom,
186
+ labels/arrows, popups and overlays. Copy the generated `render_edges(...)` code out, or
187
+ download the self-contained `map.html`.
188
+ - **Dashboard** — the same knobs, but the product is a **sidebar dashboard** (query box, verb
189
+ buttons, results table, detail panel) built on the JavaScript API. Preview it live, download
190
+ `dashboard.html`.
191
+
192
+ ## Rendering parameters
193
+
194
+ The keywords of `rs.render_edges(gdf, ...)` — the ones you'll actually reach for. Full
195
+ reference with every type and edge case: [docs/parameters.md](docs/parameters.md).
196
+
197
+ **Core**
198
+
199
+ | Parameter | Default | What it does |
200
+ |---|---|---|
201
+ | `backend` | `"web"` | `"web"` (MapLibre, the flagship) / `"folium"` (Leaflet + legends) / `"lonboard"` (GPU) |
202
+ | `palette` | `"highsat"` | Class colour palette: `"highsat"` / `"carto"` / `"mono"`, or your own |
203
+ | `basemap` | `"voyager"` | Background map: `voyager`, `positron`, `dark_matter`, `osm`, `satellite`, `blank`, `blank_dark` |
204
+ | `basemaps` | all built-ins | The set offered in the in-map base-layer dropdown |
205
+ | `name` | `"roadstyle"` | Page / layer title |
206
+ | `settings` | `None` | Per-call settings override (dict or path) — see [Settings](#settings--one-defaults-file-your-overrides-on-top) |
207
+
208
+ **Filtering**
209
+
210
+ | Parameter | Default | What it does |
211
+ |---|---|---|
212
+ | `include` / `exclude` | `None` | Keep / drop road classes (`include=["motorway","primary"]`); `_link` variants follow automatically |
213
+ | `filter_col` | `None` | Let the filter panel list a different column than the one that drives styling |
214
+ | `minzoom` | `None` (off) | Hide minor classes when zoomed out: `True` for the built-in table, or a `{class: zoom}` dict. Applies to the vector tiles too |
215
+
216
+ **Colour by data**
217
+
218
+ | Parameter | Default | What it does |
219
+ |---|---|---|
220
+ | `color_by` | `None` | Colour by a column instead of road class |
221
+ | `colors` | `None` | Categorical `{value: "#hex"}` map, or `"self"` to use the column's value as the literal colour |
222
+ | `cmap` / `vmin` / `vmax` | `None` | Numeric colour ramp (`"viridis"`, …) and its value range |
223
+ | `width_by` | `None` | `(min_px, max_px)` — scale line width with the numeric value |
224
+ | `color_table` | `None` | Per-edge colours: `{edge_id: "#hex"}` dict / Series / DataFrame (gray fallback, class widths kept) |
225
+ | `color_options` | `None` | Bake **several** colour layers + a client-side *Colour by* dropdown: `{"Traffic": {"color_by": "aadt", "cmap": "viridis"}, ...}` |
226
+
227
+ **Camera & 3D** (web backend)
228
+
229
+ | Parameter | Default | What it does |
230
+ |---|---|---|
231
+ | `view_3d` | `False` | Tilted camera + extruded, ramped, cased 3D bridge decks + an on-map 2D/3D toggle |
232
+ | `pitch` / `bearing` | settings | Starting camera tilt / rotation |
233
+
234
+ **UI toggles** (web backend)
235
+
236
+ | Parameter | Default | What it does |
237
+ |---|---|---|
238
+ | `arrows` | `True` | One-way direction chevrons |
239
+ | `labels` | `True` | Curved street-name labels |
240
+ | `filter_control` | `True` | The collapsible road-class filter panel (doubles as a colour legend) |
241
+ | `basemap_switcher` | `True` | The base-layer dropdown |
242
+ | `road_popup` | `True` | Click popup: `True` (curated fields) / `[fields]` / `"all"` / `"panel"` (docked read-out) / `False` |
243
+ | `tooltip` | `None` (off) | Hover tooltip fields (list of columns) |
244
+ | `hover_color` / `select_color` | violet | Highlight colours for hovered / selected roads |
245
+
246
+ **Extra content** (web backend)
247
+
248
+ | Parameter | Default | What it does |
249
+ |---|---|---|
250
+ | `overlays` | `None` | Your own layers (`Overlay(...)`) — zones under the roads, POIs on top, clickable, with a Layers toggle |
251
+ | `boundary` | `None` | Dashed outline of the clip area, drawn on top |
252
+ | `selected` | `None` | Pre-highlighted edges (folium backend) |
253
+
254
+ **Output & scale**
255
+
256
+ | Parameter | Default | What it does |
257
+ |---|---|---|
258
+ | `compress` | `True` | Gzip the inlined data (3–4× smaller files; `compress=False` for plain JSON) |
259
+ | `tiles` | `False` | Embedded-PMTiles vector tileset — for ~10⁵-edge networks (needs the `tiles` extra) |
260
+
261
+ **Column mapping**
262
+
263
+ | Parameter | Default | What it does |
264
+ |---|---|---|
265
+ | `highway_col` | `"highway"` | The road-class column that drives styling |
266
+ | `tunnel_col` / `bridge_col` / `layer_col` | `"tunnel"` / `"bridge"` / `"layer"` | Grade-separation columns |
267
+
268
+ Everything *stylistic* — the actual colours, widths, casing, label/arrow cosmetics, camera
269
+ defaults, bridge-deck geometry — is deliberately **not** a keyword but a
270
+ [setting](#settings--one-defaults-file-your-overrides-on-top).
271
+
272
+ ## Data contract — which column powers what
273
+
274
+ Only two things are required; every other column lights up a feature when present and is
275
+ skipped when absent:
276
+
277
+ | Column | Values | Powers |
278
+ |---|---|---|
279
+ | *geometry* | LineString (any CRS) | **required** — the edges themselves |
280
+ | `highway` | OSM class (`motorway`…`service`) | **required** — colour, width, casing, draw order |
281
+ | `name` | text | street-name labels + the popup title |
282
+ | `oneway` | `True`/`False` / `yes`/`no` | direction arrows (without it, one-way is inferred from reverse-geometry twins) |
283
+ | `bridge` / `tunnel` | truthy | grade separation: tunnels below, bridges on decks above, 3D decks in `view_3d` |
284
+ | `layer` | int | stacking order of bridges/tunnels; negative → below ground even without `tunnel` |
285
+ | `edge_id` | id (64-bit safe) | popups + click-to-copy; ids > 2⁵³ are kept exact as strings |
286
+ | anything else | anything | shown in the click popup / hover tooltip, queryable from JavaScript |
287
+
288
+ Networks exported by [duckOSM](https://github.com/Khoshkhah/duckOSM) (`duckosm export-gis`)
289
+ carry exactly this column set.
290
+
291
+ ## Recipes
292
+
293
+ Each of these is one call — details behind the links.
294
+
295
+ ```python
296
+ # colour each edge from your own table (cluster / route / metric per edge)
297
+ rs.render_edges(edges, color_table={"4897…": "#e6194B", "5193…": "#3cb44b"})
298
+
299
+ # several colour layers in one map, switchable client-side (no re-render)
300
+ rs.render_edges(edges, palette="mono", color_options={
301
+ "Road class": {},
302
+ "Traffic": {"color_by": "aadt", "cmap": "viridis"},
303
+ "Speed": {"color_by": "maxspeed_kmh", "cmap": "magma"}})
304
+
305
+ # your own layers under/over the roads
306
+ rs.render_edges(edges, overlays=[
307
+ rs.Overlay(zones, placement="under", color="#2d6cdf", opacity=0.25, label="Zones",
308
+ popup=["taz_id", "population"]),
309
+ rs.Overlay(sensors, placement="over", color="#ffd166", radius=6, label="Sensors")])
310
+
311
+ # your own tile server as a base map
312
+ rs.register_basemap(rs.Basemap(key="lm", label="Lantmäteriet",
313
+ url="https://tiles.example.se/{z}/{x}/{y}.png", attr="© LM"))
314
+
315
+ # a static PNG for a paper, through a real headless browser (pip install playwright)
316
+ rs.snapshot(rs.render_edges(edges, view_3d=True), "fig.png",
317
+ center=(18.076, 59.303), zoom=16, pitch=60)
318
+
319
+ # roads straight from DuckDB
320
+ edges = rs.from_duckdb(con, "SELECT edge_id, highway, name, ST_AsWKB(geom) AS geometry FROM edges")
321
+ ```
322
+
323
+ More: [the gallery](docs/gallery.md) — one screenshot + recipe per look.
324
+
325
+ ## Drive the map from JavaScript
326
+
327
+ Every in-map control is a thin UI over a `window.rs*` function, and the baked features are a
328
+ queryable table — so a saved map can power your own dashboard with plain HTML:
329
+
330
+ ```js
331
+ const ids = rsQuery(p => p.lanes >= 2 && p.maxspeed_kmh > 30); // WHERE clause → id set
332
+ rsFilter(ids); // show only these rsFilter(null) resets
333
+ rsColor(ids, "#ff00aa"); // paint them one colour rsColor(null) resets
334
+ rsHighlight(ids); // selection glow
335
+ rsGetProps(ids); // the rows behind the ids — table-ready
336
+ rsFocus(ids); // fly the camera to fit them
337
+ rsSelect(id); // select + popup, like a click
338
+
339
+ rsSetBasemap("dark_matter"); rsSetClasses(["primary","secondary"]);
340
+ rsSetColorField("Traffic"); rsSetOverlay("Zones", false); rsSetView3D(true);
341
+
342
+ document.addEventListener("rs:select", e => showSidebar(e.detail.properties));
343
+ ```
344
+
345
+ Everything works the same on overlays (pass the overlay's label as the last argument) and on
346
+ tiled maps. Full API table: [docs/web-backend.md](docs/web-backend.md#the-javascript-api-windowrs).
347
+ Ready-made scaffolding: [`ui/`](ui/) — `python ui/dashboard/build.py your_edges.gpkg [--tiles]`
348
+ builds a complete sidebar dashboard on this API.
349
+
350
+ ## Settings — one defaults file, your overrides on top
351
+
352
+ EVERY styling default — palettes, opacities, casing, the width/draw-order model, base map,
353
+ camera, labels, arrows, bridge decks — ships in one file, `roadstyle/data/defaults.json`. You
354
+ never edit it; you state only what changes, at any of five levels (later wins):
355
+
356
+ 1. `~/.config/roadstyle/roadstyle.json` — personal defaults
357
+ 2. `./roadstyle.json` — project-local
358
+ 3. `$ROADSTYLE_CONFIG=/path/to/file.json` — per run
359
+ 4. `rs.use_settings({...})` — from code, applies immediately
360
+ 5. `render_edges(..., settings={...})` — this one call only
361
+
362
+ ```jsonc
363
+ {
364
+ "palettes": { "highsat": { "service": { "fill": "#E0E0E0" } } }, // retint one class
365
+ "config": { "basemap": "dark_matter", "labels": { "color": "#8899aa" } },
366
+ "roads": { "z_order": { "service": 5 }, "width": { "secondary": { "18": 14 } } }
367
+ }
368
+ ```
369
+
370
+ Details: [docs/palettes.md](docs/palettes.md).
371
+
372
+ ## Command line
373
+
374
+ No Python required — point the `roadstyle` command at any road file:
375
+
376
+ ```bash
377
+ roadstyle edges.gpkg -o map.html --basemap dark_matter # styled interactive map
378
+ roadstyle edges.gpkg --view-3d --tiles # 3D + embedded vector tiles
379
+ roadstyle edges.gpkg --include motorway trunk primary
380
+ roadstyle edges.gpkg --color-by aadt --cmap viridis --width-by 1 6
381
+ roadstyle edges.gpkg -f spec -o map_data.json # JSON spec for your own frontend
382
+ ```
383
+
384
+ Every flag mirrors a `render_edges` keyword; `roadstyle --help` lists them all.
385
+
386
+ ## Documentation
387
+
388
+ | | |
389
+ |---|---|
390
+ | [Gallery](docs/gallery.md) | one screenshot + recipe per look |
391
+ | [Parameter reference](docs/parameters.md) | every keyword, type, and default |
392
+ | [Web backend](docs/web-backend.md) | grade separation, 3D, vector tiles, colour options, the full JS API |
393
+ | [Choosing an engine](docs/engines.md) | web vs folium vs lonboard, data-size guidance |
394
+ | [Palettes & settings](docs/palettes.md) | the built-in palettes and the override system |
395
+ | [When to use roadstyle](docs/comparison.md) | vs `.explore()`, prettymaps, kepler.gl, raw MapLibre |
396
+ | [Notebooks](notebooks/) | a runnable manual, one topic per notebook |
397
+ | [UI templates](ui/) | the dashboard scaffolding + the studio |
398
+
399
+ Full MkDocs site: [khoshkhah.github.io/roadstyle](https://khoshkhah.github.io/roadstyle/)
400
+ (`mkdocs serve` locally).
401
+
402
+ ## License
403
+
404
+ [MIT](LICENSE). Base-map tiles are third-party services (CARTO, OSM, Esri) with their own
405
+ attribution and terms; the styling spec is transcribed from the cartographic design docs in the
406
+ osm-traffic-enrichment project.