Table of Contents

How to Create Map Tiles

Allthough there are open street map tile servers, they are not intended for mass tile downloads. This article describes one way of hosting and downloading map tiles locally.

This is based on tilemaker, tileserver-gl and data in the format of *.mbtiles .

Create a working directory maps/. You can either follow the instructions of this tutorial, to create your own up to date *.mbtiles files or download one from here:

If this is not up to date enough, keep reading…

Tilemaker

Build and install tilemaker. As this tool uses tons of RAM I recommend to run this natively, without docker.

Tilemaker (docker)

cd maps/
git clone https://github.com/systemed/tilemaker.git
cd tilemaker
docker build -t tilemaker .

Tilemaker (native, macOS)

brew install lua@5.1
brew install protobuf
brew install boost
brew install shapelib
brew install sqlite3

switch -std=c++11 to -std=c++14 in Makefile, then continue wi

tileserver-gl

The easiest way to run tileserver-gl is

docker run --rm -it -v /Users/paul/data/maps/:/maps/ -p 1080:8080 maptiler/tileserver-gl --mbtiles /maps/yourmap.mbtiles

tileserver-gl (custom styles)

Create a directory tileserver-gl-custom and add the following docker file:

FROM maptiler/tileserver-gl
LABEL maintainer="Paul Smith <code@uvwxy.de>"

ADD style.json /app/node_modules/tileserver-gl-styles/styles/basic-preview/style.json

Then add a custom style.json to create your custom styling into this folder:

Build the container in the directory with:

docker build -t tileserver-gl-custom .

Comparison of style

The result is mainly increased contrast, and road colors.

Creating .mbtiles

Download openstreetmap data in the *.pbf format to your working directory.

  • see: https://download.geofabrik.de

Download ocean shapefiles and extract the fiels to the folder ./coastline/ from:

  • https://osmdata.openstreetmap.de/data/water-polygons.html “Format: Shapefile, Projection: WGS84 (Large polygons are split)”

Create an *.mbtiles file from the openstreetmap data ( *.pbf ):

time tilemaker/tilemaker \
    --input your-map.osm.pbf \
    --process ./tilemaker-osw-tiles-11-14.lua \
    --config ./tilemaker-osw-tiles-11-14.json \
    --output your-map.osm.mbtiles

Config Files

The biggest challenge of creating maptiles with tilemaker/tileserver-gl is the required memory.

I approached this by taking the original files and decimating them as much as possible to obtain “usable” maps and run times.

Performance of tilemaker-osw-tiles.*

These config files are based on the original tilemaker-openmaptiles.json/.lua from https://github.com/systemed/tilemaker/tree/master/resources.

Converting belgium-latest.osm.pbf with the original config files takes:

real    6m52.943s
user    44m29.355s
sys     1m0.178s

With tilemaker-osw-tiles.json/lua it is a bit quicker:

real    2m48.898s
user    7m29.045s
sys     0m14.416s

Converting germany-latest.osm.pbf with the original config files takes:

real    144m31.561s
user    337m33.332s
sys     578m57.424s

With tilemaker-osw-tiles.json/lua it is a bit quicker:

real	82m9.525s
user	426m54.306s
sys     60m43.066s

Converting germany-latest.osm.pbf required around 25GB of RAM. (more than 32GB with the original json/lua).

This is achieved by leaving out any pois/houses/gardens/… . The layers 4-14 are created.

Creating *.png tiles

Next step is to download all *.png tiles provided/generated by the local tileserver-gl. You can do this by downloading them from localhost. This will most likely result in thousands to millions of files.

Writing millions of tiles onto an SD card directly, one by one, will likely fail, as in my case, I broke a 128GB uSD card…

The recommended approach is to create a RAM disk and then dd the resulting image as a single write onto the sdcard.

HowTo - MacOS

Creat a RAM disk with a FAT32 partition.

# 4GB: 8388608
# 8GB: 16777216
# 16GB: 33554432
diskutil erasevolume fat32 'TILEDISK' `hdiutil attach -nomount ram://33554432`

Downloading tiles

To automate the downloading of the the tiles I wrote a nodejs script:

Next, download the tiles to you RAM disk.

Example (Brussels area, layers 13 to 17 (incl.)):

node get-tiles.js 4.21 50.76 4.51 50.95 13-17 /Volumes/TILEDISK/map/
Creating jobs
Adding jobs for layer 13
Adding jobs for layer 14
Adding jobs for layer 15
Adding jobs for layer 16
Adding jobs for layer 17
Will download 16530 tiles into  /Volumes/TILEDISK/map/
100 / 16530
200 / 16530
....
16530 / 16530

Use this tool to create the left, bottom, right, top coordinates of your desired rectangle of map data and zoom layer:

This also provides count/size estimations of the created png data.

Writing the partition to the SD card

For the next part I assume, you know what you are doing:

⚠️ First, find out which disk ist your SD card. ⚠️ ⚠️ If you get this wrong you will mess up your system ⚠️

You can check with:

df -h

Writing the RAM disk to your systems disk

In my case the RAM disk was /dev/rdisk5:

sudo dd if=/dev/rdisk5 of=/Users/paul/data/maps/TILEDISK

Writing the system from your disk to SD

In my case the sd card was /dev/rdisk2:

brew install pv # install helper, to monitor progress
pv /Users/paul/data/maps/TILEDISK | sudo dd of=/dev/rdisk2 bs=1024000

Mounting a previously crated Image

Mount the image:

hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount /Users/paul/data/maps/TILEDISK

Check the resulting output under which device the raw disk image is now available, and then mount it:

hdiutil attach -readonly /dev/rdisk3