我们如何将颜色从顺序比例加载到 D3 v5 中的地图中?

How can we load color from a sequential scale into a map in D3 v5?

我正在 D3 v5 中构建等值线,但是当我尝试加载与红色顺序比例对应的状态颜色时,没有任何反应,所有状态都保持灰色,如下图所示:

这是我用来制作地图的代码:

const second_height = 850;

const projection = d3.geoMercator()
                     .center([ 132, -28 ])
                     .translate([ second_width/2, second_height/2 ])
                     .scale(1000);

const path = d3.geoPath().projection(projection);

//Color scale that I am trying to put in the map            
const second_color  = d3.scaleQuantize().range(['#ffffcc','#ffeda0','#fed976','#feb24c','#fd8d3c','#fc4e2a','#e31a1c','#b10026']);


const second_svg = d3.select("#chart2")
    .append("svg")
    .attr("width", second_width)
    .attr("height", second_height);

//Load in GeoJSON data
d3.json('https://gist.githubusercontent.com/GerardoFurtado/02aa65e5522104cb692e/raw/8108fbd4103a827e67444381ff594f7df8450411/aust.json')
    .then(json => onGeoJsonLoaded(json))
    .catch(err => console.log('ERROR: ', err));
  
const onGeoJsonLoaded = json => {
//Bind data and create one path per GeoJSON feature
const states = second_svg.selectAll('g.state')
                         .data(json.features)
                         .enter()
                         .append('g')
                         .classed('state', true);


d3.csv('data/Waste_Per_State_Per_Capita(1).csv').then(function(data) {


//The domain of the color scale
  second_color.domain([
        d3.min(data, function(d) { return d.Total; }), 
        d3.max(data, function(d) { return d.Total; })
    ]);

        for (var i = 0; i < data.length; i++) {
    
            
            var data_States = data[i].States;
           
            
            var dataValue = parseFloat(data[i].Total);

            for (var j = 0; j < json.features.length; j++) {
            
                var json_States = json.features[j].properties.STATE_NAME;
    
                if (data_States == json_States) {
            
                    json.features[j].properties.value = dataValue;
                    
                    //Stop looking through the JSON
                    break;
                    
                }
            }       
        }
      })

      


    states.append('path')
        .attr("d", path)
        .attr("stroke", 'white')
        .attr("fill", function(d) {

            const value = d.properties.value;
            
            console.log(d.properties);
                     
            if (value) {

              return second_color(value);
            } else {

              return "#ccc";
            }
          });

      states.append("text")
            .attr("fill", "darkslategray")
            .attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
            .attr("text-anchor", "middle")
            .attr("dy", 15)
            .text(function(d) {
                 return d.properties.STATE_NAME;
            });


d3.json('data/Waste_Per_State_Per_Capita.json')
    .then(dataJson => onDataJsonLoaded(dataJson))
    .catch(err => console.log('ERR: ', err));
  
 
}


const tooltipPath = (width, height, offset, radius) => {
    const left = -width / 2;
    const right = width / 2;
    const top = -offset - height;
    const bottom = -offset;

    // Creating a polygon for containing data.
    return `M 0,0 
      L ${-offset},${bottom} 
      H ${left + radius}
      Q ${left},${bottom} ${left},${bottom - radius}  
      V ${top + radius}   
      Q ${left},${top} ${left + radius},${top}
      H ${right - radius}
      Q ${right},${top} ${right},${top + radius}
      V ${bottom - radius}
      Q ${right},${bottom} ${right - radius},${bottom}
      H ${offset} 
      L 0,0 z`;
}

const onDataJsonLoaded = json => {
  
  const rows = Object.keys(json[0]).filter(n => n !== 'States');
  
  const second_tooltip = second_svg.append('g')
                                   .classed('tooltip', true)
                                   .style('visibility', 'hidden');

  second_tooltip.append('path')
                .attr('d', tooltipPath(200, 80, 5, 5))
  rows.forEach((row, index) => {

    second_tooltip.append('text')
                  .text(`${row} :`)
                  .attr('x', -70)
                  .attr('y', -68 + index * 18);
    second_tooltip.append('text')
                  .classed(row.replace(' ', '_'), true)
                  .attr('x', 30)
                  .attr('y', -68 + index * 18);
     second_tooltip.append('text')
                  .text(`(kg/year)`)
                  .attr('x', 50)
                  .attr('y', -68 + index * 18);
    });

    
  second_svg.selectAll('g.state')
    .on('mouseenter', d => {
      const stateData = json.find(s => s.States == d.properties.STATE_NAME);
      rows.forEach(row => second_tooltip.select(`.${row.replace(' ', '_')}`).text(stateData[row]));
      second_tooltip.attr('transform', `translate(${path.centroid(d)})`);
      second_tooltip.style('visibility', 'visible');
    })
    .on('mouseleave', () => tooltip.style('visibility', 'hidden'));
};

至于我用来加载总值以创建色标的CSV文件,你可以在这里看到:

States,Energy Recovery,Disposal,Recycling,Total
South Australia,36,75,7,118
ACT,53,70,0,123
New South Wales,28,80,48,156
Victoria,51,108,14,173
Tasmania,47,138,0,185
Queensland,50,143,10,203
Northern Territory,34,203,0,237
Western Australia,29,163,29,221

你能告诉我为什么颜色无法加载到等值线中吗?我该如何解决? 谢谢!

  1. 获取取值范围:
const valueRange = json.reduce((r, s) => r ? 
  [Math.min(r[0], s.Total), Math.max([1], s.Total)] :
  [s.Total, s.Total], null);
  1. 构建色标:
const color = d3.scaleLinear()
  .domain(valueRange)
  .range(["#FF0000", "#800000"]);
  1. 为每个州的路径设置颜色:
svg.selectAll('g.state')
  .select('path')
  .style('fill', d => {
    const stateData = json.find(s => s.State === d.properties.STATE_NAME);
    return stateData ? color(stateData.Total) : 'white';
  })

const w = 850;
const h = 700;

//Define map projection // geoEqualEarth
const projection = d3.geoMercator()
    .center([ 132, -28 ])
    .translate([ w/2, h/2 ])
    .scale(1000);


            //Define path generator
const path = d3.geoPath()
    .projection(projection);

    //Create SVG
const svg = d3.select("svg")
    .attr('width', w)
  .attr('height', h)

//Load in GeoJSON data
d3.json('https://gist.githubusercontent.com/GerardoFurtado/02aa65e5522104cb692e/raw/8108fbd4103a827e67444381ff594f7df8450411/aust.json')
    .then(json => onGeoJsonLoaded(json))
  .catch(err => console.log('GEO ERROR: ', err));
  
const onGeoJsonLoaded = json => {
//Bind data and create one path per GeoJSON feature
const states = svg.selectAll('g.state')
      .data(json.features)
        .enter()
        .append('g')
    .classed('state', true);
    
 states.append('path')
        .attr("d", path)
        .attr("stroke", 'white');
        //.attr("fill", (d, i) => color[i]);
                  
        //States
  states.append("text")
            .attr("fill", "white")
            .attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
            .attr("text-anchor", "middle")
        .attr("dy", 15)
            .text(function(d) {
                        return d.properties.STATE_NAME;
            });

                //Append the name
   d3.json('https://api.jsonbin.io/b/60af2dc3d0f4985540524d62')
   .then(dataJson => onDataJsonLoaded(dataJson))
   .catch(err => console.log('DATA JSON ERR: ', err));
}

const   tooltipPath = (width, height, offset, radius) => {
    const left = -width / 2
    const right = width / 2
    const top = -offset - height
    const bottom = -offset
    return `M 0,0 
      L ${-offset},${bottom} 
      H ${left + radius}
      Q ${left},${bottom} ${left},${bottom - radius}  
      V ${top + radius}   
      Q ${left},${top} ${left + radius},${top}
      H ${right - radius}
      Q ${right},${top} ${right},${top + radius}
      V ${bottom - radius}
      Q ${right},${bottom} ${right - radius},${bottom}
      H ${offset} 
      L 0,0 z`
}

const onDataJsonLoaded = json => {
  const valueRange = json.reduce((r, s) => r ? 
    [Math.min(r[0], s.Total), Math.max([1], s.Total)] :
    [s.Total, s.Total], null);
  
  const color = d3.scaleLinear()
    .domain(valueRange)
    .range(["#FF0000", "#800000"]);
    
  const states = svg.selectAll('g.state');
    
  states.select('path')
    .style('fill', d => {
        const stateData = json.find(s => s.State === d.properties.STATE_NAME);
      return stateData ? color(stateData.Total) : 'white';
    })

const rows = Object.keys(json[0]).filter(n => n !== 'State');
  
  const tooltip = svg.append('g')
    .classed('tooltip', true)
    .style('visibility', 'hidden');
  tooltip.append('path')
    .attr('d', tooltipPath(150, 80, 5, 5))
  rows.forEach((row, index) => {
    tooltip.append('text')
        .text(`${row} :`)
      .attr('x', -70)
      .attr('y', -68 + index * 18);
    tooltip.append('text')
      .classed(row.replace(' ', '_'), true)
      .attr('x', 40)
      .attr('y', -68 + index * 18)
    }
  );
    
  states
    .on('mouseenter', d => {
      const stateData = json.find(s => s.State === d.properties.STATE_NAME);
      rows.forEach(row => tooltip.select(`.${row.replace(' ', '_')}`).text(stateData[row]));
      tooltip.attr('transform', `translate(${path.centroid(d)})`);
      tooltip.style('visibility', 'visible');
    })
    .on('mouseleave', () => tooltip.style('visibility', 'hidden'));
};
.tooltip > path {
  fill: white;
  stroke: black;
}

.tooltip > text {
  font-family: "Ubuntu";
  font-size: 12px;
  fill: black;
  stroke: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg/>