Published
Edited
Nov 4, 2020
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
interactiveMap = {
// const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);
const canvas = d3
.create("canvas")
.attr("width", width)
.attr("height", height)
.node();
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext("2d");
scaleCanvas(canvas, ctx);

function draw() {
ctx.fillStyle = "rgba(255,255,255,0.5)";
ctx.fillRect(0, 0, width, height);

simulation.nodes().forEach(d => {
let opacity = 0.2 + d.value;
let fill = "lightgray";
// if (tt.eevp > 90) {
if (d.stateWinner) {
if (d.results.trumpd < d.results.bidenj) {
fill = blue;
} else if (d.results.trumpd > d.results.bidenj) {
fill = red;
}
}
// }
let rgb = d3.rgb(fill);

ctx.strokeStyle = `rgba(${rgb.r},${rgb.g},${rgb.b},${0.1}`;
// ctx.globalAlpha = opacity;
ctx.beginPath();
ctx.moveTo(d.cx, d.cy);
ctx.lineTo(d.x, d.y);
ctx.stroke();
});

simulation.nodes().forEach(d => {
// let problem = shirleyDataByFIPS.get(d.id);
// if (problem) {// return "orange";}
let opacity = 0.2 + d.value;
let fill = "lightgray";
// if (tt.eevp > 90) {
if (d.stateWinner) {
if (d.results.trumpd < d.results.bidenj) {
fill = blue;
} else if (d.results.trumpd > d.results.bidenj) {
fill = red;
}
}
// }
let rgb = d3.rgb(fill);

ctx.fillStyle = `rgba(${rgb.r},${rgb.g},${rgb.b},${opacity}`;
// ctx.globalAlpha = opacity;
ctx.beginPath();
ctx.arc(d.x, d.y, d.r * 0.9, 0, Math.PI * 2);
ctx.fill();
});
}
draw();
// svg.append("g").attr("transform", "translate(${height},20)");
// .append(() => legend({ ramp, title: data.title, width: 260 }));

simulation.on("tick", draw);

invalidation.then(() => simulation.stop());

return canvas;
}
Insert cell
simulation.nodes().filter(d => d.eevp > 90)
Insert cell
nytPres = d3.json(
"https://static01.nyt.com/elections-assets/2020/data/api/2020-11-03/national-map-page/national/president.json"
)
Insert cell
simulation = {
let counties = circles.map(d => {
let tt = nytPresCountiesByFIPS.get(d.id);
if (!tt) return { ...d };
let tot = tt.tot_exp_vote ? tt.tot_exp_vote : tt.votes2016;
let r = Math.sqrt(tot * 0.001) * 0.4 + 1;
let stateFips = d.id.slice(0, 2);
let stateAbbr = stateAbbrFips[stateFips];
let state = nytPresStatesByAbbr.get(stateAbbr);

return {
...d,
...tt,
r,
value: tt.votes / tot,
stateWinner: state.result,
state
};
});
return (
d3
.forceSimulation(counties)
// .force("x", d3.forceX(width / 2).strength(0.001))
// .force("y", d3.forceY(height / 2).strength(0.001))
.force('charge', d3.forceManyBody().strength(0.1))
.force('center', d3.forceCenter(width / 2, height / 2).strength(0.000001))
.force('collision', d3.forceCollide().radius(d => d.r * 0.95))
);
}
Insert cell
forceX = d => {
if (target == "center") {
return d.cx;
} else if (target == "winners") {
if (d.eevp > 90) {
if (d.leader_party_id == "republican") {
return width - 20;
} else if (d.leader_party_id == "democrat") {
return 20;
}
}
}
return d.cx;
}
Insert cell
forceY = d => {
if (target == "center") {
return d.cy;
} else if (target == "winners") {
if (d.eevp > 90) {
// return d.value * height;
}
}

return d.cy;
}
Insert cell
{
simulation
.force("x", d3.forceX(forceX).strength(0.04))
.force("y", d3.forceY(forceY).strength(0.08));
simulation.alphaDecay(0.03);
simulation.alphaTarget(0.5);
simulation.restart();
}
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
import { scaleCanvas } from "@john-guerra/canvas-retina-display"
Insert cell
nytPres.meta.version
Insert cell
numformat = d3.format(",d")
Insert cell
tooltip = html`<div id="svg-tip" style="position:fixed; pointer-events: none; background: rgba(255,255,255,0.8); padding: 5px;">
Name: <span class="county-name"></span> County, <span class="state"></span><br>
Votes 2020: <span class="county-votes2020"></span><br>
Expected: <span class="county-expected"></span><br>
Votes 2016: <span class="county-votes2016"></span><br>
</div>`
Insert cell
mapSizedByVote = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

// svg.append("g").attr("transform", "translate(${height},20)");
// .append(() => legend({ ramp, title: data.title, width: 260 }));

svg
.append("g")
.selectAll("circle")
.data(circles.filter(d => !!d.winner))
.join("circle")
.attr("fill", d => {
let tt = nytPresCountiesByFIPS.get(d.id);
if (!tt) return "lightgray";

if (tt.results.trumpd < tt.results.bidenj) {
return blue;
} else if (tt.results.trumpd > tt.results.bidenj) {
return red;
}
return "lightgray";
})
.attr("stroke", d => {
let problem = shirleyDataByFIPS.get(d.id);
if (problem) {
return "orange";
}
return "none";
})
.attr("opacity", d => {
let tt = nytPresCountiesByFIPS.get(d.id);
if (!tt) return 0.1;
return (
0.2 + tt.votes / (tt.tot_exp_vote ? tt.tot_exp_vote : tt.votes2016)
);
})
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => {
let tt = nytPresCountiesByFIPS.get(d.id);
if (!tt) return false;
let r = tt.tot_exp_vote ? tt.tot_exp_vote : tt.votes2016;
r = Math.sqrt(r * 0.001) * 0.4 + 1;
return r;
})
.on("click", (event, d) => {
let tt = nytPresCountiesByFIPS.get(d.id);
console.log("county", d, tt);
})
.append("title")
.text(d => d.properties.name);

return svg.node();
// return html`<div style='color:#111'};'>${svg.node()}</div>`;
}
Insert cell
map = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

// svg.append("g").attr("transform", "translate(${height},20)");
// .append(() => legend({ ramp, title: data.title, width: 260 }));

svg
.append("g")
.selectAll("circle")
.data(circles.filter(d => !!d.winner))
.join("circle")
.attr("fill", d => {
let tt = nytPresCountiesByFIPS.get(d.id);
if (!tt) return "lightgray";

if (tt.results.trumpd < tt.results.bidenj) {
return blue;
} else if (tt.results.trumpd > tt.results.bidenj) {
return red;
}
return "lightgray";
})
.attr("stroke", d => {
let problem = shirleyDataByFIPS.get(d.id);
if (problem) {
return "orange";
}
return "none";
})
.attr("opacity", d => {
let tt = nytPresCountiesByFIPS.get(d.id);
if (!tt) return 0.1;
return (
0.2 + tt.votes / (tt.tot_exp_vote ? tt.tot_exp_vote : tt.votes2016)
);
})
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => d.p)
.on("click", (event, d) => {
let tt = nytPresCountiesByFIPS.get(d.id);
console.log("county", d, tt);
})
.append("title")
.text(d => d.properties.name);

return svg.node();
// return html`<div style='color:#111'};'>${svg.node()}</div>`;
}
Insert cell
nytPresCounties = {
let data = [];
nytPres.data.races.forEach(r => {
data = data.concat(r.counties);
});
return data;
}
Insert cell
ky = nytPresCounties.filter(d => d.fips.slice(0, 2) == "21")
Insert cell
nytPresStatesByAbbr = new Map(nytPres.data.races.map(d => [d.state_id, d]))
Insert cell
nytPresCountiesByFIPS = new Map(nytPresCounties.map(d => [d.fips, d]))
Insert cell
// import { circles } from "@codingwithfire/force-bubble-map-with-textures-js"
circles = FileAttachment("circles2016.json").json()
Insert cell
lightBlue = "#52b2e8"
Insert cell
lightRed = "#ee448a"
Insert cell
blue = "#002868"
Insert cell
red = "#bf0a30"
Insert cell
height = 600
Insert cell
import { data as shirleyData } from "@sxywu/7-voting-problems-on-election-day"
Insert cell
shirleyData
Insert cell
shirleyDataByFIPS = new Map(shirleyData.map(d => [d.FIPS, d]))
Insert cell
// nytSummary = d3.json(
// "https://static01.nyt.com/elections-assets/2020/data/liveModel/2020-11-03/president/summary.json"
// )
Insert cell
[flData, gaData, miData, ncData, ohData, paData, txData]
Insert cell
d3 = require("d3")
Insert cell
cnnData = d3.json(
"https://politics-elex-results.data.api.cnn.io/results/view/2020-national-races-PG.json"
)
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