Published
Edited
Oct 21, 2020
Importers
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// updates the selectedCounty autocomplete when clicking on the map
viewof selectedCounty.value = map;
viewof selectedCounty.dispatchEvent(new CustomEvent("input"));
}
Insert cell
Insert cell
Insert cell
Insert cell
barTest = barChart(
data[Math.floor(Math.random() * data.length)],
undefined,
false
)
Insert cell
barChart = (indicatorData, xMax, showAxisX = true) => {
const w = width;
const h = 175;
const margin = { top: 24, right: 24, bottom: 36, left: 175 };

const data = normalizeBarData(indicatorData);

const colorScale = d3
.scaleOrdinal()
.domain(categories)
.range(palette);

const xScale = d3
.scaleLinear()
.domain([0, xMax || d3.max(data, d => d.value)])
.range([margin.left, w - margin.right]);

const yScale = d3
.scaleBand()
.domain(categories)
.range([h - margin.bottom, margin.top])
.padding(0.1);

const xAxis = g =>
g
.attr("transform", `translate(0, ${h - margin.bottom})`)
.call(d3.axisBottom(xScale).tickFormat(d => formatPct(d)))
.call(g => g.select(".domain").remove())
.call(g => g.attr("font-size", 12))
.call(g => g.attr("text-anchor", "middle"));

const yAxis = g =>
g
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(yScale).tickFormat(d => d.replace(/\_/gi, " ")))
.call(g => g.select(".domain").remove())
.call(g => g.attr("font-size", 12));

const svg = d3.create("svg").attr("viewBox", [0, 0, w, h]);

const barGroups = svg
.append("g")
.attr("id", "bars")
.selectAll("g")
.data(data)
.join("g");

barGroups
.append("rect")
.attr("x", d => xScale(0))
.attr("y", d => yScale(d.name))
.attr("height", yScale.bandwidth())
.attr("width", d => xScale(d.value) - xScale(0))
.attr("fill", d => colorScale(d.name));

barGroups
.append("text")
.attr("fill", "#333")
.attr("font-size", 12)
.attr("font-family", "sans-serif")
.attr("font-style", d => (d.value !== null ? "normal" : "italic"))
.attr("x", d => {
if (d.value === null) {
return xScale(0);
} else if (xScale(d.value) - xScale(0) <= 3 * 12) {
return xScale(d.value) + 6;
} else {
return xScale(d.value) - 12 * 3;
}
})
.attr("y", d => yScale(d.name))
.attr("dy", yScale.bandwidth() / 2 + 4)
// .attr("dx", 10)
.text(d =>
d.value !== null ? d3.format(".1f")(d.value * 100) + "%" : "no data"
);

svg
.append("text")
.text(indicatorData.indicator)
.attr("fill", "#333")
.attr("font-size", 12)
.attr("font-family", "sans-serif")
.attr("font-weight", "bold")
.attr("x", 12)
.attr("y", 12);

showAxisX && svg.append("g").call(xAxis);
svg.append("g").call(yAxis);

return svg.node();
}
Insert cell
Insert cell
mapTest = doMap(countiesJoinedGeom)
Insert cell
doMap = (features, options = {}) => {
const w = options.width || width;
const h = options.height || 600;
const p = options.padding || 20;

const projection = d3
.geoAlbersUsa()
.fitExtent([[p, p], [w - p, h - p]], countiesGeo);

const path = d3.geoPath(projection);

const zoom = d3
.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed);

const container = html`<div class="container">
<div class="legend"></legend>
</div>`;

const styles = html`<style>
.container {
position: relative;
}
.container .legend {
position: absolute;
right: 6px;
top: 6px;
background: rgba(255, 255, 255, 0.8);
padding: 6px 6px 2px;
}
</style>`;

container.append(styles);

container.querySelector(".legend").append(
legend({
color: colorScale,
title: "Ratio of deaths from COVID-19",
height: 45
})
);

const svg = d3
.select(container)
.append("svg")
.attr("viewBox", [0, 0, w, h]);

const g = svg
.append("g")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round");

g.append("g")
.attr("id", "nation-boundary")
.attr("stroke-width", 0.8)
.append("path")
.attr("d", path(topojson.mesh(us, us.objects.nation)))
.attr("fill", "#eee")
.attr("stroke", "#888");

if (features) {
g.append("g")
.attr("id", "counties-data")
.selectAll("path")
.data(features)
.join("path")
.attr("d", d => path(d))
.attr("fill", d => colorScale(d.properties.rate))
.on("click", (event, d) => {
container.value = `${d.properties.data[0].county_name}, ${d.properties.data[0].state}`;
container.dispatchEvent(new CustomEvent("input"));
})
.on("mouseover", ({ currentTarget }) => {
d3.select(currentTarget)
.attr("stroke", "yellow")
.attr("stroke-width", 2);
})
.on("mouseout", ({ currentTarget }) => {
d3.select(currentTarget)
.attr("stroke", "none")
.attr("stroke-width", 0);
});
}

g.append("g")
.attr("id", "county-boundaries")
.attr("stroke-width", 0.5)
.append("path")
.attr(
"d",
path(
topojson.mesh(
us,
us.objects.counties,
(a, b) => a !== b && ((a.id / 1000) | 0) === ((b.id / 1000) | 0)
)
)
)
.attr("fill", "none")
.attr("stroke", "#e2e2e2");

g.append("g")
.attr("id", "state-boundaries")
.attr("stroke-width", 0.7)
.append("path")
.attr("d", path(topojson.mesh(us, us.objects.states)))
.attr("fill", "none")
.attr("stroke", "#888");

svg
.append("rect")
.attr("width", w)
.attr("height", h)
.attr("fill", "none")
.attr("stroke", "#222")
.attr("stroke-width", 2);

function zoomed({ transform }) {
g.attr("transform", transform);
g.selectAll("g").attr("stroke-width", 1 / transform.k);
}

svg.call(zoom);
container.value = null;

return container;
}
Insert cell
Insert cell
colorScale = d3
.scaleLinear()
.domain([0.03, 0.2, 0.38]) // coresponds to covid-19 deaths / total deaths
.range(["#d7b5d8", "#df65b0", "#ce1256"])
.interpolate(d3.interpolateHcl)
Insert cell
formatPct = d3.format(".0%")
Insert cell
formatDate = date =>
date.toLocaleString("en-US", {
month: "long",
day: "numeric",
year: "numeric"
})
Insert cell
getMaxValue = countyState => {
const data = groupedByFipsCode.get(countyStateFipsLookup.get(countyState));
return d3.max(d3.merge(data.map(normalizeBarData)), d => d.value);
}
Insert cell
normalizeBarData = indicatorData => {
return Object.keys(indicatorData)
.filter(key => categories.has(key))
.map(key => ({ name: key, value: indicatorData[key] }));
}
Insert cell
Insert cell
Insert cell
sampleDatum = JSON.stringify(
data[Math.floor(Math.random() * data.length)],
null,
2
)
Insert cell
selectedDatum = groupedByFipsCode.get(
selectedCounty ? countyStateFipsLookup.get(selectedCounty) : defaultFips
)
Insert cell
defaultFips = data.find(
d => d.county_name === "Alameda County" && d.state === "CA"
).fipscode
Insert cell
categories = new Set(
"non_hispanic_white,non_hispanic_black,non_hispanic_asian,non_hispanic_american_indian,hispanic".split(
","
)
)
Insert cell
uniqueIndicators
Insert cell
uniqueCounties = [...groupedByCountyNameState.keys()]
Insert cell
countiesJoinedGeom = [...groupedByFipsCode.keys()].map(d => {
const geo = countiesGeoByFips.get(d)[0];
const data = groupedByFipsCode.get(d);
geo.properties.rate = +d3.format(".2f")(
data[0].covid_19_deaths_total / data[0].all_deaths_total
);
geo.properties.data = data;
return geo;
})
Insert cell
countyStateFipsLookup = data.reduce((map, d) => {
if (!map.has(d.fipscode)) {
map.set(`${d.county_name}, ${d.state}`, d.fipscode);
}
return map;
}, new Map())
Insert cell
groupedByCountyNameState = d3.group(data, d => `${d.county_name}, ${d.state}`)
Insert cell
groupedByIndicator = d3.group(data, d => d.indicator)
Insert cell
groupedByFipsCode = d3.group(data, d => d.fipscode)
Insert cell
data = rawData.map(parseDatum)
Insert cell
parseDatum = ({
start_week,
end_week,
data_as_of,
all_deaths_total,
covid_19_deaths_total,
non_hispanic_white,
non_hispanic_black,
non_hispanic_asian,
non_hispanic_american_indian,
hispanic,
fipscode,
...rest
}) => {
return {
...rest,
start_week: new Date(start_week),
end_week: new Date(end_week),
data_as_of: new Date(data_as_of),
all_deaths_total: handleValue(all_deaths_total),
covid_19_deaths_total: handleValue(covid_19_deaths_total),
non_hispanic_white: handleValue(non_hispanic_white),
non_hispanic_black: handleValue(non_hispanic_black),
non_hispanic_asian: handleValue(non_hispanic_asian),
non_hispanic_american_indian: handleValue(non_hispanic_american_indian),
hispanic: handleValue(hispanic),
fipscode: fipscode.length < 5 ? `0${fipscode}` : fipscode
};
}
Insert cell
handleValue = value => (value && typeof +value === "number" ? +value : null)
Insert cell
rawData
Insert cell
countiesGeoByFips = d3.group(countiesGeo.features, d => d.id)
Insert cell
countiesGeo = topojson.feature(us, "counties")
Insert cell
us = d3.json("https://cdn.jsdelivr.net/npm/us-atlas@3/counties-10m.json")
Insert cell
uniqueIndicators.join(", ")
Insert cell
Insert cell
import {
data as rawData,
uniqueIndicators
} from "@clhenrick/data-intake-provisional-covid-19-death-counts-by-county-and"
Insert cell
Insert cell
Insert cell
topojson = require("topojson-client@3")
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