How the alurail Map Engine Was Engineered
A technical deep-dive into the geospatial topology, dead-reckoning kinematics, bundle journey stitching, and static index pipelines powering real-time railway simulation in Indonesia.
1. Cartographic Mesh & Topological Track Ingestion
Mapping railway networks across an archipelagic country presents unique spatial challenges. In standard road mapping, nodes and edges adhere to omnidirectional planar graphs. Railways, however, obey rigid physical constraints: narrow turning radii based on the track gauge (1,067 mm Cape Gauge and 1,435 mm Standard Gauge for Whoosh), crossover switches, and strict directional block sections dividing tracks.
To resolve uncoordinated raw polyline segments between stations into clean, continuous corridors without third-party geocoding API rate limits or latency, the engine utilizes a multi-stage pre-processing pipeline:
- Raw Geometry Ingestion: Ingest track vector polylines from OpenRailwayMap (ORM) raw nodes and OpenStreetMap contributors.
- AABB Spatial Bounding Pruning: Prunes candidate track polylines using fast 2D Axis-Aligned Bounding Box (AABB) checks before computing costly spherical trigonometric distances, reducing lookup complexity from $O(N)$ to $O(\log N)$.
- Station Node Snapping: Snapping WGS-84 coordinates of official passenger stations to nearest rail coordinates within a 500-meter buffer, calculating offset for sidings/shunting tracks.
- Track Gauge Constraints: Distinguishing between 1,067 mm Cape Gauge (Java & Sumatra mainlines), 1,435 mm Standard Gauge (Whoosh HSR and Makassar-Parepare), and dual-gauge segments (e.g. Solo Purwosari).
- Graph Topology Dijkstra / Connection Verification: Pre-computing permissible paths between sequential station milestones defined in official GAPEKA timetable manifests.
function isPointNearSegmentAABB(float $pLat, float $pLon, array $segA, array $segB, float $bufferDeg = 0.005): bool {
// Quick rectangular boundary check before executing trigonometric formulas
$minLat = min($segA[0], $segB[0]) - $bufferDeg;
$maxLat = max($segA[0], $segB[0]) + $bufferDeg;
$minLon = min($segA[1], $segB[1]) - $bufferDeg;
$maxLon = max($segA[1], $segB[1]) + $bufferDeg;
return ($pLat >= $minLat && $pLat <= $maxLat && $pLon >= $minLon && $pLon <= $maxLon);
}
function geodesicTrackSnap($nodeLat, $nodeLon, array $trackPolylinePoints, $toleranceMeters = 500.0) {
$minDistance = 1e9;
$snappedCoord = null;
for ($i = 0; $i < count($trackPolylinePoints) - 1; $i++) {
// Fast AABB pruning to bypass distant segments instantly
if (!isPointNearSegmentAABB($nodeLat, $nodeLon, $trackPolylinePoints[$i], $trackPolylinePoints[$i+1])) {
continue;
}
$dist = haversineSegmentDist($nodeLat, $nodeLon, $trackPolylinePoints[$i], $trackPolylinePoints[$i+1]);
if ($dist < $minDistance && $dist <= $toleranceMeters) {
$minDistance = $dist;
$snappedCoord = projectPointOnSegment($nodeLat, $nodeLon, $trackPolylinePoints[$i], $trackPolylinePoints[$i+1]);
}
}
return $snappedCoord ?: [$nodeLat, $nodeLon];
}
2. Dead Reckoning Kinematics & Motion Interpolation
While airplanes use ADS-B and ocean vessels carry AIS transponders, most passenger trains in developing national networks operate without standardized public GPS beacons. To provide an intuitive live radar map that reflects actual operational conditions, the engine utilizes a robust kinematic dead-reckoning model.
Every active train is evaluated through real-time clock cycles:
- The system queries the current active timetable block: origin departure ($t_{\text{dep}}$) to next station arrival ($t_{\text{arr}}$).
- Proportional transit progress $\alpha \in [0, 1]$ is computed: $\alpha = \frac{t_{\text{now}} - t_{\text{dep}}}{t_{\text{arr}} - t_{\text{dep}}}$.
- Rather than applying naive linear velocity ($v = s/t$), the engine applies an S-curve acceleration and deceleration curve taking into account the maximum allowable speed (TASPAT) for each section: accelerating out of origin station, cruising, and braking into destination.
- The resulting milestone distance is projected onto the pre-sliced polyline segment, yielding exact latitude and longitude coordinates.
function calculateKinematicProgress(float $tDepUnix, float $tArrUnix, float $tNowUnix): float {
if ($tNowUnix <= $tDepUnix) return 0.0;
if ($tNowUnix >= $tArrUnix) return 1.0;
// Linear time progress alpha clamped to [0.0, 1.0]
$alpha = ($tNowUnix - $tDepUnix) / ($tArrUnix - $tDepUnix);
// Cubic Hermite smoothstep polynomial (3t^2 - 2t^3) emulating train mass, acceleration, and deceleration
return $alpha * $alpha * (3.0 - 2.0 * $alpha);
}
function calculateTrackBearing(lat1, lon1, lat2, lon2) {
const toRad = Math.PI / 180;
const dLon = (lon2 - lon1) * toRad;
const phi1 = lat1 * toRad;
const phi2 = lat2 * toRad;
const y = Math.sin(dLon) * Math.cos(phi2);
const x = Math.cos(phi1) * Math.sin(phi2) - Math.sin(phi1) * Math.cos(phi2) * Math.cos(dLon);
// Normalize forward azimuth bearing to [0, 360) degrees
return (Math.atan2(y, x) * (180 / Math.PI) + 360) % 360;
}
3. Through-Train (Bundle) Unification Architecture
In operational railway practice, many prominent train services change their operational train number midway through their journey due to reverse-direction terminal loops, regional operational splits, or historic route concessions.
For instance, KA Serayu operates between Kroya and Pasar Senen via Bandung, changing operational numbers in sequence (e.g. 283A → 284B). Historically, relational database lookups treated these as separate isolated journeys, showing confusing short runtimes (e.g. 5 hours) and incomplete segment maps instead of the genuine 9+ hour end-to-end corridor.
The engine resolves this through an operational bundling layer:
- Sequential Leg Stitching: Grouping multi-leg rail journeys by physical consist identifier to preserve unified journey continuity.
- Realistic Journey Metrics: Computing cumulative aggregate distance, aggregate stop counts, and complete origin-to-destination running duration.
- Adaptive Prop View: Displaying the full unified master corridor on the main train fleet page while maintaining granular segment-specific geometry on individual schedule detail views.
class ThroughTrainCorridor {
public string $consistCode; // Physical train consist identifier
public string $masterName; // e.g. "KA Serayu"
public array $legs = []; // Sequential trip legs [ '283A' => [...], '284B' => [...] ]
public function stitchContinuousPolyline(): array {
$unifiedPolyline = [];
foreach ($this->legs as $idx => $leg) {
$coords = $leg['polyline'];
// Normalize reverse terminal switchbacks (e.g. Kroya/Kiaracondong turn-around)
if ($idx > 0 && !empty($unifiedPolyline)) {
$lastVertex = end($unifiedPolyline);
$headVertex = $coords[0];
$tailVertex = end($coords);
// If tail is closer to the previous leg endpoint than head, reverse sequence
if (haversineDistance($lastVertex, $tailVertex) < haversineDistance($lastVertex, $headVertex)) {
$coords = array_reverse($coords);
}
}
$unifiedPolyline = array_merge($unifiedPolyline, $coords);
}
return $unifiedPolyline;
}
}
4. High-Throughput Static Spatial Caching
Serving thousands of concurrent transit fans tracking train movements during high-traffic periods (e.g. Lebaran and Year-End holiday operations) requires an architecture that avoids disk I/O and expensive spatial joins on the primary relational database.
The platform achieves this through an optimized spatial caching strategy:
- Schedule Pre-compilation: When an active GAPEKA timetable edition is published or modernized, station-to-station corridor polylines, station stops, and running rosters are compiled into ultra-fast pre-serialized cache payloads.
- Dual-Driver Storage Architecture: Local server NVMe memory serves as the primary sub-millisecond cache tier, with automatic synchronization to Cloudflare R2 Edge Storage to ensure resilience and global CDN availability.
- Client-Side Map Mesh: High-performance client-side coordinate caching allows instant switching across 187 train services and 3,673 daily trips without recurring network requests.
function serveStaticSpatialPayload(string $indexKey, string $jsonPayload, int $maxAgeSeconds = 300): void {
// Non-cryptographic fast 64-bit hash for ETag validation
$etag = '"' . hash('xxh64', $jsonPayload) . '"';
header('Content-Type: application/json; charset=UTF-8');
header('ETag: ' . $etag);
header("Cache-Control: public, max-age={$maxAgeSeconds}, stale-while-revalidate=600");
// If client cache matches ETag, return 304 Not Modified immediately with 0 bytes body
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) === $etag) {
http_response_code(304);
exit;
}
echo $jsonPayload;
exit;
}
5. Design Tokens & Frontend System Architecture
The frontend UI is built purely with native web platform standards: semantic HTML5, pure CSS variables, and focused JavaScript modules, avoiding heavy client bundle overhead to ensure instant page load speeds and smooth navigation on mobile devices.
- Adaptive CSS Variable Architecture: Dynamic token switching (
--up-bg,--up-surface,--up-brand) provides unified dark and light theme consistency across all views. - Hardware-Accelerated Markers: Train markers and station dots utilize custom Leaflet SVG/canvas renderers, ensuring 60fps panning and zooming even on entry-level mobile devices.
- Resilient Media Fallbacks: Cabin interior photography and fleet images seamlessly fall back to branded badges if remote assets are missing, preserving pristine visual hierarchy.
function projectWGS84ToCanvasPixel(lat, lon, zoom, tileSize = 256) {
// Project geographical WGS-84 coordinate into normalized Mercator screen-space pixel coordinates
const sinLat = Math.sin(lat * (Math.PI / 180));
const scale = tileSize * Math.pow(2, zoom);
const pixelX = scale * ((lon + 180) / 360);
const pixelY = scale * (0.5 - Math.log((1 + sinLat) / (1 - sinLat)) / (4 * Math.PI));
return { x: Math.round(pixelX), y: Math.round(pixelY) };
}
6. Open-Source Geospatial Stack & Computational Foundations
The alurail simulation engine is built on open standards and collaborative open-source technology. We recognize, formally attribute, and build directly upon these foundational open-source components:
- OpenStreetMap (OSM) [ODbL 1.0]: Provides planetary geodetic coordinate grids, coastline boundaries, and baseline geographic terrain vectors.
- OpenRailwayMap (ORM) [CC-BY-SA 2.0]: Supplies granular railway infrastructure vectors, switches, track gauge classifications (Cape Gauge 1,067mm and Standard Gauge 1,435mm), signals, and station yard track layouts.
- PostgreSQL & PostGIS [GPL-2.0+]: Serves as the high-throughput spatial computing core, processing topology graphs, bounding-box spatial indexes, and Dijkstra / pgRouting shortest-path calculations.
- Leaflet.js [2-Clause BSD]: Powers the lightweight client-side map canvas, GPU-accelerated coordinate projections, and smooth canvas marker renderers.
- Chaikin Corner-Cutting Smoothing: Mathematically cuts sharp polyline vertices at 25% and 75% intervals over iterative passes, producing organic, curved railway track vectors matching real minimum turning radii ($R_{\text{min}} \ge 250\text{m}$).
- Hidden Markov Model (HMM) Viterbi Filtering: Disentangles sensor emission probabilities ($p(z_t | x_t)$ assuming Gaussian error $\sigma \approx 10\text{m}$) from network transition probabilities ($p(x_t | x_{t-1})$ comparing topological track distance with Euclidean distance $|\Delta d|$), eliminating GPS drift when passenger trains traverse deep mountain cuttings or dense urban viaducts.
function chaikinCurveSmoothing(array $vertices, int $refinements = 2): array {
if (count($vertices) < 3) return $vertices;
for ($it = 0; $it < $refinements; $it++) {
$refined = [$vertices[0]]; // Always preserve starting station origin
for ($i = 0; $i < count($vertices) - 1; $i++) {
$p0 = $vertices[$i];
$p1 = $vertices[$i + 1];
// Point Q: 75% P0 + 25% P1
$q = [$p0[0] * 0.75 + $p1[0] * 0.25, $p0[1] * 0.75 + $p1[1] * 0.25];
// Point R: 25% P0 + 75% P1
$r = [$p0[0] * 0.25 + $p1[0] * 0.75, $p0[1] * 0.25 + $p1[1] * 0.75];
$refined[] = $q;
$refined[] = $r;
}
$refined[] = end($vertices); // Always preserve terminal destination
$vertices = $refined;
}
return $vertices;
}
Need Further Information or Looking to Collaborate?
Our engineering team is actively open to geospatial data collaboration, transit research partnerships, and telemetry system integration. Connect with us directly via WhatsApp, email, or our official contact form.