- Rust 80.1%
- Go 16.6%
- Just 2.1%
- Dockerfile 1%
- HTML 0.2%
| go-version | ||
| src | ||
| tests | ||
| .gitignore | ||
| .jjignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| Containerfile | ||
| Justfile | ||
| README.md | ||
| TODO.md | ||
gpx2svg
Convert GPX, KML, or KMZ track files into SVG images with optional OpenStreetMap tile backgrounds.
gpx2svg --in track.gpx --out track.svg
gpx2svg --in track.gpx --out track.svg --map --zoom 14
Install
cargo install --path .
Or build locally:
cargo build --release
# binary at target/release/gpx2svg
Usage
gpx2svg --in <file> [options]
| Flag | Default | Description |
|---|---|---|
--in <path> |
required | Input file: GPX, KML, or KMZ |
--out <path> |
out.svg |
Output SVG path; - writes to stdout |
--width <px> |
1000 |
Canvas width (ignored in map mode) |
--height <px> |
600 |
Canvas height (ignored in map mode) |
--pad <px> |
0 |
Pixel padding (no-map mode) |
--pad-pct <0–50> |
10.0 |
Padding percentage (map mode) |
--bg <color> |
white |
Background fill |
--stroke <color> |
#2b6cb0 |
Track stroke colour (single-input mode; use --colors for multiple tracks) |
--sw <float> |
3.0 |
Stroke width in px |
--markers / --no-markers |
on | Start/end circle markers |
--map / --no-map |
off | Embed OSM tile background |
--tile-source <name|url> |
osm |
Tile provider (see below) |
--zoom <1–18> |
13 |
Tile zoom level |
--map-opacity <0–1> |
0.8 |
Map tile opacity |
--attribution / --no-attribution |
on | Attribution box |
--attribution-text <str> |
Override attribution text | |
--colors <c1,c2,...> |
auto | Per-track stroke colours (CSV); auto-assigns Okabe-Ito palette |
--labels <l1,l2,...> |
filename stems | Per-track legend labels (CSV) |
--legend / --no-legend |
auto | Colour legend (auto-on when >1 track) |
--tile-cache-ttl <days> |
30 |
Tile cache TTL (0 = never expire) |
--verbose / -v |
off | Log per-tile cache hit/miss to stderr |
All diagnostic output goes to stderr; stdout carries SVG bytes only.
Output
Output is always SVG. SVG is lossless, scalable, and embeds map tiles as base64 data URIs — no external files needed. For raster or other formats, pipe through a standard tool:
# PNG via rsvg-convert (high quality, fast)
gpx2svg --in track.gpx | rsvg-convert -o out.png
# PNG at specific width
gpx2svg --in track.gpx --map | rsvg-convert -w 1920 -o out.png
# PNG or JPEG via ImageMagick
gpx2svg --in track.gpx | magick svg:- out.png
gpx2svg --in track.gpx | magick svg:- -quality 85 out.jpg
# AVIF
gpx2svg --in track.gpx | rsvg-convert | magick - out.avif
# PDF (vector, lossless)
gpx2svg --in track.gpx | rsvg-convert -f pdf -o out.pdf
Examples
# SVG, no map
gpx2svg --in ride.gpx --out ride.svg
# With OSM tiles at zoom 15, piped to PNG
gpx2svg --in ride.gpx --map --zoom 15 | rsvg-convert -o ride.png
# Write SVG to stdout
gpx2svg --in ride.gpx --out -
# Custom colours, larger canvas
gpx2svg --in ride.gpx --stroke '#e53e3e' --sw 4 --width 1920 --height 1080 --out ride.svg
# Subtle map background
gpx2svg --in ride.gpx --map --map-opacity 0.4 --out ride.svg
# No attribution
gpx2svg --in ride.gpx --no-attribution --out ride.svg
# KML input
gpx2svg --in route.kml --out route.svg --map
# OpenTopoMap tiles
gpx2svg --in track.gpx --map --tile-source otm --out topo.svg
# Multiple GPX files on one map (auto-coloured)
gpx2svg --in morning.gpx evening.gpx --out combined.svg --map
# Override per-track colours and labels
gpx2svg --in a.gpx b.gpx --colors '#e69f00,#56b4e9' --labels 'Monday,Tuesday' --out combined.svg
# Cache management
gpx2svg --cache-info
gpx2svg --clear-cache --tile-source osm
Tile sources
| Name | Provider |
|---|---|
osm (default) |
OpenStreetMap |
otm |
OpenTopoMap |
carto-light |
Carto Positron |
carto-dark |
Carto Dark Matter |
stamen-terrain |
Stadia Stamen Terrain |
stamen-toner |
Stadia Stamen Toner |
esri-satellite |
ESRI World Imagery |
Or supply a custom URL template: --tile-source 'https://example.com/{z}/{x}/{y}.png'
When --map is set, tiles are fetched and cached at
~/.cache/gpx2svg/tiles/ (SHA-256-keyed, persistent across runs). The zoom
level is automatically adjusted ±5 levels from the requested value to optimise
aspect ratio and keep tile count ≤128.
Please follow the OSM tile usage policy.
Map data © OpenStreetMap contributors.
Repository layout
. Rust implementation (this crate)
go-version/ Original Go implementation (reference)
License
MIT