Trip Planner for SOTA

My home QTH is in Duisburg, NRW. There are no mountains nearby. The Sauerland is about an hour away, the nearest 10-point summit over two hours. If you want several high-pointers close enough together for a multi-activation day, you are looking at the Thuringian Forest, the Black Forest, or the Alps, all of which are four to six hours of driving.

That makes trip planning an optimisation problem. Straight-line distance on a map is not a reliable guide to drive time, and a cluster of summits that looks useful may contain mostly expired references. So I put together a script to do the maths properly. It was written with help from Claude (Anthropic), mostly as a way to iterate quickly on the data pipeline and routing logic. The code is on Codeberg.

What the Script Does

sota_trip_planner.py takes a home location and a maximum drive time, then works out which areas offer the best return on that travel budget, either in raw points or in number of activations.

1. Load and filter the summit database. The SOTA programme publishes a CSV of all summits worldwide, currently around 170,000 entries. The script keeps only summits with a current ValidFrom/ValidTo window and within a straight-line pre-filter distance, which typically reduces the working set to a few thousand candidates.

2. Fetch real driving times via OSRM. The script queries the OSRM public routing server using the Table API, which returns one-to-many drive times in a single request. Batches of 100 summits are sent at a time to stay within the server’s URL length limit. Results are cached locally, so re-running with different parameters costs nothing.

3. Cluster summits into areas. Summits within 40 km of each other are grouped into a cluster and scored either by total points available or by count of reachable summits. The top clusters are ranked in a table with a detail view per area.

4. Exclude already-activated summits. With --callsign, the script fetches your activation history for the current year from the official SOTA API and removes those summits before clustering. A locally downloaded log CSV works as a fallback.

5. Fetch trail data. When generating an HTML report, the script queries the SOTA SMP API (api-db.sota.org.uk/smp/gpx/summit) for route data and adds path length, ascent, and descent to the detail view where available. The same API is used by sotl.as to display routes.

Getting Started

Python 3.8+, no external libraries needed. Clone or download from codeberg.org/ituri/sota-trip-planner.

The summit list CSV is downloaded automatically from sotadata.org.uk on first run and kept up to date via conditional HTTP requests (ETag/If-Modified-Since). No manual download needed.

# From Duisburg, 4h window, sort by points
python3 sota_trip_planner.py

# Sort by number of activations instead
python3 sota_trip_planner.py --mode summits

# Different QTH, wider window
python3 sota_trip_planner.py --home "Munich, Germany" --hours 5

# Exclude summits already activated this year
python3 sota_trip_planner.py --callsign DA2PK --hours 4

# Generate an HTML report
python3 sota_trip_planner.py --callsign DA2PK --hours 4 --html report.html

# Use a local log file instead of the API
python3 sota_trip_planner.py --callsign DA2PK --log DA2PK_activator.csv --hours 4

Terminal Output

Without --html, the script prints a ranked overview table followed by a detail section for the top three clusters. The run below shows a 4-hour window from Duisburg with already-activated summits filtered out.

Terminal output showing ranked cluster table and detail section

HTML Report

The --html flag generates a self-contained HTML file. It includes an overview table of the top 20 areas ranked by points or summit count, with drive times colour-coded by distance.

HTML overview table showing top summit clusters ranked by points

The detail section below the table covers each cluster individually. It lists every summit with its code, altitude, points, drive time, and, where available, the path length and elevation profile from the SMP API.

Detail view showing summit list with path, ascent and descent columns

Trail data is fetched concurrently from the SMP API and cached in sota_trail_cache.json, so subsequent runs are fast. Not every summit has a track; those that do show the shortest available route.

Results from NRW

With a 4-hour cap from Duisburg, there is exactly one active 10-point summit in range: the Großer Feldberg in the Taunus at 2h26. Extending to 4.5 hours adds a handful more in the northern Black Forest. At around 5.5 hours the Swabian Alb becomes accessible with a dense cluster of nine 10-pointers within 40 km of each other.

The best balance of drive time and point density for an overnight trip from NRW is the Thuringian Forest. Großer Inselsberg (3h51, 10+3pts) and Großer Beerberg (4h12, 10+3pts) are close together, and there are ten further 6-point summits in the same area. A realistic Saturday could cover four or five activations.

Notes

The OSRM public server is a shared resource. The script sleeps 500ms between batches and caches all results, so normal use should be fine. Re-running for analysis with --analyse-only makes no OSRM requests at all.

Activation history is fetched from the official SOTA API (api-db2.sota.org.uk). The script resolves the callsign to a UserID first, then retrieves the full activation log. Thanks to Manuel (HB9DQM) for the pointers on how to use the API. The --log fallback accepts a standard V2 activator CSV from sotadata.org.uk if the API is unavailable.

Trail data comes from the SOTA SMP API (api-db.sota.org.uk/smp/gpx/summit/{code}), the same source that sotl.as uses to display routes. The script calculates total distance, ascent, and descent from the raw track points, applying a km-to-m correction for the small number of tracks that store altitude in the wrong unit.

The summit CSV contains around 170,000 entries, roughly half of which have expired ValidTo dates and are no longer part of the programme. The script filters these out before routing.