如何为D3.js制作的Choropleth Map添加年份滑块更新数据
Adding a Year Slider to Your D3.js Choropleth Map
Hey there! Let's get that year slider working so you can toggle between your crime data years smoothly. Here's a step-by-step breakdown of how to modify your existing code:
Step 1: Set Up Slider & Year Mapping
First, we'll create a slider (plus a label to show the active year) and map each year to its corresponding data property name in your CSV:
// Define year options and their matching data keys const yearOptions = [2010, 2011, 2012, 2013, 2014, 2015]; const yearToDataKey = { 2010: 'ten', 2011: 'eleven', 2012: 'twelve', 2013: 'thirteen', 2014: 'fourteen', 2015: 'fifteen' }; // Create slider container in the DOM const sliderWrapper = d3.select("body").append("div") .style("margin", "2rem auto") .style("width", "350px") .style("text-align", "center"); // Add year display label const currentYearLabel = sliderWrapper.append("span") .attr("id", "active-year") .style("font-size", "1.2rem") .style("margin-right", "10px") .text("2010"); // Add the range slider input const yearSlider = sliderWrapper.append("input") .attr("type", "range") .attr("min", 0) .attr("max", yearOptions.length - 1) .attr("value", 0) .style("width", "280px");
Step 2: Create a Reusable Map Update Function
Instead of hardcoding i.ten everywhere, we'll make a function that refreshes the map colors and tooltips based on the selected year:
function refreshMap(selectedDataKey) { // Update color scale to match the selected year's data range const maxCrimes = d3.max(data, d => d[selectedDataKey]); color.domain([1, maxCrimes]); // Update state fill colors svg.selectAll(".states path") .attr("fill", function(d) { let fillColor = "black"; data.forEach(row => { if (row.states === d.properties.name) { fillColor = color(row[selectedDataKey]); } }); return fillColor; }); // Update tooltip content to reflect selected year tip.html(function(d) { let region = ""; let crimeCount = 0; data.forEach(row => { if (row.states === d.properties.name) { region = row.region; crimeCount = row[selectedDataKey]; } }); return `<div>State: ${d.properties.name}</div> <div>Region: ${region}</div> <div>NumCrimes: ${crimeCount}</div>`; }); }
Step 3: Initialize Map & Bind Slider Events
Modify your ready function to start with 2010 data, then add an event listener to the slider to trigger updates when it's adjusted:
function ready([us]) { // Initialize tooltip tip = d3.tip().attr('class', 'd3-tip'); svg.call(tip); // Add state paths (remove hardcoded fill logic — refreshMap handles this) svg.append("g") .attr("class", "states") .selectAll("path") .data(topojson.feature(us, us.objects.states).features) .enter().append("path") .attr("d", path) .on('mouseover', tip.show) .on('mouseout', tip.hide); // Add state borders (keep your existing code here) svg.append("path").attr("class", "state-borders") .datum(topojson.mesh(us, us.objects.states, (a, b) => a !== b)) .attr("d", path); // Initialize map with 2010 data refreshMap(yearToDataKey[2010]); // Slider change event handler yearSlider.on("input", function() { const selectedIndex = +this.value; const selectedYear = yearOptions[selectedIndex]; const dataKey = yearToDataKey[selectedYear]; // Update the year label currentYearLabel.text(selectedYear); // Refresh the map with selected year's data refreshMap(dataKey); }); }
Step 4: Simplify Data Loading
Clean up your CSV loading callback to avoid repetitive code:
var promises = [ d3.json("stateborder.json"), d3.dsv(",", "stateInfo.csv", d => ({ states: d.States, region: d.Region, ten: d['2010'], eleven: d['2011'], twelve: d['2012'], thirteen: d['2013'], fourteen: d['2014'], fifteen: d['2015'] })) ]; // Store CSV data correctly and initialize the map Promise.all(promises).then(([us, csvData]) => { data = csvData; ready([us]); });
Quick Notes:
- The
yearToDataKeyobject makes it easy to link human-readable years to your data's property names, so you don't have to hardcode values everywhere. - Centralizing the map update logic in
refreshMapkeeps your code DRY (Don't Repeat Yourself) and ensures the map and tooltips stay in sync. - The slider uses index values to map to your year array, making it easy to add/remove years later if needed.
内容的提问来源于stack exchange,提问作者Poskaz




