The biggest event for me this year was completely outside of work and had nothing to do with statistics or R: I got married. We technically met at the Open Stats meetup and I did build our wedding website with RMarkdown, so R was still involved. We just returned from our around-the-world honeymoon so I thought the best way to track our travels would be with maps and globes using leaflet
and threejs
.
Before we get to any code, the following packages were used in making this post.
This was an extensive trip that, in addition to traditional vacation activities, included a few visits to clients and speaking and a few conferences and meetups. In all, we visited, London, Singapore, Hong Kong, Auckland, Queenstown, Bora Bora, Tahiti, Moorea, San Jose and San Francisco, with a connection or two in between.
The airport/ferry codes for our trip were the following.
Origin | Destination | Airline |
---|---|---|
JFK | LGW | Norwegian Air |
LHR | SIN | Singapore Airlines |
SIN | HKG | Singapore Airlines |
HKG | AKL | Cathay Pacific |
AKL | ZQN | Air New Zealand |
ZQN | AKL | Air New Zealand |
AKL | PPT | Air New Zealand |
PPT | BOB | Air Tahiti |
BOB | MOZ | Air Tahiti |
MOZ | PPT | Terevau |
PPT | LAX | Air Tahiti Nui |
LAX | SJC | Alaska Airlines |
SFO | JFK | JetBlue |
Converting these to latitude and longitude is easy thanks to Open Flights.
# read in the data
airports <- readr::read_csv('https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports-extended.dat',
# give it good column names since the data are headerless
col_names=c('ID', 'Name', 'City', 'Country',
'IATA', 'ICAO', 'Latitude', 'Longitude',
'Altitude', 'Timezone', 'DST', 'Tz',
'Type', 'Source'))
We then use filter
to get just the ports we visited. Notice how we use a second tbl
inside filter
.
visited <- airports %>%
select(Name, City, Country, IATA, Latitude, Longitude) %>%
filter(IATA %in% (codes %>% select(Origin, Destination) %>% unlist))
DT::datatable(visited, elementId='AirportsTable',
rownames=FALSE,
extensions=c('FixedHeader', 'Scroller'),
options=list(
dom='<"top"f>rt<"bottom"i><"clear">'
,
scrollY=200,
scroller=TRUE
)
) %>%
DT::formatRound(columns=c('Latitude', 'Longitude'), digits=2)
We then manually reorder the airports so that edges can be drawn nicely between them. This is akin to creating an edgelist of airport-pairs. This is not the most robust way of creating this list, but suffices for our purposes.
visitedOrdered <- visited %>%
slice(c(12, 1, 2, 8, 7, 5, 6, 5, 13, 3, 4, 13, 10, 11, 12))
For the first visualization let’s create a map using leaflet
.
# initialize the widget
leaflet(data=visitedOrdered) %>%
# overlay map tiles
addTiles() %>%
# plot lines from one point to another
addPolylines(lng=~Longitude, lat=~Latitude) %>%
# add markers with city names
addMarkers(lng=~Longitude, lat=~Latitude, popup=~City)
Unfortunately, this doesn’t quite capture the directions of the flights as it makes it look like we flew back west to get to Papeete. So let’s try a globe instead using threejs
.
We augment the edgelist of airports so that it has the latitude and longitude of the origin and destination airports for each flight.
flightPaths <- codes %>%
left_join(visited %>% select(IATA, Longitude, Latitude), by=c('Origin'='IATA')) %>%
rename(oLong=Longitude, oLat=Latitude) %>%
left_join(visited %>% select(IATA, Longitude, Latitude), by=c('Destination'='IATA')) %>%
rename(dLong=Longitude, dLat=Latitude)
DT::datatable(visited, elementId='FlightPathLatLong',
rownames=FALSE,
extensions=c('FixedHeader', 'Scroller'),
options=list(
dom='<"top"f>rt<"bottom"i><"clear">'
,
scrollY=200,
scroller=TRUE
)
) %>%
DT::formatRound(columns=c('Latitude', 'Longitude'), digits=2)
Now we can provide that data to threejs
. We first specify an image to overlay on the globe. Then we specify the latitude and longitude of visited airports. After that, we provide the origin and destination latitudes and longitudes of our flights. The rest of the arguments are cosmetic.
globejs(
# the image to overlay on the globe
img="http://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73909/world.topo.bathy.200412.3x5400x2700.jpg",
# lat/long of visited airports
lat=visited$Latitude, long=visited$Longitude,
# lat/long of origin and destination
arcs=flightPaths %>% select(oLat, oLong, dLat, dLong),
# cosmetic adjustments
arcsHeight=.4, arcsLwd=7, arcsColor="red", arcsOpacity=.95,
atmosphere=FALSE, fov=30, rotationlat=0.3, rotationlong=.8*pi)
We now calculate the total distance traveled (not including car trips) using Haversine Distance to account for the curvature of the Earth.
distHaversine(visitedOrdered %>% select(Longitude, Latitude), r=3959) %>% sum
## [1] 28660.52
So we traveled 3,760 more miles than the circumference of the Earth.
Beyond the epic proportions of our travel, this honeymoon was outstanding from the sheer length, to the vastly different places we visited, to the food we ate and the sights we saw, to the activities we participated in, to the great people along the way. And, of course, it’s amazing to spend a month traveling with your favorite person.
Jared Lander is the Chief Data Scientist of Lander Analytics a New York data science firm, Adjunct Professor at Columbia University, Organizer of the New York Open Statistical Programming meetup and the New York and Washington DC R Conferences and author of R for Everyone.