Published
Edited
Nov 4, 2020
Importers
Insert cell
Insert cell
govChart = raceBarChart(govResultsData)
Insert cell
Insert cell
agChart = raceBarChart(agResultsData)
Insert cell
Insert cell
ltGovChart = raceBarChart(ltGovResultsData)
Insert cell
Insert cell
sosChart = raceBarChart(sosResultsData)
Insert cell
Insert cell
tresChart = raceBarChart(tresResultsData)
Insert cell
tresResultsData = getOfficeRaceResults("Treasurer")
Insert cell
amend1Chart = raceBarChart(amend1ResultsData)
Insert cell
amend1ResultsData = getBallotMeasureResults("1 - Two-Term Limit More Offices")
Insert cell
amend3Chart = raceBarChart(amend3ResultsData)
Insert cell
amend3ResultsData = getBallotMeasureResults("3 - Change Redistricting Process")
Insert cell
prezChart = raceBarChart(prezResultsData)
Insert cell
prezResultsData = getOfficeRaceResults("President")
Insert cell
stateSen19Chart = raceBarChart(stateSen19ResultsData)
Insert cell
stateSen19ResultsData = getLegRaceResults("State Senate", 19)
Insert cell
stateRep44Chart = raceBarChart(stateRep44ResultsData)
Insert cell
stateRep44ResultsData = getLegRaceResults("State House", 44)
Insert cell
stateRepChart47 = raceBarChart(stateRep47ResultsData)
Insert cell
stateRep47ResultsData = getLegRaceResults("State House", 47)
Insert cell
stateRep50Chart = raceBarChart(stateRep50ResultsData)
Insert cell
stateRep50ResultsData = getLegRaceResults("State House", 50)
Insert cell
Insert cell
Insert cell
formatCandidate = (c, vote_total) => {
return {
name: `${c.first} ${c.last}`,
party: c.party,
color: parties[c.party].color,
is_incumbent: Boolean(c.incumbent),
votes: c.voteCount,
percent: c.voteCount / vote_total
}
}
Insert cell
getOfficeRaceResults = office_name => {
const raceData = resultsJSON.races.filter(d => d.officeName == office_name)[0];
const candidatesData = raceData.reportingUnits[0].candidates;
const total_votes = d3.sum(Array.from(candidatesData, d => d.voteCount));
const candidates = candidatesData.map(d => formatCandidate(d, total_votes));
const race_title = raceData.officeName == "President" ? `${raceData.officeName} (Missouri Results)` : raceData.officeName;
return {
race_title: raceData.officeName,
description: raceData.description,
reporting_time: raceData.reportingUnits[0].lastUpdated,
total_votes: total_votes,
source: "Associated Press",
precincts_reporting: raceData.reportingUnits[0].precinctsReporting,
precincts_total: raceData.reportingUnits[0].precinctsTotal,
precincts_reporting_pct: raceData.reportingUnits[0].precinctsReportingPct,
candidates: candidates,
};
}
Insert cell
getLegRaceResults = (office_name, seat_num) => {
const raceData = resultsJSON.races
.filter(d => d.officeName == office_name && +d.seatNum == seat_num)[0];
const candidatesData = raceData.reportingUnits[0].candidates;
const total_votes = d3.sum(Array.from(candidatesData, d => d.voteCount));
const candidates = candidatesData.map(d => formatCandidate(d, total_votes));
const race_title = raceData.officeName == "President" ? `${raceData.officeName} (Missouri Results)` : raceData.officeName;
return {
race_title: `${raceData.officeName} ${raceData.seatName}`,
description: raceData.description,
reporting_time: raceData.reportingUnits[0].lastUpdated,
total_votes: total_votes,
source: "Associated Press",
precincts_reporting: raceData.reportingUnits[0].precinctsReporting,
precincts_total: raceData.reportingUnits[0].precinctsTotal,
precincts_reporting_pct: raceData.reportingUnits[0].precinctsReportingPct,
candidates: candidates,
};
}
Insert cell
formatBallotMeasureOption = (o, vote_total) => {
return {
name: o.last,
color: o.last == 'Yes' ? '#268327' : '#cb2a06',
votes: o.voteCount,
percent: o.voteCount / vote_total
}
}
Insert cell
getBallotMeasureResults = name => {
const raceData = resultsJSON.races
.filter(d => d.raceType == "Ballot Issue" && d.seatName == name)[0]
const candidatesData = raceData.reportingUnits[0].candidates;
const total_votes = d3.sum(Array.from(candidatesData, d => d.voteCount));
const candidates = candidatesData.map(d => formatBallotMeasureOption(d, total_votes));
return {
race_title: `${raceData.officeName} ${raceData.seatName}`,
description: raceData.description,
reporting_time: raceData.reportingUnits[0].lastUpdated,
total_votes: total_votes,
source: "Associated Press",
precincts_reporting: raceData.reportingUnits[0].precinctsReporting,
precincts_total: raceData.reportingUnits[0].precinctsTotal,
precincts_reporting_pct: raceData.reportingUnits[0].precinctsReportingPct,
candidates: candidates,
};
}
Insert cell
raceData = resultsJSON.races
.filter(d => d.raceType == "Ballot Issue" && d.seatName == "3 - Change Redistricting Process")[0]
Insert cell
Insert cell
Insert cell
barHeight = 75
Insert cell
raceBarChart = results => {
const data = results['candidates'].sort(function(a, b) {
return d3.descending(a.percent, b.percent)
});
const margin = ({top: 120, right: 15, bottom: 50, left: 100});
let width = 600;
let height = Math.ceil((data.length + 0.1) * barHeight) + margin.top + margin.bottom;

const svg = d3.create('svg')
.attr("viewBox", [0, 0, width, height])

let x = d3.scaleLinear()
.domain([0, 1])
.range([margin.left, width - margin.right])

let y = d3.scaleBand()
.domain(d3.range(data.length))
.rangeRound([margin.top, height - margin.bottom])
.padding(0.1);

let format = x.tickFormat();

let xAxis = g => g
.attr("transform", `translate(0,${Math.ceil((data.length + 0.1) * barHeight) + margin.top})`)
.call(d3.axisBottom(x).ticks(4).tickFormat(d => d3.format(".0%")(d)))
.call(g => g.select(".domain").remove())
.attr("color", "grey");

let yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickFormat(i => data[i].name)
.tickSizeOuter(0))
.attr("font-size", "1em")

svg.append("g")
.selectAll("rect")
.data(data)
.join("rect")
.attr("fill", d => d.color)
.attr("x", x(0))
.attr("y", (d, i) => y(i))
.attr("width", d => x(d.percent) - x(0))
.attr("height", y.bandwidth());

svg.append("g")
.attr("fill", "white")
.attr("text-anchor", "end")
.attr("font-family", "sans-serif")
.attr("font-weight", "bold")
.attr("font-size", "16px")
.selectAll("text")
.data(data)
.join("text")
.attr("x", d => x(d.percent))
.attr("y", (d, i) => y(i) + y.bandwidth() / 2)
.attr("dy", "0.35em")
.attr("dx", -10)
.attr("font-size", "16px")
.text(d => `${d3.format(".2%")(d.percent)} (${d3.format(",")(d.votes)} ballots)`)
.call(text => text.filter(d => d.percent < 50) // short bars
.attr("dx", +10)
.attr("fill", "black")
.attr("text-anchor", "start"));

svg.append("g")
.call(xAxis);

const axis = svg.append("g")
.call(yAxis)

const title = svg.append("text")
.attr("class", "info")
.attr("x", 5)
.attr("y", 25)
.attr("text-anchor", "start")
.style("font-size", "30px")
.style("font-family", "PT Sans")
.style("font-weight", "bold")
.text(results.race_title)

let offset = 5;

if(results.description) {
svg.append("text")
.attr("class", "info")
.attr("x", 5)
.attr("y", 50)
.attr("text-anchor", "start")
.style("font-size", "16px")
.style("font-family", "PT Sans")
.style("font-weight", "bold")
.text(results.description);
offset = 30;
}

svg.append("text")
.attr("class", "info")
.attr("x", 5)
.attr("y", 50 + offset)
.attr("text-anchor", "start")
.style("font-size", "16px")
.style("font-family", "PT Sans")
.text(getUpdateText(results));

svg.append("text")
.attr("x", 5)
.attr("y", 70 + offset)
.attr("text-anchor", "start")
.style("font-size", "16px")
.style("font-family", "PT Sans")
.text(`Source: ${results.source}`);
setTimeout(()=>{
axis.selectAll(".tick text")
.call(wrap, margin.left-10);
}, 0);
return svg.node();
}
Insert cell
getUpdateText = results => {
return `${d3.format(".0%")(results.precincts_reporting / results.precincts_total)} precincts reporting as of ${moment(results.reporting_time).fromNow()}`
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
results = attachment.text()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
races = resultsXML.getElementsByTagName('RACE_INFO')
Insert cell
races
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
moment(prezResultsData.reporting_time).fromNow()
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