Sat 28 December 2013

Visualizing Seattle's 911 calls

Data · JavaScript · Seattle · Python

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!

You should .

Comments