diff --git a/caravel/assets/visualizations/heatmap.css b/caravel/assets/visualizations/heatmap.css index bce1248212303..38d6d6cb2abfe 100644 --- a/caravel/assets/visualizations/heatmap.css +++ b/caravel/assets/visualizations/heatmap.css @@ -1,5 +1,13 @@ +.heatmap .slice_container { + position: relative; + top: 0; + left: 0; + height: 100%; +} + .heatmap .axis text { font: 10px sans-serif; + text-rendering: optimizeLegibility; } .heatmap .axis path, @@ -30,6 +38,7 @@ color: #fff; border-radius: 2px; pointer-events: none; + z-index: 1000; } /* Creates a small triangle extender for the tooltip */ diff --git a/caravel/assets/visualizations/heatmap.js b/caravel/assets/visualizations/heatmap.js index c9100e2c0f917..55ec7ef46efde 100644 --- a/caravel/assets/visualizations/heatmap.js +++ b/caravel/assets/visualizations/heatmap.js @@ -11,19 +11,15 @@ require('./heatmap.css'); // Inspired from http://bl.ocks.org/mbostock/3074470 // https://jsfiddle.net/cyril123/h0reyumq/ function heatmapVis(slice) { - var margins = { - t: 10, - r: 10, - b: 50, - l: 60 - }; function refresh() { - var width = slice.width(); - var height = slice.height(); - var hmWidth = width - (margins.l + margins.r); - var hmHeight = height - (margins.b + margins.t); - var fp = d3.format('.3p'); + var margin = { + top: 10, + right: 10, + bottom: 35, + left: 35 + }; + d3.json(slice.jsonEndpoint(), function (error, payload) { var matrix = {}; if (error) { @@ -33,6 +29,24 @@ function heatmapVis(slice) { var fd = payload.form_data; var data = payload.data; + // Dynamically adjusts based on max x / y category lengths + function adjustMargins(data, margins) { + var pixelsPerCharX = 4.5; // approx, depends on font size + var pixelsPerCharY = 6.8; // approx, depends on font size + var longestX = 1; + var longestY = 1; + var datum; + + for (var i = 0; i < data.length; i++) { + datum = data[i]; + longestX = Math.max(longestX, datum.x.length || 1); + longestY = Math.max(longestY, datum.y.length || 1); + } + + margins.left = Math.ceil(Math.max(margins.left, pixelsPerCharY * longestY)); + margins.bottom = Math.ceil(Math.max(margins.bottom, pixelsPerCharX * longestX)); + } + function ordScale(k, rangeBands, reverse) { if (reverse === undefined) { reverse = false; @@ -53,12 +67,20 @@ function heatmapVis(slice) { return d3.scale.ordinal().domain(domain).rangeBands(rangeBands); } } + adjustMargins(data, margin); + + var width = slice.width(); + var height = slice.height(); + var hmWidth = width - (margin.left + margin.right); + var hmHeight = height - (margin.bottom + margin.top); + var fp = d3.format('.3p'); + var xScale = ordScale('x'); var yScale = ordScale('y', undefined, true); var xRbScale = ordScale('x', [0, hmWidth]); var yRbScale = ordScale('y', [hmHeight, 0]); var X = 0, - Y = 1; + Y = 1; var heatmapDim = [xRbScale.domain().length, yRbScale.domain().length]; var color = px.color.colorScalerFactory(fd.linear_color_scheme); @@ -72,10 +94,7 @@ function heatmapVis(slice) { .range([0, hmHeight]) ]; - var container = d3.select(slice.selector) - .style("left", "0px") - .style("position", "relative") - .style("top", "0px"); + var container = d3.select(slice.selector); var canvas = container.append("canvas") .attr("width", heatmapDim[X]) @@ -83,8 +102,8 @@ function heatmapVis(slice) { .style("width", hmWidth + "px") .style("height", hmHeight + "px") .style("image-rendering", fd.canvas_image_rendering) - .style("left", margins.l + "px") - .style("top", margins.t + "px") + .style("left", margin.left + "px") + .style("top", margin.top + "px") .style("position", "absolute"); var svg = container.append("svg") @@ -95,7 +114,7 @@ function heatmapVis(slice) { .style("position", "absolute"); var rect = svg.append('g') - .attr("transform", "translate(" + margins.l + "," + margins.t + ")") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .append('rect') .style('fill-opacity', 0) .attr('stroke', 'black') @@ -110,18 +129,23 @@ function heatmapVis(slice) { return [k[1] - 20, x]; }) .html(function (d) { + var s = ""; var k = d3.mouse(this); var m = Math.floor(scale[0].invert(k[0])); var n = Math.floor(scale[1].invert(k[1])); if (m in matrix && n in matrix[m]) { var obj = matrix[m][n]; - var s = ""; s += "
" + fd.all_columns_x + ": " + obj.x + "
"; s += "
" + fd.all_columns_y + ": " + obj.y + "
"; s += "
" + fd.metric + ": " + obj.v + "
"; s += "
%: " + fp(obj.perc) + "
"; - return s; + tip.style("display", null); + } else { + // this is a hack to hide the tooltip because we have map it to a single + // d3-tip toggles opacity and calling hide here is undone by the lib after this call + tip.style("display", "none"); } + return s; }); rect.call(tip); @@ -133,6 +157,7 @@ function heatmapVis(slice) { return !(i % (parseInt(fd.xscale_interval, 10))); })) .orient("bottom"); + var yAxis = d3.svg.axis() .scale(yRbScale) .tickValues(yRbScale.domain().filter( @@ -143,16 +168,15 @@ function heatmapVis(slice) { svg.append("g") .attr("class", "x axis") - .attr("transform", "translate(" + margins.l + "," + (margins.t + hmHeight) + ")") + .attr("transform", "translate(" + margin.left + "," + (margin.top + hmHeight) + ")") .call(xAxis) .selectAll("text") .style("text-anchor", "end") - .attr("transform", "rotate(-45)") - .style("font-weight", "bold"); + .attr("transform", "rotate(-45)"); svg.append("g") .attr("class", "y axis") - .attr("transform", "translate(" + margins.l + ", 0)") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .call(yAxis); rect.on('mousemove', tip.show); @@ -196,6 +220,7 @@ function heatmapVis(slice) { context.putImageData(image, 0, 0); imageObj.src = canvas.node().toDataURL(); } + slice.done(); }); diff --git a/caravel/forms.py b/caravel/forms.py index 875395f95bb87..b5ff1db0bfefc 100644 --- a/caravel/forms.py +++ b/caravel/forms.py @@ -139,7 +139,7 @@ def __init__(self, viz): 'Color Scheme', choices=self.choicify([ 'fire', 'blue_white_yellow', 'white_black', 'black_white']), - default='fire', + default='blue_white_yellow', description=""), 'normalize_across': SelectField( 'Normalize Across', choices=self.choicify([