Merge pull request #5205 from solgenomics/topic/generic_trial_upload
[sgn.git] / js / source / legacy / solGS / heatMap.js
blob92c693d9818633ee28b3ce664eb4d22c5efc2a33
1 /**
2  * heatmap plotting using d3
3  * Isaak Y Tecle <iyt2@cornell.edu>
4  *
5  */
8 var solGS = solGS || function solGS() {};
10 solGS.heatmap = {
12   plot: function (data, heatmapCanvasDiv, heatmapPlotDivId, downloadLinks) {
13     if (heatmapCanvasDiv == null) {
14       alert("The div element where the heatmap to draw is missing.");
15     }
17     if (jQuery(heatmapPlotDivId).length == 0) {
18       var divId = heatmapPlotDivId.replace(/#/, "");
19       jQuery(heatmapCanvasDiv).append("<div id=" + divId + "></div>");
20     }
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;
29     var corr = [];
30     var coefs = [];
32     for (var i = 0; i < values.length; i++) {
33       var rw = values[i];
34       var rwPvalues;
35       if (pvalues) {
36         rwPvalues = pvalues[i];
37       }
39       for (var j = 0; j < nLabels; j++) {
40         var clNm = labels[j];
41         var rwVl = rw[clNm];
43         var rwPV;
44         if (pvalues) {
45           rwPV = rwPvalues[clNm];
46         } else {
47           rwPV = "NA";
48         }
49         if (!rwVl && rwVl !== 0) {
50           rwVl = "NA";
51         }
53         if (rwVl != "NA") {
54           coefs.push(rwVl);
55           rwVl = d3.format(".2f")(rwVl);
56         }
58         corr.push({ row: i, col: j, value: rwVl, pvalue: rwPV });
59       }
60     }
62     if (heatmapCanvasDiv.match(/#/) == null) {
63       heatmapCanvasDiv = "#" + heatmapCanvasDiv;
64     }
66     if (heatmapPlotDivId) {
67       if (!heatmapPlotDivId.match(/#/)) {
68         heatmapPlotDivId = "#" + heatmapPlotDivId;
69       }
70     } else {
71       heatmapPlotDivId = "#heatmap_plot";
72     }
74     var fs = "0.95em";
76     if (nLabels >= 100) {
77       height = 600;
78       width = 600;
79       fs = "0.75em";
80     } else {
81       height = 500;
82       width = 500;
83     }
85     if (nLabels < 20) {
86       height = height * 0.5;
87       width = width * 0.5;
88     }
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;
94     var nve = "#6A0888";
95     var pve = "#86B404";
96     var nral = "#98AFC7"; //blue gray
97     var txtColor = "#523CB5";
99     var rmax = d3.max(coefs);
100     var rmin = d3.min(coefs);
101     var coefRange = [];
102     var coefDom = [];
104     if (rmin >= 0 && rmax > 0) {
105       rmax = rmax;
106       coefDom = [0, rmax];
107       coefRange = ["white", pve];
108     } else if (rmin < 0 && rmax > 0) {
109       if (-rmin > rmax) {
110         rmax = -rmin;
111       }
112       coefDom = [-rmax, 0,  rmax];
113       coefRange = [nve, "white", pve];
114     } else if (rmin <= 0 && rmax < 0) {
115       coefDom = [rmin, 0];
116       coefRange = [nve, "white"];
117     }
118     
119     var corZscale = d3.scaleLinear().domain(coefDom).range(coefRange);
120     var xAxisScale = d3.scaleBand()
121                 .range([0, width])      
122                 .domain(labels)
123                 .padding(0.00);
125     var yAxisScale = d3.scaleBand()
126                 .range([height, 0])
127                 .domain(labels)
128                 .padding(0.00);
129     
130     var xAxis = d3.axisBottom(xAxisScale).tickSizeOuter(0).tickPadding(5);
131     var yAxis  = d3.axisLeft(yAxisScale).tickSizeOuter(0);
133     var svg = d3
134       .select(heatmapPlotDivId)
135       .insert("svg", ":first-child")
136       .attr("height", totalH)
137       .attr("width", totalW);
138      
139     var  corrplot = svg.append("g")
140       .attr("id", heatmapPlotDivId)
141       .attr("transform", "translate(0, 0)");
143     corrplot
144       .append("g")
145       .attr("class", "y_axis")
146       .attr("transform", `translate(${pad.left}, ${pad.top})`)
147       .call(yAxis)
148       .selectAll("text")
149       .attr("x", -10)
150       .attr("y", 0)
151       .attr("dy", ".3em")
152       .attr("fill", txtColor)
153       .style("font-size", fs);
155     corrplot
156       .append("g")
157       .attr("class", "x_axis")
158       .attr("transform", `translate(${pad.left}, ${pad.top + height})`)
159       .call(xAxis)
160       .selectAll("text")
161       .style("text-anchor", "end")
162       .attr("x", "-10")
163       .attr("y", 0)
164       .attr("dy", ".3em")
165       .attr("transform", "rotate(-90)")
166       .attr("fill", txtColor)
167       .style("font-size", fs);
168       
169     corrplot
170       .selectAll()
171       .data(corr)
172       .attr("transform", `translate(${pad.left}, ${pad.top})`)
173       .enter()
174       .append("rect")
175       .attr("class", "cell")
176       .attr("x", function (d) {
177         return pad.left +  xAxisScale(labels[d.col]);
178       })
179       .attr("y", function (d) {
180         return pad.top + yAxisScale(labels[d.row]);
181       })
182       .attr("width", xAxisScale.bandwidth())
183       .attr("height", yAxisScale.bandwidth())
184       .style("stroke",  function (d) {
185         if (d.value == "NA") {
186           return "white";
187         } else {
188         return txtColor;
189         }})
190       .style("stroke-opacity", 0.2)
191       .attr("fill", function (d) {
192         if (d.value == "NA") {
193           return "white";
194         } else {
195         return corZscale(d.value);
196         }})
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");
202           corrplot
203             .append("text")
204             .attr("id", "corrtext")
205             .html(function () {
206               if (d.pvalue != "NA") {
207                 var pv;
208                 if (labels[d.row] === labels[d.col]) {
209                   pv = 0.000;
210                 } else {
211                   pv = d.pvalue;
212                 }
213                 return `${labels[d.row]} vs. ${labels[d.col]}:  
214               ${d.value}, &alpha;: <${d3.format(".3f")(pv)}`;
215               } else {
216                 return `${labels[d.row]} vs. ${labels[d.col]}:  
217                 ${d.value}`;
218               }
219             })
220             .style("fill", function () {
221               if (d.value > 0) {
222                 return pve;
223               } else if (d.value == 0) {
224                 return nral;
225               } else if (d.value < 0) {
226                 return nve;
227               }
228             })
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");
234         }
235       })
236       .on("mouseout", function () {
237         d3.selectAll("text.corrlabel").remove();
238         d3.selectAll("text#corrtext").remove();
239         d3.select(this).attr("stroke", "white");
240       });
242     corrplot
243       .append("rect")
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]])
257     }
259     if (heatmapCanvasDiv.match(/kinship/)) {
260       legendValues.push(
261         [legendValues.length + 1, "Diag: inbreeding coefficients"],
262         [legendValues.length + 2, "Off-diag: kinship coefficients"]
263       );
264     }
266     var legendX = pad.left + width + 30;
267     var legendY = pad.top;
269     var legend = corrplot
270       .append("g")
271       .attr("class", "cell")
272       .attr(
273         "transform",
274         `translate(${legendX}, ${legendY})`
275       )
276     
277     var recLH = 20;
278     var recLW = 20;
280     legend = legend
281       .selectAll("rect")
282       .data(legendValues)
283       .enter()
284       .append("rect")
285       .attr("x", 1)
286       .attr("y", function (d) {
287         return d[0] * recLH;
288       })
289       .attr("width", recLH)
290       .attr("height", recLW)
291       .style("stroke", "White")
292       .attr("fill", function (d) {
293         if (d == "NA") {
294           return "white";
295          } else {
296           return corZscale(d[1]);
297         }
298       });
300     var legendTxt = corrplot
301       .append("g")
302       .attr(
303         "transform",
304         `translate(${legendX + 30}, ${legendY + 0.5 * recLH})`
305       )
306       .attr("id", "legendtext");
308     legendTxt
309       .selectAll("text")
310       .data(legendValues)
311       .enter()
312       .append("text")
313       .attr("fill", txtColor)
314       .style("fill", txtColor)
315       .attr("x", 1)
316       .attr("y", function (d) {
317         return d[0] * recLH;
318       })
319       .text(function (d) { return d[1];
320       })
321       .attr("dominant-baseline", "middle")
322       .attr("text-anchor", "start");
324     if (downloadLinks) {
325       if (!heatmapPlotDivId.match("#")) {
326         heatmapPlotDivId = "#" + heatmapPlotDivId;
327       }
328       jQuery(heatmapPlotDivId).append('<p style="margin-left: 40px">' + downloadLinks + "</p>");
329     }
330   },
332   ///////