Skip to main content

Convert GPX routes into glanceable, map-centric motorcycle navigation PDFs for tank-bag use.

Project description

GPXSheet

Motorcycle sport-touring route awareness generator.

GPXSheet is a Python command-line application and reusable library that converts GPX routes into highly glanceable, map-centric motorcycle navigation PDFs optimized for tank-bag use.

It is not a rally roadbook and not a GPS replacement. The goal is route awareness: a rider should be able to glance at the printed sheet for less than one second and immediately understand what road they're on, what the next navigation decision is, how far away it is, what comes after, and where they are within the overall route.

See PRODUCT.md for the full design specification.

Status

v0.1.0 — Phase 1 complete: analysis engine, schematic strip, tank-bag PDF, and a publish-ready package.

  • Route analysis — GPX (track/route/waypoints) → decision points, fuel, reassurance markers, road segments; analyze text output.
  • Decision detection is two-tier. A geometry baseline (honest, but over-detects on twisty roads — it can't tell a curve from a junction) and an OSM mode that derives decisions from durable road-name changes, so a 22 mi switchback climb collapses to one clean segment ("onto Mount Hamilton Road").
  • Schematic map strip — stylized (default) or faithful turns, collision-placed labels with dashed leaders, and the road-name ribbon.
  • Tank-bag PDF — route-aware pagination; portrait roadbook (stacked strip lanes, the default) or landscape (one strip/page); page mileage in the header, progress bar.
  • Packaged for pip install gpxsheet (+ [osm] extra); PEP 561 typed.

validate (CLI) is still a stub; a web service is future work.

Installation

pip install gpxsheet            # core (GPX -> strip / PDF)
pip install "gpxsheet[osm]"     # + OpenStreetMap enrichment (heavy geo stack)

Development

git clone <repo-url> gpxsheet && cd gpxsheet
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,osm]"             # drop ,osm to skip the OSM stack
python -m build && twine check dist/*   # build + check the distribution
# publish (maintainer only): twine upload dist/*

Usage

OpenStreetMap enrichment and the portrait roadbook layout are on by default; both can be turned off, and OSM degrades gracefully (to geometry-only, with a warning) when the osm extra is missing, the route is too sparse to follow roads, or the live Overpass query fails.

gpxsheet generate route.gpx -o route.pdf            # portrait roadbook + OSM (defaults)
gpxsheet generate route.gpx --landscape -o route.pdf
gpxsheet generate route.gpx --no-osm -o route.pdf   # geometry-only (no network)
#   portrait knobs: --lanes N (lanes/page) --lane-decisions M (decisions/lane)

gpxsheet analyze route.gpx                           # text analysis
gpxsheet strip   route.gpx -o route_strip.png        # single schematic strip PNG

OSM queries the live Overpass API (seconds for rural routes, up to minutes for dense urban; cached by osmnx). The end-to-end query is covered by an integration test gated behind GPXSHEET_LIVE_OSM=1 so CI/offline stay network-free.

Library

from gpxsheet import generate_pdf

# Library defaults are landscape + geometry-only (predictable/offline); pass
# use_osm=True and/or orientation="portrait" to match the CLI product defaults.
generate_pdf("route.gpx", "route.pdf", profile="sport-touring", fuel_range=180)

Web service

A FastAPI service exposes the engine over REST. Renders are slow (matplotlib + live OSM), so generation runs as a background job (Dramatiq + Redis) with results in object storage (MinIO); /v1/analyze returns the structured analysis as JSON.

Self-hosted stack (API + worker + Redis + MinIO):

docker compose up --build
#   API   -> http://localhost:8000/docs
#   MinIO -> http://localhost:9001  (minioadmin / minioadmin)

curl -F gpx=@route.gpx "http://localhost:8000/v1/jobs?orientation=portrait" # -> {id, status}
curl http://localhost:8000/v1/jobs/<id>          # poll until status=done
curl -L http://localhost:8000/v1/jobs/<id>/result -o route.pdf

Endpoints: POST /v1/jobs (upload GPX + params → 202), GET /v1/jobs/{id}, GET /v1/jobs/{id}/result (streams, or 303 → presigned URL), POST /v1/analyze, GET /healthz. Single-process dev mode (in-memory, synchronous, no Redis/MinIO):

pip install -e ".[service]"
uvicorn gpxsheet.service.asgi:app        # worker not needed in dev mode

Config is env-driven (GPXSHEET_REDIS_URL switches on the prod path; see gpxsheet/service/settings.py).

Before exposing the service to the public internet, read docs/security-audit.md. Key hardening knobs: GPXSHEET_API_KEYS (comma-separated; enables X-API-Key/Bearer auth + per-key rate limits), GPXSHEET_RATE_LIMIT_PER_MIN, GPXSHEET_MAX_UPLOAD_BYTES, GPXSHEET_MAX_POINTS, GPXSHEET_ALLOW_OSM (set 0 to block outbound Overpass calls), GPXSHEET_CORS_ORIGINS, GPXSHEET_ENABLE_HSTS, and the MinIO credentials (the prod path refuses to boot on the minioadmin defaults). The service must run behind a TLS-terminating reverse proxy.

License

GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later) — see LICENSE. Copyright © 2026 Paul Traina. Because the AGPL covers use over a network, anyone who runs a modified version of the web service must offer its users the corresponding source.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gpxsheet-0.1.1.tar.gz (80.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gpxsheet-0.1.1-py3-none-any.whl (70.3 kB view details)

Uploaded Python 3

File details

Details for the file gpxsheet-0.1.1.tar.gz.

File metadata

  • Download URL: gpxsheet-0.1.1.tar.gz
  • Upload date:
  • Size: 80.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gpxsheet-0.1.1.tar.gz
Algorithm Hash digest
SHA256 61a3b1df639cca783c9eaee61c43ae6556acaff29f3641cddc98ac0ff5d7aa9c
MD5 b2bd8a8272606831fc03888661774aaf
BLAKE2b-256 53072ecacff26a168eba91769e4ce1c8f93038dc188e9f0d82e663b125208044

See more details on using hashes here.

Provenance

The following attestation bundles were made for gpxsheet-0.1.1.tar.gz:

Publisher: publish.yml on pleasantone/gpxsheet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gpxsheet-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: gpxsheet-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 70.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gpxsheet-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 aa817e6007b21119bed6103e8966533296497084223fec3bd0aaf4a862ca40fe
MD5 df4da42a893a53ad32ce2407f4efaccf
BLAKE2b-256 dedb9771a7e6e99d433484d9260d5a2738cdc9bf823d1fac708aa28da0c0cfc0

See more details on using hashes here.

Provenance

The following attestation bundles were made for gpxsheet-0.1.1-py3-none-any.whl:

Publisher: publish.yml on pleasantone/gpxsheet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page