Skip to main content

Enrich GPX tracks with Points of Interest from OpenStreetMap via configurable YAML profiles

Project description

gpx-poi-enricher

A three-command toolkit for building POI-enriched GPX files from a Google Maps route.

CI Python 3.10+ License: MIT PyPI


Table of Contents


The Pipeline

Google Maps URL
      │
      ▼
 maps-to-gpx          ← converts a directions URL to a routed GPX track
      │
      ▼
gpx-split-waypoints   ← adds evenly-spaced split markers (optional, for long routes)
      │
      ▼
gpx-poi-enricher      ← queries OpenStreetMap for POIs along the track
      │
      ▼
 waypoints.gpx        ← import into Garmin / OsmAnd / Google My Maps <https://www.google.com/mymaps>

All three commands are installed together and work independently or in sequence. In the end, if the result file is too large (>5MB) for your consumer app, https://www.gpxtokml.com/ helps a lot.


Features

  • maps-to-gpx — convert a Google Maps directions URL (including short maps.app.goo.gl links) directly to a routed GPX file. Handles place-name waypoints via Nominatim geocoding and routes via the public OSRM API. No API key required.
  • gpx-split-waypoints — add evenly-spaced split waypoints to a GPX track, useful for importing oversized files into apps that enforce a waypoint limit.
  • gpx-poi-enricher — enrich any GPX track with Points of Interest from OpenStreetMap. 11 ready-to-use profiles covering camping, beaches, playgrounds, theme parks, restaurants, and more.
  • Country-aware search terms: automatically detects which country each section of the route passes through and queries in the local language (DE, FR, ES, EN).
  • Searches OpenStreetMap via the public Overpass API with multi-endpoint fallback for reliability.
  • Configurable search radius, sampling interval, and batch size per profile — all overridable on the command line.
  • Extensible: add a new profile by dropping a single YAML file into the profiles/ directory.

Installation

From PyPI (recommended)

pip install gpx-poi-enricher

From source (development)

git clone https://github.com/devmarkusb/gpx-poi-enricher.git
cd gpx-poi-enricher
pip install -e ".[dev]"

Quick Start

Full pipeline — Google Maps URL to a campsite waypoint file:

# Step 1: convert a Google Maps directions link to a GPX track
maps-to-gpx "https://www.google.com/maps/dir/Paris/Lyon/Barcelona/" route.gpx

# Step 2 (optional): add split markers every ~10% of the route
gpx-split-waypoints route.gpx route-split.gpx 10

# Step 3: find all campsites within 10 km of the route
gpx-poi-enricher route.gpx camping.gpx --profile camping

Command: maps-to-gpx

Converts a Google Maps directions URL to a routed GPX file.

  • Follows redirects for short maps.app.goo.gl URLs
  • Handles both path-style (/maps/dir/A/B/C) and query-style (?api=1&origin=...) URLs
  • Geocodes place names via Nominatim (no API key)
  • Routes via the public OSRM API (no API key)
  • Writes a <trk> with the full routed geometry and <wpt> markers for each stopover
usage: maps-to-gpx [-h] [--mode {driving,cycling,walking}] [--name NAME]
                   url output_gpx

positional arguments:
  url                   Google Maps directions URL (full or short
                        maps.app.goo.gl link)
  output_gpx            Output GPX file path

options:
  -h, --help            show this help message and exit
  --mode {driving,cycling,walking}
                        Transport mode for routing (default: driving)
  --name NAME           Track name written into the GPX file (default: Route)

Examples:

# Path-style URL with place names
maps-to-gpx "https://www.google.com/maps/dir/Paris/Lyon/Marseille/" route.gpx

# Short URL
maps-to-gpx "https://maps.app.goo.gl/ABC123" route.gpx

# Query-style URL with multiple stopovers, custom name
maps-to-gpx "https://www.google.com/maps/dir/?api=1&origin=Paris&destination=Barcelona&waypoints=Lyon|Avignon" route.gpx --name "France to Spain"

# Cycling route
maps-to-gpx "https://www.google.com/maps/dir/Amsterdam/Utrecht/" route.gpx --mode cycling

Command: gpx-split-waypoints

Adds evenly-spaced waypoints along a GPX track, named Split 1, Split 2, etc. Useful when an app or device has a waypoint import limit and a long route needs to be broken into manageable segments.

usage: gpx-split-waypoints input.gpx output.gpx [segments]

positional arguments:
  input.gpx     Input GPX file with a track
  output.gpx    Output GPX file (original track + split waypoints)
  segments      Number of equal segments to split into (default: 10)

Examples:

# Split into 10 equal segments (default)
gpx-split-waypoints route.gpx route-split.gpx

# Split into 5 segments
gpx-split-waypoints route.gpx route-split.gpx 5

Command: gpx-poi-enricher

Queries OpenStreetMap for Points of Interest along a GPX track and writes them as waypoints to a new GPX file.

usage: gpx-poi-enricher [-h] [--profile PROFILE] [--max-km MAX_KM]
                        [--sample-km SAMPLE_KM] [--batch-size BATCH_SIZE]
                        [--country-sample-km COUNTRY_SAMPLE_KM]
                        [--progress-interval SEC] [--verbose]
                        [--list-profiles]
                        [input_gpx] [output_gpx]

positional arguments:
  input_gpx             Input GPX file with a track
  output_gpx            Output GPX file (waypoints only)

options:
  -h, --help            show this help message and exit
  --profile PROFILE     Profile id, e.g. camping or playground
                        (case-insensitive)
  --max-km MAX_KM       Override max distance from track (km)
  --sample-km SAMPLE_KM
                        Override track sampling interval (km)
  --batch-size BATCH_SIZE
                        Override Overpass query batch size
  --country-sample-km COUNTRY_SAMPLE_KM
                        Min distance (km) between Nominatim reverse-geocode
                        calls (default: 40)
  --progress-interval SEC
                        Print progress to stderr every SEC seconds
                        (default: 5; 0 = off)
  --verbose             Print verbose Overpass error bodies
  --list-profiles       List built-in profiles and exit

Examples:

# Find campsites within 10 km of the route
gpx-poi-enricher route.gpx camping.gpx --profile camping

# Find playgrounds within 5 km (overriding the profile default of 3 km)
gpx-poi-enricher route.gpx playgrounds.gpx --profile playground --max-km 5

# List all available built-in profiles
gpx-poi-enricher --list-profiles

Key options explained

  • --max-km — How far from the track a POI may be to be included. Larger values cast a wider net but produce more results and slower queries.
  • --sample-km — The track is sampled at this interval before querying Overpass. Smaller values give denser coverage but proportionally more API calls.
  • --batch-size — How many sample points are bundled into a single Overpass request. Tune this if you hit timeouts on slow connections.
  • --country-sample-km — Distance between Nominatim reverse-geocode lookups used to determine the current country. Increase to reduce Nominatim traffic on very long routes.

Built-in Profiles

Profile ID Description Default max_km
camping Campsite / motorhome stopover 10.0
playground Playground 3.0
outdoor_pool Outdoor pool / water park / thermal bath 10.0
beach Beach / swimming lake 25.0
theme_park Theme park / amusement park 10.0
zoo Zoo / petting zoo 12.0
aquarium Aquarium 15.0
mcdonalds McDonald's restaurants 5.0
restaurant Family-friendly restaurant with kids menu 5.0
kids_activities Kids activities of all kinds 15.0
attractions Family-friendly sights, viewpoints, museums 20.0

Creating Custom Profiles

A profile is a plain YAML file placed in the profiles/ directory. The filename without extension becomes the profile ID.

# profiles/my_profile.yaml
id: my_profile
description: "My custom POI type"
symbol: Flag, Blue          # Garmin symbol name shown in the output GPX

defaults:
  max_km: 8.0               # default search radius from the track
  sample_km: 4.0            # sample the track every N km
  batch_size: 5             # sample points per Overpass request
  retries: 3                # number of Overpass retry attempts

# One or more OSM tag matchers. Results matching ANY entry are included.
tags:
  - key: tourism
    value: museum
  - key: historic
    value: castle
  # Optional sub-filter: both conditions must match
  - key: amenity
    value: fast_food
    and:
      key: cuisine
      value: pizza

# Country-specific search terms used to name waypoints.
# Omit or set to {} if not needed.
terms:
  DE: ["Museum", "Schloss", "Burg"]
  FR: ["musée", "château"]
  ES: ["museo", "castillo"]
  EN: ["museum", "castle"]

All fields in defaults can be overridden on the command line. The terms map is keyed by ISO 3166-1 alpha-2 country code; the tool selects the appropriate language based on the country detected along the route.


How It Works

maps-to-gpx

  1. URL expansion — Short maps.app.goo.gl links are resolved by following HTTP redirects.
  2. Waypoint extraction — The URL path (/maps/dir/A/B/C) or query parameters (origin, waypoints, destination) are parsed. Coordinate waypoints (48.8566,2.3522) are used directly; place-name waypoints are geocoded via Nominatim.
  3. Routing — Waypoints are sent to the OSRM public routing API, which returns a full road-snapped geometry.
  4. GPX output — The routed geometry is written as a <trk> element; the user waypoints (start, stopovers, end) are written as <wpt> elements.

gpx-poi-enricher

  1. Track sampling — The input GPX track is sampled at sample_km intervals, producing a list of coordinates.
  2. Country detection — Every country_sample_km kilometres, the tool calls the Nominatim reverse-geocoding API to determine the current country code. This allows the profile's search terms to be localised.
  3. Overpass queries — Sample points are grouped into batches. For each batch, an Overpass API query is built from the profile's tags and the country-appropriate terms. The query fetches all matching OSM nodes and ways within max_km of each sample point.
  4. Deduplication — Results from all batches are merged and deduplicated by OSM ID.
  5. GPX output — Each unique POI is written as a <wpt> element to the output GPX file. Tracks and routes are intentionally excluded so the file contains only waypoints.

The tool rotates through several public Overpass API endpoints and retries failed requests automatically, making it resilient to temporary rate limits.


Data Attribution

Map data is sourced from OpenStreetMap contributors and is made available under the Open Database License (ODbL).

© OpenStreetMap contributors

Geocoding and reverse geocoding are performed by the Nominatim service, provided by the OpenStreetMap Foundation.

Routing is performed by the OSRM public demo server, also based on OpenStreetMap data (ODbL).

Please respect the usage policies of all three services:

  • Nominatim: maximum 1 request per second; identify your application with a meaningful User-Agent.
  • Overpass API: avoid bulk downloads; the tool's default batch and retry settings are chosen to be a considerate citizen of the public infrastructure.
  • OSRM demo server: personal/non-commercial use; attribution required; access may be withdrawn without notice — consider self-hosting for production workloads.

Contributing

Contributions are welcome. Please read CONTRIBUTING.md before opening a pull request.

Bug reports and feature requests can be filed as GitHub issues.


License

This project is licensed under the MIT License — see the LICENSE file for details.

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

gpx_poi_enricher-0.1.0.tar.gz (74.9 kB view details)

Uploaded Source

Built Distribution

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

gpx_poi_enricher-0.1.0-py3-none-any.whl (25.1 kB view details)

Uploaded Python 3

File details

Details for the file gpx_poi_enricher-0.1.0.tar.gz.

File metadata

  • Download URL: gpx_poi_enricher-0.1.0.tar.gz
  • Upload date:
  • Size: 74.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for gpx_poi_enricher-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9d640fb8a5e4f0c54d66d02c3ac7adcf0d1d442879ddefec31bad6382354ab1d
MD5 580ccd9b575158284a8d1622712a1fd0
BLAKE2b-256 8961fcec925360347d506a43312184be6f20a2be7074b113785604aecc355269

See more details on using hashes here.

File details

Details for the file gpx_poi_enricher-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: gpx_poi_enricher-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 25.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for gpx_poi_enricher-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3e50e05b09e6ea034ed46f2ba5058b06c14ec23e7c8f6b4e484c36cb24ac30d3
MD5 74edebb25ae82d80076c79ae14d290e8
BLAKE2b-256 b3c9bd05cbfd28735a1e5600e52168e7057355fe7071591c162fd1e7cb1c09ab

See more details on using hashes here.

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