Published
Edited
May 11, 2021
Insert cell
Insert cell
Insert cell
viewof countyMap = {
// center and scale state
const projection = d3.geoIdentity()
.fitSize([width, height], topojson.feature(geo, geo.objects.states.geometries.find(f => f.id === "48")));
const path = d3.geoPath();
path.projection(projection);

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewbox", [0, 0, width, height]);
// draw counties
svg.append("g")
.selectAll("path")
.data(topojson.feature(geo, geo.objects.counties).features.filter(d => d.id.slice(0, 2) === "48"))
.join("path")
.attr("fill", d => colorScale(turnoutPerc.get(d.id)))
.attr("d", path)
.on('mousemove', (event,d) => {
console.log(d.id.includes("48"))
d3.select(event.currentTarget)
.raise()
.style('stroke', "#000")
.style('stroke-width', 2)
.style('cursor', 'pointer');
event.preventDefault();
let [x,y] = d3.pointer(event)
let ttPos = positionTooltip(x,y);
let countyData = {
showTT: true,
FIPS: d.id,
mouse: {x: ttPos.x, y: ttPos.y}
}
updateCurrentCounty(countyData);
})
.on('mouseout', (event, d) => {
updateCurrentCounty({
showTT: false,
FIPS: null
});
d3.select(event.currentTarget).style('stroke', 'none').style('cursor', 'default');
})
.on('mousedown', (event,d) => {
event.preventDefault();
});
function updateCurrentCounty(d){
// emit the id of the current hovered county
svg.node().value = d;
svg.node().dispatchEvent(new CustomEvent("input"));
}
updateCurrentCounty({
showTT: false,
FIPS: null
});
// draw states
svg.append("path")
.datum(topojson.mesh(geo, geo.objects.states.geometries.find(f => f.id === "48"), (a, b) => a !== b))
.attr("fill", "none")
.attr("stroke", "#000")
.attr("stroke-linejoin", "round")
.attr("d", path);
// wrap the svg node in a div to make the whole tooltip thing work
return document.createElement("div").appendChild(svg.node());
}
Insert cell
ttText = {
if (!countyMap.showTT) return;
if (!countyProps) return;
let {countyName, percFromMean, state} = countyProps;
let popFmt = d3.format(",")
let percFmt = d3.format(".1f")
if (state == 'Alaska'){
return `Alaska, ${countyName}!`
}
return percFromMean > 0 ?
`${countyName}!` :
`${countyName}!`
}
Insert cell
tooltip = {
if (!countyMap.showTT) return;
if (!countyProps) return;
const container = d3.create('div')
.attr('id', 'tooltip-container')
// Header
const header = container.append('div')
.attr('class', 'tt-header');
const location = header.append('div')
.attr('class', 'tt-location')
location.append('h1')
.attr('class','tt-county')
.html(countyProps.countyName);
location.append('h2')
.attr('class', 'tt-state')
.html(countyProps.state);
// text
container.append('div')
.attr('class', 'tt-text')
.html(ttText)
return document.createElement('div').appendChild(container.node());
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3.min(data, d => d.turnoutPerc)
Insert cell
colorScale = d3.scaleDivergingPow([30, 55.5, 80], d3.interpolateRdYlGn).unknown('#fff');
Insert cell
Insert cell
turnoutPerc = new Map(data.map(d => [d.FIPS, d.turnoutPerc]))
Insert cell
stateAbbrevByFIPS = new Map(data.map(d => [d.FIPS, d.stateAbbrev]))
Insert cell
width = 900
Insert cell
height = .65 * width
Insert cell
Insert cell
Insert cell
data = d3.csvParse(await FileAttachment("tx_turnout_2016_county.csv").text(), d => {
d.totalVotes = +d.totalVotes;
d.VAP = +d.VAP;
d.turnoutPerc = +d.turnoutPerc;
d.nNonVote = +d.nNonVote;
d.percFromMean = +d.percFromMean;
return d;
});
Insert cell
geoTexasCounties = geo.objects.counties.geometries.filter(f => f.id.includes("48"))
Insert cell
geo = FileAttachment("counties-albers-10m-topo@1.json").json()
Insert cell
Insert cell
import {ramp} from "@d3/color-legend"
Insert cell
import {serialize} from "@mbostock/saving-svg"
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