Visualizing Seattle's 911 calls
<!DOCTYPE html>
Okay, so how did I do it?
Getting the data
This part was actually the quickest of all. The city of Seattle releases quite a bit of interesting data, and constantly-updated 911 metadata is one of those data-dumps: it came in CSV and API form (more about this in a second), so as a jumping-off point I downloaded the CSV.
The CSV was 973,569 lines. It took some time.
Creating the map
I just discovered Folium, which is essentially a Python wrapper around the excellent Leaflet.js mapping library. With it, creating an output map was easy:
import folium
from collections import namedtuple
FILEPATH = "seattle_911.html"
SEATTLE_COORDINATES = (47.5951, -122.3326)
SOURCE_FILE = "Seattle_Police_Department_911_Incident_Response.csv"
MAX_LINES = 10000
seattle_map = folium.Map(location=SEATTLE_COORDINATES)
Location = namedtuple('Location', ['latitude', 'longitude'])
with open(SOURCE_FILE, 'r') as opened_file:
# Skip the first line because its header information.
for call in opened_file.readlines()[1:MAX_LINES]:
parsed_call = call.split(",")
location = Location(parsed_call[-3], parsed_call[-4])
seattle_map.simple_marker(location)
seattle_map.create_map(FILEPATH)
Open the output HTML, though, and we get this monstrosity:
Jeez, that’s not exactly helpful. Plus, the final HTML file is a bit north of forty thousand lines long, since folium
is using four lines per marker: there’s gotta be a better way than a 1.1MB file, right?
So, tackling the death-by-markers problem first, I turn to the comically easy-to-use clustering plugin: there’s no code to show for this step, since it literally only requires you to change L.LayerGroup
to L.markerClusterGroup
.
For the second problem, I ended up sort of scrapping the existing code I had and turning from a csv+Python approach to a API+Javascript one. All of the City of Seattle data is exposed via a simple endpoint, and we can access that with jQuery like so:
jQuery.get(API_ENDPOINT + "$offset=" + offset, function(data) {
for (var index = 0; index < data.length; index++) {
incident = data[index];
loc = incident["incident_location"];
marker = L.marker([loc["latitude"], loc["longitude"]]);
marker.bindPopup(incident["event_clearance_description"]);
map.addLayer(marker);
}
}
Simple, right?
You can also view the entire source of the embedded map, if you’d prefer. Hope you enjoyed this — and let me know if there’s anything you think I can improve/include!