chart2 = {
const width = 1000;
const height = 700;
const color = d3.scaleDiverging(d3.interpolateRdBu)
.domain([0.5, 0, -0.5]);
const sizeScale = d3.scaleSqrt()
.domain([0, 1])
.range([0.5, 2]);
let currentYear = 2024;
const projection = d3.geoAlbers()
.fitExtent([[100, 50], [width - 50, height - 50]], { type: "FeatureCollection", features: virginiaCounties2024 });
const path = d3.geoPath().projection(projection);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("style", "max-width: 100%; height: auto;");
svg.append("path")
.datum(topojson.mesh(us, us.objects.states))
.attr("fill", "none")
.attr("stroke", "#ccc")
.attr("d", path);
svg.append("path")
.datum(
topojson.mesh(us, us.objects.counties, (a, b) =>
a !== b && a.id.startsWith("51") && b.id.startsWith("51")
)
)
.attr("fill", "none")
.attr("stroke", "#aaa")
.attr("stroke-width", 0.5)
.attr("d", path);
// === Prepare Voting Data Maps ===
const votingDataMap2020 = new Map(
voteData2020.map(d => [
d["FIPS Code"],
{
fips: d["FIPS Code"],
locality: d.Locality,
biden: +d["Joe Biden Votes"],
trump: +d["Donald Trump Votes"],
total: +d["Total Votes"]
}
])
);
const votingDataMap2024 = new Map(
voteData2024.map(d => [
d["FIPS Code"],
{
fips: d["FIPS Code"],
locality: d.Locality,
harris: +d["Kamala Harris Votes"],
trump: +d["Donald Trump Votes"],
total: +d["Total Votes"]
}
])
);
const totalVotesByYear = {
2020: d3.sum(voteData2020, d => +d["Total Votes"]),
2024: d3.sum(voteData2024, d => +d["Total Votes"])
};
// === Counties group ===
const countiesGroup = svg.append("g")
.attr("stroke", "#444")
.attr("stroke-width", 0.5);
const counties = countiesGroup.selectAll("path")
.data(virginiaCounties2024) // Initially 2024
.join("path")
.attr("d", path)
.attr("fill", d => getCountyColor(d, votingDataMap2024, 2024))
.attr("transform", d => getCountyTransform(d, votingDataMap2024, 2024))
.append("title")
.text(d => getCountyTooltip(d, votingDataMap2024, 2024));
// === Helper Functions ===
function getCountyKey(d) {
return d.id;
}
function getVotingData(d, votingDataMap) {
return votingDataMap.get(getCountyKey(d));
}
function getCountyColor(d, votingDataMap, year) {
const county = getVotingData(d, votingDataMap);
if (!county) return "#ccc";
const [demVotes, repVotes] = (year === 2020)
? [county.biden, county.trump]
: [county.harris, county.trump];
const diff = (repVotes - demVotes) / (demVotes + repVotes);
return color(diff);
}
function getCountyTransform(d, votingDataMap, year) {
const [x, y] = path.centroid(d);
const county = getVotingData(d, votingDataMap);
if (!county) return `translate(${x},${y})`; // If no data, return without scaling
// Determine the leading candidate and their votes based on the year
let leadingCandidateVotes = 0;
if (year === 2020) {
leadingCandidateVotes = county.biden > county.trump ? county.biden : county.trump; // For 2020, choose the leading candidate
} else if (year === 2024) {
leadingCandidateVotes = county.harris > county.trump ? county.harris : county.trump; // For 2024, choose the leading candidate
}
// Calculate the vote percentage of the leading candidate in the county
const percentage = leadingCandidateVotes / county.total;
// Apply the scaling based on the percentage of votes the leading candidate received
const scale = percentage; // Adjust scale as necessary
// Return the translation and scaling
return `translate(${x},${y}) scale(${scale}) translate(${-x},${-y})`;
}
function getCountyTooltip(d, votingDataMap, year) {
const county = getVotingData(d, votingDataMap);
if (!county) return `${d.properties.name}\nNo data`;
const [demVotes, repVotes, demName] = (year === 2020)
? [county.biden, county.trump, "Biden"]
: [county.harris, county.trump, "Harris"];
const diff = Math.abs(demVotes - repVotes) / (demVotes + repVotes) * 100;
// Who’s in front?
const isDemLeading = demVotes > repVotes;
const leaderName = isDemLeading ? demName : "Trump";
const leaderVotes = isDemLeading ? demVotes : repVotes;
// Build tooltip text
return [
`${county.locality}`,
`${leaderName} Lead: ${diff.toFixed(1)}%`,
`${leaderName} Votes: ${leaderVotes.toLocaleString()}`
].join("\n");
}
// === Update Function ===
function update(year) {
currentYear = year;
const votingDataMap = year === 2020 ? votingDataMap2020 : votingDataMap2024;
countiesGroup.selectAll("path")
.data(virginiaCounties2024)
.join("path")
.attr("d", path)
.attr("fill", d => getCountyColor(d, votingDataMap, year))
.attr("transform", d => getCountyTransform(d, votingDataMap, year))
.select("title")
.text(d => getCountyTooltip(d, votingDataMap, year));
}
// === Return ===
return Object.assign(svg.node(), {
update,
scales: { color }
});
}