Spreading the Translink Love
First off this isn’t a post about #opendata. I’m going to keep my trap shut about that once and for all. If I start such a conversation off I will cause the usual gasps and responses on Twitter.
As a passenger I’ve never really had issue with Translink. The service is usually good and the trains are punctual. So much so that Nick Hewer and Margaret Mountford were singing Translink’s NIRailways service’s 99% punctuality rate compared to the rest of the UK’s private sector offerings.
So to that end, by way of a tribute let’s look at converting that love into graph database nodes. Okay as links go that was pretty lame, regardless, let’s get started.
Londonderry to Great Victoria Street
The route from Londonderry to Great Victoria Street (GVS) is nothing short of stunning, well the coastline bit is.
What we are dealing with here is a large graph database. Each station represents a node and the connecting line represents an edge. Programatically we could recreate this map (I’m just doing the Londonderry to GVS part) and traverse the graph accordingly (something we’ll look at in part 2). Nodes and edges in a tradition database are a bit of a pain to design and populate. Graph databases though are much easier for this sort of work.
Neo4J – The Node of Node Hall
You can download Neo4J Community edition for free. Download the distribution and then untar or unzip it to a clean directory. For this demo I’m not fussed about authentication so I’m going to disable (it’s only working locally, if it were like then make sure it’s enabled).
The conf directory has a file called neo4j-server.properties and you need to make sure the following line is false.
dbms.security.auth_enabled=false
To start Neo4J run the following command from the home directory of the Neo4J distribution.
bin/neo4j start
This will start the server and also the web based browser console (more on that later).
Nodes and Relationships with Clojure
I’m going to code up the bulk of what goes on to get our nodes (the stations) and the edges (the connecting line between the stations) in to Neo4J. Clojure are an open source wrapper than connects with Neo4J’s REST interface called Neocons. I’m going to use Leiningen to create an app project and handle all the dependencies.
So, to create a new app from the command line:
lein new app nirailways
Then you need to edit project.clj and add the Neocons dependency to the :dependencies map.
[clojurewerkz/neocons "3.0.0"]
All done.
In src/nirailways/core.clj is where the Clojure code will reside. I’ll breakdown the coding elements here, to see everything have a look at the Github repository.
Establishing a Connection
Before you can establish a connect you need to add the required libraries to the code.
(ns nirailways.core (:require [clojurewerkz.neocons.rest :as nr] [clojurewerkz.neocons.rest.nodes :as nn] [clojurewerkz.neocons.rest.relationships :as nrl]))
You need a connection to the database. In it’s most basic way using nr/connect function will do that for you.
(nr/connect "http://localhost:7474/db/data")
Creating Nodes
Each node will be a station and will have two properties, the name of the station and the type it is, for this case it will just be “station”. We could add bus stations later if we wanted.
Using nn/create method will create a node taking it’s properties as a Clojure map.
londonderry (nn/create conn {:name "Londonderry" :type "station"})
There we are, a node. Now there are no other stations just this one. So in order to get Neo4J to do something I need to write a function that will create all the nodes for us.
(defn create-nodes-edges [] (let [conn (nr/connect "http://localhost:7474/db/data") londonderry (nn/create conn {:name "Londonderry" :type "station"}) bellarena (nn/create conn {:name "Bellarena" :type "station"}) castlerock (nn/create conn {:name "Castlerock" :type "station"}) coleraine (nn/create conn {:name "Coleraine" :type "station"}) ballymoney (nn/create conn {:name "Ballymoney" :type "station"}) cullybacky (nn/create conn {:name "Cullybacky" :type "station"}) ballymena (nn/create conn {:name "Ballymena" :type "station"}) antrim (nn/create conn {:name "Antrim" :type "station"}) mossleywest (nn/create conn {:name "Mossley West" :type "station"}) yorkgate (nn/create conn {:name "Yorkgate" :type "station"}) belfastcentral (nn/create conn {:name "Belfast Central" :type "station"}) botanic (nn/create conn {:name "Botanic" :type "station"}) cityhospital (nn/create conn {:name "City Hospital" :type "station"}) gtvictoriastreet (nn/create conn {:name "Great Victoria Street" :type "station"})......
That will create all the station nodes for us. Now then only thing is they are connected to nothing.
Creating Relationships Between the Nodes
With two nodes we can create relationships. The nrl/create function will take two nodes and create a relationship between then. We’ll call this relationship a connection and I’ll add a property called “distance” in there too but I’m setting all the distances to ten miles as I don’t know what they are.
rel1 (nrl/create conn londonderry bellarena :connection {:direction "all" :distance "10"})
Adding this to the node creation function will generate all the nodes and relationships in one go. Notice for Coleraine there are two relationship nodes. One points to Ballymoney (onwards to GVS) and then another to University (on the Portrush line).
(defn create-nodes-edges [] (let [conn (nr/connect "http://localhost:7474/db/data") londonderry (nn/create conn {:name "Londonderry" :type "station"}) bellarena (nn/create conn {:name "Bellarena" :type "station"}) castlerock (nn/create conn {:name "Castlerock" :type "station"}) coleraine (nn/create conn {:name "Coleraine" :type "station"}) ballymoney (nn/create conn {:name "Ballymoney" :type "station"}) cullybacky (nn/create conn {:name "Cullybacky" :type "station"}) ballymena (nn/create conn {:name "Ballymena" :type "station"}) antrim (nn/create conn {:name "Antrim" :type "station"}) mossleywest (nn/create conn {:name "Mossley West" :type "station"}) yorkgate (nn/create conn {:name "Yorkgate" :type "station"}) belfastcentral (nn/create conn {:name "Belfast Central" :type "station"}) botanic (nn/create conn {:name "Botanic" :type "station"}) cityhospital (nn/create conn {:name "City Hospital" :type "station"}) gtvictoriastreet (nn/create conn {:name "Great Victoria Street" :type "station"}) university (nn/create conn {:name "University" :type "station"}) dhuvarren (nn/create conn {:name "Dhu Varren" :type "station"}) portrush (nn/create conn {:name "Portrush" :type "station"}) rel1 (nrl/create conn londonderry bellarena :connection {:direction "all" :distance "10"}) rel2 (nrl/create conn bellarena castlerock :connection {:direction "all" :distance "10"}) rel3 (nrl/create conn castlerock coleraine :connection {:direction "all" :distance "10"}) rel4 (nrl/create conn coleraine ballymoney :connection {:direction "all" :distance "10"}) rel5 (nrl/create conn coleraine university :connection {:direction "all" :distnace "10"}) rel6 (nrl/create conn ballymoney cullybacky :connection {:direction "all" :distance "10"}) rel7 (nrl/create conn cullybacky ballymena :connection {:direction "all" :distance "10"}) rel8 (nrl/create conn ballymena antrim :connection {:direction "all" :distance "10"}) rel9 (nrl/create conn antrim mossleywest :connection {:direction "all" :distance "10"}) rel10 (nrl/create conn mossleywest yorkgate :connection {:direction "all" :distance "10"}) rel11 (nrl/create conn yorkgate belfastcentral :connection {:direction "all" :distance "10"}) rel12 (nrl/create conn belfastcentral botanic :connection {:direction "all" :distance "10"}) rel13 (nrl/create conn botanic cityhospital :connection {:direction "all" :distance "10"}) rel14 (nrl/create conn cityhospital gtvictoriastreet :connection {:direction "all" :distance "10"}) rel15 (nrl/create conn university dhuvarren :connection {:direction "all" :distance "10"}) rel16 (nrl/create conn dhuvarren portrush :connection {:direction "all" :distance "10"}) ]))
We can run this method through the REPL, there’s no output it will just report nil as there’s no return value (though Clojure will return something).
(create-node-edges)
Viewing The Results
So far we’ve started the Neo4J server, written some Clojure code to handle the REST API calls to create the stations and the line connections. Looking at the Neo4J browser (open a browser and go to http://localhost:7474) we can see the nodes connected in graph form.
Click on the top left icon of the three circles. Then click the “connection” icon. You should see the node graph show up (you can maximise this to full screen).
Clicking on the node itself will show the detail in the bottom left.
Next Time….
We’ll look at using Clojure to traverse the graph and even maybes throw in some Dijkstas algorithm for shortest path calculations. I might even get all the NI Railways stations in at some point too….
Want To Try?
- Download Neo4J
- Download Clojure
- Download Leiningen
- Download The Github Repository