Terry
Terry

Reputation: 66123

d3.js Heatmap legend

I have managed to generate a heatmap based on some sample gene expression data, and I am assigning a fill to each cell depending on the level of gene expression. I am mapping the fill against a colorbrewer scale, i.e.:

var z           = d3.scale.log().base(2).domain([min, max]).range([0,1]),
    fills       = colorbrewer.RdBu[6];
    heatmapFill = d3.scale.linear().domain(d3.range(0, 1, 1.0 / (fills.length - 1))).range(fills);

…which works totally fine for me. However, I am facing a problem with assigning these fills to the accompanying legend, which have a #NaNNaNNaN fill.

// Get data
svg.selectAll('.tile')
    .data(data.melted)
    .enter()
    .append('rect')
    .attr({
        'x': function(d) { return x(d.condition); },
        'y': function(d) { return y(d.geneID); },
        'fill': function(d) { return heatmapFill(z(d.value)); },
        'width': x.rangeBand(),
        'height': y.rangeBand()
    });
// Add a legend for the color values.
var legend = svg.selectAll(".legend")
    .data(z.ticks())
    .enter()
    .append("g")
        .attr({
            'class': 'legend',
            'transform': function(d, i) {
                return "translate(" + (i * 40) + "," + (height + margin.bottom - 40) + ")";
            }
        });

legend.append("rect")
.attr({
    'width': 40,
    'height': 20,
    'fill': z  // Problem likely to be arising from here
});

Based on my understanding, it seems that I am unable to fetch the value of the fill, based on the value of z.

Here's the complete fiddle: http://jsfiddle.net/teddyrised/jLxbawhz/2/


If I choose not to map my expression level against a colorbrewer scale but simply a start and an end color, the script works fine (http://jsfiddle.net/teddyrised/jLxbawhz/3/):

// Scale
var z = d3.scale.log().base(2).domain([min, max]).range(['white','steelblue']);

// Get data
svg.selectAll('.tile')
    .data(data.melted)
    .enter()
    .append('rect')
    .attr({
        'x': function(d) { return x(d.condition); },
        'y': function(d) { return y(d.geneID); },
        'fill': function(d) { return z(d.value); },
        'width': x.rangeBand(),
        'height': y.rangeBand()
    });
// Add a legend for the color values.
var legend = svg.selectAll(".legend")
    .data(z.ticks())
    .enter()
    .append("g")
        .attr({
            'class': 'legend',
            'transform': function(d, i) {
                return "translate(" + (i * 40) + "," + (height + margin.bottom - 40) + ")";
            }
        });

legend.append("rect")
.attr({
    'width': 40,
    'height': 20,
    'fill': z
});

Upvotes: 1

Views: 4405

Answers (1)

Mark
Mark

Reputation: 108537

Not sure I'm completely following your code, but shouldn't your legend fill take the same function as your heatmap fill?

legend.append("rect")
  .attr({
    'width': 40,
    'height': 20,
    'fill': function(d) { 
        return heatmapFill(z(d)); 
    }
});

Updated fiddle.

Upvotes: 1

Related Questions