如何根据数据值将特定颜色应用于D3.js映射? [英] How to apply specific colors to D3.js map based on data values?

查看:52
本文介绍了如何根据数据值将特定颜色应用于D3.js映射?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一张美国县地图,该地图应该以动画方式显示14天之内各县的用水量.我需要以红色(小于50毫米),绿色(大于49毫米且小于100毫米)和蓝色(大于100毫米)显示颜色.我从Mike Bostock和Rich Donohue改编了以下代码:

I have a US counties map that's supposed to show in animation the amount of water yield for counties in a 14-day period. I need to show the colors in red (less than 50 mm), green(greater than 49 mm and lower than 100 mm), and blue (greater than 100 mm). I've adapted from Mike Bostock and Rich Donohue the following codes:

<style>
.county {
    fill:steelblue;
    stroke: #fff; /*White*/
    stroke-width: .5px;
}

#play, #clock {
    position: absolute;
    /*top: 15px;*/
}

#play {
    /*left: 15px;*/
    left: 160px;
    top: 140px;
}

#clock {
    left: 220px;
    top: 148px;
}

<button id="play">Play</button>
<span id="clock">Day</span>
<h1 style="text-align:center">14-Day Water Yield By County</h1>

<div id="svgDiv1" style="text-align:center">
<svg width="960" height="600" stroke-linejoin="round" stroke-linecap="round">
    <defs>
        <filter id="blur">
            <feGaussianBlur stdDeviation="5"></feGaussianBlur>
        </filter>
    </defs>
</svg>

<script>

//globals
var width, height, projection, path, group, graticule, svg, defs, attributeArray = [], currentAttribute = 0, playing = false;

function init() {

    setMap();
    animateMap();

}

function setMap() {

    svg = d3.select("svg");

    defs = svg.select("defs");

    path = d3.geoPath();

    d3.json("/topo/us-10m.v1.json", function (error, us) {
        if (error) throw error;

        defs.append("path")
            .attr("id", "nation")
            .attr("d", path(topojson.feature(us, us.objects.counties)));

        svg.append("use")
            .attr("xlink:href", "#nation")
            .attr("fill-opacity", 0.2)
            .attr("filter", "url(#blur)");

        svg.append("use")
            .attr("xlink:href", "#nation")
            .attr("fill", "#fff");

        svg.append("path")
            .attr("fill", "none")
            .attr("stroke", "#777")
            .attr("stroke-width", 0.70)
            .attr("d", path(topojson.mesh(us, us.objects.counties, function (a, b) { return a !== b; })));
    });

    loadData();  // let's load our data next
}

function loadData() {
    queue()   // queue function loads all external data files asynchronously
      .defer(d3.json, "/topo/us-10m.v1.json")  // our geometries
      .defer(d3.csv, "/data/wtryld.csv")  // and associated data in csv file
      .await(processData);   // once all files are loaded, call the processData function passing the loaded objects as arguments
}

function processData(error, us, countyData) {
    // function accepts any errors from the queue function as first argument, then
    // each data object in the order of chained defer() methods above
    if (error) throw error;

    //Get values from geojson
    var conus = topojson.feature(us, us.objects.counties); // store the path in variable for ease

    //Get values from csv file
    for (var i in conus.features) {    // for each geometry object
        for (var j in countyData) {  // for each row in the CSV
            if (conus.features[i].id == countyData[j].id) {  // if they match
                for (var k in countyData[i]) {   // for each column in the a row within the CSV
                    if (k != 'id' && k != 'County') {  // select only number of days as column headings
                        if (attributeArray.indexOf(k) == -1) {
                            attributeArray.push(k);  // add new column headings to our array for later
                        }
                        conus.features[i].properties[k] = Number(countyData[j][k])  // add each CSV column key/value to geometry object
                    }
                }
                break;  // stop looking through the CSV since we made our match
            }
        }
    }
    d3.select('#clock').html(attributeArray[currentAttribute]);  // populate the clock initially with the current day
    drawMap(conus);  // let's mug the map now with our newly populated data object
}

//Sort function; can specify multiple columns to sort: propSort("STATE", "COUNTY");
function propSort(props) {
    if (!props instanceof Array) props = props.split(",");
    return function sort(a, b) {
        var p;
        a = a.properties;
        b = b.properties;
        for (var i = 0; i < props.length; i++) {
            p = props[i];
            if (typeof a[p] === "undefined") return -1;
            if (a[p] < b[p]) return -1;
            if (a[p] > b[p]) return 1;
        }
        return 0;
    };
}

function drawMap(conus) {

    svg.selectAll(".feature")   // select country objects (which don't exist yet)
      .data(conus.features)   // bind data to these non-existent objects
      .enter().append("path") // prepare data to be appended to paths
      .attr("class", "county") // give them a class for styling and access later
      .attr("id", function (d) { return d.properties.id; }, true)  // give each a unique id for access later
      .attr("d", path); // create them using the svg path generator defined above

    var dataRange = getDataRange(); // get the min/max values from the current day's range of data values
    d3.selectAll('.county')  // select all the counties
        .attr('fill-opacity', function (d) {
            return getColor(d.properties[attributeArray[currentAttribute]], dataRange);  // give them an opacity value based on their current value
        });
}

function sequenceMap() {
    var dataRange = getDataRange(); // get the min/max values from the current year's range of data values
    d3.selectAll('.county').transition()  //select all the counties and prepare for a transition to new values
      .duration(300)  // give it a smooth time period for the transition
      .attr('fill-opacity', function (d) {
          return getColor(d.properties[attributeArray[currentAttribute]], dataRange);  // the end color value
      })
}

function getColor(valueIn, valuesIn) {
    // create a linear scale
    var color = d3.scale.linear()
      .domain([valuesIn[0], valuesIn[1]])  // input uses min and max values
      .range([.3, 1]);   // output for opacity between .3 and 1 %

    return color(valueIn);  // return that number to the caller
}

function getDataRange() {
    // function loops through all the data values from the current data attribute
    // and returns the min and max values

    var min = Infinity, max = -Infinity;
    d3.selectAll('.county')
        .each(function (d, i) {
          var currentValue = d.properties[attributeArray[currentAttribute]];
          if (currentValue <= min && currentValue != -99 && currentValue != 'undefined') {
              min = currentValue;
          }
          if (currentValue >= max && currentValue != -99 && currentValue != 'undefined') {
              max = currentValue;
          }
      });
    return [min, max];
}

function animateMap() {

    var timer;  // create timer object
    d3.select('#play')
      .on('click', function () {  // when user clicks the play button
          if (playing == false) {  // if the map is currently playing
              timer = setInterval(function () {   // set a JS interval
                  if (currentAttribute < attributeArray.length - 1) {
                      currentAttribute += 1;  // increment the current attribute counter
                  } else {
                      currentAttribute = 0;  // or reset it to zero
                  }
                  sequenceMap();  // update the representation of the map
                  d3.select('#clock').html(attributeArray[currentAttribute]);  // update the clock
              }, 2000);

              d3.select(this).html('Stop');  // change the button label to stop
              playing = true;   // change the status of the animation
          } else {    // else if is currently playing
              clearInterval(timer);   // stop the animation by clearing the interval
              d3.select(this).html('Play');   // change the button label to play
              playing = false;   // change the status again
          }
      });
}


window.onload = init();  // magic starts here

上面的代码通过使用填充不透明度来应用色度"颜色.仅有深浅不一的深蓝色.但是我需要应用绿色,蓝色和红色.

The above code applies "choropleth" color by using fill-opacity. Only steelblue color in differing shades. But I need to apply green, blue and red colors.

感谢任何帮助.

推荐答案

您可以直接使用自己的颜色输出颜色,而不是使用CSS来设置所有要素的颜色,然后将线性比例中的不透明度值应用于每个要素比例尺(D3比例尺范围接受颜色).然后,只需设置填充即可,而不是设置填充不透明度.

Rather than using css to set the color of all features and then applying an opacity value from your linear scale to each feature, you can output a color directly with your scale (D3 scale ranges accept colors). Then, instead of setting your fill-opacity, just set the fill.

例如:

var color = d3.scale.linear()
  .domain([0, 9])  
  .range(["blue", "green"]); 
  
var svg = d3.select('body')
    .append('svg')
    .attr('width',500)
    .attr('height',200);
    
svg.selectAll('rect')
    .data(d3.range(10))
    .enter()
    .append('rect')
    .attr('x',function(d,i) { return i * 40; })
    .attr('y',30)
    .attr('width',30)
    .attr('height',30)
    .attr('fill',function(d,i) { return color(i); });
      

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

请确保您的CSS仍未指定深蓝色.

Just be sure that your css doesn't still specify a steel blue color.

您还可以使用十六进制颜色代码或指定多个步骤:

You can also use hex color codes or specify multiple steps:

var color = d3.scale.linear()
  .domain([0, 5, 9])  
  .range(["blue", "yellow", "green"]); 
  
var svg = d3.select('body')
    .append('svg')
    .attr('width',500)
    .attr('height',200);
    
svg.selectAll('rect')
    .data(d3.range(10))
    .enter()
    .append('rect')
    .attr('x',function(d,i) { return i * 40; })
    .attr('y',30)
    .attr('width',30)
    .attr('height',30)
    .attr('fill',function(d,i) { return color(i); });

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

但是,如果您想为每个值指定清晰的步骤,则可能需要阈值刻度:

Though, you may want a threshold scale if you want clear steps for each value:

var color = d3.scale.threshold()
  .domain([2, 5, 9])  
  .range(["blue","yellow","green","orange"]); 
  
var svg = d3.select('body')
    .append('svg')
    .attr('width',500)
    .attr('height',200);
    
svg.selectAll('rect')
    .data(d3.range(10))
    .enter()
    .append('rect')
    .attr('x',function(d,i) { return i * 40; })
    .attr('y',30)
    .attr('width',30)
    .attr('height',30)
    .attr('fill',function(d,i) { return color(i); });

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

与阈值范围的域相比,范围中的元素多.想象一个阈值,它会有一个大于的值,一个小于的值.

There is one more element in the range than the domain for the threshold scale. Imagine a single threshold, it would have one value for more than, one value for less than.

这篇关于如何根据数据值将特定颜色应用于D3.js映射?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆