This is a Map Utils project written in Rust for Orienteering.
The ground-truth renderer's output (left) side by side with MaUR-O's own rendering of the same map (right). Map: Valserena, owned by Polisportiva Masi and made by Samuele Curzio and Remo Madella.
It renders OpenOrienteering Mapper
.omap/.xmap files to raster images (PNG, BMP, TIFF, JPEG) from the command
line, without needing Mapper itself, Qt, or a graphical environment installed.
Alongside the renderer, it ships a small pair of benchmarking tools that build
and run suites of maps to measure how closely the renderer's output matches a
ground-truth reference — useful both for tracking rendering fidelity over time
and for spotting real regressions among the noise two rasterizers naturally
disagree on.
Dependencies:
- A working
cargo/Rust toolchain, version 1.88 or newer (edition 2021). 1.88 is the floor set by theimagecrate; everything else in the dependency tree asks for less.
That is all it takes to build: no C toolchain, no pkg-config, no -dev
package. Drawing text correctly wants one ordinary runtime library on top —
see Fonts and fontconfig.
MacOS and Windows have not been tested; this project has only been built and run on Linux so far.
Build everything with:
cargo build --releaseThe three executables are then at:
target/release/map_to_imagetarget/release/benchmarktarget/release/create_benchmark
To render text correctly, MaUR-O wants a fontconfig runtime library — already present on essentially every desktop Linux install:
- Debian/Ubuntu:
apt install libfontconfig1 - Fedora:
dnf install fontconfig - Arch Linux:
pacman -S fontconfig
It is opened by name when the first map with text is drawn, never linked, so it is a requirement for correct output rather than for building. If it is missing, MaUR-O prints a warning once and carries on.
That fallback matters more than it sounds. fontconfig is what resolves a
family name the same way Qt and Mapper do. Without it the job falls to
fontdb alone, which picks a different font for a generic family like "Sans
Serif" — and the substituted font has different glyph widths and line
heights. So the text is not merely set in another typeface, it is drawn in
different positions: labels shift, and successive lines of a block drift
further apart as the line-height error accumulates. Nothing about the image
looks broken, because the geometry is untouched and only the text is wrong.
Concretely, rendering maps/city_sample.omap with and without fontconfig
moves 0.66% of all pixels, at a mean error of 499 out of 765 — the scale
of black glyphs landing on white paper, not of soft edges. A benchmark run
counts every one of those as a real difference rather than as antialiasing
(see Antialiasing), so a suite
run on a machine without fontconfig reports spurious failures on every map
carrying text.
On MacOS, on Windows, and on any other platform without fontconfig, this fallback is what runs.
Renders a single map to a raster image.
map_to_image [-r px-per-meter] [-f meters] <map-file> [image-file]
| Argument | Meaning |
|---|---|
<map-file> |
The .omap/.xmap file to render. |
[image-file] |
Where to write the image. The file suffix (.png, .bmp, .tif, .jpg) selects the format; defaults to the map file's name with a .png suffix. |
-r, --resolution <N> |
Pixels per meter on the ground (default 3). |
-f, --frame <N> |
Width of the white frame added on every side, in meters on the ground (default 50). |
This is the tool to reach for to just look at a map:
cargo build --release
./target/release/map_to_image maps/forest_sample.omap
maps/forest_sample.omap rendered by map_to_image.
It prints the image size, the ground size, and the map scale it read, and exits non-zero if the map cannot be read (2), its geometry cannot be built (3), or the image cannot be saved (4).
Builds a benchmark archive: it takes a set of maps and a ground-truth
renderer, has that renderer draw each map, and packages both together
following the naming rules benchmark expects.
create_benchmark [OPTIONS] <renderer> <source>
| Option | Meaning |
|---|---|
<renderer> |
Path to the ground-truth renderer executable — see below. |
<source> |
Either a folder, searched recursively for .omap/.xmap files, or a single map file, which is instead split into one generated map per symbol on a grid of test objects. |
-o, --output <FILE> |
Archive path to write. Defaults to benchmarks/benchmark_<source name>_<resolution>_px_m.zip; the containing folder is created if needed. |
--force |
Overwrite the output archive if it already exists. |
-r, --resolution <N> |
Resolution of the reference images, in pixels per meter on the ground (default 3). |
-f, --frame <N> |
Width of the white frame added on every side, in meters on the ground (default 50). |
--filter <TEXT> |
Only include maps whose name contains this text. |
cargo build --release
./target/release/create_benchmark /path/to/map_to_image maps/ISOM.omap
A handful of the maps create_benchmark generates when <source> is a single map file: one per symbol, each a grid of test objects at every size and rotation that symbol needs checked.
This tool needs an external, ground-truth OMap renderer — it does not
render the reference images itself, since the whole point is to compare this
project's own renderer against an independent one. Any command-line tool
that reads a map file and two options (resolution, frame) and writes an
image works; the
mapper_cmd_rederer fork of
OpenOrienteering Mapper's own command line renderer, built from source, is
the one this project was benchmarked against.
Runs a whole benchmark suite — a zip archive of maps plus reference images — in one pass: renders every map, compares it against its reference, and writes a difference report.
benchmark [OPTIONS] <archive.zip>
| Option | Meaning |
|---|---|
<archive.zip> |
The benchmark archive, as produced by create_benchmark. |
--results <DIR> |
Where the run's output folder is created (default Results). |
--names-only |
Check and, if needed, correct the archive's file naming without rendering anything. |
--tolerance <N> |
Per-pixel difference (summed over R+G+B, 0–765) below which a pixel does not count as wrong (default 3). |
--keep-antialiasing |
Report every differing pixel as real, instead of separating out ones two rasterizers can legitimately disagree about (see Antialiasing). |
--crops <N> |
How many worst-region crops to save per differing map, 0 for as many as it takes to cover every difference (default 0). |
--crop-size <N> |
Side length of a cropped region, in pixels (default 128). |
--zoom <N> |
Width a cropped region is enlarged to, in pixels (default 512). |
--overview <N> |
Width the whole side-by-side image is scaled down to, in pixels, 0 for full size (default 2000). |
--filter <TEXT> |
Only run maps whose name contains this text. |
cargo build --release
./target/release/benchmark benchmarks/benchmark_ISOM_3_px_m.zipResolution and frame are not options here: they are always read back out of
the archive's own info.txt, written by create_benchmark when the archive
was made, so a run can't silently drift from the resolution and frame the
reference images were actually drawn at.
Each run gets its own timestamped folder under Results/, holding
results.txt (a table of how much each map differs, worst first),
predictions/ (the rendered maps), and differences/ (side-by-side images
and crops for every map with a real, non-antialiasing difference). Exit
codes: 0 success, 1 a usage or archive problem, 2 a map failed to
render.
Below are the first entries of the results.txt of a run produced against the
ISSprOM 2019 symbol set at 6 pixels per meter: the worst maps are reported at
the top, so this is where to look first. For what each column means, see
Running a benchmark archive.
real antialiasing wrong largest mean error of wrong px map
------- ------------ ------- ------- ---------------------- --------------------------------------------------
0.0156% 1.5679% 1.5836% 35 7.56 ± 4.15 079__area_404_Rough_open_land_with_scattered_trees
0.0148% 2.7276% 2.7424% 70 11.05 ± 7.59 076__area_402_Open_land_with_scattered_trees
0.0147% 1.9598% 1.9745% 40 7.93 ± 4.36 107__area_501.3_Paved_area_with_scattered_trees
0.0022% 0.8021% 0.8042% 765 45.77 ± 50.07 131__line_512.1_Bridge__minimum_width
0.0007% 4.4276% 4.4283% 127 17.91 ± 8.16 067__area_308_Marsh
A crop from differences/ on a real map: expected (left), predicted (middle), and the diff (right), where orange is just antialiasing — not a bug — and the red cluster is a real error, a pixel one renderer drew and the other did not.
MaUR-O reads the OpenOrienteering Mapper .omap/.xmap format and renders
it in pure Rust, with a dedicated crate for each concern (XML parsing, path
stroking and rasterization, font shaping...). How closely its output matches
a ground-truth renderer, how the source is organized, and how the
benchmarking tools tell a real rendering bug from the noise two different
rasterizers' antialiasing produces, are documented in
ImplementationDetails.md.
No known bugs on the sample maps (maps/city_sample.omap and
maps/forest_sample.omap). There are a handful of minor, mostly cosmetic
differences on the ISOM/ISSprOM per-symbol benchmarks, and a few genuine
placement/shape bugs found on a private dataset of real maps — see
bug.md for all of them, with pictures.