Published
Edited
Mar 25, 2021
2 forks
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
viewof CountyMap = {
let value = null;
const svg = d3.create("svg")
.attr("viewBox", [0, 0, 975, 610])
// svg.append("g")
// .attr("transform", "translate(610,20)")
// .append(() => legend({color:colorScale, title: `% Difference in ${dataInfo.variable}`, width: 260 }));
// draw counties
svg.append("g")
.selectAll("path")
.data(topojson.feature(geography, geography.objects.counties).features)
.enter().append("path")
.attr("d", path)
// !!!! HERE !!!! This is the conditional check for the population and death count threshholds
.attr("fill",
data =>
((_.find(populationData, feature => +feature.GEOID == +data.id)[populationVariable] > topQuantilePop)
&&
(_.find(normalizedDeathData, feature => +feature.fips == +data.id)[currentDate] > topQuantileDeath))
?
'rgb(0,0,0)'
:
'rgb(240,240,240)'
)
.attr('id', data => data.properties.name)
.attr('class', 'county');

// state outline
svg.append("g")
.selectAll("path")
.data(topojson.feature(geography, geography.objects.states).features)
.enter().append("path")
.attr("d", path)
.attr("fill", 'none')
.attr("stroke", d => 'white')
.attr('id', d=> d.properties.name)
.attr('class', 'state');
// state background
svg.append("path")
.datum(topojson.mesh(geography, geography.objects.states, (a, b) => a !== b))
.attr("fill", "none")
.attr("stroke", d => 'white')
.attr("stroke-linejoin", "round")
.attr("pointer-events", "none")
.attr("d", path);
// // thanks! https://observablehq.com/@duynguyen1678/choropleth-with-tooltip
const tooltip = svg.append("g")

svg
.selectAll(".county")
.on("touchmove mousemove", function(event, d) {
tooltip.call(
callout,
`${d.properties.name}
`
);
tooltip.attr("transform", `translate(${d3.pointer(event, this)})`);
d3.select(this)
.raise();
})
.on("touchend mouseleave", function() {
tooltip.call(callout, null);
d3.select(this)
.attr("stroke", null)
.lower();
});

const outline = svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-linejoin", "round")
.attr("pointer-events", "none");

return Object.assign(svg.node(), {value: null});
}
Insert cell
Insert cell
cdcDeathData = d3.csv(`https://gist.githubusercontent.com/nofurtherinformation/977077f4e1874333f570515007537ebb/raw/fde4cc565c4e6ce8e4200ef63fc23438792ac9e9/cdc_deaths.csv`)
Insert cell
rawPopulationData = d3.csv('https://gist.githubusercontent.com/nofurtherinformation/28547271cf378dc19984c9aeb4673b7f/raw/6614436c28e4c0b11d915b1216e1b1cb09ec190f/DS01_2018_C.csv')
Insert cell
populationData = {
const cdcGeoids = cdcDeathData.map(row => +row.fips)
return rawPopulationData.filter(row => cdcGeoids.includes(+row['GEOID']))
}
Insert cell
geography = fetch('https://cdn.jsdelivr.net/npm/us-atlas@3/counties-albers-10m.json').then(r => r.json())
Insert cell
Insert cell
// get the columns from the first data to select
populationColumns = Object.keys(populationData[0]).slice(3,)
Insert cell
// map out the population data values based on the populationVariable
populationVariableData = _.map(populationData, o => +o[populationVariable]).sort((a, b) => a - b)
Insert cell
Insert cell
topQuantilePop = {
const response = await gdaProxy.QuantileBreaks(+quantileNumbers, populationVariableData)
return response.bins.breaks[+quantileNumbers-1]
}
Insert cell
Insert cell
dates = Object.keys(cdcDeathData[1001])
Insert cell
// list of date indices
dateIndices = Array.from({length: Object.keys(cdcDeathData[1001]).length-1}, (_, i) => i+1)
Insert cell
currentDate = dates[dateSelector]
Insert cell
Insert cell
normalizedDeathData = {
// declare return data object
let returnData = [];
// loop through CDC rows of counties
for (let i=0; i<cdcDeathData.length; i++){
// add a temporary object for this county
const currentGEOID = cdcDeathData[i].fips
let tempObj = {
'fips':currentGEOID
}
// find the county population data
const countyData = _.find(populationData, feature => +feature.GEOID == +currentGEOID)
// if missing population data, skip
if (countyData === undefined) {
continue
}
const countyPopulation = countyData?.totPopE
// list of date columns
const tempKeys = Object.keys(cdcDeathData[i]).slice(1,)
// loop through date columns to calculate 7-day average of deaths per 100k
for (let n=0; n<tempKeys.length; n++){
tempObj[tempKeys[n]] = (cdcDeathData[i][tempKeys[n]]/countyPopulation)*100_000
}
returnData.push(tempObj)
}
return returnData
}
Insert cell
// // map out the population data values based on the populationVariable
deathDataCurrentDate = _.map(normalizedDeathData, feature => +feature[currentDate]).sort((a, b) => a - b)
Insert cell
topQuantileDeath = {
const response = await gdaProxy.QuantileBreaks(+quantileNumbers, deathDataCurrentDate)
return response.bins.breaks[+quantileNumbers-1]
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more