2 * heatmap plotting using d3
3 * Isaak Y Tecle <iyt2@cornell.edu>
8 var solGS = solGS || function solGS() {};
12 plot: function (data, heatmapCanvasDiv, heatmapPlotDivId, downloadLinks) {
13 if (heatmapCanvasDiv == null) {
14 alert("The div element where the heatmap to draw is missing.");
17 if (jQuery(heatmapPlotDivId).length == 0) {
18 var divId = heatmapPlotDivId.replace(/#/, "");
19 jQuery(heatmapCanvasDiv).append("<div id=" + divId + "></div>");
22 data = JSON.parse(data);
24 var labels = data.labels;
25 var values = data.values;
26 var pvalues = data.pvalues;
27 var nLabels = labels.length;
32 for (var i = 0; i < values.length; i++) {
36 rwPvalues = pvalues[i];
39 for (var j = 0; j < nLabels; j++) {
45 rwPV = rwPvalues[clNm];
49 if (!rwVl && rwVl !== 0) {
55 rwVl = d3.format(".2f")(rwVl);
58 corr.push({ row: i, col: j, value: rwVl, pvalue: rwPV });
62 if (heatmapCanvasDiv.match(/#/) == null) {
63 heatmapCanvasDiv = "#" + heatmapCanvasDiv;
66 if (heatmapPlotDivId) {
67 if (!heatmapPlotDivId.match(/#/)) {
68 heatmapPlotDivId = "#" + heatmapPlotDivId;
71 heatmapPlotDivId = "#heatmap_plot";
86 height = height * 0.5;
90 var pad = { left: 150, top: 20, right: 250, bottom: 100 };
91 var totalH = height + pad.top + pad.bottom;
92 var totalW = width + pad.left + pad.right;
96 var nral = "#98AFC7"; //blue gray
97 var txtColor = "#523CB5";
99 var rmax = d3.max(coefs);
100 var rmin = d3.min(coefs);
104 if (rmin >= 0 && rmax > 0) {
107 coefRange = ["white", pve];
108 } else if (rmin < 0 && rmax > 0) {
112 coefDom = [-rmax, 0, rmax];
113 coefRange = [nve, "white", pve];
114 } else if (rmin <= 0 && rmax < 0) {
116 coefRange = [nve, "white"];
119 var corZscale = d3.scaleLinear().domain(coefDom).range(coefRange);
120 var xAxisScale = d3.scaleBand()
125 var yAxisScale = d3.scaleBand()
130 var xAxis = d3.axisBottom(xAxisScale).tickSizeOuter(0).tickPadding(5);
131 var yAxis = d3.axisLeft(yAxisScale).tickSizeOuter(0);
134 .select(heatmapPlotDivId)
135 .insert("svg", ":first-child")
136 .attr("height", totalH)
137 .attr("width", totalW);
139 var corrplot = svg.append("g")
140 .attr("id", heatmapPlotDivId)
141 .attr("transform", "translate(0, 0)");
145 .attr("class", "y_axis")
146 .attr("transform", `translate(${pad.left}, ${pad.top})`)
152 .attr("fill", txtColor)
153 .style("font-size", fs);
157 .attr("class", "x_axis")
158 .attr("transform", `translate(${pad.left}, ${pad.top + height})`)
161 .style("text-anchor", "end")
165 .attr("transform", "rotate(-90)")
166 .attr("fill", txtColor)
167 .style("font-size", fs);
172 .attr("transform", `translate(${pad.left}, ${pad.top})`)
175 .attr("class", "cell")
176 .attr("x", function (d) {
177 return pad.left + xAxisScale(labels[d.col]);
179 .attr("y", function (d) {
180 return pad.top + yAxisScale(labels[d.row]);
182 .attr("width", xAxisScale.bandwidth())
183 .attr("height", yAxisScale.bandwidth())
184 .style("stroke", function (d) {
185 if (d.value == "NA") {
190 .style("stroke-opacity", 0.2)
191 .attr("fill", function (d) {
192 if (d.value == "NA") {
195 return corZscale(d.value);
197 .attr("stroke", "white")
198 .attr("stroke-width", 1)
199 .on("mouseover", function (d) {
200 if (d.value != "NA") {
201 d3.select(this).attr("stroke", "green");
204 .attr("id", "corrtext")
206 if (d.pvalue != "NA") {
208 if (labels[d.row] === labels[d.col]) {
213 return `${labels[d.row]} vs. ${labels[d.col]}:
214 ${d.value}, α: <${d3.format(".3f")(pv)}`;
216 return `${labels[d.row]} vs. ${labels[d.col]}:
220 .style("fill", function () {
223 } else if (d.value == 0) {
225 } else if (d.value < 0) {
229 .attr("x", pad.left + 40)
230 .attr("y", pad.top - 10)
231 .attr("font-weight", "bold")
232 .attr("dominant-baseline", "middle")
233 .attr("text-anchor", "middle");
236 .on("mouseout", function () {
237 d3.selectAll("text.corrlabel").remove();
238 d3.selectAll("text#corrtext").remove();
239 d3.select(this).attr("stroke", "white");
244 .attr("transform", `translate(${pad.left}, ${pad.top})`)
245 .attr("height", height)
246 .attr("width", width)
247 .attr("fill", "none")
248 .attr("stroke", txtColor)
249 .attr("stroke-width", 1)
250 .attr("pointer-events", "none");
252 var bins = d3.ticks(d3.min(coefs), d3.max(coefs), 10)
253 var legendValues = [];
255 for (var i = 0; i < bins.length; i++) {
256 legendValues.push([i, bins[i]])
259 if (heatmapCanvasDiv.match(/kinship/)) {
261 [legendValues.length + 1, "Diag: inbreeding coefficients"],
262 [legendValues.length + 2, "Off-diag: kinship coefficients"]
266 var legendX = pad.left + width + 30;
267 var legendY = pad.top;
269 var legend = corrplot
271 .attr("class", "cell")
274 `translate(${legendX}, ${legendY})`
286 .attr("y", function (d) {
289 .attr("width", recLH)
290 .attr("height", recLW)
291 .style("stroke", "White")
292 .attr("fill", function (d) {
296 return corZscale(d[1]);
300 var legendTxt = corrplot
304 `translate(${legendX + 30}, ${legendY + 0.5 * recLH})`
306 .attr("id", "legendtext");
313 .attr("fill", txtColor)
314 .style("fill", txtColor)
316 .attr("y", function (d) {
319 .text(function (d) { return d[1];
321 .attr("dominant-baseline", "middle")
322 .attr("text-anchor", "start");
325 if (!heatmapPlotDivId.match("#")) {
326 heatmapPlotDivId = "#" + heatmapPlotDivId;
328 jQuery(heatmapPlotDivId).append('<p style="margin-left: 40px">' + downloadLinks + "</p>");