Finnish Topographic Maps as Vector Tiles: A Complete Pipeline
Finland's National Land Survey (Maanmittauslaitos, or MML) publishes comprehensive topographic data under an open license. Turning it into smooth, interactive vector-tile maps requires navigating Finnish coordinate systems, massive shapefiles, and a tile generation pipeline with opinions. This is the complete path from raw download to rendered map.
The Starting Point: MML Open Data
Maanmittauslaitos — Finland's National Land Survey — publishes its topographic data under a Creative Commons 4.0 Attribution license. This is genuinely good news for anyone who wants to build map applications over Finnish terrain without paying Esri licensing fees or trying to extract useful data from OpenStreetMap's relatively sparse coverage of Finnish forests and waterways.
What's available in the MML open data catalogue is substantial:
- Topographic Database (MTK) — the core vector dataset: roads, railways, waterways, buildings, land use polygons, forests, power lines, shorelines, and more
- Elevation data — a national DEM at 2-metre resolution, which is excellent for contour generation and hillshading
- Aerial imagery — ortho photos at various resolutions (not used in this pipeline, but available)
- Address data — building-level addresses across Finland
- Administrative boundaries — municipalities, regions
The download process runs through tiedostopalvelu.maanmittauslaitos.fi. You can download by municipality, by map sheet index, or use the WFS endpoints for smaller programmatic extracts. For a national tile pipeline, I downloaded the full MTK dataset as shapefiles — about 22 GB compressed. This took a while. Finland is 338,000 square kilometres, most of it either forest or water, and the MTK captures all of it at high resolution.
Step Zero: The Coordinate System Problem
Everything MML publishes is in ETRS-TM35FIN, which is EPSG:3067. This is the Finnish national projected coordinate system — a transverse Mercator projection using the GRS80 ellipsoid, with a central meridian at 27° East. It's accurate for Finland. It is also completely unknown to every web mapping library in existence.
The web tile world runs on two systems: WGS84 geographic coordinates (EPSG:4326) for data storage, and Web Mercator (EPSG:3857) for tile rendering. Mapbox, MapLibre, Leaflet, OpenLayers — all expect one of these. Your first processing step on any MML data is reprojection.
The tool for this is GDAL's ogr2ogr. For large shapefiles, the reproject-on-ingest approach works well:
ogr2ogr \
-f GeoJSON \
-s_srs EPSG:3067 \
-t_srs EPSG:4326 \
-progress \
roads_wgs84.geojson \
mtk_roads.shp
The full MTK reprojection is several hours of CPU time if you're doing it sequentially. I parallelised across layers using a small shell script. You need patience, a decent CPU, and enough disk space to hold both the source shapefiles and the reprojected GeoJSON or GeoPackage output simultaneously. Budget 60–80 GB total during the conversion phase.
Understanding the MTK Structure
The MTK (Maastotietokanta) is organised into feature classes that broadly correspond to what you'd find in any topographic database, but with Finnish specifics. The key layers for a topo basemap:
- Road network — classified by type (motorways, main roads, local roads, forest roads, paths). The road classification codes are Finnish-specific and map only loosely to OpenStreetMap highway tags.
- Waterways — rivers, streams, canals, drainage ditches. Finland has approximately 187,888 lakes (yes, that's the official number), so the water layer is dense.
- Buildings — footprints with type codes; useful for urban areas but sparse in rural zones
- Land cover — forest type (coniferous, deciduous, mixed), marshes, open areas
- Elevation contours — not in the MTK directly; generated separately from the DEM raster
- Power lines, pipelines, fences — linear features that matter for off-trail navigation
- POI layer — cottages, campsites, wilderness huts, rapids, falls, peaks
One thing that caught me early: the MTK building layer uses a type code system that isn't documented in the English-language materials. The Finnish documentation is complete; the English documentation is not. If you're going to do anything more than basic rendering, you will need to read Finnish, or at minimum work alongside someone who does.
Tippecanoe: Generating the Tiles
Mapbox's Tippecanoe is the standard tool for converting GeoJSON into MBTiles vector tile files. It handles simplification, attribute filtering, and zoom-level behaviour. For Finnish data at national scale, the parameters require some thought.
The core concern is tile size. The default Tippecanoe configuration will produce tiles that are too large at low zoom levels because Finnish forests and water bodies are represented as very detailed polygons. A lake with 10,000 vertices in the source data does not need 10,000 vertices in a zoom-8 tile. Tippecanoe's --simplification and --coalesce-densest-as-needed flags are your friends here.
My final invocation for the forest/land cover layer:
tippecanoe \
-o forests.mbtiles \
-l forests \
--minimum-zoom=5 \
--maximum-zoom=14 \
--simplification=4 \
--coalesce-densest-as-needed \
--extend-zooms-if-still-dropping \
--drop-densest-as-needed \
--force \
forests_wgs84.geojson
Road data needed different treatment — polylines simplify less aggressively than polygons, and road topology needs to remain connected at all zoom levels. I used --simplification=2 for roads and kept them from zoom 6 upward.
Tile generation at national scale for all layers combined took approximately 8 hours on a 16-core machine. The resulting MBTiles database for all layers combined (roads, water, forests, buildings, contours, POIs) came to about 4.2 GB.
Elevation Contours from 2-Metre DEM
The MML DEM at 2-metre resolution is one of the best publicly available elevation datasets in Europe. Most countries offer 10-metre or 25-metre DEMs; Finland's 2-metre product makes a genuine difference for visualising terrain in a country where relief is subtle. Finnish topography doesn't have dramatic alpine features — the highest point, Halti, is 1,328 metres — but the difference between a 2-metre and 10-metre DEM is the difference between useful and useless for hiking route planning in the forest zones.
Contour generation from the DEM uses GDAL again:
gdal_contour \
-a elev \
-i 5 \
-f GeoJSON \
dem_2m_merged.tif \
contours_5m.geojson
I generated both 5-metre and 25-metre contour intervals from the same DEM. The 5-metre contours appear at zoom 13 and above; the 25-metre index contours at zoom 10 and above. This mirrors the convention used on printed Finnish 1:25,000 topo sheets, which felt like the right reference point for this project.
The contour GeoJSON for the full country at 5-metre intervals is enormous — over 15 GB uncompressed. Tippecanoe handles it, but you want to run it on a machine with at least 32 GB of RAM and be prepared for a multi-hour run. Running out of memory mid-job and having to restart is an experience I've had once; I added swap space before any subsequent large runs.
The Style Challenge: Finnish Cartographic Conventions
This is where the project becomes less about data engineering and more about cartography. Finnish topographic maps have a distinct visual language: an earthy, muted palette; specific symbols for features like wilderness huts (autiotupa), fishing spots, rapids; blue-grey water with precise shoreline treatment; coniferous forest rendered differently from deciduous; marshes with their distinctive hatching pattern.
MapLibre GL uses a JSON style specification where you define how each vector layer should be rendered. Getting Finnish topo conventions into that format requires translating between two different style systems.
Here's a simplified fragment showing how I style road classifications. Finnish road class codes in the MTK map to five rendering classes:
{
"id": "roads-casing",
"type": "line",
"source": "mml-topo",
"source-layer": "roads",
"filter": ["!=", ["get", "luokka"], 12],
"paint": {
"line-color": "#c8b89a",
"line-width": [
"interpolate", ["exponential", 1.5], ["zoom"],
5, ["match", ["get", "luokka"],
[1, 2], 2,
[3, 4], 1,
0.5],
12, ["match", ["get", "luokka"],
[1, 2], 8,
[3, 4], 5,
2],
18, ["match", ["get", "luokka"],
[1, 2], 20,
[3, 4], 14,
6]
],
"line-gap-width": [
"interpolate", ["exponential", 1.5], ["zoom"],
5, 0,
12, ["match", ["get", "luokka"],
[1, 2], 4, [3, 4], 2, 0],
18, ["match", ["get", "luokka"],
[1, 2], 12, [3, 4], 8, 2]
]
}
}
The luokka attribute is the Finnish MTK road class code: 1 is motorway, 2 is main road, 3 is regional road, 4 is local road, and higher numbers are forest roads and paths. The interpolate / exponential expression scales line width smoothly between zoom levels rather than jumping discretely, which is what makes MapLibre maps feel fluid rather than snapping.
Symbols: Finnish POI Conventions
One of the more time-consuming parts of the project was recreating Finnish cartographic symbols as SVG sprites for MapLibre. The standard Finnish topo symbols — the wilderness hut icon, the lean-to shelter icon, the spring, the isolated tree, the rapid — are not in any open symbol library. I drew them by hand in Inkscape, referencing scanned 1:50,000 Finnish topo sheets as the source of truth.
MapLibre expects a sprite sheet: a single PNG with all symbols and a companion JSON file describing the pixel coordinates and dimensions of each symbol. I generated these with the spritezero tool, which accepts a directory of SVGs and outputs the paired sprite/JSON.
Getting symbols to render at the right size across display densities (standard vs retina) requires providing both a 1x and 2x sprite sheet. This is a small but non-obvious requirement that isn't prominently documented. I spent an afternoon confused about why my symbols looked blurry on a retina display before finding the relevant MapLibre spec section.
Hosting: Martin Tile Server on a Small VPS
The MBTiles database needs a tile server that can serve individual tiles from the SQLite-based MBTiles file in response to HTTP requests. I used Martin, the Rust-based tile server from the MapLibre project. It's fast, it's lightweight, and it serves MBTiles files directly without any intermediate layer.
The server configuration is a single YAML file. Martin on a 2-core, 4GB RAM VPS handles the tile serving comfortably for personal use. The monthly cost for that tier on a European VPS provider (I use Hetzner) is roughly 5 euros. For a personal project, this is very reasonable.
The one operational consideration: tile caching. Martin doesn't have built-in caching; it reads from the MBTiles database on every request. For low traffic this is fine. If you're serving to many concurrent users, you'll want Nginx or Varnish in front of Martin, or a CDN with tile caching rules. For kherrala.fi usage this hasn't been a concern.
The Open Source Contribution
The full pipeline — the Tippecanoe configurations, the ogr2ogr reprojection scripts, the CartoCSS style rules, the Martin deployment configuration — is available in two forks on my GitHub: kherrala/mml-mapserver and kherrala/mml-vectortiles. The forks are adapted from the upstream mml-mapserver project with Finnish-specific tuning accumulated over the course of this project.
The CartoCSS style in the vectortiles fork covers all major MTK feature classes with Finnish-appropriate rendering. It's not complete — there are edge cases in the building type codes and some POI categories I haven't styled yet — but it's a usable starting point for anyone who wants to build over MML data without starting from scratch.
The Result
The end product is a topographic basemap covering all of Finland at zoom levels 1 through 18, with 5-metre contour lines, proper Finnish land cover rendering, and the full MTK road and waterway network. It looks like a Finnish topo map, not a navigation app. It renders smoothly in MapLibre GL. It costs roughly 5 euros per month to host.
The pipeline from raw MML download to rendered map takes about two days of processing time and one week of styling. Most of that week is spent looking at scanned Finnish topo sheets and asking yourself whether your forest polygons are the right shade of green.
If you're building any application over Finnish geographic data — hunting platforms, outdoor routing, municipal services, environmental monitoring — the MML open data pipeline is worth understanding. The licensing is clean, the data quality is high, and the 2-metre DEM in particular is a significant asset. The coordinate system problem is a one-time hurdle. Everything after that is engineering.