Stophface Stophface - 3 months ago 44
Javascript Question

Dynamically create legend in d3

I am using

d3.scale.quantize()
and the
colorBrewer
to get a colorscale.

....
var extent = d3.extent(collection.features, function(d) {
return d.properties.mean;
});

var colorScale = d3.scale.quantize()
.domain(extent)
.range(colorbrewer.RdYlBu[8]);
...


That gives me
8
different colors, corresponding to certain ranges of given values.
I then use
coloScale
to fill the
svg


....
.attr("fill-opacity", 0.1)
.attr("stroke", "grey")
.style("fill", function(d) {
return colorScale(d.properties.mean);
});
...


How do I know which range of values corresponds to the color
"#fdae61"
or
"#fee090"
? How would I access these values?
I want to make a legend that changes dynamically when I change the number of input colors, i.e. from
8
to
3
as well as the color used in the colorscale, i.e. from
RdYlBu
to
YlGn
.

I think I am very close.
I have this

var scale = d3.scale.ordinal()
.domain()
.range(colorScale.range());


I only need to fill the
domain
with the corresponding values which I then can use to easily create my legend...
However, I do not know how to extract them dynamically.
Something like
colorScale.invertExtent("#fdae61")
is to static since I would need to change the colors etc. whenever I change my colorscale.

Answer

If you want to create an automatic legend, my suggestion is that you create a dataset based on your colorScale domain and range, and bind this dataset to your legend. This way, the dataset changes whenever you change the domain or the range of you scale.

For instance, if you have this scale, with the domain going from 0 to 500:

var colorScale = d3.scale.quantize()
    .domain([0, 500])
    .range(["#d73027", "#f46d43", "#fdae61", "#fee090", "#e0f3f8",
           "#abd9e9", "#74add1", "#4575b4"]);//this is colorBrewer.RdYlBu[8]

You could create an array that has all the ranges of values. This will be our dataset, named colorRange:

var colorRange = [];    
for(var i = 0; i < colorScale.range().length; i++){
    colorRange.push(colorScale.invertExtent(colorScale.range()[i])[0]);
};

Based on the previous code, if we console.log this array, we get:

console.log(colorRange);// returns [0, 62.5, 125, 187.5, 250, 312.5, 375, 437.5]

Which contains the corresponding domain values for your 8 colors. If we for instance remove two colors from colorScale range, we have now:

console.log(colorRange);// returns [0, 83.333, 166.666, 250, 333.333, 416.666]

Once you have this colorRange array, you not only have the domain values for your legend, but you can easily set the colors as well, using:

colorScale(colorRange[i])

Where i goes from the first value to the last.

PS: If you were using a quantile scale instead, we could drop the cumbersome for loop and simply use [0].concat(colorScale.quantiles()) to create our array.