OSM Vector Tiles & Rendering Pipelines Jump to heading
Everything earlier on this site produces data: parsed elements, normalized tags, validated geometry, rows in a warehouse. This section covers the point at which that data has to be looked at — and the specific, unusual constraints that appear when a continent of geometry must be delivered to a browser a few hundred kilobytes at a time.
It serves mapping engineers building a tile pipeline, ETL developers whose normalized output feeds a map, and GIS analysts who need to understand why a feature visible at one zoom vanishes at the next. The unifying idea is that vector tiles are not a picture format and not a data format, but a transport format with its own geometry model, and most of the surprises come from treating them as either of the other two.
The Tile Model Jump to heading
A tile set is a pyramid. At zoom 0 the whole world is one tile; each zoom level quadruples the tile count, so zoom 14 has over 268 million tiles and zoom 20 has more than a trillion. That growth is the central engineering fact of the whole section: you cannot render every tile at every zoom, so a tile pipeline is mostly a set of decisions about what to leave out.
Tiles are addressed by a zoom level and an x/y pair in a quadtree, which is the same structure discussed in Spatial Index Selection: R-tree vs H3 vs Quadkey — a quadkey is a tile address written as a single string, and the interchangeability is useful when you want to join tiles to indexed data.
Each tile carries layers, each layer carries features, and each feature carries geometry plus attributes. Crucially, the tile carries no styling: no colours, no line widths, no fonts. Those live in a style document applied by the client. A pipeline that bakes styling decisions into tile attributes has confused the two, and will need to regenerate the whole pyramid the first time a designer changes their mind.
Geometry: the Tile-Local Integer Grid Jump to heading
The single most consequential detail of the Mapbox Vector Tile format is that geometry is not stored in geographic coordinates. Each tile defines an extent — conventionally 4096 — and all coordinates inside it are integers on a grid from 0 to that extent, relative to the tile’s own corner.
Three things follow, and each one causes a recognisable class of bug.
Precision is a function of zoom. At the equator, a zoom-14 tile is roughly 2.4 kilometres across, so one grid unit at extent 4096 is about 60 centimetres. At zoom 8 the same grid spans a tile roughly 156 kilometres wide, and one unit is about 38 metres. Geometry is quantised to that grid, which is why a coastline traced to centimetre precision looks blocky at low zoom regardless of how carefully it was simplified.
Features must be clipped. A road crossing a tile boundary appears in both tiles, clipped at the edge. Clipping exactly at the boundary produces visible seams when the client draws a thick line, which is why tiles carry a buffer: geometry is kept slightly beyond the edge so the client has something to draw into the margin. Choosing that buffer is a real trade-off, developed in Choosing Tile Extent and Buffer Values.
A feature can be split. A long motorway is not one feature in the tile set; it is one feature per tile it passes through, each carrying the same identifier and attributes. Clients that assume feature uniqueness across tiles — for hover highlighting, for counting — get this wrong in ways that only appear at tile boundaries.
Generators and What They Assume Jump to heading
Three tools dominate OSM tile production, and they differ less in output than in what they expect you to have already done.
Tippecanoe takes GeoJSON and produces an MBTiles archive. It is unopinionated about schema — whatever properties your features carry become tile attributes — and extremely good at the zoom-dependent decisions: dropping features as you zoom out, coalescing, and hitting a tile size budget automatically. It expects you to have already turned OSM into GeoJSON, which is a job for the export workflows in Exporting OSM to GeoParquet & PostGIS. Building OSM Tiles with Tippecanoe covers its model.
Planetiler reads a PBF directly and produces a tile archive in one pass, with the schema expressed in code. It is built for planet-scale throughput and is the fastest route from an extract to a complete tile set, at the cost of a less flexible intermediate stage.
Tilemaker also reads PBF directly, with the schema expressed as a Lua profile plus a JSON configuration. It is lighter than Planetiler and the profile is easy to iterate on, which makes it a good fit for a custom schema over a regional extract. Both are covered in Planetiler & Tilemaker Workflows.
Generalization: What to Draw When You Zoom Out Jump to heading
A base map at zoom 5 cannot contain every building in Europe, and the tile size budget will not permit it even if you wanted to. Generalization is the set of decisions that make a low-zoom tile both small and legible, and it has three distinct mechanisms.
Selection drops whole features below a zoom: minor roads disappear before major ones, small lakes before large ones. This is usually driven by a rank attribute computed once during layer assignment rather than recomputed per zoom.
Simplification reduces vertex counts. The important subtlety is that simplification tolerance should be expressed in tile grid units, not metres, because the grid is what the geometry will be quantised to anyway. Simplifying to a finer tolerance than the grid does nothing except cost time.
Aggregation merges adjacent features into one — a block of buildings into a built-up area, a cluster of small water bodies into a single polygon. This is the hardest of the three and the one that most improves low-zoom legibility. Merging Adjacent OSM Polygons for Low Zoom works through it, and Simplifying OSM Geometry per Zoom Level covers the first two.
Schema Design: the Contract Between Pipeline and Style Jump to heading
Between the OSM tag model and the map a reader sees sits a tile schema: the list of layer names, the attributes each layer carries, and the zoom range over which each feature type appears. It is the most under-appreciated artefact in the whole pipeline, because it is simultaneously a data contract and a design document, and the two audiences want different things from it.
The pipeline wants a schema that is cheap to produce: attributes derived directly from tags, few enough distinct values that the per-layer value tables stay small, and rules simple enough to evaluate per feature without a database lookup. The style wants a schema that is expressive: enough distinction between a motorway and a residential street to draw them differently, enough attributes to place labels, and stable layer names that do not change when the pipeline is refactored.
Three conventions keep both audiences satisfied. Name layers by what they are, not by how they look — a layer called transportation survives a redesign that a layer called thick_orange_lines does not. Collapse tag values into a small closed vocabulary rather than passing raw OSM values through: mapping several dozen highway values onto half a dozen classes shrinks the value table, makes the style simpler, and insulates the map from a new tag appearing upstream. And version the schema explicitly, because a style built against one version of a layer list will break silently against another — a feature that stops appearing is much harder to notice than one that errors.
Reusing an established schema rather than inventing one is usually the right call for a general-purpose base map, because it lets you adopt existing styles without writing one. A custom schema earns its keep when the map is thematic — a cycling map, an accessibility map, a utility network — and the attributes you need are precisely the ones a general schema discards. The tag-to-schema mapping itself is the same work as Mapping OSM Tags to a Fixed Schema with YAML, applied with a cartographic rather than an analytic target.
Counting the Pyramid Before You Build It Jump to heading
The arithmetic of tile counts is worth doing explicitly once, because it converts vague worries about scale into a number you can plan around. A zoom level (z) contains (4^{z}) tiles covering the whole world, so the cumulative count from zoom 0 to a maximum (Z) is
For (Z = 14) that is over 357 million tiles worldwide — but the great majority of them are ocean, and a generator that skips empty tiles produces a small fraction of that. For a single country the land-covering share is what matters, and it scales with area rather than with the world total.
Two planning rules follow. First, the maximum zoom is the dominant cost decision: each additional level roughly quadruples the tile count, so extending from zoom 14 to zoom 16 is a sixteen-fold increase for detail most readers reach by overzooming anyway. Second, empty tiles must be genuinely skipped, not stored empty, because three hundred million zero-byte objects is a storage problem in its own right regardless of the bytes involved. Both are decisions to make before the first full run, not after it has been going for six hours.
Serving, Packaging and Invalidation Jump to heading
A generated tile set has to reach clients, and the packaging choice shapes the operational model.
MBTiles is a SQLite database of tiles, easy to generate and to query, requiring a server process to read it. PMTiles is a single file with an embedded index designed to be read by HTTP range requests, which means it can be served directly from object storage with no server at all — an operationally dramatic simplification covered in Serving PMTiles from Object Storage. Loose directories of individual tile files are simple but produce hundreds of millions of small objects, which most storage systems handle poorly.
Invalidation is where tiles meet the rest of this site. When an OSM diff arrives, some tiles are now wrong and most are not. Regenerating everything is wasteful; regenerating nothing is incorrect. The middle path is to compute a dirty tile list from the changed elements’ coordinates, which is exactly what Computing a Dirty Tile List from an .osc File does, and then re-render only those — plus their ancestors, since a change at zoom 14 also affects the zoom 13 tile containing it. Invalidating Tile Caches After an OSM Diff covers the cache side.
Validation and Error Handling Jump to heading
| Condition | Root cause | Detection | Remediation |
|---|---|---|---|
| Visible seams at tile edges | Buffer too small for the rendered line width | Gaps along tile boundaries at some zooms | Increase the buffer, or reduce the styled width |
| Tiles exceed the size budget | Too many features or attributes at that zoom | Generator warnings, or oversized tiles in the archive | Drop features by rank; prune attributes |
| Features vanish unpredictably | Automatic feature dropping to meet the budget | Missing features correlate with dense areas | Make the dropping explicit and rank-driven |
| Blocky geometry at low zoom | Grid quantisation, not simplification | Vertices snap to a coarse lattice | Expected; simplify in grid units and accept it |
| Duplicate features on hover | One feature split across several tiles | The same id appears in adjacent tiles | Deduplicate on feature id in the client |
| Restyling requires a rebuild | Styling baked into attributes | Colour or width values present in tile data | Move styling to the client style document |
| Stale tiles after an update | No invalidation from the diff stream | Old geometry persists in cached tiles | Compute dirty tiles and purge those keys |
Performance and Scale Jump to heading
Two budgets govern a tile pipeline, and they pull against each other.
The per-tile size budget is what clients can fetch and decode quickly — a few hundred kilobytes is the usual working ceiling, and well under a hundred is a better target for a base map. Every feature and every attribute competes for it.
The total generation cost is the pyramid. Generating zoom 0 to 14 for a continent is a large batch job; generating zoom 0 to 20 is not feasible, which is why high-zoom detail is normally served by overzooming — the client scales a zoom-14 tile up rather than fetching a zoom-18 one that was never generated.
The practical consequences: choose a maximum zoom deliberately and accept overzoom above it; rank features once so zoom-dependent dropping is a lookup rather than a computation; and treat attribute count as a first-class budget line, because a string attribute repeated across a million features is measured in gigabytes.
Licensing and Attribution Jump to heading
Vector tiles derived from OpenStreetMap are a derived database under the Open Database Licence, and the attribution obligation travels with them. Practically this means the map client must display attribution, and the tile set’s metadata should record its source and the extract it came from. Because a tile set is typically served to many consumers who never see your pipeline, the attribution in the style document is the only place most people will encounter it — which makes it a required output of the pipeline rather than a courtesy. The obligations themselves are worked through in OSM Licensing & ODbL Compliance.
Topics in This Section Jump to heading
- The Mapbox Vector Tile Spec & Tile Geometry — the integer grid, command encoding, clipping and buffers, and the bugs each one produces.
- Building OSM Tiles with Tippecanoe — GeoJSON in, MBTiles out, with zoom-dependent dropping under control.
- Planetiler & Tilemaker Workflows — PBF-to-tiles in one pass, with the schema expressed in code or a profile.
- Serving & Invalidating OSM Tiles — packaging formats, serverless delivery, and purging what a diff made wrong.
- Cartographic Generalization of OSM Data — selection, simplification and aggregation as zoom decreases.
Frequently Asked Questions Jump to heading
Why is my geometry blocky at low zoom even though I did not simplify it?
Because vector tile geometry is quantised to an integer grid defined by the tile’s extent. At zoom 8 with the conventional extent, one grid unit is tens of metres, so every coordinate snaps to a lattice of that spacing regardless of the precision in your source data. This is inherent to the format rather than a defect, and it is why simplification tolerance should be expressed in grid units — simplifying finer than the grid costs time and changes nothing.
Why does the same road appear as several features?
Because features are clipped to tile boundaries, so a road crossing three tiles exists as three separate geometries, one per tile, each carrying the same identifier and attributes. This is how the format keeps tiles independent and cacheable. Clients that highlight on hover or count features need to deduplicate on the feature identifier, otherwise a long road appears once per tile it touches.
How do I stop features from disappearing as I zoom out?
Make the dropping explicit rather than leaving it to the generator’s size-budget heuristic. Compute a rank attribute for every feature during layer assignment — road classification, water body area, settlement population — and drive minimum zoom from that rank. The generator then drops by your rule rather than by whichever features happened to be encountered when the tile ran out of room, which is what makes the result predictable and reviewable.
Do I need a tile server?
Not necessarily. A single-file archive with an embedded index can be served directly from object storage using HTTP range requests, which removes the server entirely and leaves you with a static file and a content delivery network. A server process is still worth having when you need per-request logic — access control, on-the-fly filtering, or dynamic layers — but for a static base map it is an operational cost with no corresponding benefit.
How do I update tiles after an OSM diff without regenerating everything?
Compute the set of tiles the changed elements touch, expand it to include ancestor tiles at lower zooms, and re-render only those. A minutely diff typically touches a tiny fraction of a continent’s tiles, so the saving is enormous. The ancestors matter because a change at high zoom also alters the generalized representation at lower zooms, and forgetting them leaves a map that is correct when you zoom in and wrong when you zoom out.
Related Jump to heading
- Parsing & Tag Normalization Workflows — the normalized features a tile pipeline consumes.
- OSM Replication & Diff Sync — the change stream that drives invalidation.
- Spatial Index Selection: R-tree vs H3 vs Quadkey — the quadtree addressing tiles share with quadkeys.
- Exporting OSM to GeoParquet & PostGIS — producing the intermediate a GeoJSON-based generator needs.
- OSM Data Quality & Validation — geometry problems are far more visible once rendered.
- OSM Licensing & ODbL Compliance — the attribution a served tile set must carry.
Up one level: OSM Data Processing & QA Pipelines.