title: How to apply specific colors to D3.js map based on data values?
body: <p>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:</p>
  
  <pre><code>&lt;style&gt;
  .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;
  }
  </code></pre>
  
  <p></p>
  
  <pre><code>&lt;button id="play"&gt;Play&lt;/button&gt;
  &lt;span id="clock"&gt;Day&lt;/span&gt;
  &lt;h1 style="text-align:center"&gt;14-Day Water Yield By County&lt;/h1&gt;
  
  &lt;div id="svgDiv1" style="text-align:center"&gt;
  &lt;svg width="960" height="600" stroke-linejoin="round" stroke-linecap="round"&gt;
      &lt;defs&gt;
          &lt;filter id="blur"&gt;
              &lt;feGaussianBlur stdDeviation="5"&gt;&lt;/feGaussianBlur&gt;
          &lt;/filter&gt;
      &lt;/defs&gt;
  &lt;/svg&gt;
  </code></pre>
  
  <p></p>
  
  <pre><code>&lt;script&gt;
  
  //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' &amp;&amp; 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 &lt; props.length; i++) {
              p = props[i];
              if (typeof a[p] === "undefined") return -1;
              if (a[p] &lt; b[p]) return -1;
              if (a[p] &gt; 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 &lt;= min &amp;&amp; currentValue != -99 &amp;&amp; currentValue != 'undefined') {
                min = currentValue;
            }
            if (currentValue &gt;= max &amp;&amp; currentValue != -99 &amp;&amp; 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 &lt; 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
  </code></pre>
  
  <p></p>
  
  <p>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.</p>
  
  <p>Appreciate any help.</p>