map1 = {
map.style("background-color", republicanVotes < democratVotes ? "lightblue": "rosybrown");
let select_year2 = 1988
let select_var = "inc_past_4"
var most_recent = data.filter(d => d.Year === select_year2);
var most_recent_obj = new Map(most_recent.map(d => [d.State, +d.margin]))
var max_margin = d3.max(most_recent, d => Math.abs(+d.margin))
var max_var = d3.max(data, d => Math.abs(+d[select_var]))
var avg_inc_past = d3.max(income.filter(d => d.Year === select_year2), d => d[select_var])
var color = d3.scaleSequential([max_margin, -max_margin], d3.interpolateRdBu)
const color2 = value => {
if (value >= 0) {
return "red";
} else {
return "blue";
}
};
var republicanVotes = most_recent.reduce((total, obj) => {
if (obj.Party === 'R') {
return total + obj.Votes;
} else {
return total;
}
}, 0); // Start with a total of 0
// Get the counts of Republican electoral votes
var democratVotes = most_recent.reduce((total, obj) => {
// Check if the 'Party' is 'D'
if (obj.Party === 'D') {
// Add the value of the 'Votes' column to the total
return total + obj.Votes;
} else {
// If 'Party' is not 'D', return the current total unchanged
return total;
}
}, 0); // Start with a total of 0
// Get candidate names
var candidates2 = candidates.filter(d => d.Year === select_year2);
var dem_can = candidates2.filter(d => d.party_simplified == "DEMOCRAT")
.map(d => d.candidate);
var rep_can = candidates2.filter(d => d.party_simplified == "REPUBLICAN")
.map(d => d.candidate);
/////////////////////////////////////////////// SVG and its parts
// Add paths for each state
const paths = map.selectAll("path")
.data(topojson.feature(shapefile, shapefile.objects.states).features)
.join("path")
.attr("d", path)
.attr("fill", d => {
return color2(most_recent_obj.get(d.properties.name));
})
.style("stroke", "black") // Add black stroke color
.style("stroke-width", 1) // Add stroke width
.style("vector-effect", "non-scaling-stroke") // Prevent stroke width from scaling with zoom
.on("mouseover", function(event, d) {
const stateData = most_recent.find(state => state.State === d.properties.name);
// Fade other circles by reducing opacity
map.selectAll("circle")
.transition()
.duration(150)
.style("opacity", circle => (circle.State === d.properties.name ? 1 : 0.05));
// Fade other states by reducing opacity
map.selectAll("path")
.transition()
.duration(150)
.style("opacity", state => (state.State === d.properties.name ? 1 : 0.2));
// Calculate position for tooltip
const tooltipX = path.centroid(d)[0] + 100; // Adjust x position to the right of the state's centroid
const tooltipY = path.centroid(d)[1]; // Keep y position same as state's centroid
// Append tooltip text box to the SVG on mouseover
const tooltipBox = map.append("g")
.attr("class", "tooltip-box")
.attr("transform", `translate(${tooltipX}, ${tooltipY})`);
tooltipBox.append("rect")
.attr("width", 160) // Adjust width as needed
.attr("height", 75) // Adjust height as needed
.attr("x", -75) // Adjust x position to center the box
.attr("y", -40) // Adjust y position to center the box
.style("fill", "white")
.style("stroke", "black");
// Append tooltip text to the tooltip box
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("State: " + d.properties.name)
.attr("y", "-25"); // Adjust vertical position of text
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("Change in Income: " + Math.round(10 * stateData[select_var]) / 10 + "%")
.attr("y", "-10"); // Adjust vertical position of text
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("Margin of Victory: " + Math.abs(Math.round(10 * stateData.margin) / 10) + "%" + (stateData.margin < 0 ? " D" : " R"))
.attr("y", "5"); // Adjust vertical position of text
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("Electoral Votes: " + stateData.Votes)
.attr("y", "20"); // Adjust vertical position of text
})
.on("mouseout", function() {
// Restore state opacity
map.selectAll("path")
.transition()
.duration(100)
.style("opacity", 1);
// Restore opacity of all circles on mouseout
map.selectAll("circle")
.transition()
.duration(100)
.style("opacity", 1);
// Remove tooltip text from the SVG on mouseout
map.selectAll(".tooltip-box").remove(); // Remove elements with the "tooltip-box" class
});
// Add text for each state
const labels = map.selectAll("text")
.data(topojson.feature(shapefile, shapefile.objects.states).features)
.join("text")
.text(d => {
const stateData = most_recent.find(state => state.State === d.properties.name);
return stateData ? stateData.Votes : ""; // Display electoral votes or empty string if not found
})
.attr("y", d => d.properties.name === "District of Columbia" ? path.centroid(d)[1] + 5 :
(d.properties.name === "Michigan" ? path.centroid(d)[1] + 10 : path.centroid(d)[1]))
.attr("x", d => d.properties.name === "District of Columbia" ? path.centroid(d)[0] - 5 :
(d.properties.name === "Florida" ? path.centroid(d)[0] + 10 :
(d.properties.name === "Louisiana" ? path.centroid(d)[0] - 13 : path.centroid(d)[0])))
.style("fill", "white")
.style("font-size", "13px")
.on("mouseover", function(event, d) {
const stateData = most_recent.find(state => state.State === d.properties.name);
// Fade other circles by reducing opacity
map.selectAll("circle")
.transition()
.duration(150)
.style("opacity", circle => (circle.State === d.properties.name ? 1 : 0.05));
// Fade other states by reducing opacity
map.selectAll("path")
.transition()
.duration(150)
.style("opacity", state => (state.State === d.properties.name ? 1 : 0.2));
// Calculate position for tooltip
const tooltipX = path.centroid(d)[0] + 100; // Adjust x position to the right of the state's centroid
const tooltipY = path.centroid(d)[1]; // Keep y position same as state's centroid
// Append tooltip text box to the SVG on mouseover
const tooltipBox = map.append("g")
.attr("class", "tooltip-box")
.attr("transform", `translate(${tooltipX}, ${tooltipY})`);
tooltipBox.append("rect")
.attr("width", 160) // Adjust width as needed
.attr("height", 75) // Adjust height as needed
.attr("x", -75) // Adjust x position to center the box
.attr("y", -40) // Adjust y position to center the box
.style("fill", "white")
.style("stroke", "black");
// Append tooltip text to the tooltip box
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("State: " + d.properties.name)
.attr("y", "-25"); // Adjust vertical position of text
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("Change in Income: " + Math.round(10 * stateData[select_var]) / 10 + "%")
.attr("y", "-10"); // Adjust vertical position of text
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("Margin of Victory: " + Math.abs(Math.round(10 * stateData.margin) / 10) + "%" + (stateData.margin < 0 ? " D" : " R"))
.attr("y", "5"); // Adjust vertical position of text
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("Electoral Votes: " + stateData.Votes)
.attr("y", "20"); // Adjust vertical position of text
})
.on("mouseout", function() {
// Restore state opacity
map.selectAll("path")
.transition()
.duration(100)
.style("opacity", 1);
// Restore opacity of all circles on mouseout
map.selectAll("circle")
.transition()
.duration(100)
.style("opacity", 1);
// Remove tooltip text from the SVG on mouseout
map.selectAll(".tooltip-box").remove(); // Remove elements with the "tooltip-box" class
});
// Add a title
const title = map.append("text")
.attr('x', width/2)
.attr("y", 40)
.text(select_year2 + ' Election Results')
.style("font-size", "28px")
.style("font-weight", "bold")
// Add a subtitle
const subtitle = map.append("text")
.attr("x", width/2)
.attr("y", 60)
.text("by electoral votes")
.style("font-size", "12px")
.style("font-weight", "bold")
// Add a legend
const legend = map.append("g")
.attr("transform", `translate(${width - 400}, 20)`);
legend.append("text")
.attr("y", 70)
.style("font-weight", "bold")
.style("font-size", "16px")
.text("Party");
legend.append("rect")
.attr("x", -40)
.attr("y", 80)
.attr("width", 20)
.attr("height", 20)
.style("fill", "red");
legend.append("text")
.attr("x", 15)
.attr("y", 95)
.text("Republican");
legend.append("rect")
.attr("x", -40)
.attr("y", 110)
.attr("width", 20)
.attr("height", 20)
.style("fill", "blue");
legend.append("text")
.attr("x", 11)
.attr("y", 125)
.text("Democrat");
/////////////////////////////////////////////// Add electoral votes bar legend
// Add electoral votes bar legend
const legend2 = map.append("g")
.attr("transform", `translate(${width - 200}, 0)`);
legend2.append("text")
.attr("x", 20)
.attr("y", 250)
.style("font-weight", "bold")
.style("font-size", "14px")
.text("Electoral Votes");
// Add electoral votes bars for Republican
legend2.append("rect")
.attr("x", 0)
.attr("y", 260)
.attr("width", 50)
.attr("height", republicanVotes/2)
.style("fill", "red")
.style("stroke", "black");
legend2.append("text")
.attr("x", 25)
.attr("y", 260 + republicanVotes/4)
.style("fill", "white")
.style("font-size", "14px")
.text(republicanVotes); // Display number of votes
legend2.append("text")
.attr("x", 85)
.attr("y", 260 + republicanVotes/4)
.style("font-size", "14px")
.text(rep_can); // Display candidate
// Add electoral votes bars for Democrat
legend2.append("rect")
.attr("x", 0)
.attr("y", 260 + republicanVotes/2)
.attr("width", 50)
.attr("height", democratVotes/2)
.style("fill", "blue")
.style("stroke", "black");
legend2.append("text")
.attr("x", 25)
.attr("y", 260 + republicanVotes/2 + democratVotes/4)
.style("fill", "white")
.style("font-size", "14px")
.text(democratVotes); // Display number of votes
legend2.append("text")
.attr("x", 85)
.attr("y", 260 + republicanVotes/2 + democratVotes/4)
.style("font-size", "14px")
.text(dem_can); // Display candidate
// Add 270 to win line
map.append("line")
.attr("x1", width - 200)
.attr("y1", 260 + 538/4)
.attr("x2", width - 150)
.attr("y2", 260 + 538/4)
.style("stroke", "black")
.style("stroke-width", 1.5);
map.append("text")
.attr("x", width - 230)
.attr("y", 255 + 538/4)
.text("270 votes")
.style("font-size", "12px")
.style("fill", "black");
map.append("text")
.attr("x", width - 230)
.attr("y", 265 + 538/4)
.text("to win")
.style("font-size", "12px")
.style("fill", "black");
/////////////////////////////////////////////// Bubble bar
// Add a bubble bar with all of the states
const bubble_bar = map.append("g");
bubble_bar.append("line") // Line for all of the bubbles to be on
.attr("x1", width * .15)
.attr("y1", 730)
.attr("x2", width * .85)
.attr("y2", 730)
.style("stroke", "black")
.style("stroke-width", 1.5);
bubble_bar.append("line") // Line for the minimum value
.attr("x1", width * .15)
.attr("y1", 715)
.attr("x2", width * .15)
.attr("y2", 745)
.style("stroke", "black")
.style("stroke-width", .7);
bubble_bar.append("line") // Line for the maximum value
.attr("x1", width * .85)
.attr("y1", 715)
.attr("x2", width * .85)
.attr("y2", 745)
.style("stroke", "black")
.style("stroke-width", .7);
bubble_bar.append("line") // Line for 0% change
.attr("x1", width/2)
.attr("y1", 715)
.attr("x2", width/2)
.attr("y2", 745)
.style("stroke", "black")
.style("stroke-width", .7);
bubble_bar.append("line") // Line for US Avg
.attr("x1", (width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)))
.attr("y1", 715)
.attr("x2", (width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)))
.attr("y2", 758)
.style("stroke", "white")
.style("stroke-width", .8);
bubble_bar.append("text") // Text for the minimum
.attr("x", width * .15 + 2)
.attr("y", 758)
.text(-1 * Math.round(max_var * 10)/10 + "%")
.style("font-size", "12px")
.style("fill", "black");
bubble_bar.append("text") // Text for the maximum
.attr("x", width * .85 + 2)
.attr("y", 758)
.text(Math.round(max_var * 10)/10 + "%")
.style("font-size", "12px")
.style("fill", "black");
bubble_bar.append("text") // Text for 0% change
.attr("x", width/2 + 4.3)
.attr("y", 758)
.text("0.0%")
.style("font-size", "12px")
.style("fill", "black");
bubble_bar.append("text") // Text for US avg
.attr("x", (width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)) + 2)
.attr("y", 771)
.text("US Average")
.style("font-size", "12px")
.style("fill", "white");
bubble_bar.append("text")
.attr("x", width/2)
.attr("y", 680)
.text("Change in Real Income Past Four Years")
.style("font-size", "16px")
.style("fill", "black");
bubble_bar.append("text")
.attr("x", width/2)
.attr("y", 700)
.text("Colored By Margin of Victory")
.style("font-size", "12px")
.style("fill", "black");
// Add circles for each state
const circles = map.selectAll("circle")
.data(most_recent)
.enter()
.append("circle")
.attr("cx", d => {
// Calculate the x-coordinate based on the inc_past value and the width of the line
return (width * 0.15) + (((max_var + d[select_var]) / (max_var * 2)) * (width * 0.7));
})
.attr("cy", 730) // y-coordinate on the line
.attr("r", 8.5) // Adjust the radius of the circles
.style("fill", d => color(d.margin)) // Set the fill color based on the margin value
.on("mouseover", function(event, d) {
// Fade other circles by reducing opacity
map.selectAll("circle")
.transition()
.duration(150)
.style("opacity", circle => (circle === d ? 1 : 0.05));
// Fade other states by reducing opacity
map.selectAll("path")
.transition()
.duration(150)
.style("opacity", state => (state.properties.name === d.State ? 1 : 0.2));
// Append tooltip text box to the SVG on mouseover
const tooltipBox = map.append("g")
.attr("class", "tooltip-box")
.attr("transform", `translate(${(width * 0.15) + (((max_var + d[select_var]) / (max_var * 2)) * (width * 0.7))}, 680)`);
tooltipBox.append("rect")
.attr("width", 160) // Adjust width as needed
.attr("height", 75) // Adjust height as needed
.attr("x", -75) // Adjust x position to center the box
.attr("y", -40) // Adjust y position to center the box
.style("fill", "white")
.style("stroke", "black");
// Append tooltip text to the tooltip box
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("State: " + d.State)
.attr("dy", "-25"); // Adjust vertical position of text
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("Change in Income: " + Math.round(10 * d[select_var]) / 10 + "%")
.attr("dy", "-10"); // Adjust vertical position of text
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("Margin of Victory: " + Math.abs(Math.round(10 * d.margin) / 10) + "%" + (d.margin < 0 ? " D" : " R"))
.attr("dy", "5"); // Adjust vertical position of text
tooltipBox.append("text")
.style("font-size", "12px")
.style("fill", "black")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.text("Electoral Votes: " + d.Votes)
.attr("dy", "20"); // Adjust vertical position of text
})
.on("mouseout", function() {
// Restore state opacity
map.selectAll("path")
.transition()
.duration(100)
.style("opacity", 1);
// Restore opacity of all circles on mouseout
map.selectAll("circle")
.transition()
.duration(100)
.style("opacity", 1);
// Remove tooltip text from the SVG on mouseout
map.selectAll(".tooltip-box").remove(); // Remove elements with the "tooltip-box" class
});
////////////////
const btns = map.append("g");
function updateMap() {
(select_year2 === 2016) ? (select_year2 = 1988) : select_year2 += 4
var max_var = d3.max(data, d => Math.abs(+d[select_var]));
// Filter down to the data for the new year
var most_recent = data.filter(d => d.Year === select_year2);
// Represent as a Map object to more easily access the values for each state
var most_recent_obj = new Map(most_recent.map(d => [d.State, +d.margin]))
// Get candidate names for the new year
var candidates2 = candidates.filter(d => d.Year === select_year2);
var dem_can = candidates2.filter(d => d.party_simplified == "DEMOCRAT")
.map(d => d.candidate);
var rep_can = candidates2.filter(d => d.party_simplified == "REPUBLICAN")
.map(d => d.candidate);
var max_margin = d3.max(most_recent, d => Math.abs(+d.margin))
var avg_inc_past = d3.max(income.filter(d => d.Year === select_year2), d => d[select_var])
var color = d3.scaleSequential([max_margin, -max_margin], d3.interpolateRdBu)
var republicanVotes = most_recent.reduce((total, obj) => {
// Check if the 'Party' is 'R' (Republican)
if (obj.Party === 'R') {
// Add the value of the 'Votes' column to the total
return total + obj.Votes;
} else {
// If 'Party' is not 'R', return the current total unchanged
return total;
}
}, 0); // Start with a total of 0
var democratVotes = most_recent.reduce((total, obj) => {
// Check if the 'Party' is 'D'
if (obj.Party === 'D') {
// Add the value of the 'Votes' column to the total
return total + obj.Votes;
} else {
// If 'Party' is not 'D', return the current total unchanged
return total;
}
}, 0);
// Update the title with the new year
title.text(select_year2 + ' Election Results');
// Update paths with new data
paths.data(topojson.feature(shapefile, shapefile.objects.states).features)
.transition()
.duration(600)
.attr("fill", d => {
return color2(most_recent_obj.get(d.properties.name));
});
// Update labels with new data
labels.data(topojson.feature(shapefile, shapefile.objects.states).features)
.transition()
.duration(600)
.text(d => {
const stateData = most_recent.find(state => state.State === d.properties.name);
return stateData ? stateData.Votes : ""; // Display electoral votes or empty string if not found
});
// Update circles with new data
circles.data(most_recent)
.transition()
.duration(600)
.style("fill", d => color(d.margin))
.attr("cx", d => {
// Calculate the x-coordinate based on the inc_past value and the width of the line
return (width * 0.15) + (((max_var + d[select_var]) / (max_var * 2)) * (width * 0.7));
});
bubble_bar.selectAll("text")
.text((d, i) => i === 0 ? (-1 * Math.round(max_var * 10)/10 + "%") :
(i === 1 ? (Math.round(max_var * 10)/10 + "%") :
(i === 2 ? ("0.0%") :
(i === 3 ? ("US Average") :
(i === 4 ? (select_var === "inc_past_2" ? ("Change in Real Income Past Two Years") :
(select_var === "inc_past_1" ? ("Change in Real Income Over Past Year"): ("Change in Real Income Past Four Years"))) :
(i === 5 ? ("Colored By Margin of Victory") : ""))))))
.attr("x", (d, i) => i === 0 ? (width * .15 + 2) :
(i === 1 ? (width * .85 + 2) :
(i === 2 ? (width/2 + 4.3) :
(i === 3 ? ((width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)) + 2) :
(i === 4 ? (width/2) : width/2)))))
bubble_bar.selectAll("line") // Line for US Avg
.attr("x1", (d, i) => i === 0 ? (width * .15) :
(i === 1 ? (width * .15) :
(i === 2 ? (width * .85) :
(i === 3 ? (width/2) : ((width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)))))))
.attr("x2", (d, i) => i === 0 ? (width * .85) :
(i === 1 ? (width * .15) :
(i === 2 ? (width * .85) :
(i === 3 ? (width/2) : ((width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)))))));
// Update electoral votes bars with new data
legend2.selectAll("rect")
.transition()
.duration(600)
.attr("height", (d, i) => i % 2 === 0 ? (republicanVotes/2) : (democratVotes/2))
.attr("y", (d, i) => i % 2 === 0 ? 260 : (260 + republicanVotes/2))
legend2.selectAll("text")
.transition()
.duration(600)
.text((d, i) => i === 0 ? ("Electoral Votes") :
(i === 1 ? (republicanVotes) :
(i === 2 ? (rep_can) :
(i === 3 ? (democratVotes) : dem_can))))
.attr("y", (d, i) => i === 0 ? 250 :
(i === 1 ? 260 + republicanVotes/4 :
(i === 2 ? 260 + republicanVotes/4 :
(i === 3 ? 260 + republicanVotes/2 + democratVotes/4 : 260 + republicanVotes/2 + democratVotes/4))));
// Update background color
map.style("background-color", republicanVotes > democratVotes ? "rosybrown" : "lightblue");
};
function updateMap2() {
(select_year2 === 1988) ? (select_year2 = 2016) : select_year2 -= 4
var max_var = d3.max(data, d => Math.abs(+d[select_var]));
// Filter down to the data for the new year
var most_recent = data.filter(d => d.Year === select_year2);
// Represent as a Map object to more easily access the values for each state
var most_recent_obj = new Map(most_recent.map(d => [d.State, +d.margin]))
// Get candidate names for the new year
var candidates2 = candidates.filter(d => d.Year === select_year2);
var dem_can = candidates2.filter(d => d.party_simplified == "DEMOCRAT")
.map(d => d.candidate);
var rep_can = candidates2.filter(d => d.party_simplified == "REPUBLICAN")
.map(d => d.candidate);
var max_margin = d3.max(most_recent, d => Math.abs(+d.margin))
var avg_inc_past = d3.max(income.filter(d => d.Year === select_year2), d => d[select_var])
var color = d3.scaleSequential([max_margin, -max_margin], d3.interpolateRdBu)
var republicanVotes = most_recent.reduce((total, obj) => {
// Check if the 'Party' is 'R' (Republican)
if (obj.Party === 'R') {
// Add the value of the 'Votes' column to the total
return total + obj.Votes;
} else {
// If 'Party' is not 'R', return the current total unchanged
return total;
}
}, 0); // Start with a total of 0
var democratVotes = most_recent.reduce((total, obj) => {
// Check if the 'Party' is 'D'
if (obj.Party === 'D') {
// Add the value of the 'Votes' column to the total
return total + obj.Votes;
} else {
// If 'Party' is not 'D', return the current total unchanged
return total;
}
}, 0);
// Update the title with the new year
title.text(select_year2 + ' Election Results');
// Update paths with new data
paths.data(topojson.feature(shapefile, shapefile.objects.states).features)
.transition()
.duration(600)
.attr("fill", d => {
return color2(most_recent_obj.get(d.properties.name));
});
// Update labels with new data
labels.data(topojson.feature(shapefile, shapefile.objects.states).features)
.transition()
.duration(600)
.text(d => {
const stateData = most_recent.find(state => state.State === d.properties.name);
return stateData ? stateData.Votes : ""; // Display electoral votes or empty string if not found
});
// Update circles with new data
circles.data(most_recent)
.transition()
.duration(600)
.style("fill", d => color(d.margin))
.attr("cx", d => {
// Calculate the x-coordinate based on the inc_past value and the width of the line
return (width * 0.15) + (((max_var + d[select_var]) / (max_var * 2)) * (width * 0.7));
});
bubble_bar.selectAll("text")
.text((d, i) => i === 0 ? (-1 * Math.round(max_var * 10)/10 + "%") :
(i === 1 ? (Math.round(max_var * 10)/10 + "%") :
(i === 2 ? ("0.0%") :
(i === 3 ? ("US Average") :
(i === 4 ? (select_var === "inc_past_2" ? ("Change in Real Income Past Two Years") :
(select_var === "inc_past_1" ? ("Change in Real Income Over Past Year"): ("Change in Real Income Past Four Years"))) :
(i === 5 ? ("Colored By Margin of Victory") : ""))))))
.attr("x", (d, i) => i === 0 ? (width * .15 + 2) :
(i === 1 ? (width * .85 + 2) :
(i === 2 ? (width/2 + 4.3) :
(i === 3 ? ((width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)) + 2) :
(i === 4 ? (width/2) : width/2)))))
bubble_bar.selectAll("line") // Line for US Avg
.attr("x1", (d, i) => i === 0 ? (width * .15) :
(i === 1 ? (width * .15) :
(i === 2 ? (width * .85) :
(i === 3 ? (width/2) : ((width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)))))))
.attr("x2", (d, i) => i === 0 ? (width * .85) :
(i === 1 ? (width * .15) :
(i === 2 ? (width * .85) :
(i === 3 ? (width/2) : ((width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)))))));
// Update electoral votes bars with new data
legend2.selectAll("rect")
.transition()
.duration(600)
.attr("height", (d, i) => i % 2 === 0 ? (republicanVotes/2) : (democratVotes/2))
.attr("y", (d, i) => i % 2 === 0 ? 260 : (260 + republicanVotes/2))
legend2.selectAll("text")
.transition()
.duration(600)
.text((d, i) => i === 0 ? ("Electoral Votes") :
(i === 1 ? (republicanVotes) :
(i === 2 ? (rep_can) :
(i === 3 ? (democratVotes) : dem_can))))
.attr("y", (d, i) => i === 0 ? 250 :
(i === 1 ? 260 + republicanVotes/4 :
(i === 2 ? 260 + republicanVotes/4 :
(i === 3 ? 260 + republicanVotes/2 + democratVotes/4 : 260 + republicanVotes/2 + democratVotes/4))));
// Update background color
map.style("background-color", republicanVotes > democratVotes ? "rosybrown" : "lightblue");
};
function updateMap3(select_var2) {
select_year2 = select_year2 += 4
select_year2 = select_year2 -= 4
var max_var = d3.max(data, d => Math.abs(+d[select_var2]));
// Filter down to the data for the new year
var most_recent = data.filter(d => d.Year === select_year2);
// Represent as a Map object to more easily access the values for each state
var most_recent_obj = new Map(most_recent.map(d => [d.State, +d.margin]))
// Get candidate names for the new year
var candidates2 = candidates.filter(d => d.Year === select_year2);
var dem_can = candidates2.filter(d => d.party_simplified == "DEMOCRAT")
.map(d => d.candidate);
var rep_can = candidates2.filter(d => d.party_simplified == "REPUBLICAN")
.map(d => d.candidate);
var max_margin = d3.max(most_recent, d => Math.abs(+d.margin))
var avg_inc_past = d3.max(income.filter(d => d.Year === select_year2), d => d[select_var2])
var color = d3.scaleSequential([max_margin, -max_margin], d3.interpolateRdBu)
var republicanVotes = most_recent.reduce((total, obj) => {
// Check if the 'Party' is 'R' (Republican)
if (obj.Party === 'R') {
// Add the value of the 'Votes' column to the total
return total + obj.Votes;
} else {
// If 'Party' is not 'R', return the current total unchanged
return total;
}
}, 0); // Start with a total of 0
var democratVotes = most_recent.reduce((total, obj) => {
// Check if the 'Party' is 'D'
if (obj.Party === 'D') {
// Add the value of the 'Votes' column to the total
return total + obj.Votes;
} else {
// If 'Party' is not 'D', return the current total unchanged
return total;
}
}, 0);
// Update the title with the new year
title.text(select_year2 + ' Election Results');
// Update paths with new data
paths.data(topojson.feature(shapefile, shapefile.objects.states).features)
.transition()
.duration(600)
.attr("fill", d => {
return color2(most_recent_obj.get(d.properties.name));
});
// Update labels with new data
labels.data(topojson.feature(shapefile, shapefile.objects.states).features)
.transition()
.duration(600)
.text(d => {
const stateData = most_recent.find(state => state.State === d.properties.name);
return stateData ? stateData.Votes : ""; // Display electoral votes or empty string if not found
});
// Update circles with new data
circles.data(most_recent)
.transition()
.duration(600)
.style("fill", d => color(d.margin))
.attr("cx", d => {
// Calculate the x-coordinate based on the inc_past value and the width of the line
return (width * 0.15) + (((max_var + d[select_var2]) / (max_var * 2)) * (width * 0.7));
});
bubble_bar.selectAll("text")
.text((d, i) => i === 0 ? (-1 * Math.round(max_var * 10)/10 + "%") :
(i === 1 ? (Math.round(max_var * 10)/10 + "%") :
(i === 2 ? ("0.0%") :
(i === 3 ? ("US Average") :
(i === 4 ? (select_var2 === "inc_past_2" ? ("Change in Real Income Past Two Years") :
(select_var2 === "inc_past_1" ? ("Change in Real Income Over Past Year"): ("Change in Real Income Past Four Years"))) :
(i === 5 ? ("Colored By Margin of Victory") : ""))))))
.attr("x", (d, i) => i === 0 ? (width * .15 + 2) :
(i === 1 ? (width * .85 + 2) :
(i === 2 ? (width/2 + 4.3) :
(i === 3 ? ((width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)) + 2) :
(i === 4 ? (width/2) : width/2)))))
bubble_bar.selectAll("line") // Line for US Avg
.attr("x1", (d, i) => i === 0 ? (width * .15) :
(i === 1 ? (width * .15) :
(i === 2 ? (width * .85) :
(i === 3 ? (width/2) : ((width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)))))))
.attr("x2", (d, i) => i === 0 ? (width * .85) :
(i === 1 ? (width * .15) :
(i === 2 ? (width * .85) :
(i === 3 ? (width/2) : ((width * 0.15) + (((max_var + avg_inc_past) / (max_var * 2)) * (width * 0.7)))))));
// Update electoral votes bars with new data
legend2.selectAll("rect")
.transition()
.duration(600)
.attr("height", (d, i) => i % 2 === 0 ? (republicanVotes/2) : (democratVotes/2))
.attr("y", (d, i) => i % 2 === 0 ? 260 : (260 + republicanVotes/2))
legend2.selectAll("text")
.transition()
.duration(600)
.text((d, i) => i === 0 ? ("Electoral Votes") :
(i === 1 ? (republicanVotes) :
(i === 2 ? (rep_can) :
(i === 3 ? (democratVotes) : dem_can))))
.attr("y", (d, i) => i === 0 ? 250 :
(i === 1 ? 260 + republicanVotes/4 :
(i === 2 ? 260 + republicanVotes/4 :
(i === 3 ? 260 + republicanVotes/2 + democratVotes/4 : 260 + republicanVotes/2 + democratVotes/4))));
// Update background color
map.style("background-color", republicanVotes > democratVotes ? "rosybrown" : "lightblue");
};
btns.append("rect")
.attr("x", width - 120)
.attr("y", 740)
.attr("width", 100)
.attr("height", 40)
.style("fill", "lightgrey")
.style("stroke", "black")
.on("click", updateMap);
btns.append("text")
.attr("x", width - 72)
.attr("y", 764)
.text("Next Election")
.style("font-size", "14px")
.style("fill", "black")
.on("click", updateMap);
btns.append("rect")
.attr("x", 20)
.attr("y", 740)
.attr("width", 100)
.attr("height", 40)
.style("fill", "lightgrey")
.style("stroke", "black")
.on("click", updateMap2);
btns.append("text")
.attr("x", 68)
.attr("y", 764)
.text("Last Election")
.style("font-size", "14px")
.style("fill", "black")
.on("click", updateMap2);
function updateVar4() {
select_var = "inc_past_4"
updateMap3(select_var);
};
function updateVar2() {
select_var = "inc_past_2"
updateMap3(select_var);
};
function updateVar1() {
select_var = "inc_past_1"
updateMap3(select_var);
};
btns.append("rect") // Pick change over 4 years
.attr("x", width - 94)
.attr("y", 575)
.attr("width", 82)
.attr("height", 25)
.style("fill", "lightgrey")
.style("stroke", "black")
.on("click", updateVar4);
btns.append("rect") // Pick change over 2 years
.attr("x", width - 94)
.attr("y", 610)
.attr("width", 82)
.attr("height", 25)
.style("fill", "lightgrey")
.style("stroke", "black")
.on("click", updateVar2);
btns.append("rect") // Pick change over 1 year
.attr("x", width - 94)
.attr("y", 645)
.attr("width", 82)
.attr("height", 25)
.style("fill", "lightgrey")
.style("stroke", "black")
.on("click", updateVar1);
btns.append("text") // Text change over 4 years
.attr("x", width - 54)
.attr("y", 590)
.style("font-size", "8px")
.text("Change over 4 years")
.on("click", updateVar4);
btns.append("text") // Text change over 2 years
.attr("x", width - 54)
.attr("y", 625)
.style("font-size", "8px")
.text("Change over 2 years")
.on("click", updateVar2);
btns.append("text") // Text change over 1 year
.attr("x", width - 54)
.attr("y", 660)
.style("font-size", "8px")
.text("Change over 1 year")
.on("click", updateVar1);
// Return svg
return map.node()
}