Architectural Deep-Dive: Building a High-Performance 3D Digital Twin of 500k+ Buildings in the Browser
How we engineered a lightweight, interactive 3D web map using the Google Open Buildings dataset, GEE-derived height rasters, Maplibre GL, and real-time EV charging simulations
Architectural Deep-Dive: Building a High-Performance 3D Digital Twin of 500k+ Buildings in the Browser
How we engineered a lightweight, interactive 3D web map using the Google Open Buildings dataset, GEE-derived height rasters, Maplibre GL, and real-time EV charging simulations.
Introduction: The Web Digital Twin Challenge
Urban digital twins have traditionally been the domain of heavy desktop GIS packages (like ArcGIS Pro or QGIS) or massive, proprietary enterprise platforms. These setups require high-end workstations and proprietary licenses, creating a barrier to entry for urban planners, municipal bodies, and citizens in developing urban centers.
Building a fully interactive, web-based digital twin that runs at 60 FPS in a standard mobile or desktop browser is a daunting engineering challenge. To visualize a city the size of Chennai (covering over 1,189 square kilometers of the Chennai Metropolitan Area), we had to process and render over 525,000 buildings, overlay spatial datasets, and support real-time simulations.
A naive representation of this data in standard GeoJSON format exceeds 1.5 GB—a size that would crash any web browser, blow past mobile data limits, and stall CPU main threads.
In this article, we details the end-to-end engineering pipeline we built to solve this problem:
- Ingestion & Streaming Filter: Streaming and filtering millions of footprints on-the-fly.
- Vectorized Spatial Joins: Merging building shapes with satellite height data using matrix math.
- Urban Semantics: Running a spatial rule-engine to classify building use.
- Data Compacting: Compressing the payload by 96% (from 1.5 GB down to 60 MB).
- High-Performance Web 3D Rendering: Delivering a smooth, hardware-accelerated client app featuring real-time simulators and custom night maps.
The System Architecture
The following diagram illustrates the architecture of our geospatial processing and visualization pipeline:
Interactive 5-Stage Spatial Data Engineering Pipeline
1. Streaming & Spatial Filtering
Bypasses heavy downloads by filtering candidate footprints on-the-fly.
1. Streaming & Spatial Filtering of Big Geospatial Data
The Google Open Buildings dataset is an open-source resource containing millions of AI-detected building footprints. However, download sizes are immense. The tile covering Chennai and surrounding parts of Southern India (3a5_buildings.csv.gz) is a gzipped CSV containing millions of building rows.
To avoid downloading gigabytes of unnecessary data onto local machines, we built a streaming pipeline in Python that filters and processes coordinates on the fly.
On-the-Fly Stream Filtering
We combined shell utilities (curl, gunzip, and awk) directly within a subprocess pipe. This allowed us to filter rows by their bounding boxes before they were even decompressed into python memory:
curlpulls the compressed stream.gunzipdecompresses the stream line-by-line.awkfilters coordinates based on bounding box limits.- Result: We shrunk a massive tile down to a temporary local CSV containing only the candidate structures inside the rectangular boundary of Chennai.
Boundary Intersection via Prepared Geometries
A bounding box is a rectangular approximation. The actual political boundary of the Chennai Metropolitan Area (CMA) is a highly complex, irregular polygon. Running standard point-in-polygon checks (like polygon.contains(point)) for hundreds of thousands of candidate buildings is computationally expensive.
To optimize this, we utilized Prepared Geometries from the shapely library:
shapely.prepared.prep sets up an in-memory spatial index (using a modified R-tree structure) that accelerates geometric containment queries. By doing a quick containment check on the building's centroid before parsing its complex polygon structure (via WKT parser), we completed the filtering of 525,000+ exact footprints in seconds.
2. Vectorized Spatial Joins: Merging Footprints and Raster Heights
The Google Open Buildings dataset provides footprints, but to create a true 3D Digital Twin, we needed heights. We used a building-height raster generated from Google Earth Engine (GEE).
Raster data is stored as a 2D grid of pixels (each pixel corresponding to a real-world cell, in our case, representing height in meters). Vector data (the building footprints) consists of latitude/longitude coordinates. Intersecting half a million vector shapes with a high-resolution GeoTIFF raster using traditional spatial libraries is slow.
Slicing with Vectorized Coordinates
Instead of looping and sampling the raster pixel-by-pixel, we vectorized the coordinate transform using numpy and rasterio:
Rather than querying the filesystem or raster structure iteratively, rowcol uses matrix algebra on the affine transformation to map coordinates to pixel rows/columns in a single execution. The sampling step is a direct array index band1[rows, cols], leveraging optimized C-underpinnings of NumPy.
Any invalid pixels (e.g. NaNs, nodata values, or heights below 0) were normalized to a default single-story height of 3.0m.
Test real-time building floor count & UDPFI classification
{
"type": "Feature",
"properties": {
"h": 18.5,
"a": 450,
"u": "AM"
}
}Short keys (h, a, u) and 2-character land-use codes allow Maplibre GL vertex shaders to style 525,000 extruded 3D features dynamically in WebGL memory.
3. Classifying Building Use via Spatial Heuristics
Real-world digital twins must map building functions (Residential, Commercial, Industrial) to help urban planners. Since open building datasets rarely include land-use labels, we engineered a spatial heuristic classifier in Python.
Our rule-engine utilizes two spatial attributes: building height (representing capacity) and footprint area (representing floorplate size). First, we estimate the floor count:
Using these values, we ran a heuristic tree aligned with local urban typologies:
This classification translates building geometries into urban semantics, allowing the frontend to style the city according to UDPFI (Urban Development Plans Formulation and Implementation) town planning color standards.
4. Shrinking 1.5 GB GeoJSON to 60 MB
Loading a 1.5 GB GeoJSON in a browser is impractical. We implemented a multi-stage optimization pipeline to compress the geospatial payload by over 96%:
1. Coordinate Precision Reduction
Double-precision coordinates in GeoJSON (e.g., 80.146249102948123) contain up to 15 decimal places. That represents sub-millimeter precision—far beyond the resolution of satellite datasets.
- 6 decimal places (
80.146249) provides 10 cm accuracy, which is perfect for building outlines. - By rounding all coordinates to 6 decimal places, we cut millions of characters:
2. Attribute Compacting (Short-Coding)
GeoJSON properties add redundant string overhead. Key names like height and estimated_use repeat in every single building feature. We compacted keys and mapped values to two-character codes:
heighth(rounded to 1 decimal place)areaa(converted to integer)estimated_useu(using codes likeRLfor Residential Low,CRfor Commercial Retail,IWfor Industrial)
3. Minification and Gzip Level 9
We dumped the JSON without any indentation or whitespaces using python's separators (’,’, ’:’), and then compressed it using gzip's maximum compression level (compresslevel=9):
Result: The file size dropped from a raw 1.47 GB to a 60 MB .geojson.gz archive.
Calculates BMS cell protection taper phases (>80% State of Charge)
5. High-Performance Client-Side Rendering & EV Simulation
In the client browser, we integrated a combination of technologies to load, decompress, and render the digital twin at high framerates.
On-the-Fly Client-Side Decompression
Rather than downloading an uncompressed file or relying on server-side middleware (which isn't always supported on basic CDNs), the web app fetches the .geojson.gz array buffer and decompresses it client-side using fflate:
fflate is a lightweight, pure-JS decompression library that is significantly faster than standard browser-based decoding libraries. It completes decompression and JSON parsing of half a million features in less than 2 seconds on modern laptops.
WebGL-Accelerated 3D Rendering
For rendering, we used Maplibre GL JS, an open-source WebGL/WebGPU-based library. We defined a fill-extrusion layer, mapping building heights (h) and use classifications (u) to WebGL paint properties:
By delegating geometry extrusion and styling to vertex shaders on the GPU, the browser handles hundreds of thousands of polygons without burdening the JavaScript execution stack.
Dynamic Night Mode Paint Engine
To prevent map flashes when toggling between themes, we implemented a paint engine that changes the colors of the underlying Maplibre vector tiles on the fly, instead of changing the map style:
Non-Linear EV Battery Charging Simulator
We overlaid the Digital Twin with India-wide EV charging locations. To show how a digital twin can run interactive models, we built a client-side EV charging duration simulator.
Lithium-ion batteries do not charge linearly. Fast charging is effective up to 80% State of Charge (SoC). Beyond 80%, the battery management system (BMS) reduces charging speeds to prevent cell degradation.
Our simulator calculates charging duration by splitting the charging profile into fast and slow phases when the target charge exceeds 80%:
When users hover over a charging station, the map calculates exact charging times based on the selected vehicle's battery capacity, the station's charger type (AC Type-2 vs DC CCS2), and the user's targeted charge levels.
Conclusion: Key Takeaways for Geospatial Engineers
Our project demonstrates that complex 3D digital twins can run effectively on standard web browsers. When building web-based geospatial tools, consider these key strategies:
- Optimize at Source: Stream and filter datasets using command-line pipelines (like
awkorgrep) before loading them into memory. - Vectorize Spatial Operations: Avoid loops in python. Use libraries like NumPy and Rasterio to perform affine transformations on arrays.
- Minimize Payload Size: Round coordinate decimals, shorten keys, map variables to small codes, and use high-level Gzip compression.
- Use GPU Acceleration: Delegate 3D rendering to WebGL/WebGPU by utilizing Maplibre GL.
- Add Interactive Simulations: Use client-side logic to run models, keeping your web applications fast and engaging.
Published in Research & Policy Essays
Discuss this methodology →