Setting up a map server takes several technical steps: getting the geographic data, turning it into vector tiles, then serving them through a dedicated server.
This article explains how to build an infrastructure that generates and serves maps from OpenStreetMap (OSM) data.
We will cover:
-
the data sources
-
extracting an area
-
generating vector tiles
-
optimizing the data
-
deploying a tile server
Pipeline overview
Generating a vector map usually follows this pipeline:
OpenStreetMap (PBF) => osmium (extraction / cleanup) => tilemaker / planetiler => MBTiles / PMTiles => TileServer / Martin => Client (MapLibre / Mapbox GL)
Each step transforms the data to make it more compact, faster to load and suited to web display.
Plan your own route
MapPlanner helps you calculate distances, elevation, duration and plan your hikes, runs and bike rides on an interactive map.
- Automatic distance & elevation calculation
- Topographic, satellite maps
- GPX/KML export for your GPS
- Real-time GPS tracking
1. Mapping tools and resources
Before starting, it helps to know the main data resources and tools of the mapping ecosystem.
General tools
A few useful resources:
Handy tools to work with the data:
-
Compute a bounding box
https://tools.geofabrik.de/calc/ -
Extract a geographic area
https://extract.bbbike.org/ -
Extract specific data
https://overpass-turbo.eu/
These tools let you, for example:
-
select a precise geographic area
-
fetch only some OSM objects
-
prepare the data before transforming it.
2. Geographic data sources
A map is built from several kinds of data:
-
vector data (roads, buildings, POIs)
-
topographic data (relief, glaciers)
-
simplified global data (continents, oceans)
Topographic data
This data enriches the map with natural features:
-
relief
-
oceans
-
glaciers
-
urban areas
Sources:
OpenStreetMap data
OSM data is distributed in the PBF (Protocolbuffer Binary Format) format.
This format is compact and optimized for processing.
World
Europe / regions
3. Downloading and extracting the data
Full OSM files can be extremely large.
For example:
-
whole planet: more than 80 GB in PBF format
-
Europe: several tens of GB
It is therefore often better to work on a sub-area.
Installing osmium
Osmium is a fast tool for handling PBF files.
Installation:
sudo apt-get install osmium-tool
Extracting a geographic area
The following command extracts a specific region from a larger file.
osmium extract --bbox=2.68,50.72,7.55,54.12 --set-bounds --strategy=smart europe-latest.osm.pbf --output netherlands.osm.pbf
Explanation:
-
--bboxsets the geographic area -
--set-boundswrites the bounds into the file -
--strategy=smartoptimizes the extraction
4. Converting to vector tiles
OSM data is vector data, but it cannot be used directly for web map rendering.
It has to be converted to the Mapbox Vector Tiles (MVT) format.
This conversion:
-
cuts the data into tiles
-
simplifies the geometries
-
improves client-side performance.
The tool used here is Tilemaker.
5. Installing Tilemaker
Install the required dependencies:
sudo apt install libboost-dev libboost-filesystem-dev libboost-iostreams-dev libboost-program-options-dev libboost-system-dev shapelib lua5.4 liblua5.4-dev libprotobuf-dev libshp-dev libsqlite3-dev protobuf-compiler rapidjson-dev
Clone the project:
git clone https://github.com/systemed/tilemaker.git
cd tilemaker
Build:
make
sudo make install
6. Configuring Tilemaker
Tilemaker works with two main files:
JSON file
It describes:
-
the layers
-
the zoom levels
-
the data sources
resources/config-openmaptiles.json
Lua file
The Lua file defines how OSM objects are turned into map layers.
resources/process-openmaptiles.lua
For example, this file decides:
-
which roads are displayed
-
which buildings are included
-
which POIs appear.
Documentation:
https://github.com/systemed/tilemaker/blob/master/docs/CONFIGURATION.md
7. Extra data for Tilemaker
Some data has to be added to improve the visual quality of the maps.
Coastlines and land cover
Run the scripts:
./get-coastline.sh
./get-landcover.sh
These scripts download:
-
coastlines
-
oceans
-
some simplified natural areas.
Handling cliffs
Tilemaker handles cliffs as polygons.
In OSM, however, they are usually mapped as lines.
They therefore have to be extracted separately.
Installing Osmosis:
https://wiki.openstreetmap.org/wiki/Osmosis/Installation
Extraction:
./bin/osmosis --rbf europe-latest.osm.pbf --way-key-value keyValueList=natural.cliff --wb europe_cliffs.osm.pbf
Conversion to a shapefile:
sudo apt-get install gdal-bin
ogr2ogr -lco ENCODING=UTF-8 -f "ESRI Shapefile" europe_cliffs.shp europe_cliffs.osm.pbf lines
Then copy the generated files (.shp, .shx, .dbf, .prj) into tilemaker’s landcover/ directory, the one referenced by the configuration below.
Then add the layer to the configuration:
"cliff": { "minzoom": 8, "maxzoom": 14, "source": "landcover/europe_cliffs.shp" },
8. Optimizing the PBF file
Before generating the tiles, optimizing the data is recommended.
osmium cat -f pbf your-file.osm.pbf -o optimized.osm.pbf
This operation:
-
reorganizes the data
-
speeds up processing.
Typical usage:
-
2 GB of RAM
-
about 14 minutes
9. Generating the vector tiles
The tiles are generated with Tilemaker.
./tilemaker FILE.osm.pbf --output FILE.mbtiles --config resources/config-map-planner.json --process resources/process-map-planner.lua
Result:
-
an MBTiles file
-
holding all the vector tiles.
10. Generation performance
Performance depends on the area processed.
Examples:
| Area | Time | RAM |
|---|---|---|
| Europe | 30 min | 40 GB |
| North America | 24 min | 40 GB |
| World | 1 h 20 | 60 GB |
Recommended disk space: ≈600 GB
Options to reduce RAM usage
Tilemaker offers several options:
-
--store -
--shard-stores -
--compact
To use --compact, the data has to be renumbered:
osmium renumber -o optimized-renumbered.osm.pbf optimized.osm.pbf
11. Alternative: Planetiler
Planetiler is a modern alternative to Tilemaker.
Installation:
sudo apt install default-jre default-jdk
Generation:
java -Xmx136g -jar planetiler.jar --config=config.properties
Example:
https://onthegomap.github.io/planetiler-demo/#9.09/46.122/6.155
12. Alternative: OpenMapTiles
OpenMapTiles provides a complete Docker environment.
Clone the project:
git clone https://github.com/openmaptiles/openmaptiles.git
cd openmaptiles/
nano .env
Edit:
PGPORT=5432
...
MAX_ZOOM=14
Generation:
./quickstart.sh france
13. Optimizing client-side rendering
Rendering happens in the browser.
Rendering time depends mainly on:
render time = constant time
+ [ number of sources * per source time ]
+ [ number of layers * per layer time ]
+ [ number of vertices * per vertex time ]
Possible optimizations:
-
fewer layers
-
fewer sources
-
fewer POIs
-
drop unneeded IDs
-
limit filter computations.
14. Installing the tile server
The server used is TileServer GL.
Create the structure:
mkdir tileserver
cd tileserver
mkdir data fonts glyphs sprites styles
Layout:
-
data → mbtiles files
-
fonts → fonts
-
styles → Mapbox styles
15. Configuring the server
Create config.json:
{
"options": {
"paths": {
"root": "",
"fonts": "fonts",
"sprites": "sprites",
"styles": "styles",
"mbtiles": "data"
},
"domains": [
"maps.map-planner.com",
"maps-a.map-planner.com",
"maps-b.map-planner.com"
],
"frontPage": true,
"formatQuality": {
"jpeg": 80,
"webp": 90
},
"maxScaleFactor": 3,
"maxSize": 2048,
"pbfAlias": "pbf",
"serveAllFonts": false,
"serveAllStyles": false,
"serveStaticMaps": true,
"tileMargin": 0
},
"styles": {
"map-planner": {
"style": "map-planner/style.json"
}
},
"data": {
"world": {
"mbtiles": "world.mbtiles"
},
"natural_earth_relief": {
"mbtiles": "natural_earth_relief.mbtiles"
}
}
}
The styles are then declared in the full configuration.
16. Starting the server
With Docker:
sudo docker pull maptiler/tileserver-gl
sudo docker run -it -v $(pwd):/data -p 5010:8080 maptiler/tileserver-gl -c config.json
NPM alternative:
npm install tileserver-gl-light
17. Running it as a systemd service
Create a service:
sudo nano /etc/systemd/system/tileserver.service
Start it:
sudo systemctl daemon-reload
sudo systemctl enable tileserver.service
sudo systemctl start tileserver.service
sudo systemctl status tileserver.service
18. MBTiles vs PMTiles
Comparison:
| Format | Characteristics |
|---|---|
| MBTiles | local SQLite database |
| PMTiles | single file optimized for HTTP, lighter (15–25%) |
Conversion
./pmtiles convert natural_earth_relief.mbtiles natural_earth_relief.pmtiles
19. Alternative server: Martin
Martin is a modern vector tile server written in Rust.
Documentation:
20. Using it with MapLibre
Install PMTiles:
yarn add pmtiles
Register the protocol:
import { addProtocol } from 'maplibre-gl';
import { Protocol as PMTilesProtocol } from 'pmtiles';
const protocol = new PMTilesProtocol();
addProtocol('pmtiles', protocol.tile);