John Lekberg


Chicago - Proximity ot MMR vaccination sites

Created 2019-09-23.

An improved heatmap showing distance to nearest MMR vaccination sites in Chicago, along with communities that have low penetration.

This graphic uses data from the City of Chicago:

This improves upon my previous composition "Chicago, Distance to MMR vaccination sites" by

Three regions of the city are relatively far from vaccination sites:

Because O'Hare airport is not residential, I don't think it's a problem that the airport is far from an MMR vaccination site.

It's not clear to me how much of a problem this is for the noted community areas on the West Side and the Far Southeast Side. Questions that I have are:

I think that the biggest limitation of this graphic is that it is static, so the threshold used to determine if a community area is "far" from a vaccination site cannot changed by a viewer.

I used a combination of Python and R to generate this. My Python code used the Shapely library, which allows manipualtion and analysis of geometric objects in the Cartesian plane. This library greatly simplified the Python code that I wrote to compute distance to vaccination sites and determine which communities were affected.


Code used to create "Chicago - Proximity ot MMR vaccination sites"


create.py - Python 3 code used to create datasets for the graphic: distance.csv, distant_communities_shapes.csv, distant_communities_labels.csv.
import json
import csv
from itertools import product
from collections import namedtuple

from shapely.geometry.polygon import Polygon
from shapely.geometry import MultiPoint, MultiPolygon, Point
import shapely.ops

with open("Special_Vaccine_Locations_-_MMR.csv") as infile:
    reader = csv.DictReader(infile)
    vaccination_sites = MultiPoint([
        (float(row['longitude']), float(row['latitude']))
        for row in reader
    ])

with open("Boundaries - Community Areas (current).geojson") as infile:
    community_data = json.load(infile)

community_areas = {
    feature["properties"]["area_numbe"]: MultiPolygon([
        Polygon(polygon[0]) # only exterior rings
        for polygon in feature["geometry"]["coordinates"]
    ])
    for feature in community_data["features"]
}

chicago = shapely.ops.unary_union(community_areas.values())

lon_min, lat_min, lon_max, lat_max = chicago.bounds

# Having NumPy trouble, so I reinvented the wheel. Don't do this. Instead:
# <https://docs.scipy.org/doc/numpy/reference/generated/numpy.linspace.html>
#
linspace = lambda a, b, n: ( a + i*(b-a)/(n-1) for i in range(n) )

lon_density = 100
lat_density = 100

sample_points = MultiPoint([
    Point(lon, lat)
    for lon, lat in product(
        linspace(lon_min, lon_max, lon_density),
        linspace(lat_min, lat_max, lat_density),
    )
]).intersection(chicago)

threshold = .055 # emperically determined

# Find communities that have sample points less than a threshold
distant_points = MultiPoint([
    point
    for point in sample_points
    if threshold <= point.distance(vaccination_sites)
])

distant_communities = {
    name: shape
    for name, shape in community_areas.items()
    if shape.intersects(distant_points)
}

with open("distance.csv", "w") as outfile:
    writer = csv.DictWriter(outfile, fieldnames="longitude latitude distance".split())
    writer.writeheader()
    for point in sample_points:
        writer.writerow({
            "longitude": point.x,
            "latitude": point.y,
            "distance": point.distance(mmr_vaccination_sites)
        })

with open("distant_communities_shapes.csv", "w") as outfile:
    writer = csv.DictWriter(outfile, fieldnames="longitude latitude community GROUP_ID".split())
    writer.writeheader()
    for community, shape in distant_communities.items():
        for n, polygon in enumerate(shape.geoms):
            for longitude, latitude in polygon.exterior.coords:
                writer.writerow({
                    "longitude": longitude,
                    "latitude": latitude,
                    "community": community,
                    "GROUP_ID": f"{community}-{n}",
                })

with open("distant_communities_labels.csv", "w") as outfile:
    writer = csv.DictWriter(outfile, fieldnames="longitude latitude community".split())
    writer.writeheader()
    for community, shape in distant_communities.items():
        writer.writerow({
            "longitude": shape.centroid.x,
            "latitude": shape.centroid.y,
            "community": community
        })




render.r - R code used to create the graphic.
library(ggplot2)
library(magrittr)
library(viridis)
library(dplyr)

distance <- read.csv("distance.csv")
distant_communities_shapes <- read.csv("distant_communities_shapes.csv")
distant_communities_labels <- read.csv("distance_communities_labels.csv")

ohare <- 76 # O'Hare Community Number

ggplot() +
  # SCALE
  scale_fill_viridis(
    option="magma", direction=-1,
    limits=c(0, 0.110), breaks=c(0, 0.110),
    labels = c("At vaccination site", "Further from\nvaccination site"),
    name = ""
  ) +
  # COORD
  coord_fixed(1) +
  # GUIDE
  ggtitle("Chicago - Proximity to MMR vaccination sites") +
  # ELEMENT
  geom_tile(aes(longitude, latitude, fill=distance), distance) +
  # THEME
  theme_bw() +
  # Annotate - distant communities
  geom_path(aes(longitude, latitude, group=GROUP_ID),
            distant_communities_shapes %>% filter(community != ohare), size=0.5) +
  geom_text(aes(longitude, latitude, label=community),
            distant_communities_labels %>% filter(community != ohare), color="white") +
  # Annotate - O'Hare airport
  annotate("rect", xmin=-87.95, ymin=41.94, xmax=-87.87, ymax=42.02, alpha=0, color="black") +
  annotate("text", x=-87.91, y=41.93, label="O'Hare Airport")