import d3 from 'd3'; import { colorScalerFactory } from '../javascripts/modules/colors'; const $ = require('jquery'); d3.tip = require('d3-tip'); require('./heatmap.css'); // Inspired from http://bl.ocks.org/mbostock/3074470 // https://jsfiddle.net/cyril123/h0reyumq/ function heatmapVis(slice) { function refresh() { const margin = { top: 10, right: 10, bottom: 35, left: 35, }; d3.json(slice.jsonEndpoint(), function (error, payload) { const data = payload.data; // Dynamically adjusts based on max x / y category lengths function adjustMargins() { const pixelsPerCharX = 4.5; // approx, depends on font size const pixelsPerCharY = 6.8; // approx, depends on font size let longestX = 1; let longestY = 1; let datum; for (let 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); } margin.left = Math.ceil(Math.max(margin.left, pixelsPerCharY * longestY)); margin.bottom = Math.ceil(Math.max(margin.bottom, pixelsPerCharX * longestX)); } function ordScale(k, rangeBands, reverse = false) { let domain = {}; $.each(data, function (i, d) { domain[d[k]] = true; }); domain = Object.keys(domain).sort(function (a, b) { return b - a; }); if (reverse) { domain.reverse(); } if (rangeBands === undefined) { return d3.scale.ordinal().domain(domain).range(d3.range(domain.length)); } return d3.scale.ordinal().domain(domain).rangeBands(rangeBands); } slice.container.html(''); const matrix = {}; if (error) { slice.error(error.responseText, error); return; } const fd = payload.form_data; adjustMargins(); const width = slice.width(); const height = slice.height(); const hmWidth = width - (margin.left + margin.right); const hmHeight = height - (margin.bottom + margin.top); const fp = d3.format('.3p'); const xScale = ordScale('x'); const yScale = ordScale('y', undefined, true); const xRbScale = ordScale('x', [0, hmWidth]); const yRbScale = ordScale('y', [hmHeight, 0]); const X = 0; const Y = 1; const heatmapDim = [xRbScale.domain().length, yRbScale.domain().length]; const color = colorScalerFactory(fd.linear_color_scheme); const scale = [ d3.scale.linear() .domain([0, heatmapDim[X]]) .range([0, hmWidth]), d3.scale.linear() .domain([0, heatmapDim[Y]]) .range([0, hmHeight]), ]; const container = d3.select(slice.selector); const canvas = container.append('canvas') .attr('width', heatmapDim[X]) .attr('height', heatmapDim[Y]) .style('width', hmWidth + 'px') .style('height', hmHeight + 'px') .style('image-rendering', fd.canvas_image_rendering) .style('left', margin.left + 'px') .style('top', margin.top + 'px') .style('position', 'absolute'); const svg = container.append('svg') .attr('width', width) .attr('height', height) .style('left', '0px') .style('top', '0px') .style('position', 'absolute'); const rect = svg.append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') .append('rect') .style('fill-opacity', 0) .attr('stroke', 'black') .attr('width', hmWidth) .attr('height', hmHeight); const tip = d3.tip() .attr('class', 'd3-tip') .offset(function () { const k = d3.mouse(this); const x = k[0] - (hmWidth / 2); return [k[1] - 20, x]; }) .html(function () { let s = ''; const k = d3.mouse(this); const m = Math.floor(scale[0].invert(k[0])); const n = Math.floor(scale[1].invert(k[1])); if (m in matrix && n in matrix[m]) { const obj = matrix[m][n]; s += '