Crime Density in Seattle
Contributed by Daniel Donohue. Daniel was a student of the NYC Data Science Academy 12-week full-time data science bootcamp from Sep. 23 to Dec. 18, 2015. This post was based on his second in-class project (due after the 4th week of the program).
Daniel Michael Donohue
11 November 2015
Introduction
For my second project, which was to build and deploy an application made in Shiny, I chose to focus on a dataset of police report incidents in Seattle, WA. What I did with this dataset is make an interactive heat map of crime density based on a user's selection of a date range and a subset of offense types. Theoretically, it could be used to vet a neighborhood's safety (for instance, if a person is moving and wants to see what their new neighborhood is like), or to give law enforcement information so that they can more effectively battle certain types of crime.
The Application
You can demo the application here (please don't forget to close your browser's window when you're done!)
The application itself has four main components. Along the left-hand side the user will find a place to enter a range of dates, and a checkbox group where they can select which types of crimes they want to be displayed. The main panel contains a leaflet map, with a heat map overlaid. The base map layer is created through the R package rChart's binding to the Leaflet library:
output$my.map <- renderMap({
my.map <- Leaflet$new()
my.map$setView(c(47.5982623, -122.3415519), 12)
my.map$tileLayer(provider = "Esri.WorldStreetMap")
my.map
})
This creates the map layer, centers it on a particular latitude/longitude, sets an appropriate zoom level, and chooses the style of the map. The heat layer component depends entirely on Leaflet.heat, a JavaScript plugin written by Vladimir Agafonkin. It requires a JSON object of latitude/longitude pairs. When a user selects a range of dates and a subset of crimes, the reactive element
get.spd.arr <- reactive({
spd.dat <- spd[spd$Offense %in% input$sel.crime &
(spd$Date >= input$sel.date[1] &
spd$Date <= input$sel.date[2]), ]
spd.arr <- toJSONArray2(spd.dat[c(3, 4)], json = FALSE, names = FALSE)
return(spd.arr)
subsets the spd dataframe (the object containing all observations) based on the selection, and returns a JSON array of latitude/longitude pairs to be fed into the heat layer function:
output$spd.map <- renderUI({
tags$body(tags$script(HTML(sprintf("var addressPoints = %s
if (typeof heat === typeof undefined) {
heat = L.heatLayer(addressPoints, {maxZoom: 9, radius: 20, blur: 30});
heat.addTo(map);
} else {
heat.setOptions({maxZoom: 9, radius: 20, blur: 30});
heat.setLatLngs(addressPoints);
}
", rjson::toJSON(get.spd.arr())
))))
})
This takes the JSON array returned by the reactive element, converts it to a JSON object, and then supplies this to the JavaScript function heatLayer, which draws the heat layer on top of the Leaflet map based on the closeness of latitude/longitude pairs. Originally, the heat layer was being placed over existing heat layers every time the state of the map changed, so the if...else statement is a bit of JavaScript that prevents this from happening.
Conclusion
Working with Shiny was fun. There's a lot you can do with it, and I look forward to incorporating it into future projects. If I were to improve on this project, I would like to have a user be able to enter an address or a name of a landmark, and have the map automatically zoom in on that location. It would also be useful to have the application be updated as new police reports are added to the dataset. Finally, it would be nice to expand to other cities.