2026-08-07
2026-08-07

Tile Hunting with Open-Source Tools

Tile hunting adds a simple exploration game to cycling, walking, running, and other outdoor activities. A map is divided into a fixed grid, and every tile crossed by one of my recorded GPS tracks is marked as explored.

This creates a different motivation from conventional activity statistics. Distance, speed, and elevation still matter, but tile hunting also rewards curiosity. A single missing tile can be enough reason to leave a familiar route, investigate a side road, or visit a nearby place that I have never seen before.

Two sizes of tiles

The grid is based on the same Web Mercator tile system commonly used by online maps. At every zoom level, the world is divided into more tiles: increasing the zoom level by one splits every tile into four smaller tiles—two in each direction.

I collect tiles at two zoom levels:

  • Zoom level 14 (z14) for larger-scale exploration
  • Zoom level 17 (z17) for detailed local exploration

At the latitude of Germany, a z14 tile is roughly 1.5 kilometres wide. This makes it a useful size for cycling: the tiles are small enough to encourage exploration but large enough that a substantial area can be covered during a bicycle ride.

The z17 grid is considerably finer. There are three zoom levels between z14 and z17, and every additional level halves the width of a tile. A z17 tile is therefore one eighth as wide as a z14 tile, or roughly 190 metres across in Germany.

In terms of area, one z14 tile contains an ( 8 \times 8 ) grid of z17 tiles:

[ 8 \times 8 = 64 ]

The finer z17 tiles are sometimes called squadratinhos. They work particularly well for walking and for detailed exploration close to home. While a bicycle ride can quickly cross a large number of z14 tiles, filling the z17 grid may require visiting individual streets, paths, parks, and corners of a neighbourhood.

When is a tile explored?

The basic rule is deliberately simple: a tile counts as explored when at least one recorded activity enters it.

Once a track enters a tile, that tile becomes part of my explored map. It does not matter how far I travel through it—a short visit across one corner is sufficient.

This simplicity occasionally makes route planning more interesting than expected. A road may appear to pass through a tile but remain just outside its boundary. Conversely, a small detour of only a few metres may be enough to collect it.

Building a cluster

Collecting isolated tiles is enjoyable, but the more interesting goal for me is to create a large connected area.

The software I use distinguishes between an explored tile and a cluster tile. An explored tile becomes part of the cluster calculation when all four of its direct neighbours have also been explored:

  • the tile to the north,
  • the tile to the south,
  • the tile to the east,
  • and the tile to the west.

Diagonal neighbours do not count.

This means that simply drawing a narrow line of explored tiles through the landscape does not create a large cluster. To activate the tiles along that line, I also need to explore the surrounding tiles. The result rewards broad, continuous coverage rather than isolated routes.

Adjacent cluster tiles are grouped into connected clusters. A single missing tile can prevent parts of an explored area from joining together, while filling one strategically placed gap may connect several smaller clusters into a much larger one.

That is why gaps become particularly tempting. On the map, they are immediately visible as holes in an otherwise explored region. Filling them often becomes the objective of my next ride.

Growing the maximum square

The maximum square is a related but even stricter challenge. It is the largest completely explored square that can be placed on the tile grid.

A maximum square of size ( n ) must contain ( n \times n ) explored tiles without any gaps. For example:

  • a square of size 5 contains 25 tiles,
  • a square of size 10 contains 100 tiles,
  • and a square of size 20 contains 400 tiles.

Increasing a square from size ( n ) to size ( n + 1 ) becomes progressively harder. It requires adding an entire row and an entire column around the existing square:

[ (n + 1)^2 - n^2 = 2n + 1 ]

Growing a 10 × 10 square into an 11 × 11 square therefore requires at least 21 additional tiles—and possibly more if the square has to move in a different direction to avoid inaccessible areas.

The square must also remain completely filled. One missing tile caused by a river, railway, motorway, military area, industrial site, or private property can block expansion in that direction. This turns the maximum square into both an exploration challenge and a routing puzzle.

In practice, the cluster and maximum square encourage slightly different strategies:

  • Total explored tiles reward going anywhere new.
  • The cluster rewards connecting and filling a wider region.
  • The maximum square rewards systematic and gap-free exploration.

I mainly use the z14 grid for bicycle rides and for extending my regional cluster and maximum square. The much finer z17 grid provides a separate challenge for local exploration, where individual streets and paths make a visible difference.

Commercial platforms such as Squadrats and VeloViewer provide similar functionality. My setup, however, is based on open-source tools and keeps the activity data under my control.

Recording and importing activities

Most of my activity data comes from Garmin Connect. I record walks and other activities with a Garmin Instinct 2, while bicycle rides are recorded with a Garmin Edge Explore 2.

To retrieve the original activity files from Garmin Connect, I use GarminDB. Besides downloading activity files, GarminDB can import Garmin health and activity data into local SQLite databases. Importantly, it retains the downloaded FIT and JSON files, so they can be processed again without repeatedly downloading them from Garmin Connect.

These activity files are then imported into Geo Activity Playground, a self-hosted application for exploring GPS tracks. It accepts several common formats, including FIT, GPX, TCX, KML, and CSV.

Geo Activity Playground provides much more than tile hunting—such as heatmaps, activity statistics, equipment tracking, and yearly summaries—but its Explorer Tiles are the feature that interests me most.

Planning routes to unexplored tiles

Knowing which tiles are missing is only half the job. I also need a convenient way to plan routes through them.

Geo Activity Playground exposes the explored tiles through a map tile server. I add that server as a custom overlay in Bikerouter, my preferred tool for planning bicycle routes.

Bikerouter supports custom tile layers, so my explored area can be displayed directly on top of the routing map. I can immediately see:

  • which tiles I have already visited,
  • where there are gaps in my cluster,
  • and which roads could connect new tiles to the existing area.

From there, I plan a route that passes through the missing tiles, export it, and transfer it to my bicycle computer. After completing the ride, the new Garmin activity passes through the same processing pipeline and the additional tiles appear on the map.

The result is a satisfying loop:

  1. Record an activity with a Garmin device.
  2. Extract the original activity file using GarminDB.
  3. Import it into Geo Activity Playground.
  4. Inspect the newly explored tiles.
  5. Plan the next ride in Bikerouter.
  6. Extend the connected cluster.

Displaying the results on my website

The tile server is not limited to route planning. I also use it to display my explored area on the /tiles page of this website.

The map itself loads the tiles exposed by Geo Activity Playground. Additional statistics are queried through Datasette, which provides a web interface and JSON API for the underlying SQLite data.

This is currently a small workaround: Geo Activity Playground does not yet expose the statistics I need through a dedicated API. Datasette lets me access them without introducing a separate application or duplicating the data.

Why I prefer this setup

The setup has more moving parts than registering with a commercial tile-hunting service, but it also gives me considerably more control:

  • My original activity files remain available locally.
  • The processing is performed with open-source software.
  • I am not tied to a particular activity platform.
  • The tile layer can be used in different maps and routing tools.
  • I can publish the map and statistics on my own website.
  • I can adapt the workflow when my requirements change.

Most importantly, tile hunting gives me a reason to explore. A small gap in the cluster can turn an ordinary bicycle ride into a route-planning puzzle—and often leads me to interesting places surprisingly close to home.