Automatic Web Symbology
The RiverscapesXML repository automatically converts new QGIS .qml symbology files into the JSON format used by the Riverscapes Viewer. This page explains how that automation works so you know what to expect when you add or change QML files.
Why this exists
Symbology for the Riverscapes Viewer is authored as QGIS styles (.qml files) because QGIS is the tool our analysts actually use. But the web viewer needs symbology in a JavaScript-friendly JSON format. The GitHub Action runs QML2WebSymbology.py to bridge the two — you maintain QML, and the first version of the JSON is generated for you.
Think of this as a sensible first pass. You may still need to tweak it if it's not right and that's up to you but we can speed up web symbology creation a lot by bootstrapping the first version.
Directory layout
| Directory | Contents | Who writes it |
|---|---|---|
Symbology/qgis/<Context>/ | Your .qml files, organized by program/context (e.g. BRAT/, Anthro/, Hydro/) | You (humans) |
Symbology/web/<Context>/ | Generated .json files, one per QML, mirroring the QGIS folder structure | The GitHub Action (bot) |
A file named Symbology/qgis/BRAT/barrier_density.qml produces Symbology/web/BRAT/barrier_density.json.
The generated JSON is also served publicly, so the viewer and external consumers read it from URLs like:
https://xml.riverscapes.xyz/Symbology/web/BRAT/barrier_density.json
When it runs
The action (.github/workflows/symbology_sync.yml) runs on every push to master that changes anything under Symbology/qgis/**.
What the converter supports
QML2WebSymbology.py handles the common QGIS style features:
- Renderer types: graduated, single symbol, categorized, and rule-based renderers
- Geometry types: points/circles, lines, and fills (polygons)
- QML dialects: both
<Option>-style (QGIS 2) and<prop>-style (QGIS 3) attributes - Colours: solid and translucent (alpha) colours, converted to
rgb()/rgba()/hsla()strings - Labels: range labels (e.g.
"1 - 2","> 10") and category labels end up in the JSONlegendso the viewer can render a legend automatically
The output JSON contains a $schema reference plus a legend (list of [colour, label] pairs) and layerStyles (renderer paint/line/circle settings).
What is not created or updated
This is important: the action only creates missing JSON files. It never overwrites or regenerates an existing JSON.
Specifically:
- Existing JSON files are left untouched. If you edit a QML whose JSON already exists, the action will not update the JSON. The bot reports it as "skipped".
- No raster symbology is produced.
.qmlfiles that contain a raster renderer are skipped (raster symbology is handled outside this converter).
Adding new symbology (recommended workflow)
- Put your
.qmlfile in the folder that matches your program, e.g.Symbology/qgis/MyProject/.- The folder name becomes part of the output path and the URL, so choose it carefully.
- Each
.qmlfile gets its own JSON; a single QML containing several layers is also fine — the converter maps them into the JSONlayerStyles.
- Commit and push to
master. The push must include changes underSymbology/qgis/for the action to run. - Within a couple of minutes the action finishes and adds the new JSON to
Symbology/web/in a follow-up commit from the bot. - Reference the JSON in the viewer, e.g.
https://xml.riverscapes.xyz/Symbology/web/MyProject/my_style.json.
Updating existing symbology
Because the action never overwrites an existing JSON, updating a style takes one extra step:
- Edit or replace the
.qmlfile inSymbology/qgis/. - Delete the corresponding
.jsoninSymbology/web/. - Commit both the QML change and the JSON deletion together and push to
master. (Deleting only the JSON will not trigger the action — the trigger watchesSymbology/qgis/, notSymbology/web/.) - The action detects the missing JSON and regenerates it from your updated QML.
Alternatively, regenerate locally and push the result yourself:
cd python
uv run python QML2WebSymbology.py --dir MyProject --force