D3热力图异常:所有区域仅显示单一颜色问题排查
I'm trying to create a D3 choropleth map using ABS CSV data and GeoJSON geographic data, but during testing all regions are assigned a single color—there's no variation in shade based on value. I believe I've bound the values to each region's properties in the GeoJSON, but the color scale isn't working as expected.

My D3 Code
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>D3: Setting path fills dynamically to generate a choropleth</title> <script type="text/javascript" src="d3.js"></script> <style type="text/css"> /* No style rules here yet */ </style> </head> <body> <script type="text/javascript"> //Width and height var w = 1000; var h = 600; var path = d3.geoPath() .projection(d3.geoMercator() .center([151,-33.5]) .scale(17000) .translate([w/2,h/2])); //Define quantize scale to sort data values into buckets of color var color = d3.scaleQuantize() .range(["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"]); //Colors derived from ColorBrewer, by Cynthia Brewer, and included in //https://github.com/d3/d3-scale-chromatic //Create SVG element var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); //Load in agriculture data d3.csv("ManagerArea.csv", function(data) { //Set input domain for color scale color.domain([ d3.min(data, function(d) { return d.value; }), d3.max(data, function(d) { return d.value; }) ]); //Load in GeoJSON data d3.json("australia_adm2.json", function(json) { //Merge the ag. data and GeoJSON //Loop through once for each ag. data value for (var i = 0; i < data.length; i++) { //Grab state name var dataState = data[i].state; //Grab data value, and convert from string to float var dataValue = parseFloat(data[i].value); //Find the corresponding state inside the GeoJSON for (var j = 0; j < json.features.length; j++) { var jsonState = json.features[j].properties.sa4_name11; if (dataState == jsonState) { //Copy the data value into the JSON json.features[j].properties.value = dataValue; //Stop looking through the JSON break; } } } //Bind data and create one path per GeoJSON feature svg.selectAll("path") .data(json.features) .enter() .append("path") .attr("d", path) .style("fill", function(d) { //Get data value var value = d.properties.value; if (value) { //If value exists… return color(value); } else { //If value is undefined… return "#ccc"; } }); }); }); </script> </body> </html>
GeoJSON Data Structure Example
{"type":"FeatureCollection", "features": [ {"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":...},"properties":{"cartodb_id":1,"sa4_name11":"Sydney - Baulkham Hills and Hawkesbury","right_dpop":38742,"right_opop":69540,"right_xcoord":150.8288053,"right_ycoord":-33.36860558}},...]
CSV Data
state,value Capital Region,15558 Central Coast,15642 Central West,13074 Coffs Harbour - Grafton,6274 Far West and Orana,7968 Hunter Valley exc Newcastle,11717 Illawarra,13278 Mid North Coast,8758 Murray,8298 New England and North West,12884 Newcastle and Lake Macquarie,15980 Richmond - Tweed,12013 Riverina,11055 Southern Highlands and Shoalhaven,7191 Sydney - Baulkham Hills and Hawkesbury,19904 Sydney - Blacktown,14710 Sydney - City and Inner South,27758 Sydney - Eastern Suburbs,24778 Sydney - Inner South West,27081 Sydney - Inner West,24055 Sydney - North Sydney and Hornsby,39796 Sydney - Northern Beaches,24080 Sydney - Outer South West,12403 Sydney - Outer West and Blue Mountains,15767 Sydney - Parramatta,20323 Sydney - Ryde,13468 Sydney - South West,15090 Sydney - Sutherland,16901
Root Cause & Solution
The most likely issue is that you're setting the color scale's domain using string values instead of numeric values. When you load the CSV, the value field is parsed as a string by default. When you call d3.min(data, d => d.value) and d3.max(data, d => d.value), you're comparing strings lexicographically (e.g., "6274" is treated as larger than "39796" because "6" > "3"), which creates an invalid domain for your quantize scale. Since the scale expects numeric inputs, all your actual numeric values end up falling into the first color bucket, resulting in uniform coloring.
Fix 1: Convert values to numbers when setting the domain
Update the color.domain() section to parse d.value as a float:
color.domain([ d3.min(data, function(d) { return parseFloat(d.value); }), d3.max(data, function(d) { return parseFloat(d.value); }) ]);
Fix 2: Parse CSV values upfront (cleaner approach)
Modify your d3.csv call to convert the value to a number when loading the data, so you don't have to parse it repeatedly:
d3.csv("ManagerArea.csv", function(d) { // Convert value to number during CSV parsing return { state: d.state, value: parseFloat(d.value) }; }, function(data) { // Now data.value is numeric, so domain works correctly color.domain([ d3.min(data, function(d) { return d.value; }), d3.max(data, function(d) { return d.value; }) ]); // Rest of your code (loading GeoJSON, merging data, etc.) stays the same });
Additional Checks
- Verify that the
statenames in your CSV exactly match thesa4_name11values in your GeoJSON (check for case sensitivity, extra spaces, or punctuation differences). Mismatches would result in gray regions, but it's still good to confirm. - Ensure you're using a compatible D3 version (your code uses v4/v5 syntax, so make sure your
d3.jsfile is from that range—older versions have different scale APIs).
内容的提问来源于stack exchange,提问作者Jgreensie

