Published
Edited
Sep 11, 2020
Insert cell
Insert cell
zipChart = Object.assign(
html`<iframe style="width:100%;height:625px;border:none;">`,
{
srcdoc: `<!DOCTYPE html>
<head>
<style>

.observablehq { margin: 0; }
.observablehq > :only-child { display: block; }
body,text {
font-family: "benton-sans", Helvetica, sans-serif;
}
body { overflow:hidden;}
</style>
<link rel="stylesheet" href="https://use.typekit.net/pbz7tnn.css">

</head>
<body>
<script type=module>

import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
import define from "https://api.observablehq.com/@pjsier/eviction-data-story-graphics.js?v=3";

new Runtime().module(define, name => {
if (name === "mtoCallsByZIPChart") {
return new Inspector(document.body);
}
});
window.setTimeout(() => window.dispatchEvent(new Event('resize')), 250);

</script></body>`
}
)
Insert cell
callsChart = Object.assign(
html`<iframe style="width:100%;height:525px;border:none;">`,
{
srcdoc: `<!DOCTYPE html>
<head>
<style>
body,text {
font-family: "benton-sans", Helvetica, sans-serif;
}
body { overflow:hidden;}
.observablehq { margin: 0; }
.observablehq > :only-child { display: block; }

</style>
<link rel="stylesheet" href="https://use.typekit.net/pbz7tnn.css">

</head>
<body>
<body>
<script type=module>

import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
import define from "https://api.observablehq.com/@pjsier/eviction-data-story-graphics.js?v=3";

new Runtime().module(define, name => {
if (name === "mtoCompareChartEmbed") {
return new Inspector(document.body);
}
});
window.setTimeout(() => window.dispatchEvent(new Event('resize')), 250);

</script></body>`
}
)
Insert cell
evictionsChart = Object.assign(
html`<iframe style="width:100%;height:525px;border:none;">`,
{
srcdoc: `<!DOCTYPE html>
<head>
<style>
body,text {
font-family: "benton-sans", Helvetica, sans-serif;
}
body {overflow:hidden;}
.observablehq { margin: 0; }
.observablehq > :only-child { display: block; }

</style>
<link rel="stylesheet" href="https://use.typekit.net/pbz7tnn.css">

</head>
<body>

<body>
<script type=module>
import {Runtime, Inspector} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";
import define from "https://api.observablehq.com/@pjsier/eviction-data-story-graphics.js?v=3";

new Runtime().module(define, name => {
if (name === "cookEvictionsChartEmbed") {
return new Inspector(document.body);

}
});
window.setTimeout(() => window.dispatchEvent(new Event('resize')), 250);


</script></body>`
}
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
zipCallChart = () => {
const chart = vl
.markGeoshape({
stroke: "#9b9c9f",
strokeWidth: 0.5
})
.project(vl.projection("mercator"))
.title({
text: "Eviction-related renter hotline calls",
subtitle: "Since March 24 moratorium",
subtitlePadding: 8,
dy: -16
})
.data(zipsByMTOCalls.features)
.encode(
vl
.color()
.fieldQ("properties.calls")
.scale({
zero: true,
range: ["rgba(0, 134, 138, 0.1)", "rgba(0, 134, 138, 1)"]
})
.title("Calls"),
vl.tooltip([
vl
.tooltip()
.fieldO("properties.zip")
.title("ZIP"),
vl
.tooltip()
.fieldQ("properties.calls")
.format(",")
.title("Calls")
])
)
.width(width)
.height(500);

return chart
.config({
title: { fontSize: 16 },
legend: { titleFontSize: 14, labelFontSize: 12, symbolStrokeWidth: 0 },
axis: { labelFontSize: 12 },
style: { cell: { stroke: "transparent" } }
})
.autosize({ type: "fit-x", contains: "padding" })
.render({ renderer: "svg" });
}
Insert cell
mtoCompareChart = () => {
const hover = vl
.selectSingle("hover")
.fields(["month", "calls", "calls19", "calls20"])
.nearest(true)
.on("mouseover")
.empty("none")
.clear("mouseout");

const month = vl
.x()
.fieldT("month")
.axis({
format: "%b",
ticks: false,
tickCount: mtoCompareMonths.length,
labelPadding: 10,
domain: false
});

const calls = vl
.y()
.fieldQ("calls")
.axis({
domain: false,
ticks: false,
labelPadding: 10,
tickCount: { interval: 5 }
});

const transforms = [
vl.fold("calls19", "calls20").as("yearVal", "calls"),
vl.calculate("datum.yearVal === 'calls19' ? '2019' : '2020'").as("year")
];

const year = vl
.color()
.field("year")
.title("Year")
.scale({ range: ["#9b9c9f", "#ed1651"] });

const lines = vl
.markLine({ strokeWidth: 5 })
.width(width)
.height(400)
.title({ text: "Eviction-related renter hotline calls", dy: -16 })

.transform(transforms)
.encode(calls.title(null), month.title(null), year);

const rule = vl
.markRule()
.width(width)
.height(400)
.encode(
month,
vl
.opacity()
.if(hover, vl.value(0.3))
.value(0),
vl.tooltip([
vl
.tooltip()
.fieldQ("calls19")
.title("2019 calls"),
vl
.tooltip()
.fieldQ("calls20")
.title("2020 calls")
])
)
.select(hover)
.transform(transforms);

return vl
.layer([lines, rule])

.config({
title: { fontSize: 16 },
legend: { titleFontSize: 14, labelFontSize: 12, symbolStrokeWidth: 5 },
axis: { labelFontSize: 12 },
style: { cell: { stroke: "transparent" } }
})
.data(mtoCallsCompare)
.autosize({ type: "fit-x", contains: "padding" })
.render({ renderer: "svg" });
}
Insert cell
cookEvictionsChart = () => {
vl.markLine({ strokeWidth: 5 })
.config({
title: { fontSize: 16 },
legend: { titleFontSize: 14, labelFontSize: 12, symbolStrokeWidth: 5 },
axis: { labelFontSize: 12 },
style: { cell: { stroke: "transparent" } }
})
.title({ text: "Cook County Eviction Filings", dy: -16 })
.data(cookEvictionsCompare)
.transform(
vl.fold("cases19", "cases20").as("yearVal", "cases"),
vl.calculate("datum.yearVal === 'cases19' ? '2019' : '2020'").as("year")
)
.encode(
vl
.y()
.fieldQ("cases")
.axis({
domain: false,
ticks: false,
labelPadding: 10,
tickCount: { interval: 5 }
})
.title(null),
vl
.x()
.fieldT("month")
.axis({
format: "%b",
ticks: false,
tickCount: compareMonths.length + 1,
labelPadding: 10,
domain: false
})
.title(null),
vl
.color()
.field("year")
.title("Year")
.scale({ range: ["#9b9c9f", "#ed1651"] }),
vl.tooltip([
vl
.tooltip()
.fieldT("month")
.format("%B")
.title("Month"),
vl
.tooltip()
.fieldQ("cases")
.title("Cases")
])
)
.autosize({ type: "fit-x", contains: "padding" })
.render({ renderer: "svg" });

const hover = vl
.selectSingle("hover")
.fields(["month", "cases", "cases19", "cases20"])
.nearest(true)
.on("mouseover")
.empty("none")
.clear("mouseout");

const month = vl
.x()
.fieldT("month")
.axis({
format: "%b",
ticks: false,
tickCount: compareMonths.length,
labelPadding: 10,
domain: false
});

const cases = vl
.y()
.fieldQ("cases")
.axis({
domain: false,
ticks: false,
labelPadding: 10,
tickCount: { interval: 5 }
});

const transforms = [
vl.fold("cases19", "cases20").as("yearVal", "cases"),
vl.calculate("datum.yearVal === 'cases19' ? '2019' : '2020'").as("year")
];

const year = vl
.color()
.field("year")
.title("Year")
.scale({ range: ["#9b9c9f", "#ed1651"] });

const lines = vl
.markLine({ strokeWidth: 5 })
.width(width)
.height(400)
.title("Cook County Eviction Filings")
.width(width)
.transform(transforms)
.encode(cases.title(null), month.title(null), year);

const rule = vl
.markRule()
.width(width)
.height(400)
.encode(
month,
vl
.opacity()
.if(hover, vl.value(0.3))
.value(0),
vl.tooltip([
vl
.tooltip()
.fieldQ("cases19")
.format(",")
.title("2019 cases"),
vl
.tooltip()
.fieldQ("cases20")
.format(",")
.title("2020 cases")
])
)
.select(hover)
.transform(transforms);

return vl
.layer([lines, rule])
.config({
title: { fontSize: 16 },
legend: { titleFontSize: 14, labelFontSize: 12, symbolStrokeWidth: 5 },
axis: { labelFontSize: 12 },
style: { cell: { stroke: "transparent" } }
})
.data(cookEvictionsCompare)
.autosize({ type: "fit-x", contains: "padding" })
.render({ renderer: "svg" });
}
Insert cell
Runtime = await import("@observablehq/runtime@4/dist/runtime.js")
Insert cell
customWidth = function() {
return new Runtime.Library().Generators.observe(function(change) {
let width = change(cookEvictionsChartEmbed.clientWidth);
function resized() {
let w = cookEvictionsChartEmbed.clientWidth;
if (w !== width) change((width = w));
}
window.addEventListener("resize", resized);
return function() {
window.removeEventListener("resize", resized);
};
});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
cookEvictions2020 = d3
.csvParse(
`month,cases
2020-01,2662
2020-02,2455
2020-03,1368
2020-04,474
2020-05,287
2020-06,321
2020-07,530`
)
.map(({ month, cases }) => ({ month, cases: +cases }))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more