Published
Edited
May 4, 2020
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

svg.append(legend)
.attr("transform", "translate(250,150)"); //legend location

svg.append("g")
.selectAll("path")
.data(topojson.feature(polygons, polygons.objects.NYCountiesWGS1984).features) //read in data
.join("path")
.attr("stroke", "grey")
.attr("fill", d => color(data.get(d.properties[idAttribute]))) //read in FIPS code
.attr("d", path)
.append("title")
.text(d => `${d.properties[idAttribute]}, ${format(data.get(d.properties[idAttribute]))}`);

return svg.node();
}
Insert cell
//Interpretation of Spatial Patterns
//The areas of high covid cases smoothly changes as the population density changes. There are not many areas where neighboring counties jump from one side of the legend to another. There are some strong clustering of high covid per capita rate and high population density counties in and around the NYC area.
Insert cell
// http://www.joshuastevens.net/cartography/make-a-bivariate-choropleth-map/
schemes = [
{
name: "RdBu",
colors: [
"#e8e8e8", "#e4acac", "#c85a5a",
"#b0d5df", "#ad9ea5", "#985356",
"#64acbe", "#627f8c", "#574249"
]
},
{
name: "BuPu",
colors: [
"#e8e8e8", "#ace4e4", "#5ac8c8",
"#dfb0d6", "#a5add3", "#5698b9",
"#be64ac", "#8c62aa", "#3b4994"
]
},
{
name: "GnBu",
colors: [
"#e8e8e8", "#b5c0da", "#6c83b5",
"#b8d6be", "#90b2b3", "#567994",
"#73ae80", "#5a9178", "#2a5a5b"
]
},
{
name: "PuOr",
colors: [
"#e8e8e8", "#e4d9ac", "#c8b35a",
"#cbb8d7", "#c8ada0", "#af8e53",
"#9972af", "#976b82", "#804d36"
]
}
]
Insert cell
labels = ["low", "", "high"]
Insert cell
n = Math.floor(Math.sqrt(colors.length))
Insert cell
//creates a scale, Pop. Density
x = d3.scaleQuantile(Array.from(data.values(), d => d[0]), d3.range(n))
Insert cell
//creates a scale, COVID-19 Cases
y = d3.scaleQuantile(Array.from(data.values(), d => d[1]), d3.range(n))
Insert cell
path = d3.geoPath().projection(projection)
Insert cell
//Rotate the map sets the longitude of origin for our UTM projection.
projection = d3.geoTransverseMercator().rotate([87,0]).fitExtent([[10, 10], [width, height]], polygon_features);
Insert cell
polygon_features = topojson.feature(polygons, polygons.objects.NYCountiesWGS1984)
Insert cell
//read in csv file and map the data in one step
data = Object.assign(new Map(d3.csvParse(await FileAttachment("NYPOP.csv").text(), ({FIPS_CODE, Population, Area, CaronavirusCases}) => [FIPS_CODE, [+Population/+Area,100*+CaronavirusCases/Population]])), {title: ["Pop. Density", "% COVID-19 Cases"]})
//last part goes on the legend axis
Insert cell
//change this
idAttribute = "FIPS_CODE"
Insert cell
//read in polygon features, join with FIPS code
polygons = FileAttachment("NYCountiesWGS1984 (1).json").json()
Insert cell
//canvas height
height = 610
Insert cell
//canvas width
width = 875
Insert cell
//creates customized legend
legend = () => {
const k = 24;
const arrow = DOM.uid();
return svg`<g font-family=sans-serif font-size=10>
<g transform="translate(-${k * n / 2},-${k * n / 2}) rotate(-45 ${k * n / 2},${k * n / 2})">
<marker id="${arrow.id}" markerHeight=10 markerWidth=10 refX=6 refY=3 orient=auto>
<path d="M0,0L9,3L0,6Z" />
</marker>
${d3.cross(d3.range(n), d3.range(n)).map(([i, j]) => svg`<rect width=${k} height=${k} x=${i * k} y=${(n - 1 - j) * k} fill=${colors[j * n + i]}>
<title>${data.title[0]}${labels[j] && ` (${labels[j]})`}
${data.title[1]}${labels[i] && ` (${labels[i]})`}</title>
</rect>`)}
<line marker-end="${arrow}" x1=0 x2=${n * k} y1=${n * k} y2=${n * k} stroke=black stroke-width=1.5 />
<line marker-end="${arrow}" y2=0 y1=${n * k} stroke=black stroke-width=1.5 />
<text font-weight="bold" dy="0.71em" transform="rotate(90) translate(${n / 2 * k},6)" text-anchor="middle">${data.title[0]}</text>
<text font-weight="bold" dy="0.71em" transform="translate(${n / 2 * k},${n * k + 6})" text-anchor="middle">${data.title[1]}</text>
</g>
</g>`;
}
Insert cell
color = {
return value => {
if (!value) return "#ccc";
let [a, b] = value;
return colors[y(b) + x(a) * n];
};
}
Insert cell
//format values in legend and data
format = (value) => {
if (!value) return "N/A";
let [a, b] = value;
return `${a} ${data.title[0]}${labels[x(a)] && ` (${labels[x(a)]})`}
${b}% ${data.title[1]}${labels[y(b)] && ` (${labels[y(b)]})`}`;
}
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3@5")
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