OpenHistoricalMap logo OpenHistoricalMap

📊 OHM Vector Tile - Admin Boundary Optimization Results

Posted by Rub21 on March 24, 2025 in English. Last updated on March 27, 2025.

Vector tiles API: https://vtiles.openhistoricalmap.org/

One of the most frequently added data types in OpenHistoricalMap are boundaries. These are often represented as relations and ways, these relations are imported into the PostGIS tiler database. Many of these polygons are quite complex, sometimes covering entire countries or continents and for each relation/way, a polygons are generated.

Examples:
  • Around 362 boundary relations represent British Empire on OHM over time.
  • Around 1100 boundary relations, represent San JosĂ© - California on OHM over time.

Previous Workflow for Vector Tiles

In the previous version, vector tiles were generated directly from raw boundary geometries. This method resulted in slower tile response times and significantly larger tile sizes, particularly when handling complex or high-resolution polygons.

We use PostGIS’s ST_AsMVTGeom function to generate vector tiles. When working with ST_AsMVTGeom and complex, global-scale geometries—especially those with thousands of vertices—the clipping process (cutting to the !BBOX!) can introduce significant computational overhead. This becomes especially problematic at lower zoom levels, where a single geometry may span multiple tiles, triggering expensive intersection and transformation operations.

Over time, many users began noticing that tiles were loading too slowly, particularly in regions with dense boundary data. This performance bottleneck in the boundaries layer also negatively impacted the loading speed of other map layers, resulting in an overall degraded map experience.


Improving Performance with Complex Boundaries

📎 From: GitHub Issue #800

To address the previous version of the tiler, we conducted a detailed analysis of the boundary data and implemented a merging strategy.

We decided to use only a member according to their admin_level and historical timeframes (start_date and end_date).
The dates were crucial for reducing duplication and redundancy. For example, we found many cases where the same geometry (way) was reused across multiple relations with different time spans:

  • Relation 1: start_date = 1990-12-31, end_date = 2000-01-01
  • Relation 2: start_date = 2000-01-02, end_date = 2009-12-31
  • Relation 3: start_date = 2010-01-02, end_date = 2024-12-31

Instead of treating each as a separate geometry, we merged them into a simplified timeline-aware representation, significantly reducing the number of features that needed to be processed and served.

From the previous example, we end up with only 2 geometries:

  • Merge Relation 1 and 2 (overlapping dates): start_date = 1990-12-31, end_date = 2009-12-31
  • Keep Relation 3 (non-overlapping): start_date = 2010-01-02, end_date = 2024-12-31
Real-world Example

The way 198892846 originally belonged to 1,169 different relations.
After applying our optimization, it was merged into a single line geometry.
As a result, we introduced a column called merged_row_count, which indicates how many relations have been consolidated into one line.

Merged Example

This optimization has greatly improved tile loading times and reduced the size of the vector tiles, offering a better user experience across the map.


📏 Vector Tile Size Statistics

🔗 View Stats (San José)

Metric 🔴 Before Optimization 🟢 After Optimization Improvement
Total tiles 18 9 📦 Fewer tiles due to better data distribution
Min tile size 18.83 KB 5.16 KB đź”˝ Smaller minimal size
Avg tile size 1591.79 KB 193.46 KB âś… ~88% smaller on average
Max tile size 7854.09 KB 445.94 KB âś… ~94% smaller max tile
land_ohm_lines - Coords per tile ~663,300 ~7,963 âś… ~99% drop in geometry complexity
land_ohm_lines - Avg tile size 1530.21 KB 120.53 KB âś… Huge efficiency gain
land_ohm_lines - Max tile size 3617.17 KB 1559.44 KB âś… Reduced by more than half

Image

⚙️ Tiler Query Performance Statistics

Aspect 🟥 Before Optimization (Slow Query) 🟩 After Optimization (Fast Query)
Execution Time 934.813 ms 12.048 ms âś… (~77x faster)
Rows Returned 531 63
Access Type Index Scan Parallel Bitmap Heap Scan
Index Used idx_mview_admin_boundaries_lines_z10_12_geom mview_admin_boundaries_z10_12_geometry_idx
Estimated Row Size ~3836 bytes ~95 bytes
Columns Selected 100+ (JSON tags, computed dates, multilingual names) Minimal (geometry, IDs, dates)

This was one of the most impactful performance improvements we’ve implemented in our vector tiles — but we’re still working on others, which I’ll continue sharing in upcoming posts.

Also related post here: https://forum.openhistoricalmap.org/t/better-performance-less-clutter-and-the-end-of-an-era/426

Discussion

Log in to leave a comment