Skip to content

JoepSchyns/explorinator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

63 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Explorinator Explorinator

Explorinator combines hiking routes from multiple major aggregators into a single searchable map.

Background

I used to be a big fan of gpsies.com. Gpsies hosted routes from individual contributors, which meant you could see clusters of routes in a given area and plan trips around them. After Gpsies was acquired by Alltrails, I lost that ability. Alltrails is a better service in many ways, but it does not let you see all available routes at once on a map and neither does any other major aggregators I know of (Komoot, Wikiloc, Outdooractive, etc.).

How it works

Inspired by waymarkedtrails.org, I built a web application that renders routes directly on a map. Unlike the major aggregators, routes do not need to be loaded individually. Instead, map tiling is used to load both the base map and the routes for a given area simultaneously, making it possible to see all available routes at once. While Waymarked Trails uses static bitmap tiles, this project uses vector tiles, which enables frontend styling and interactivity.

One of the key features of route aggregators is search. With static tiles this is not feasible, but since the introduction of Martin it is possible to pass query parameters and dynamically fetch vector tiles. This project exposes filters for route length, route type (loop vs. point-to-point), and data source. Users can adjust these filters and watch the map update in real time.

Data

To populate the application, I first wrote an ingestion script for OpenStreetMap, assuming local governments would have uploaded their trails there. Coverage turned out to be uneven, some provinces (in the Netherlands) are well-represented while others contain little useful data (note to Dutch local governments: please upload your public trails to OSM). Because most high-quality route data is locked behind commercial aggregators, I also wrote ingestion scripts for several other sources and was able to collect all their "publicly" accessible data. As this is likely a legal grey area, I am not sharing that code or the data. It is, however, very much possible.

Features

Routes are styled using MapLibre and rendered in the colors defined by their original source. Because styling happens client-side, colors remain consistent across the app and the map stays interactive. Where routes overlap, clicking any point on the map brings up a list of all routes passing through it. Selecting a route from the list opens a detail view with metadata and a direct GPX download. A feature that is frustratingly locked behind a paywall on most major aggregators.

Reflections

The application has been running well. The application has 140k+ routes from various sources collected across the Netherlands, with filtering fast enough to run on a resource-limited NAS. After using it for a while, though, I think I understand why the major aggregators don't offer an "all routes" view: there can be too many routes in certain areas even after filtering, showing them all clutters the map and can make it hard to find anything actually worth hiking.

This raises an interesting question about curation. Aggregators likely rank by popularity, which creates a feedback loop where less-discovered routes never surface. I see two ways around this: better filtering, or shifting the model entirely. Using routes not as the primary object but as a signal for where good paths exist, and dynamically generating routes from that graph. The latter is almost certainly what Komoot does behind the scenes, judging by their data, and it is the more elegant solution.

In the future I may play around with clustering and filtering based on terrain type, but for now I am happy to have a working prototype that scratches an itch I have had for years.

Architecture

graph TD
    Browser["Browser"]

        Caddy["Caddy\n:80 · :443"]
        Proxy["Nginx\n:80"]

        Frontend["Angular\n:80"]
        API["FastAPI\n:8000"]
        Martin["Martin tile server\n:3000"]
        DB[("PostgreSQL + PostGIS")]


    Ingest["Ingest scripts\nOSM and others"]

    Browser -->|HTTPS| Caddy
    Caddy --> Proxy
    Proxy -->|"/"| Frontend
    Proxy -->|"/api/"| API
    Proxy -->|"/martin/"| Martin
    API --> DB
    Martin -->|"vector tiles"| DB
    Ingest -.->|"offline"| DB
Loading

Screenshots

All routes around Deventer
All routes around Deventer
Filter panel
Filters for length, route type and data source
Route list popup
Clicking the map shows matching routes on that point
Route detail view
Detail view of a single route
Every route in the Netherlands
Every hiking route in the Netherlands

Running

Start the full stack with:

docker compose up

This starts Nginx, the Angular frontend, the FastAPI service, the Martin tile server, and PostgreSQL. See the individual READMEs for running each part in isolation or for ingesting data:

About

Explorinator combines hiking routes from multiple major aggregators into a single searchable map

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors