Skip to content

Train Graph Building

jloh02 edited this page Jun 11, 2021 · 4 revisions

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.

Linear Train Vertex Creation

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}$)' 

Handling Branches

Some MRT services (ie EW and CC) have branching services, some of which require changing trains while others do not.

Configuration

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.

Pseudocode

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)

Loops

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)

Interchanges

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

Frequency and Operational Hours

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