UniTO/anno3/avrc/assignments/dataviz/static/js/d3-heatmap2.js
2024-10-29 09:11:05 +01:00

464 lines
No EOL
13 KiB
JavaScript
Executable file

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.d3 = global.d3 || {})));
}(this, (function (exports) { 'use strict';
/* global d3 */
function cantorPair (x, y) {
var z = ((x + y) * (x + y + 1)) / 2 + y;
return z
}
var heatmap = function () {
var svg = null;
var columns = 0;
var rows = 0;
var title = '';
var subtitle = '';
var legendLabel = '';
var width = 960;
var margin = {
top: 20,
right: 0,
bottom: 0,
left: 0
};
var gridSize = null;
var colorScale = null;
var xAxisScale = null;
var yAxisScale = null;
var xAxisTickFormat = d3.format('.0f');
var yAxisTickFormat = d3.format('.2s');
var xAxisLabels = null;
var yAxisLabels = null;
var xAxisLabelFormat = function (d) { return d };
var yAxisLabelFormat = function (d) { return d };
var hideLegend = false;
var clickHandler = null;
var mouseOverHandler = null;
var highlight = [];
var highlightColor = '#936EB5';
var highlightOpacity = '0.4';
function click (d, i, j) {
if (typeof clickHandler === 'function') {
clickHandler(d, i, j);
}
}
function mouseOver (d, i, j) {
if (typeof mouseOverHandler === 'function') {
mouseOverHandler(d, i, j);
}
}
function getHighlightFrames () {
var highlightFrames = [];
for (var k in highlight) { // all highlights
if (highlight[k].start[0] <= highlight[k].end[0]) { // no reverse column range highlight
for (var i = highlight[k].start[0]; i <= highlight[k].end[0]; i++) {
var j = null;
if (i > highlight[k].start[0] && i < highlight[k].end[0]) { // middle columns
for (j = 0; j < rows; j++) {
highlightFrames.push([i, j]);
}
} else if (i === highlight[k].start[0]) { // start column, or start and end are the same
if (i === highlight[k].end[0]) { // ends in the same column
if (highlight[k].start[1] <= highlight[k].end[1]) { // no reverse row range highlight
for (j = highlight[k].start[1]; j <= highlight[k].end[1]; j++) {
highlightFrames.push([i, j]);
}
} else {
console.log('Error: Start row is higher than end row. No reverse range highlight.');
}
} else { // doesn't end in the same column
for (j = highlight[k].start[1]; j < rows; j++) {
highlightFrames.push([i, j]);
}
}
} else { // end column, when different than start column
for (j = highlight[k].end[1]; j >= 0; j--) {
highlightFrames.push([i, j]);
}
}
}
} else {
console.log('Error: Start column is higher than end column. No reverse range highlight.');
}
}
return highlightFrames
}
function updateHighlight () {
if (highlight && highlight.length > 0 && svg && rows && gridSize) {
var highlightFrames = getHighlightFrames();
var frames = svg.selectAll('g.highlight')
.data(highlightFrames, function (d) { return cantorPair(d[0], d[1]) });
frames.exit().remove();
frames.enter().append('g')
.attr('class', 'highlight')
.append('rect')
.attr('x', function (d) { return d[0] * gridSize })
.attr('y', function (d) { return d[1] * gridSize })
.attr('width', gridSize)
.attr('height', gridSize)
.style('fill', highlightColor)
.style('fill-opacity', highlightOpacity)
.style('pointer-events', 'none');
} else {
console.log("Error: Can't update highlight. Heatmap was not initialized yet or highlight was not defined.");
}
}
function heatmap (selection) {
var data = selection.datum();
columns = data.length;
rows = data[0].length;
gridSize = width / columns;
var height = gridSize * (rows + 2);
if (title) {
margin.top = margin.top + 50;
}
if (subtitle) {
margin.top = margin.top + 20;
}
if (!hideLegend) {
margin.bottom = margin.bottom + 50;
}
if (yAxisScale || yAxisLabels) {
margin.left = margin.left + 50;
}
var max = 0;
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < data[i].length; j++) {
if (data[i][j] > max) { max = data[i][j]; }
}
}
if (!colorScale) {
colorScale = d3.scaleLinear()
.domain([0, max / 2, max])
.range(['#FFFFDD', '#3E9583', '#1F2D86']);
// .interpolate(d3.interpolateHcl);
}
svg = selection
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
if (yAxisScale || yAxisLabels) {
if (yAxisScale) {
var y = d3.scaleLinear()
.domain(yAxisScale)
.range([height, 0]);
svg.append('g')
.attr('transform', 'translate(3,-12)')
.attr('class', 'rowLabel axis')
.call(d3.axisLeft(y)
.ticks(20)
.tickFormat(yAxisTickFormat));
} else {
svg.selectAll('.rowLabel')
.data(yAxisLabels)
.enter().append('text')
.text(yAxisLabelFormat)
.attr('x', 0)
.attr('y', function (d, i) { return i * gridSize })
.style('text-anchor', 'end')
.attr('transform', 'translate(-6,' + gridSize / 1.2 + ')')
.attr('class', 'rowLabel mono axis');
}
}
if (xAxisScale || xAxisLabels) {
if (xAxisScale) {
var x = d3.scaleLinear()
.domain(xAxisScale)
// .range([0, width - margin.left - margin.right - 40])
.range([0, width - margin.left - margin.right]);
svg.append('g')
.attr('transform', 'translate(5,3)')
.attr('class', 'columnLabel axis')
.call(d3.axisTop(x)
.ticks(20)
.tickFormat(xAxisTickFormat));
} else {
svg.selectAll('.columnLabel')
.data(xAxisLabels)
.enter().append('text')
.text(xAxisLabelFormat)
.attr('y', function (d, i) { return i * gridSize })
.attr('x', 0)
.style('text-anchor', 'beginning')
.attr('transform', 'translate(' + gridSize / 1.4 + ', -6) rotate(270)')
.attr('class', 'columnLabel mono axis');
}
}
svg.selectAll('g.column')
.data(data)
.enter().append('g')
.each(function (d, i) { // function (d, i, j) might replace .each.
d3.select(this).selectAll('rect')
.data(d)
.enter().append('rect')
.attr('x', function (d) { return i * gridSize }) // column
.attr('y', function (d, j) { return j * gridSize }) // row
.attr('class', 'bordered')
.attr('width', gridSize)
.attr('height', gridSize)
.style('stroke', 'white')
.style('stroke-opacity', 0.6)
.style('fill', function (d) { return colorScale(d) })
.style('pointer-events', 'all')
.on('mouseover', function (d, j) { return mouseOver(d, i, j) })
.on('click', function (d, j) { return click(d, i, j) });
});
if (highlight && highlight.length > 0) {
updateHighlight();
}
// Append title to the top
if (title) {
svg.append('text')
.attr('class', 'title')
.attr('x', width / 2)
.attr('y', -60)
.style('text-anchor', 'middle')
.text(title);
}
if (subtitle) {
svg.append('text')
.attr('class', 'subtitle')
.attr('x', width / 2)
.attr('y', -40)
.style('text-anchor', 'middle')
.text(subtitle);
}
if (!hideLegend) {
// Extra scale since the color scale is interpolated
var countScale = d3.scaleLinear()
.domain([0, max])
.range([0, width]);
// Calculate the variables for the temp gradient
var numStops = 10;
var countRange = countScale.domain();
var countPoint = [];
countRange[2] = countRange[1] - countRange[0];
for (var i = 0; i < numStops; i++) {
countPoint.push(i * countRange[2] / (numStops - 1) + countRange[0]);
}// for i
// Create the gradient
svg.append('defs')
.append('linearGradient')
.attr('id', 'legend-traffic')
.attr('x1', '0%').attr('y1', '0%')
.attr('x2', '100%').attr('y2', '0%')
.selectAll('stop')
.data(d3.range(numStops))
.enter().append('stop')
.attr('offset', function (d, i) {
return countScale(countPoint[i]) / width
})
.attr('stop-color', function (d, i) {
return colorScale(countPoint[i])
});
var legendWidth = Math.min(width * 0.8, 400);
// Color Legend container
var legendsvg = svg.append('g')
.attr('class', 'legendWrapper')
.attr('transform', 'translate(' + (width / 2) + ',' + (gridSize * rows + 40) + ')');
// Draw the Rectangle
legendsvg.append('rect')
.attr('class', 'legendRect')
.attr('x', -legendWidth / 2)
.attr('y', 0)
// .attr("rx", hexRadius*1.25/2)
.attr('width', legendWidth)
.attr('height', 10)
.style('fill', 'url(#legend-traffic)');
// Append title
legendsvg.append('text')
.attr('class', 'legendTitle')
.attr('x', 0)
.attr('y', -10)
.style('text-anchor', 'middle')
.text(legendLabel);
// Set scale for x-axis
var xScale = d3.scaleLinear()
.range([-legendWidth / 2, legendWidth / 2])
.domain([0, max]);
// Define x-axis
var xAxis = d3.axisBottom()
.ticks(5)
// .tickFormat(formatPercent)
.scale(xScale);
// Set up X axis
legendsvg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0,' + (10) + ')')
.call(xAxis);
}
}
heatmap.title = function (_) {
if (!arguments.length) { return title }
title = _;
return heatmap
};
heatmap.subtitle = function (_) {
if (!arguments.length) { return subtitle }
subtitle = _;
return heatmap
};
heatmap.legendLabel = function (_) {
if (!arguments.length) { return legendLabel }
legendLabel = _;
return heatmap
};
heatmap.width = function (_) {
if (!arguments.length) { return width }
width = _;
return heatmap
};
heatmap.margin = function (_) {
if (!arguments.length) { return margin }
margin = _;
return heatmap
};
heatmap.colorScale = function (_) {
if (!arguments.length) { return colorScale }
colorScale = _;
return heatmap
};
heatmap.xAxisScale = function (_) {
if (!arguments.length) { return xAxisScale }
xAxisScale = _;
return heatmap
};
heatmap.yAxisScale = function (_) {
if (!arguments.length) { return yAxisScale }
yAxisScale = _;
return heatmap
};
heatmap.xAxisLabelFormat = function (_) {
if (!arguments.length) { return xAxisLabelFormat }
xAxisLabelFormat = _;
return heatmap
};
heatmap.yAxisLabelFormat = function (_) {
if (!arguments.length) { return yAxisLabelFormat }
yAxisLabelFormat = _;
return heatmap
};
heatmap.xAxisTickFormat = function (_) {
if (!arguments.length) { return xAxisTickFormat }
xAxisTickFormat = _;
return heatmap
};
heatmap.yAxisTickFormat = function (_) {
if (!arguments.length) { return yAxisTickFormat }
yAxisTickFormat = _;
return heatmap
};
heatmap.hideLegend = function (_) {
if (!arguments.length) { return hideLegend }
hideLegend = _;
return heatmap
};
heatmap.onClick = function (_) {
if (!arguments.length) { return clickHandler }
clickHandler = _;
return heatmap
};
heatmap.onMouseOver = function (_) {
if (!arguments.length) { return mouseOverHandler }
mouseOverHandler = _;
return heatmap
};
heatmap.xAxisLabels = function (_) {
if (!arguments.length) { return xAxisLabels }
xAxisLabels = _;
return heatmap
};
heatmap.yAxisLabels = function (_) {
if (!arguments.length) { return yAxisLabels }
yAxisLabels = _;
return heatmap
};
heatmap.highlightColor = function (_) {
if (!arguments.length) { return highlightColor }
highlightColor = _;
return heatmap
};
heatmap.highlightOpacity = function (_) {
if (!arguments.length) { return highlightOpacity }
highlightOpacity = _;
return heatmap
};
heatmap.setHighlight = function (_) {
if (!arguments.length) { return highlight }
highlight = _;
return heatmap
};
heatmap.updateHighlight = updateHighlight;
return heatmap
};
exports.heatmap = heatmap;
Object.defineProperty(exports, '__esModule', { value: true });
})));