Category: Maps

Traffic calming around Chicago schools and parks

Reducing the number and speed of automobiles near schools and parks is a proven way to reduce the number of traffic crashes involving children, part of a practice known as “traffic calming”. In Chicago a key way to do that has been to “cul-de-sac” (which I’m using as a verb) a street to prevent through traffic (reducing the number) and preventing speeding (reducing the speed).

My study of this was inspired by the above tweet, where the person is accurate when they say, “it works with any street”. Indeed: Full access to the school or park and to every property on the block is maintained, but drivers are not able to go through while pedestrians and bicyclists can (another practice called “modal filtering“).

My favorite example in Chicago is Hadiya Pendleton Park, at 4345 S Calumet Ave. This project created two mid-block cul-de-sacs and a park in the middle of a block, using vacant city-owned land on both sides. Creating new open space is a common corresponding outcome of the cul-de-sac application, which is what occurred at Funston Elementary School (see the before and after aerial photos below).

two black and white aerial photos taken of Funston Elementary School, one in 1998 showing a 4-way intersection of McLean Ave and Central Park Ave and one in 2003 showing a cul-de-sac on McLean Ave west of the school and landscaped area between the cul-de-sac and the three-way intersection of McLean and Central Park.

Through Twitter I solicited additional examples of where the city has created traffic calming near schools and parks using cul-de-sacs. Examples were submitted by RolandEmily (who mentioned Funston), Matt, Steven, and another tweeter.

Using OpenStreetMap, Overpass Query Language, and Overpass Turbo, we can find all of the schools and parks that are within a specified distance of a cul-de-sac. It turns out there are 153 schools and parks in Chicago that are within 150 feet of a cul-de-sac. (This considers only schools, parks, and cul-de-sacs, tagged as “turning circles”, currently mapped in OSM, and I have not verified each of the 153 instances.)

Map showing parks, schools, and adjacent cul-de-sacs, the results of the Overpass Turbo query.
Map showing parks, schools, and adjacent cul-de-sacs, the results of the Overpass Turbo query. An inset map shows a zoomed in portion of the map to illustrate the different types of features captured in the Overpass Turbo query (specifically it shows Graham Elementary School and McInerney Park).

The query below will find all of the cul-de-sacs (mapped as “turning circles” in OSM parlance) that are in Chicago, all of the schools and parks in Chicago, and then all of the two categories of features that are within 45 meters of each other. (Run the query and show the map, which will always grab the latest data.)

/*
example from OSM wiki: https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_API_by_Example
*/
[out:json][timeout:25];
// fetch area “Chicago” to search in
{{geocodeArea:Chicago}}->.searchArea;

// get cul-de-sacs
(
  node["highway"="turning_circle"](area.searchArea);
)->.turning_circles;

// get parks and schools
(
  way["amenity"="school"](area.searchArea);
  way["leisure"="park"](area.searchArea);
)->.schoolsParks;

// find parks and schools near cul-de-sacs
(
  way.schoolsParks(around.turning_circles:45);
)->.matchingSchoolsParks;

// find cul-de-sacs near parks and schools
(
  node.turning_circles(around.schoolsParks:45);
)->.matchingTurningCircles;

// output results to the map
(.matchingSchoolsParks; .matchingTurningCircles;);
out geom;

Read the three other blog posts I’ve written about using Overpass Turbo to quickly sift through and extract desired mapping data from OpenStreetMap.

Zoning 101: Business live/work units

This is the first post in what might become a video series about the Chicago zoning code. I picked business live/work unit because they’re a rarely seen “use” (an establishment) in Chicago, likely in part due to how few buildings are zoned to allow them and that the rules setting their minimum size might make eligible spaces doubly harder to find.

There is no order! An authentic “Zoning 101” would probably start by describing zoning, but I’m assuming you know that Chicago has a zoning code that defines what can and cannot be built or practiced on every property in the city. Business live/work units are one of those many things the code defines and regulates.

A business live/work unit is distinguished from an artist live/work unit in the Chicago zoning code in that it allows more business types – i.e. more than the creation or practice of art is allowed – but it requires that they happen on the ground floor. Artist live/work units are allowed in more zoning districts as of right (no additional permission necessary) above the ground floor.

What do you want to learn about next? Leave a comment or @ me on Twitter (stevevance).

Links to the relevant parts of the Chicago zoning code:

Chicago’s massive parking footprint – as measured on December 30, 2022

It’s been three years since I last measured how much of Chicago’s land area is occupied by parking lots and parking garages. On December 25, 2019, using data drawn into OpenStreetMap by volunteers including myself, 2.5 percent of Chicago was for car parking.

Based on additional data since then, the land area of Chicago occupied by already-mapped parking lots and garages is 176,973,866.57 square feet, or about 2.7 percent of Chicago’s area.

This means that 0.52 additional square miles have been drawn into OpenStreetMap. If it hasn’t been drawn there, we can’t measure it. This means this number is a *minimum* of the land area devoted to car parking in Chicago.

Between 2019 and 2022, 173 more of these parking lots were drawn into OpenStreetMap in Chicago. There are still so many more parking areas that are not mapped!

That converts to:

  • 4,063.32 acres
  • 7.08 mi^2 (square miles)
  • 15.93km^2 (square kilometers)
  • 2.7% area of Chicago is parking (Chicago’s land area is ~589.56 km^2 )

There are some future parking -> building conversions coming soon. The buildings will be providing parking, but it will be integrated into a mixed-use development. The parking lot in the image, for example, is slated to become an office tower.

Chicago Crash Browser updates with stats, filters, and news article links

Today I’m adding a bunch of new features to the Chicago Crash Browser, which lives on Chicago Cityscape.

But first…special access is no longer required. Anyone can create a free Cityscape account and access the map. However, only those with special access or a Cityscape Real Estate Pro account will be able to download the data.


Five new features include:

  • Statistics that update weekly to summarize what happened in the past week: the number of crashes, the number of people killed in crashes, and the number of people with the two worst tiers of injuries. The statistics are viewable to everyone, including those without access to the crash browser.
screenshot of the new weekly statistics
The statistics will update every Sunday. The numbers may change throughout the week as Chicago police officers upload crash reports.
  • For data users, the crash record ID is viewable. The crash record ID links details about the same crash across the Chicago data portal’s three tables: Crashes, Vehicles, and People. My Chicago Crash Browser is currently only using the Crashes table. Click on the “More details” arrow in the first table column.
screenshot of the data table showing the crash record ID revealed.
The crash record ID is hidden by default but can be exposed. Use this ID to locate details in the data portal’s Vehicles and People tables.
  • Filter crashes by location. There are currently two location filters: (1) on a “Pedestrian Street” (a zoning designation to, over time, reduce the prevalence of car-oriented land uses and improve building design to make them more appealing to walk next to); (2) within one block of a CTA or Metra station, important places where people commonly walk to. Select a filter’s radio button and then click “Apply filters”.
  • Filter crashes by availability of a news article or a note. I intend to attach news articles to every crash where a pedestrian or bicyclist was killed (the majority of these will be to Streetsblog Chicago articles, where I am still “editor at large”. Notes will include explanations about data changes [1] (the “map editor” mentioned in some of the notes is me) and victims’ names.
screenshot of the two types of filters

After choosing a filter’s radio button click “Apply filters” and the map and data table will update.
  • Filter by hit and run status. If the officer filling out the crash report marked it as a hit and run crash, you can filter by choosing “Yes” in the options list. “No” is another option, as is “not recorded”, which means the officer didn’t select yes or no.
  • Search by address. Use the search bar inside the map view to center the map and show crashes that occurred within one block (660 feet) of that point. The default is one block and users can increase that amount using the dropdown menu in the filter.
screenshot of the map after the search by address function has been used
Use the search bar within the map view to show crashes near a specific address in Chicago.

Footnotes

[1] The most common data change as of this writing is when a crash’s “most severe injury” is upgraded from non-fatal to fatal, but the crash report in the city’s data portal does not receive that update. This data pipeline/publishing issue is described in the browser’s “Crash data notes” section.

The “map editor” (me) will change a crash’s “most severe injury” to fatal to ensure it appears when someone filters for fatal crashes. This change to the data will be noted.

How to visualize the density of point data in a grid

A common way to show the distribution of places (like grocery stores) is to use a heat map. The map will appear “hotter” where there are many grocery stores and “colder” where there are few grocery stores. This kind of map can be useful to show gaps in distribution or a neighborhood that has a lot of grocery stores.

One issue with that kind of heat map is that the coverage areas change their shape and color if you zoom in, since the algorithm that clusters or determines what’s “nearby” or dense has fewer locations to analyze.

I prefer to use grids in the shape of square tiles, since Chicago is grid-oriented city and the vast majority of our streets and our routes move along east-west and north-south lines. The map above shows the location of subjects and topics of news articles in the Chicago Cityscape database.

I use PostGIS to set up most of my spatial data before visualizing it in QGIS.

This tutorial shows the two steps to using PostGIS to (1) create a grid based on an existing area (polygon), (2) assigning each point location to a tile in that grid and counting the number of locations in that tile.

If you don’t have PostGIS installed, you should install it if you work with spatial data a lot. It is much, much faster at performing most of the tasks you use QGIS or ArcGIS to perform. Both QGIS and ArcGIS can read and edit data stored in PostGIS.

Additionally, there is a function within QGIS that can create grids, and another function that can do comparisons by location and count/summarize, so all of this can be done without PostGIS.

For this tutorial, you will need a single polygon with which to create the grid. I used the boundary of the City of Chicago limits.

  1. Create a grid based on an existing area

1.a. Add a new function to PostGIS

To create a grid, you need a function that draws the tiles based on the polygon. I got this from The Spatial Database Advisor.

-- Create required type
DROP   TYPE IF EXISTS T_Grid CASCADE;
CREATE TYPE T_Grid AS (
  gcol  int4,
  grow  int4,
  geom geometry
);
-- Drop function is exists
DROP FUNCTION IF EXISTS ST_RegularGrid(geometry, NUMERIC, NUMERIC, BOOLEAN);
-- Now create the function
CREATE OR REPLACE FUNCTION ST_RegularGrid(p_geometry   geometry,
                                          p_TileSizeX  NUMERIC,
                                          p_TileSizeY  NUMERIC,
                                          p_point      BOOLEAN DEFAULT TRUE)
  RETURNS SETOF T_Grid AS
$BODY$
DECLARE
   v_mbr   geometry;
   v_srid  int4;
   v_halfX NUMERIC := p_TileSizeX / 2.0;
   v_halfY NUMERIC := p_TileSizeY / 2.0;
   v_loCol int4;
   v_hiCol int4;
   v_loRow int4;
   v_hiRow int4;
   v_grid  T_Grid;
BEGIN
   IF ( p_geometry IS NULL ) THEN
      RETURN;
   END IF;
   v_srid  := ST_SRID(p_geometry);
   v_mbr   := ST_Envelope(p_geometry);
   v_loCol := trunc((ST_XMIN(v_mbr) / p_TileSizeX)::NUMERIC );
   v_hiCol := CEIL( (ST_XMAX(v_mbr) / p_TileSizeX)::NUMERIC ) - 1;
   v_loRow := trunc((ST_YMIN(v_mbr) / p_TileSizeY)::NUMERIC );
   v_hiRow := CEIL( (ST_YMAX(v_mbr) / p_TileSizeY)::NUMERIC ) - 1;
   FOR v_col IN v_loCol..v_hiCol Loop
     FOR v_row IN v_loRow..v_hiRow Loop
         v_grid.gcol := v_col;
         v_grid.grow := v_row;
         IF ( p_point ) THEN
           v_grid.geom := ST_SetSRID(
                             ST_MakePoint((v_col * p_TileSizeX) + v_halfX,
                                          (v_row * p_TileSizeY) + V_HalfY),
                             v_srid);
         ELSE
           v_grid.geom := ST_SetSRID(
                             ST_MakeEnvelope((v_col * p_TileSizeX),
                                             (v_row * p_TileSizeY),
                                             (v_col * p_TileSizeX) + p_TileSizeX,
                                             (v_row * p_TileSizeY) + p_TileSizeY),
                             v_srid);
         END IF;
         RETURN NEXT v_grid;
     END Loop;
   END Loop;
END;
$BODY$
  LANGUAGE plpgsql IMMUTABLE
  COST 100
  ROWS 1000;

The ST_RegularGrid function works in the same projection as your source data.

1.b. Create a layer that has all of the tiles for just the Chicago boundary

--This creates grids of 1,320 feet square (a 2x2 block size in Chicago)
SELECT gcol, grow, geom
 into b_chicagoboundary_grid_1320squared
 FROM ST_RegularGrid((select geom from chicagoboundary where gid = 1), 1320, 1320,FALSE);

In that query, “1320” is a distance in feet for both the X and Y planes, as the “chicagoboundary” geometry is projected in Illinois StatePlane FIPS East (Feet) (EPSG/SRID 3435).

2. Assign each point location to a tile in that grid and count the number of locations in each tile

Now you’ll need a table that has POINT-type geometries in it. For the map in this tutorial, I used a layer of location-based news articles that are used in Chicago Cityscape to highlight local developments.

SELECT grid.id, count(*) as count, grid.geom
INTO news_grid
FROM news_articles, b_chicagoboundary_grid_1320squared AS grid
WHERE st_intersects(news_articles.geom, grid.geom)
GROUP by grid.id;

This query will result in a table with three columns:

  1. The ID of the tile, which is a primary key field.
  2. The number of news articles in that tile.
  3. The POLYGON geometry of that tile.
Look at these two maps (the one above, and the one below). The first map shows the whole city. The tiles are colored according to the number of news articles within the area of each tile. The darker the blue, the more news articles within that tile.
This map is zoomed in to the Woodlawn area. As you change scale (zoom in or zoom out), the size of the “heat” area (the size of each tile) doesn’t change – they are still 1,320 feet by 1,320 feet. The color doesn’t change either. The typical heat map doesn’t have these advantages.