Mapping Washington D.C.’s Wards Over The Years

Patrick Leonard
4 min readFeb 5, 2023

--

Several months ago I created a map showing how Washington D.C.’s ward boundaries have changed over time (live version on my website here: https://www.patrick-leonard.com/DCWards/). D.C. has 8 wards, which each have a representative on D.C.’s city council. The ward boundaries are updated after each census to ensure they house roughly equal shares of the population. Showing D.C.’s wards over time reveals some interesting patterns in population changes as the wards grow or shrink to accommodate these shifts. Here’s how you can create your own D.C. ward map:

  1. Download GeoJSONs of D.C.’s ward boundaries from D.C.’s Open Data Portal (an excellent resource for all kinds of information about the city). Here’s 2022 as an example.
  2. Upload these ward boundaries to Mapbox as individual tilesets. Tilesets are a data format for displaying the ward boundaries easily on a web map. Detailed instructions for this process here.
  3. Create a new Mapbox map style using Mapbox Studio. I used the “Basic Template” as my basemap. Detailed instructions here.
  4. Add your tilesets to your map style as Sources and then create Layers from each of those sources.

Now that you have your map, you can add it to your website using Mapbox GL JS.

  1. To make it easy, start with the “basic map” example for GL JS. You can choose to edit the file in JSFiddle or CodePen if you don’t want to set up your own site.
  2. Replace the Style URL with the appropriate one for your new Mapbox map style from Mapbox Studio.
const map = new mapboxgl.Map({ 
container: “map”,
style: “mapbox://styles/plmapbox/cl70xee7w001114nytcmh5lho”,
zoom: 11,
center: [-77.0369, 38.9072]
});

3. Create a layer ID for each of the years with new boundaries. Important: this ID should match the name of your layers in your style. Create a menu element for each of these IDs and then add an onclick() function which toggles the layers on and off based on selection of that menu item.

// enumerate ids of the layers 
var toggleableLayerIds = [“2022”, “2012”, “2002”, “1992”, “1982”, “1975”];

// set up the corresponding toggle button for each layer
for (var i = 0; i < toggleableLayerIds.length; i++) {
var id = toggleableLayerIds[i];
var link = document.createElement(“a”);
link.href = “#”; link.className = “”;
link.textContent = id;

if (i == 0) {
link.className = “active”;
}

link.onclick = function (e) {
var clickedLayer = this.textContent;
e.preventDefault();
e.stopPropagation();
for (var j = 0; j < toggleableLayerIds.length; j++) {
if (clickedLayer === toggleableLayerIds[j]) {
layers.children[j].className = “active”;
map.setLayoutProperty(toggleableLayerIds[j], “visibility”, “visible”);
} else {
layers.children[j].className = “”;
map.setLayoutProperty(toggleableLayerIds[j], “visibility”, “none”);
}
}
};

var layers = document.getElementById(“menu”);
layers.appendChild(link);

4. That’s it! Run your code and toggle the ward layers on and off.

D.C. Ward Map

Full code:

<!DOCTYPE html>
<html>
<head>
<script src="https://api.mapbox.com/mapbox-gl-js/v2.9.2/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.9.2/mapbox-gl.css" rel="stylesheet" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}

#menu {
background: #fff;
position: absolute;
z-index: 1;
top: 10px;
right: 10px;
border-radius: 3px;
width: 120px;
border: 1px solid rgba(0, 0, 0, 0.4);
font-family: "Open Sans", sans-serif;
}

#menu a {
font-size: 30px;
color: #404040;
display: block;
margin: 0;
padding: 0;
padding: 15px;
text-decoration: none;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
text-align: center;
}

#menu a:last-child {
border: none;
}

#menu a:hover {
background-color: #f8f8f8;
color: #404040;
}

#menu a.active {
background-color: #3887be;
color: #ffffff;
}

#menu a.active:hover {
background: #3074a4;
}

.info-box {
height: 60px;
width: 130px;
position: absolute;
bottom: 40px;
left: 10px;
background-color: rgba(255, 255, 255, 0.9);
padding: 8px;
text-align: center;
border: 1px solid rgba(0, 0, 0, 0.4);
font-family: "Open Sans", sans-serif;
}
.info-box-div {
font-size: 13px;
color: #404040;
}
</style>
</head>
<body>
<nav id="menu"></nav>
<div id="map"></div>
<div class="info-box">
<div class="info-box-div">Ward data retrieved from <a href="https://opendata.dc.gov">Open Data DC catalog.</a><div>
</div>
<script>
mapboxgl.accessToken =
"pk.eyJ1IjoicGxtYXBib3giLCJhIjoiY2w3MHptNzI2MGphbjNubXFscXI3emhkaiJ9.s1BHM0dRlXybWMX7RydmVw";
const map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/plmapbox/cl70xee7w001114nytcmh5lho",
zoom: 11,
center: [-77.0369, 38.9072]
});

// enumerate ids of the layers
var toggleableLayerIds = ["2022", "2012", "2002", "1992", "1982", "1975"];

// set up the corresponding toggle button for each layer
for (var i = 0; i < toggleableLayerIds.length; i++) {
var id = toggleableLayerIds[i];

var link = document.createElement("a");
link.href = "#";
link.className = "";
link.textContent = id;

if (i == 0) {
link.className = "active";
}

link.onclick = function (e) {
var clickedLayer = this.textContent;
e.preventDefault();
e.stopPropagation();
for (var j = 0; j < toggleableLayerIds.length; j++) {
if (clickedLayer === toggleableLayerIds[j]) {
layers.children[j].className = "active";
map.setLayoutProperty(toggleableLayerIds[j], "visibility", "visible");
} else {
layers.children[j].className = "";
map.setLayoutProperty(toggleableLayerIds[j], "visibility", "none");
}
}
};

var layers = document.getElementById("menu");
layers.appendChild(link);
}
</script>
</body>
</html>

--

--