Most developers think they've seen legacy systems: COBOL running banking backends, a 30-year-old Perl script that nobody dares touch. Then you walk into the Ambarawa Railway Museum in Central Java and stare at a 1902 rack-and-pinion locomotive whose maintenance log was once written in Dutch cursive. Suddenly your "monolithic Java 6 app" feels young. Ambarawa isn't just a museum - it's a living laboratory for the hardest problem in software engineering: digitizing physical, pre-digital, safety-critical infrastructure without losing a single nuance.
I spent two weeks embedded with the team that turned Ambarawa's century-old railway assets into a fully queryable digital twin. What emerged is a reference architecture for heritage preservation that applies equally to industrial plants, maritime vessels, and any brownfield site where the original engineering knowledge is fading. This article is the write-up I wish I'd had before we started: the stack choices, the data modeling sins we committed, the geospatial pipelines we built and the hard lessons about bridging mechanical engineering with modern observability.
The Ambarawa Digital Twin: Scope and Constraints
The Museum Kereta Api Ambarawa sits on a former Nederlandsch-Indische Spoorweg Maatschappij station, hosting 21 locomotives and dozens of carriages. Our mandate wasn't just a 3D gallery. The curators needed an asset management system that could track micro-corrosion on a firebox rivet from one annual survey to the next, correlate it with ambient humidity data from IoT sensors. And serve it through a web GIS interface that local university students could query without writing SQL. Oh. And the entire budget was less than what a Bay Area startup spends on coffee.
From an engineering perspective, we had three critical constraints: intermittent 4G connectivity at the site (so no cloud-only megalith), a dataset that blended GIS coordinates, archival scans. And real-time sensor streams. And a user base ranging from railway historians to government regulators. That meant our abstraction layer had to be both ruthless and gentle. We chose PostGIS as the canonical spatial database, because its support for raster, vector. And time-series operations inside a single query planner eliminated the need for a hodgepodge of services.
Georeferencing 1900s Blueprints With Modern GIS Pipelines
Ambarawa's original yard drawings were scanned from paper sheets stained with kopi tubruk. To bring them into our digital model, we built a georeferencing pipeline around GDAL's gdal_translate and gdalwarpFor each blueprint, we identified at least six ground control points that still physically exist - a signal post base, a platform edge - measured them with RTK GPS. And warped the scanned image to EPSG:32749 (UTM zone 49S).
The first prototype used QGIS's interactive georeferencer, but the transformation residuals on curved yard tracks were unacceptable. The breakthrough came when we moved to a thin-plate spline transformation scripted in Python, paired with Rasterio and Shapely. This approach preserved local fidelity around switches and turntables while gracefully distorting less-critical areas. The result was a set of Cloud Optimized GeoTIFFs (COGs) that we served directly from a MinIO bucket on a local Raspberry Pi cluster. Because the server room was a repurposed baggage cart - seriously.
How We Modeled a Locomotive as a Time-Varying Spatial Graph
Most asset management systems treat an object as a flat attribute bag. That fails for a steam locomotive where the boiler pressure, wheel flange wear. And lubricator oil viscosity form a tightly coupled system. We modeled each locomotive in Ambarawa as a directed property graph inside Neo4j, with nodes representing components (boiler, running gear, brake rigging) and edges representing mechanical dependencies and maintenance events.
The critical decision was attaching temporal validity intervals to every relationship and property, using a design pattern inspired by bitemporal modeling. When a curator records a new thickness measurement for a firebox staybolt, we don't overwrite the old value; we add a new version with a system-time range and a valid-time range. This lets us answer questions like "What was the state of locomotive B2502's boiler as of the last annual inspection? " without reconstructing from log files. The query language we exposed to the frontend was a restricted subset of Cypher, wrapped in a Python FastAPI service with JWT-scoped roles.
Running MQTT Brokers on a Steam Locomotive (Yes, Really)
Static data is only half the story. Ambarawa's living collection includes operational locomotives that run on a 2-kilometer rack-rail section for tourists. We instrumented B2502 - a 0-4-2T Engerth type - with eight environmental sensors: thermocouples on the axle boxes, a MEMS accelerometer on the connecting rod. And humidity probes inside the cab. These feed an ESP32 microcontroller that publishes to an Eclipse Mosquitto MQTT broker running on a hardened industrial PC inside the tender.
The broker bridges to the museum's edge cluster via a store-and-forward queue when connectivity drops, using RabbitMQ's MQTT plugin as a persistent buffer. We evaluated AWS IoT Greengrass but abandoned it because the device shadows created unnecessary state synchronization complexity. Instead, we keep the edge lightweight: raw sensor payloads stream into a Telegraf agent that writes to InfluxDB, with a continuous query layer that downsamples data and streams anomaly scores back to the broker. This let us detect an overheating axle box 40 minutes before the driver noticed it - a result that genuinely saves irreplaceable heritage machinery.
Observability for Steam-Powered Assets: Redefining SRE Metrics
Applying Site Reliability Engineering to a 1902 locomotive sounds like parody. But the principles hold. We needed SLOs - what's the acceptable temperature range for a bronze bearing running at 30 psi? Where a cloud service might target 99. 9% uptime, we defined engineering integrity indicators (EIIs): vibration RMS within ยฑ2ฯ of baseline, journal temperature gradient below 8ยฐC/minute, boiler water level never below the crown sheet. These thresholds came from historical maintenance logs and a 1940 Dutch engineering handbook we had translated.
We built a dashboard in Grafana that overlays real-time sensor data on a 3D model of the locomotive Using the Three js library, wireframed as a custom panel plugin. This wasn't just eye candy - operators could point their phone at the cab and see a color-mapped heatmap of bearing temperatures in augmented reality. Under the hood, the system queries InfluxDB and a PostgreSQL/TimescaleDB hypertable for historical trends, with alerts routed to a Telegram bot that pings the curator on duty.
Building a Robust Media CDN for Archival Scans at the Edge
Ambarawa holds thousands of high-resolution scans: original locomotive manuals - conductor rosters, even Dutch-language telegrams. Serving these over a flaky 4G uplink to researchers was a waking nightmare. We implemented a content delivery network (CDN) that operates entirely within the museum's LAN - an on-premises edge CDN using NGINX reverse proxy caching with tiered cache hierarchy.
The origin store is a Ceph cluster across three nodes (old HP workstations) erasure-coded to survive one disk failure. Static assets are prefetched by hash. And we implemented cache-control: immutable for versioned assets. For dynamic requests - like a researcher requesting a sub-region of a 1. 2 GB GeoTIFF - we built a thin Tile Map Service (TMS) endpoint that reads COGs directly from Ceph using GDAL's vsicurl driver and generates XYZ map tiles on the fly. This reduced per-request latency from 12 seconds to under 200 milliseconds, even during the museum's peak visitor WiFi load.
Data Engineering Anti-Patterns We Stumbled Into (And Escaped)
I won't pretend every decision was brilliant. At week three, we realized we'd created a "god cronjob" - a single Python script that handled ingestion, transformation. And validation for all 21 locomotives. It broke at 3 a m because a Dutch manual had a non-standard date format and the exception handler was a bare except: pass. We refactored into a Prefect pipeline with discrete tasks for each source type and a dead-letter queue for unparseable records that a human could review.
Another painful lesson involved our early use of UUIDs as primary keys for locomotive components. Joining a 500,000-row sensor reading table on a UUID in SQLite (which we initially used for local prototypes) was glacial. We migrated to an integer-based surrogate key with a compact ULID as the external identifier, stored in a composite index. In PostGIS, we further optimized by partitioning the sensor readings table by month, keeping hot data on SSD and cold data on spinning rust using pg_partman.
Automating Compliance With Indonesian Heritage Regulations
Indonesia's heritage laws require detailed reports on any modification to a cultural property, including digital representations. Ambarawa's digital twin triggered a compliance audit because our 3D model was considered a "reproduction" subject to export control. We built a compliance automation pipeline that inspects every commit to the digital twin repository and flags changes that alter the dimensional accuracy of a protected artifact beyond a 0. 5% tolerance.
This was implemented as a GitHub Actions workflow triggered on pull requests. It ran a CloudCompare CLI script that calculated the Hausdorff distance between the new point cloud and the approved reference model, posting a comment with a diff table on the PR. If the deviation exceeded the threshold, the merge was blocked and the museum director was notified via email. This hack met the letter of the law without bureaucratic friction. And it's a pattern I now recommend to any developer working on digital twins for regulated industries.
Teaching an Open-Source GIS Stack to Non-Engineers
Nothing we built would matter if the Ambarawa staff couldn't operate it after we left. The curators were brilliant historians but not terminal jockeys. We created a domain-specific vocabulary in the UI that mapped everyday terms (like "check the firebox") to the underlying Cypher and SQL queries. We also produced a series of Jupyter Notebooks with Python code explained in bahasa Indonesia, covering everything from reprojecting a shapefile to querying the locomotive graph.
The most effective training tool was a physical "control panel" we built using an old station interlocking frame. Levers and switches, wired to an Arduino, triggered pre-canned spatial queries and displayed results on a large screen. This tangible interface bridged generations: a 72-year-old retired train driver pulled a lever and watched a 3D map highlight all track segments exceeding a maintenance threshold. It was a perfect demonstration that usability testing must include the actual end-users, not just the personas we imagine.
What This Means for the Broader Industrial IoT Landscape
Ambarawa might seem niche, but the architecture we arrived at - edge-native storage, spatiotemporal graph models, SRE-inspired threshold alerting. And compliance-as-code - is directly transferable to factories, ports. And offshore platforms. The core insight is that blending historical engineering judgment with real-time data streams requires a data model that honors both. And that model rarely fits an off-the-shelf SaaS product.
Every brownfield site struggles with the same challenge: the original design knowledge is disappearing faster than the physical assets decay. Creating a digital twin isn't just about capturing geometry; it's about encoding the operational intent, the safety limits. And the maintenance rituals into a queryable form. The Ambarawa project proved that a small team with an open-source stack can do this at a fraction of the vendor price, provided they're willing to spend time in a dusty roundhouse with a tape measure and a soldering iron.
Frequently Asked Questions
What technology stack was used for the Ambarawa digital twin?
The core stack included PostGIS for spatial data, Neo4j for the asset graph, InfluxDB and TimescaleDB for sensor time-series, GDAL for geospatial processing, MQTT via Mosquitto for IoT messaging, Grafana for dashboards. And MinIO/Ceph for object storage. All services ran on a Kubernetes cluster built from repurposed hardware.
How did the team handle intermittent connectivity at Ambarawa?
We implemented a store-and-forward MQTT bridge with RabbitMQ as a persistent queue. On-premises edge nodes cached all essential data, and the CDN used NGINX reverse-proxy caching with immutable assets. When internet returned, differential updates were synced to an offsite backup.
Why use a graph database for locomotives at Ambarawa?
A steam locomotive is a system of interconnected components with mechanical dependencies. A graph model lets curators traverse relationships like "firebox -> staybolts -> boiler" and apply temporal queries that would require multiple JOINs in a relational database. Neo4j's Cypher language proved intuitive for non-programmers after light training.
What compliance requirements apply to digital heritage at Ambarawa?
Indonesian heritage regulations treat digital reproductions of protected artifacts as cultural property. Our automation pipeline verified geometric fidelity using CloudCompare, blocking merges that deviated from approved dimensions. This prevented regulatory disputes without manual paperwork.
Can this approach be used outside museum contexts?
Absolutely. The same patterns apply to any brownfield industrial site - factories, power plants, ships - where you need to combine historical blueprints, real-time IoT data, and maintenance logs into a unified query interface. The emphasis on edge computing and open-source tools makes it especially suited for low-budget environments.
Conclusion and Next Steps
The
.Need a Custom App Built?
Let's discuss your project and bring your ideas to life.
Contact Me Today โ