Color scale examples

I’m not going to explain everything, but you can look at the code and the results and see for yourself. The data is from the shape maps tutorial, where we map the area of each county in the USA.

If I were a True Mapper I’d be using colors from Color Brewer, but because it’s easier to understand with words I’m going to stick with scales like .range(['beige', 'red']) instead of .range(['#fff7ec', '#7f0000']).

Map A: Zero to high - d3.scale.linear()

The simplest choice.

var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA'] });
var color_scale = d3.scale.linear().domain([0, max_area]).range(['beige', 'red']);

Open example in new window

<div id="map"></div>
<script>
d3.json("/tutorials/assets/data/gz_2010_us_050_00_5m.json", function(error, data) {
  var counties = data['features'];

  var height = 500,  width = 600;
  var svg = d3.select("#map").append("svg").attr('height', height).attr('width', width);
  var map = svg.append("g");

  var projection = d3.geo.albersUsa().scale(800).translate([width / 2, height / 2]);

  var path = d3.geo.path().projection(projection);

  var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA'] });
  var color_scale = d3.scale.linear().domain([0, max_area]).range(['beige', 'red']);

  map.selectAll("path").data(counties).enter().append("path").attr('d', path)
      .style('fill', function(d) {
        return color_scale(d['properties']['CENSUSAREA']);
      });
});
</script>

Open example in new window

Map B: Zero to median to high - d3.scale.linear()

The median is the most popular version of the average. When you put a ‘middle’ color in, you’re really starting to make narrative decision.

var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA'] });
var median_area = d3.median( counties, function(d) { return d['properties']['CENSUSAREA'] });
var color_scale = d3.scale.linear().domain([0, median_area, max_area]).range(['blue', 'beige', 'red']);

Open example in new window

<div id="map"></div>
<script>
d3.json("/tutorials/assets/data/gz_2010_us_050_00_5m.json", function(error, data) {
  var counties = data['features'];

  var height = 500,  width = 600;
  var svg = d3.select("#map").append("svg").attr('height', height).attr('width', width);
  var map = svg.append("g");

  var projection = d3.geo.albersUsa().scale(800).translate([width / 2, height / 2]);

  var path = d3.geo.path().projection(projection);

  var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA'] });
  var median_area = d3.median( counties, function(d) { return d['properties']['CENSUSAREA'] });
  var color_scale = d3.scale.linear().domain([0, median_area, max_area]).range(['blue', 'beige', 'red']);

  console.log("Median is", median_area);
  
  map.selectAll("path").data(counties).enter().append("path").attr('d', path)
      .style('fill', function(d) {
        return color_scale(d['properties']['CENSUSAREA']);
      });
});
</script>

Open example in new window

Map C: Zero to mean to high - d3.scale.linear()

The mean is, of course, different from the median. But how does it change on a map? Depends on your data!

var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA'] });
var mean_area = d3.mean( counties, function(d) { return d['properties']['CENSUSAREA'] });
var color_scale = d3.scale.linear().domain([0, mean_area, max_area]).range(['blue', 'beige', 'red']);

Open example in new window

<div id="map"></div>
<script>
d3.json("/tutorials/assets/data/gz_2010_us_050_00_5m.json", function(error, data) {
  var counties = data['features'];

  var height = 500,  width = 600;
  var svg = d3.select("#map").append("svg").attr('height', height).attr('width', width);
  var map = svg.append("g");

  var projection = d3.geo.albersUsa().scale(800).translate([width / 2, height / 2]);

  var path = d3.geo.path().projection(projection);

  var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA'] });
  var mean_area = d3.mean( counties, function(d) { return d['properties']['CENSUSAREA'] });
  var color_scale = d3.scale.linear().domain([0, mean_area, max_area]).range(['blue', 'beige', 'red']);
  
  console.log("Mean is", mean_area);
  
  map.selectAll("path").data(counties).enter().append("path").attr('d', path)
      .style('fill', function(d) {
        return color_scale(d['properties']['CENSUSAREA']);
      });
});
</script>

Open example in new window

Map D1: Equal-number-of-elements buckets (3 buckets) - d3.scale.quantile()

No fading, just put them into 3 buckets each with the same number of counties. It doesn’t look equal, though, since 1000 small-sized counties take up much much less room than 1000 large-sized counties.

var all_areas = counties.map( function(d) { return d['properties']['CENSUSAREA']; } );
var color_scale = d3.scale.quantile().domain(all_areas).range(['blue', 'beige', 'red']);

Open example in new window

<div id="map"></div>
<script>
d3.json("/tutorials/assets/data/gz_2010_us_050_00_5m.json", function(error, data) {
  var counties = data['features'];

  var height = 500,  width = 600;
  var svg = d3.select("#map").append("svg").attr('height', height).attr('width', width);
  var map = svg.append("g");

  var projection = d3.geo.albersUsa().scale(800).translate([width / 2, height / 2]);

  var path = d3.geo.path().projection(projection);

  var all_areas = counties.map( function(d) { return d['properties']['CENSUSAREA']; } );
  var color_scale = d3.scale.quantile().domain(all_areas).range(['blue', 'beige', 'red']);
  
  console.log("Breakpoints are", color_scale.quantiles());
  
  map.selectAll("path").data(counties).enter().append("path").attr('d', path)
      .style('fill', function(d) {
        return color_scale(d['properties']['CENSUSAREA']);
      });
});
</script>

Open example in new window

Map D1: Equal-number-of-elements buckets (7 buckets) - d3.scale.quantile()

Again, it doesn’t look equal, though, since 500 small-sized counties take up much much less room than 500 large-sized counties.

var all_areas = counties.map( function(d) { return d['properties']['CENSUSAREA']; } );
var color_scale = d3.scale.quantile().domain(all_areas).range(['violet', 'indigo', 'blue', 'green', 'yellow', 'orange', 'red']);

Open example in new window

<div id="map"></div>
<script>
d3.json("/tutorials/assets/data/gz_2010_us_050_00_5m.json", function(error, data) {
  var counties = data['features'];

  var height = 500,  width = 600;
  var svg = d3.select("#map").append("svg").attr('height', height).attr('width', width);
  var map = svg.append("g");

  var projection = d3.geo.albersUsa().scale(800).translate([width / 2, height / 2]);

  var path = d3.geo.path().projection(projection);

  var all_areas = counties.map( function(d) { return d['properties']['CENSUSAREA']; } );
  var color_scale = d3.scale.quantile().domain(all_areas).range(['violet', 'indigo', 'blue', 'green', 'yellow', 'orange', 'red']);
  
  console.log("Breakpoints are", color_scale.quantiles());
  
  map.selectAll("path").data(counties).enter().append("path").attr('d', path)
      .style('fill', function(d) {
        return color_scale(d['properties']['CENSUSAREA']);
      });
});
</script>

Open example in new window

Map E1: Equal-numeric-spread buckets (3 buckets) - d3.scale.quantize()

No fading, just put them into 3 buckets that each cover the same spread of area. If we had 1-300, it would do 1-100, 101-200 and 201-300.

var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA']; } );
var color_scale = d3.scale.quantize().domain([0, max_area]).range(['blue', 'beige', 'red']);

Open example in new window

<div id="map"></div>
<script>
d3.json("/tutorials/assets/data/gz_2010_us_050_00_5m.json", function(error, data) {
  var counties = data['features'];

  var height = 500,  width = 600;
  var svg = d3.select("#map").append("svg").attr('height', height).attr('width', width);
  var map = svg.append("g");

  var projection = d3.geo.albersUsa().scale(800).translate([width / 2, height / 2]);

  var path = d3.geo.path().projection(projection);

  var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA']; } );
  var color_scale = d3.scale.quantize().domain([0, max_area]).range(['blue', 'beige', 'red']);
    
  map.selectAll("path").data(counties).enter().append("path").attr('d', path)
      .style('fill', function(d) {
        return color_scale(d['properties']['CENSUSAREA']);
      });
});
</script>

Open example in new window

Map E2: Equal-numeric-spread buckets (7 buckets) - d3.scale.quantize()

No fading, just pop them into buckets. If we had three buckets and values between 1 and 300, it would do 1-100, 101-200 and 201-300.

Now we’re going to use red, orange, yellow, green, blue, indigo and violet (a terrible color scheme, which I’ll say at least twice on this page).

var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA']; } );
var color_scale = d3.scale.quantize().domain([0, max_area]).range(['violet', 'indigo', 'blue', 'green', 'yellow', 'orange', 'red']);

Open example in new window

<div id="map"></div>
<script>
d3.json("/tutorials/assets/data/gz_2010_us_050_00_5m.json", function(error, data) {
  var counties = data['features'];

  var height = 500,  width = 600;
  var svg = d3.select("#map").append("svg").attr('height', height).attr('width', width);
  var map = svg.append("g");

  var projection = d3.geo.albersUsa().scale(800).translate([width / 2, height / 2]);

  var path = d3.geo.path().projection(projection);

  var max_area = d3.max( counties, function(d) { return d['properties']['CENSUSAREA']; } );
  var color_scale = d3.scale.quantize().domain([0, max_area]).range(['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']);
    
  map.selectAll("path").data(counties).enter().append("path").attr('d', path)
      .style('fill', function(d) {
        return color_scale(d['properties']['CENSUSAREA']);
      });
});
</script>

Open example in new window

Map F: User-defined buckets - d3.scale.threshold()

Sometimes you group elements into specific buckets for one reason or another (many of them being bad, terrible, evil reasons). This is also a horrible color scheme, but d3.scale.threshold() lets you do things like

  • Below 100 is violet
  • 100-499 is indigo
  • 500-1000 is blue
  • …etc
var color_scale = d3.scale.threshold().domain([100, 500, 1000, 2500, 5000, 10000]).range(['violet', 'indigo', 'blue', 'green', 'yellow', 'orange', 'red']);

Open example in new window

<div id="map"></div>
<script>
d3.json("/tutorials/assets/data/gz_2010_us_050_00_5m.json", function(error, data) {
  var counties = data['features'];

  var height = 500,  width = 600;
  var svg = d3.select("#map").append("svg").attr('height', height).attr('width', width);
  var map = svg.append("g");

  var projection = d3.geo.albersUsa().scale(800).translate([width / 2, height / 2]);

  var path = d3.geo.path().projection(projection);

  var all_areas = counties.map( function(d) { return d['properties']['CENSUSAREA']; } );
  var color_scale = d3.scale.threshold().domain([100, 500, 1000, 2500, 5000, 10000]).range(['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']);
  
  map.selectAll("path").data(counties).enter().append("path").attr('d', path)
      .style('fill', function(d) {
        return color_scale(d['properties']['CENSUSAREA']);
      });
});
</script>

Open example in new window

And a note

Make sure you don’t use these colors! Check out http://colorbrewer2.org/. Also, Jenks breaks are incredible.

Want to hear when I release new things?
My infrequent and sporadic newsletter can help with that.