Mapping defibrillators (AEDs) in Ottawa using R and open data

I met some of the good folk behind Open Data Ottawa on the weekend at the 2013 User Group Connect unconference. Open Data Ottawa is an initiative to see what interesting ideas might develop from having The City's data publicly available.

Back at home I had a look through the published data, which are mostly presented as individual spreadsheets. There are financial data (yawn!), and the like, but what jumped out at me was a file listing of all the city's automated external defibrillators (AEDs) in public buildings (i.e., not necessarily all public access AEDs).

The file got me thinking that it would be nice if The City had a mobile-friendly webpage (e.g., "aed.ottawa.ca") that mapped all those AED locations for quick access in an emergency. You could save the page as a bookmark or add it to your homescreen in the event that you ever need to use it. I figured that a bit of R could turn the list of AED locations into a more user friendly interactive map.

The fist challenge was to clean up the file to make it more friendly for analysis (I'll spare you the gory details). I then needed to convert The City's street-address information into latitude and longitude coordinates that could be mapped (i.e., geocode). Finally, I wanted to turn the AED coordinates into an interactive javascript object (i.e., a map) that could be pasted into a website.

Turns out Andrew Barr tackled a similar problem using the googleVis package, so I modified his code to get what I wanted:

getDocNodeVal <- function(doc, path)
{
sapply(getNodeSet(doc, path), function(el) xmlValue(el))
}

gGeoCode=function(str)
{
library(XML)
Sys.sleep(0.2) #Google's API could not keep up so I had to
#slow the code down!!

u <- paste('http://maps.google.com/maps/api/geocode/xml?sensor=false&address=', str)
doc <- xmlTreeParse(u, useInternal = TRUE)
str <- gsub(' ', '%20', str)
lat <- getDocNodeVal(doc, "/GeocodeResponse/result/geometry/location/lat")
lng <- getDocNodeVal(doc, "/GeocodeResponse/result/geometry/location/lng")
list(lat = lat, lng = lng)
}

library(googleVis)

place.names <- d$aed.address

lat.long <- lapply(place.names, FUN = gGeoCode)
lat.long.formatted <- lapply(lat.long, FUN = function(x){
paste(x$lat[1], x$lng[1], sep=":")})

#create a dataframe, formatting the place name string first and making the latlong a vector, not list
plot.data <- data.frame(name = paste(place.names, " || AED: ", d$aed.location, sep = ""), latlong = unlist(lat.long.formatted))

sites <- gvisMap(plot.data, locationvar = "latlong", tipvar="name", options = list(mapType = 'normal', showTip = 'TRUE', enableScrollWheel = 'TRUE', useMapTypeControl = 'TRUE'))
plot(sites)

Lessons learned

Relying on Google to supply the coordinate data for the AEDs is a bad idea. Google Maps often showed a location many doors away from where it intended. Imagine trying to get to an AED in a panic in such a scenario. To make this work, The City would have to get GPS coordinates for each of their AED units and put those in the datafile (or better yet, in a linked database) rather than just the street addresses.

What's missing

Well, several of the AED sites with poor address data are missing (I deleted them to save time since this is only a proof of concept). I would also want data on seasons (in the case of outdoor pools) and hours of operation so colour codes could be used to indicate if the AED is accessible at the time you are looking up locations. Also great would be allowing private building owners to submit their AED locations for mapping, if they so wished.