Basic use of the nsapi package

Roel M. Hogervorst

2018-08-10

This is a description and use of all the functions in the package.

The API returns everything in Dutch and therefore I have kept the responses in Dutch as well. The NS website does have a English translation which I will link to here: https://www.ns.nl/en/travel-information/ns-api.

First a description of how to set up your username and password and than a breakdown of all the functions.

Authentication

Before you can use any of the functions you need a username and password To get that go to: https://www.ns.nl/ews-aanvraagformulier/

You will receive a confirmation email, and finally a username and password (that can’t be changed for some reason).

You need to add the username and password to your rsession for the nsapi package to work. Why? Good question! It is not smart to type your passwords in a R session, they will end up in your history file and will be available for everyone who searches your computer. You might think that this password and account is not that important and you might be right, but let’s do the right thing anyway.

Temporary adding acountname and password to your session

A temporary way is to add them to your session with :

At the end of your session (if you go to a new project, if you restart R or if you crash) the keys are removed from memory. Keep in mind that this DOES put your password in the history file and is therefore NOT recommended.

Permanently adding username and password to your r session

Go to your global .Renviron file (or make one) and add the keys

The file will open and you can add two entries:

NSAPIPW = yourpasswordgiveninplaintextoveremailwhydopeopledothisohgod
NSAPIACCOUNT = probablyyouremail@email.com

How do you know if you succeeded?

Getting travel advise

If you’d like to know when your train from Amsterdam to Groningen goes, use the travel_advise function with departure and arrival station.

library(nsapi)
ams_to_ut <- get_travel_advise(
  fromStation = "Amsterdam Centraal",
  toStation = "Utrecht Centraal",
  #dateTime = , # I don't use it because I want to leave now!
  departure = TRUE  # I want to depart now
)
ams_to_ut[,1:5]
#>   Melding AantalOverstappen GeplandeReisTijd ActueleReisTijd
#> 1      NA                 0             0:27            0:27
#> 2      NA                 0             0:26            0:26
#> 3      NA                 0             0:27            0:27
#> 4      NA                 0             0:26            0:26
#> 5      NA                 0             0:27            0:27
#> 6      NA                 0             0:26            0:26
#> 7      NA                 0             0:27            0:27
#> 8      NA                 0             0:26            0:26
#> 9      NA                 0             0:27            0:27
#>   VertrekVertraging
#> 1              <NA>
#> 2              <NA>
#> 3              <NA>
#> 4              <NA>
#> 5              <NA>
#> 6              <NA>
#> 7              <NA>
#> 8              <NA>
#> 9              <NA>

The function returns a data.frame with 4 advises before and 4 after the time you selected.

ams_to_ut[,6:8]
#>   AankomstVertraging Optimaal GeplandeVertrekTijd
#> 1               <NA>    FALSE 2018-08-10 20:55:00
#> 2               <NA>    FALSE 2018-08-10 21:11:00
#> 3               <NA>    FALSE 2018-08-10 21:25:00
#> 4               <NA>    FALSE 2018-08-10 21:41:00
#> 5               <NA>     TRUE 2018-08-10 21:55:00
#> 6               <NA>    FALSE 2018-08-10 22:11:00
#> 7               <NA>    FALSE 2018-08-10 22:25:00
#> 8               <NA>    FALSE 2018-08-10 22:41:00
#> 9               <NA>    FALSE 2018-08-10 22:55:00

More details about the returned dataframe.

The column Reisdeel is a nested data frame with a nested data frame called stops within.

The API returns a lot of nested information, and as a consequence the data.frame is nested too.

Every row is thus an advise with arrival and departure times and travelparts, if you have to switch trains there will be more than 1 row in the ReisDeel column. For instance in the first row:

And every part of the trip has stops where the train stops:

Stationlist

Another endpoint of the API is a list of stations.

stations <- get_stationlist()
head(stations)
#>   Code                      Type        Namen Land UICCode      Lat
#> 1   HT knooppuntIntercitystation list("De....   NL 8400319 51.69048
#> 2  HTO          stoptreinstation list("Dn....   NL 8400320 51.70055
#> 3  HDE          stoptreinstation list("'t....   NL 8400388 52.40917
#> 4 AHBF knooppuntIntercitystation list("Aa....    D 8015345 50.76780
#> 5  ATN          stoptreinstation list("Aa....   NL 8400045 51.92133
#> 6   AC          stoptreinstation list("Ab....   NL 8400047 52.27850
#>        Lon   Synoniemen
#> 1 5.293620 list("He....
#> 2 5.318333 list("He....
#> 3 5.893611 list("Ha....
#> 4 6.091499           NA
#> 5 6.578627           NA
#> 6 4.977000           NA
names(stations)
#> [1] "Code"       "Type"       "Namen"      "Land"       "UICCode"   
#> [6] "Lat"        "Lon"        "Synoniemen"

This will return a data frame with all the stations in the NS API. There are not only Dutch stations, but also multiple Belgian, German, English, Austrian, French,Polish and other country stations. Every station has an international code, name, synonyms, geolocation (Lat, Lon), and type of station.

# code is not executed, I don't want to add these dependencies to the package
library(tidyverse)
library(ggrepel)
stations <- 
  get_stations() %>% 
  mutate(Naam = map_chr(Namen, ~.[["Kort"]][[1]]),
  Label = ifelse(Type == "knooppuntIntercitystation", Naam,NA) ) %>% 
  as.tibble() 

stations %>% 
  filter(Land == "NL") %>% 
  ggplot(aes(Lon, Lat, label = Label))+ 
  geom_point(alpha = 1/3)+
  geom_text_repel()+
  coord_map()+
  labs(
    title = "Only Dutch Stations",
    subtitle = "Major stations named",
    caption = "Data: NS 2018 (Dutch National Railways)"
  )
Plot of Dutch stations’ locations

Plot of Dutch stations’ locations

Departures

You can see the departure times of any station at this time by calling the departures() function.

leiden_trains <- get_departures(station = "Leiden Lammenschans")
head(leiden_trains)
#>   RitNummer         VertrekTijd   EindBestemming TreinSoort
#> 1           2018-08-10 21:57:00 Utrecht Centraal  Intercity
#> 2           2018-08-10 22:02:00  Leiden Centraal  Intercity
#> 3           2018-08-10 22:27:00 Utrecht Centraal  Intercity
#> 4           2018-08-10 22:32:00  Leiden Centraal  Intercity
#> 5           2018-08-10 22:57:00 Utrecht Centraal  Intercity
#> 6           2018-08-10 23:02:00  Leiden Centraal  Intercity
#>        RouteTekst Vervoerder VertrekSpoor Spoorwijziging
#> 1 Alphen a/d Rijn                       1               
#> 2            <NA>                       1               
#> 3 Alphen a/d Rijn                       1               
#> 4            <NA>                       1               
#> 5 Alphen a/d Rijn                       1               
#> 6            <NA>                       1

Disruptions and maintenance

These 3 functions call out to find out about disruptions and engineering on the tracks for the current time, for the next 2 weeks or a specific station. Every call returns a dataframe with “id”, “Traject”, “Periode”, “Reden”, “Bericht”, “Advies”, and “Datum”.

Bericht is often a complete message with html markup:

<p>\n<b>Wanneer: vanaf zaterdag 26 mei </b><br/>\n<b>Oorzaak: aangepaste dienstregeling</b><br/>\n<b>Advies: U kunt gebruikmaken van de gewijzigde dienstregeling</b>\n<br/>Stoptreinen tussen Roosendaal en Antwerpen Centraal/Puurs rijden in een aangepaste dienstregeling; reizigers dienen in Essen (B) over te stappen op een andere trein\n<br/> plan uw reis in de Internationale Reisplanner\n<br/> <br/>\n  <b>Extra reistijd: een kwartier tot een half uur</b><br/>\n</p>

Current disruptions (=unscheduled disruptions + current engineering work)

Showing only columns Traject and Periode:

Scheduled engineering work(=scheduled engineering work)

This call returns scheduled engineering work for the next 2 weeks.

example:

Current disruptions for a specific station (=unscheduled disruptions + current engineering work)

Showing only first 2 messages: