-
Notifications
You must be signed in to change notification settings - Fork 0
Train Graph Building
Since LTA does not directly provide train data, the only data regarding trains publicly given is via the Datamall Geospatial API. Using GeoTools and its SHP parser, the TrainStations
and TrainExits
features can be processed.
In Singapore, all train stations are given an ID (e.g. CC15). The first 2 letters indicate the line, while the following number indicate the stop sequence. Numbers are sometimes omitted due to cancelled stations. Assuming each MRT line is a straight line with no branches or loops, vertices can be created similar to that of bus stops.
Such exceptions are ignored in this process using the 2 regexes in application.yml
exclude-line-branch: '(^CE\d$)|(^CG\d$)'
exclude-line-loop: '(^BP\d{1,2}$)|(^STC$)|(^PTC$)|(^SE\d{1,2}$)|(^SW\d{1,2}$)|(^PE\d{1,2}$)|(^PW\d{1,2}$)'
Some MRT services (ie EW and CC) have branching services, some of which require changing trains while others do not.
In application.yml
, a sample branch would look like this:
branch-node: CC4
src: CE1
des: CE2
join: true
transfer-time: 0
post-branch-service:
ascending: 'Circle Line (HarbourFront)'
descending: 'Circle Line (Dhoby Ghaut/Marina Bay)'
branch-service:
ascending: 'Circle Line (HarbourFront)'
descending: 'Circle Line (Marina Bay)'
Since Linear Train Vertex Creation traverses train stations in ascending numerical order, branch-node
determines after which stage that branching has to occur. The branch's start and end point are denoted by src
and des
. The boolean join
determines if the branch joins with the main line at branch-node
or splits with it. As of date of writing this document, only joins occur on Singapore's train system. transfer-time
is the time taken to change from the main line to the branch. On the Circle line, there is no change of train required. Hence, transfer-time: 0
. After branching, the service name of the main line and branches have to change. Therefore, the post-branch-service
and branch-service
configurations determine this.
The branching logic during linear vertex creation is as such:
Setup:
Create cumulativeSumArray for each node in branch
Linear traversal:
Once see <branching node>
Determine whether <join or split (bool)>
Join:
For every node after <branching node> (check if currBranch is null)
For every node on the branch
sum <distance to <branching node> + <branch distance>> and add to cumulativeSumArray
add branch transfer time
use new service names (descending splits into 2 names, ascending use joint names)
Split:
For every node on the branch
For every node before <branching node>
sum <distance to <branching node> + <branch distance>> and add to cumulativeSumArray
add branch transfer time
use new service names (ascending splits into 2 names, descending use joint names)
Train loops (in LRTs) are handled in 2 different ways. There are 2 kinds of loops: (1) A simple circle (2) A circle loop with a line at a single interchange station. (1) describe the PTC and STC LRTs while (2) refers to the BP LRT.
Handling type (1) is relatively simple. First, create a cumulative sum array, starting from the central interchange loop (e.g. STC), making sure to store the last vertex as well - the one connecting the last node to the first.
The following pseudocode details the how to create loop vertices
let totalTime = total time to travel of loop
let cumArr = cumulative sum array
halfT = totalTime*0.5
for (i=0;i<n;i++)
for (j=i+1;j<n;j++)
t = cumArr[j]-cumArr[i];
//Taking opposite direction in loop is faster
if(totalTime-t > halfT) t = totalTime-t
createVertex(src=i,des=j,time=t)
createVertex(src=j,des=i,time=t)
Train interchanges (stations which serve 2 lines) can connect both lines by creating a vertex between each "node" (internally different nodes but same "node" in reality) with a "Walk (Interchange)" service and transfer-time
determined in application.yml
Since LTA does not easily provide an API to obtain train frequencies and operational hours, this project assumes these values taken from Land Transport Guru