Repository navigation
Tracking: QGIS project and style import parity #882
Description
Tracking issue for opening QGIS projects and styles in RustGIS as QGIS draws them. Each piece below gets its own pull request, or a few (piece 1 had a proposal issue, #883; with the plan agreed, the later pieces go straight to pull requests); this issue keeps the order and links them.
Where it stands
- Merged: feat(map): open QGIS projects as maps and apply QGIS layer styles #825 (
project.importQgis,style.importQgis), feat(labels): draw labels in an installed font family, by name #824 (label fonts), fix(map): QGIS import follow-ups: TRUE/FALSE and exponents in filters, label fonts, unread source options #844 (filters, fonts, source notes), feat(map): QGIS import reads merged categories, dashes and QGIS 1/2 names, and notes what it drops #876 (merged categories, dashes, QGIS 1.8/2.x names, notes for everything dropped), feat(map): QGIS import colours rasters as QGIS does, and QGIS raster styles apply to rasters #877 (raster renderers), test(map): a corpus of QGIS projects and styles the importer reads #881 (the test corpus: 90 generated styles and 21 generated projects), feat(map): QGIS projects in degrees open in Equirectangular, as QGIS draws them #885 (piece 1), feat(map): markers and hash marks along lines, as QGIS's marker and hashed lines draw them #900 (piece 2). - Open: feat(map): a character of a font as a marker, as QGIS's font markers draw one #926 (piece 3a: font markers as characters of their font, on points and along lines), fix(render): slanted label letters are solid, without stripes #927 (a rendering fix found on the way: stripes across large hand-slanted italic labels).
- Audit: 149 files: the corpus of test(map): a corpus of QGIS projects and styles the importer reads #881, the QGIS Training Manual and swisstopo's SMV25. They all import without a crash. Every setting is now read, noted as approximated or left out, or ignored as irrelevant to the map, except 72 project-structure details.
- Why SMV25: SMV25 serves only as a large, demanding real-world QGIS project: 66 layers and about 2,000 symbol layers combining categorized renderers, dashes, markers along lines, font markers, pattern fills, rule-based labels and data-defined settings. It uses a handful of QGIS's features heavily rather than many of them; the generated files cover the breadth. Its Swiss data isn't the goal. It isn't in the repository (no third-party vendor styles); the corpus README links its download, and comparisons on it are run locally.
- Measure: SMV25 rendered by RustGIS and by QGIS 4.2.3 at the same extent: 7.2 % of pixels differ in Winterthur's old town (1.8 × 1.1 km), 11.0 % in an overview around it (7.2 × 5.1 km). With feat(map): a character of a font as a marker, as QGIS's font markers draw one #926: 6.8 % and 10.9 %.
The pieces
Ordered by how often real projects (SMV25 and the Training Manual) hit them, and whether RustGIS already has the feature. Mapping means the importer reads QGIS's setting into something RustGIS has; feature means RustGIS needs it first.
| # | Piece | Kind | Crates | Size |
|---|---|---|---|---|
| 1 | #883, #885 Geographic coordinate systems: a project in EPSG:4326 (9 of the real projects) opens in Equirectangular instead of Web Mercator, as QGIS draws it | mapping | engine | small |
| 2 | #900 Markers along lines and hash lines: interval, placement (vertices, first/last, centre), rotation with the line (SMV25: railways, boundaries) | feature | map, render | medium |
| 3 | Font markers: a character from a font as a point symbol, with rotation (SMV25: 12 layers of map signs). 3a #926: the character, on points and along lines. 3b: several marks stacked on a point (half of SMV25's signs are a white character under a coloured one), their offsets, and rotation by a field (SMV25: - "WINKEL") |
feature | map, render | medium |
| 4 | Labels: halo colour and width, backgrounds, letter spacing, offsets, wrapping, repeat along lines | feature | map, render | large |
| 5 | Fills: line and point pattern fills, gradient fills, several fills per symbol | feature | map, render | medium |
| 6 | Data-defined properties: size, colour, rotation by field, on symbols and labels | feature | map, render | medium |
| 7 | Expressions: an expression language for feature layers (arithmetic, functions, CASE) for filters, label text and data-defined values; QGIS expressions translated to it |
feature | data, map | large |
| 8 | Renderers: rule-based renderers beyond the SQL subset, inverted polygons, merged features, heat maps | feature + mapping | map, render | medium |
| 9 | Symbols: SVG and image markers on feature layers, arrows, draw effects (shadow, glow) | feature | map, render | large |
| 10 | Sources: SpatiaLite, SQL Server, ArcGIS feature and map servers, WCS, vector tiles, delimited text with WKT, memory layers with their features, virtual layers through sql.query |
mapping | engine, map | medium |
| 11 | Project structure: groups (opacity, scale range), bookmarks, time settings, relations; notes for map themes, variables, joins, virtual fields | mapping | engine, map | medium |
| 12 | Print layouts: QGIS layout items, page sizes, map item extent, scale and theme, the atlas, onto RustGIS layouts | mapping | engine, map | large |
| 13 | Annotations and diagrams: the QGIS annotation layer as a graphics layer; per-feature diagrams | mapping + feature | engine, map, render | medium |
| 14 | Round trip: project.exportQgis and style.exportQgis, tested by reading back |
feature | map, engine | large |
Related but separate: #769 (WMTS tile matrix sets other than Web Mercator) affects QGIS projects only through their WMTS and tile layers.
How each piece is tested
- Unit tests: in
crates/map/src/qgis/tests.rs, written in the shapes QGIS writes, for each setting read. - Corpus:
every_corpus_file_readsovercrates/map/testdata/qgis/(test(map): a corpus of QGIS projects and styles the importer reads #881). A piece that needs a setting the corpus lacks adds a file throughgenerate.py. - Engine tests: for the commands (
project.importQgis,style.importQgis), includinghostile_params_never_panic. - Rendering pieces (2–9): a render test in
crates/render, plus a comparison with QGIS on SMV25 (downloaded, not in the repository) at fixed extents reported in the pull request (the pixel difference before and after).
Not planned
Mesh, point cloud and tiled-scene layers (no RustGIS equivalent), 3D views, layer actions, forms and editing widgets: noted on import.
Pieces 1 and 2 are merged, and 3a is in #926. Next is 3b, then piece 4 (labels).
Activity
pick2510 commentedon Oct 25, 2026
@giswqs Any comments on this plan, the order or the scope? Piece 1 is proposed in #883 and implemented as a draft in #885; I'll mark it ready once you're fine with the approach.
giswqs commentedon Oct 25, 2026
@pick2510 This is a great plan! Thank you for putting it together. Just added you to the RustGIS-Maintainers team. Keep going.
pick2510 commentedon Oct 25, 2026
Great. I will continue with the stepwise implementation.
feat(map): markers and hash marks along lines, as QGIS's marker and h…
feat(map): a character of a font as a marker, as QGIS's font markers …