The legend code that you're using would work perfectly well if you had an ordinal scale, where the domain is made up of discrete values that correlate to the range of colours on a one-to-one basis. But you're using a quantile scale, and so need a different approach.
For a d3 quantile scale, the domain is the list of all possible input values, and the range is a list of discrete output values. The domain list is sorted in ascending order and then divided into equal-sized groups, which are assigned to each output value from the range. The number of groups is determined by the number of output values.
With that in mind, in order to get one legend entry for each colour, you're going to need to use your colour scale's range, not the domain, as the data for your legend. Then you can use the quantileScale.invertExtent()
method to find the minimum and maximum input values that are getting drawn with that colour.
Sample code, making each legend entry a <g>
containing both the coloured rectangle and a text label showing the corresponding values.
var legend = svg.selectAll('g.legendEntry')
.data(color.range().reverse())
.enter()
.append('g').attr('class', 'legendEntry');
legend
.append('rect')
.attr("x", width - 780)
.attr("y", function(d, i) {
return i * 20;
})
.attr("width", 10)
.attr("height", 10)
.style("stroke", "black")
.style("stroke-width", 1)
.style("fill", function(d){return d;});
//the data objects are the fill colors
legend
.append('text')
.attr("x", width - 765) //leave 5 pixel space after the <rect>
.attr("y", function(d, i) {
return i * 20;
})
.attr("dy", "0.8em") //place text one line *below* the x,y point
.text(function(d,i) {
var extent = color.invertExtent(d);
//extent will be a two-element array, format it however you want:
var format = d3.format("0.2f");
return format(+extent[0]) + " - " + format(+extent[1]);
});