11/**
22 * Pre-generates static vector tiles (MVT/PBF) at build time.
33 *
4- * Reads territory GeoJSON + regions + utilities, pre-simplifies the geometry
5- * using @turf/simplify to reduce vertex count (~2.5M → ~100-200K), then builds
6- * a geojson-vt index and writes .pbf tiles for zoom 5–10.
4+ * Reads territory GeoJSON + regions + utilities, builds a geojson-vt index
5+ * and writes .pbf tiles for zoom 5–10.
6+ *
7+ * geojson-vt handles zoom-dependent simplification automatically:
8+ * - Low zoom (5-6): heavy simplification → fast rendering of many polygons
9+ * - High zoom (9-10): minimal simplification → crisp boundary detail
10+ *
11+ * No pre-simplification needed — the zoom gating in ExplorerMap.tsx ensures
12+ * only ~355 large territories render at zoom 7-8, and at zoom 9+ the viewport
13+ * is small enough that full-detail geometry is fine.
714 */
815import { readdir , readFile , mkdir , writeFile } from "node:fs/promises" ;
916import { join } from "node:path" ;
1017import geojsonvt from "geojson-vt" ;
1118import vtpbf from "vt-pbf" ;
12- import simplify from "@turf/simplify" ;
1319
1420const { fromGeojsonVt } = vtpbf ;
1521
@@ -19,17 +25,6 @@ const DATA_DIR = join(process.cwd(), "data");
1925const TERRITORIES_DIR = join ( DATA_DIR , "territories" ) ;
2026const OUT_DIR = join ( process . cwd ( ) , "public" , "tiles" ) ;
2127
22- // Simplification tolerance in degrees (~0.05° ≈ 5.5km)
23- // Aggressively reduces vertex count for tile rendering.
24- // At zoom 5-10, territories look clean — exact boundaries aren't needed for the overview.
25- // Detail panels load the original full-resolution GeoJSON when you click into a utility.
26- const SIMPLIFY_TOLERANCE = 0.05 ;
27-
28- function countVertices ( geometry ) {
29- const coords = JSON . stringify ( geometry . coordinates || [ ] ) ;
30- return ( coords . match ( / \[ [ \d . - ] + , [ \d . - ] + \] / g) || [ ] ) . length ;
31- }
32-
3328async function buildFeatureCollection ( ) {
3429 const [ regionsRaw , utilitiesRaw ] = await Promise . all ( [
3530 readFile ( join ( DATA_DIR , "regions.json" ) , "utf-8" ) ,
@@ -54,8 +49,6 @@ async function buildFeatureCollection() {
5449
5550 const files = ( await readdir ( TERRITORIES_DIR ) ) . filter ( ( f ) => f . endsWith ( ".json" ) ) ;
5651 const allFeatures = [ ] ;
57- let totalVertsBefore = 0 ;
58- let totalVertsAfter = 0 ;
5952
6053 for ( const file of files ) {
6154 try {
@@ -79,40 +72,18 @@ async function buildFeatureCollection() {
7972 } ;
8073
8174 for ( const feature of geojson . features ) {
82- const vertsBefore = countVertices ( feature . geometry ) ;
83- totalVertsBefore += vertsBefore ;
84-
85- // Pre-simplify complex geometries using Douglas-Peucker
86- let geometry = feature . geometry ;
87- if ( vertsBefore > 50 ) {
88- try {
89- const simplified = simplify (
90- { type : "Feature" , properties : { } , geometry } ,
91- { tolerance : SIMPLIFY_TOLERANCE , highQuality : true , mutate : false }
92- ) ;
93- geometry = simplified . geometry ;
94- } catch {
95- // Keep original if simplification fails
96- }
97- }
98-
99- const vertsAfter = countVertices ( geometry ) ;
100- totalVertsAfter += vertsAfter ;
101-
10275 allFeatures . push ( {
10376 type : "Feature" ,
10477 properties,
105- geometry,
78+ geometry : feature . geometry ,
10679 } ) ;
10780 }
10881 } catch {
10982 // Malformed territory files — safe to skip
11083 }
11184 }
11285
113- const reduction = ( ( 1 - totalVertsAfter / totalVertsBefore ) * 100 ) . toFixed ( 1 ) ;
11486 console . log ( `✅ Loaded ${ allFeatures . length } features from ${ files . length } territory files` ) ;
115- console . log ( ` Vertices: ${ totalVertsBefore . toLocaleString ( ) } → ${ totalVertsAfter . toLocaleString ( ) } (${ reduction } % reduction)` ) ;
11687
11788 return {
11889 type : "FeatureCollection" ,
@@ -124,6 +95,10 @@ async function generateTiles() {
12495 const fc = await buildFeatureCollection ( ) ;
12596
12697 console . log ( "Building geojson-vt index..." ) ;
98+ // tolerance 3 = default. geojson-vt automatically applies MORE simplification
99+ // at lower zoom levels and LESS at higher zoom levels. This gives us:
100+ // - Zoom 5-6: rough shapes (good — few pixels per territory)
101+ // - Zoom 9-10: crisp detailed boundaries (good — few territories visible)
127102 const index = geojsonvt ( fc , {
128103 maxZoom : 14 ,
129104 tolerance : 3 ,
@@ -134,7 +109,7 @@ async function generateTiles() {
134109 let totalTiles = 0 ;
135110
136111 for ( let z = MIN_ZOOM ; z <= MAX_ZOOM ; z ++ ) {
137- const maxCoord = 1 << z ; // 2^z
112+ const maxCoord = 1 << z ;
138113 let zoomTiles = 0 ;
139114
140115 for ( let x = 0 ; x < maxCoord ; x ++ ) {
0 commit comments