Mercurial > repos > insilico-bob > mda_heatmap_viz
view mda_heatmap_viz/static/javascript/PdfGenerator.js @ 0:15c1ee57764c draft
Uploaded
| author | insilico-bob |
|---|---|
| date | Thu, 07 Apr 2016 14:43:37 -0400 |
| parents | |
| children |
line wrap: on
line source
function openPdfPrefs(e){ maxRows = 0; userHelpClose(); var prefspanel = document.getElementById('pdfPrefsPanel'); //Add prefspanel table to the main preferences DIV and set position and display prefspanel.style.top = e.offsetTop + 15; prefspanel.style.display="inherit"; prefspanel.style.left = e.offsetLeft - prefspanel.clientWidth; } /********************************************************************************** * FUNCTION - getPDF: This function is called when the "create pdf" button is pressed. * It will check the checkboxes/radio buttons to see how the PDF is to be created using * the isChecked function. for a full list of jsPDF functions, visit here: * https://mrrio.github.io/jsPDF/doc/symbols/jsPDF.html#setLineCap **********************************************************************************/ function getPDF(){ // canvas elements need to be converted to DataUrl to be loaded into PDF updateSelection(); // redraw the canvases because otherwise they can show up blank var sumImgData = canvas.toDataURL('image/png'); var detImgData = detCanvas.toDataURL('image/png'); var mapsToShow = isChecked("pdfInputSummaryMap") ? "S" : isChecked("pdfInputDetailMap") ? "D" : "B"; var doc = isChecked("pdfInputPortrait") ? new jsPDF("p","pt") :new jsPDF("l","pt"); // landscape or portrait? var pageHeight = doc.internal.pageSize.height; var pageWidth = doc.internal.pageSize.width; doc.setFont("times"); // doc.setFont("helvetica"); // convert longest label units to actual length (11 is the max font size of the labels) // these will be the bottom and left padding space for the detail Heat Map var allLabels = document.getElementsByClassName("DynamicLabel"); var longestRowLabelUnits = 10, longestColLabelUnits = 5; for (var i = 0; i < allLabels.length; i++){ // go through all the labels and find the one that takes the most space var label = allLabels[i]; if (label.getAttribute('axis') == "Row"){ longestRowLabelUnits = Math.max(doc.getStringUnitWidth(label.innerHTML),longestRowLabelUnits); } else { longestColLabelUnits = Math.max(doc.getStringUnitWidth(label.innerHTML),longestColLabelUnits); } } longestColLabelUnits *= 11; longestRowLabelUnits *= 11; // header var headerCanvas = document.createElement('CANVAS'); // load the MDAnderson logo into a canvas, since you can't load an img directly var headerCtx = headerCanvas.getContext('2d'); var header = document.getElementsByClassName('mdaServiceHeaderLogo')[0]; headerCanvas.height = 85; // logo png's actual dimensions headerCanvas.width = 260; headerCtx.drawImage(header.children[0], 0, 0); var headerData = headerCanvas.toDataURL('image/png'); var headerHeight = header.clientHeight + 5; createHeader(); // maps var paddingLeft = 5, paddingTop = headerHeight+10; // these are the variables that we will be using repeatedly to place items var sumImgW,sumImgH,detImgW,detImgH; var detImgL = paddingLeft; if (mapsToShow == "S"){ sumImgW = pageWidth - 2*paddingLeft, sumImgH = pageHeight - paddingTop - 2*paddingLeft; doc.addImage(sumImgData, 'PNG', paddingLeft, paddingTop, sumImgW,sumImgH); } else if (mapsToShow == "D"){ detImgW = pageWidth - 2*paddingLeft - longestRowLabelUnits, detImgH = pageHeight - paddingTop - longestColLabelUnits; doc.addImage(detImgData, 'PNG', paddingLeft, paddingTop, detImgW,detImgH); } else { if (!isChecked("pdfInputPages")){ sumImgW = (pageWidth - longestRowLabelUnits - 2*paddingLeft)/2, sumImgH = pageHeight - paddingTop - longestColLabelUnits; detImgW = (pageWidth - longestRowLabelUnits - 2*paddingLeft)/2, detImgH = pageHeight - paddingTop - longestColLabelUnits; detImgL = sumImgW + 2*paddingLeft; doc.addImage(sumImgData, 'PNG', paddingLeft, paddingTop, sumImgW,sumImgH); doc.addImage(detImgData, 'PNG', detImgL, paddingTop, detImgW,detImgH); } else { sumImgW = pageWidth - 2*paddingLeft, sumImgH = pageHeight - paddingTop - 2*paddingLeft; doc.addImage(sumImgData, 'PNG', paddingLeft, paddingTop, sumImgW,sumImgH); doc.addPage(); createHeader(); detImgW = pageWidth - 2*paddingLeft - longestRowLabelUnits, detImgH = pageHeight - paddingTop - longestColLabelUnits; doc.addImage(detImgData, 'PNG', detImgL, paddingTop, detImgW,detImgH); } } // labels var detClient2PdfWRatio = detCanvas.clientWidth/detImgW; // scale factor to place the labels in their proper locations var detClient2PdfHRatio = detCanvas.clientHeight/detImgH; // row labels and col class bar labels (basically stolen from DetailHeatMapDisplay.js var headerSize = paddingTop; var colHeight = calculateTotalClassBarHeight("column") + detailDendroHeight; if (colHeight > 0) { headerSize += detImgH * (colHeight / (detailDataViewHeight + colHeight)); } var skip = (detImgH - headerSize) / dataPerCol; var fontSize = Math.min(skip - 2, 11); doc.setFontSize(fontSize); for (var i = 0; i < allLabels.length; i++){ var label = allLabels[i]; if (label.getAttribute("axis") == "Row"){ doc.text(label.offsetLeft/detClient2PdfWRatio+detImgL, label.offsetTop/detClient2PdfHRatio+paddingTop+fontSize, label.innerHTML, null); } else if (label.getAttribute("axis") == "ColumnClass"){ // change font for class bars var scale = detImgH / (detailDataViewWidth + calculateTotalClassBarHeight("row")+detailDendroWidth); var colClassInfo = getClassBarsToDraw("column"); var names = colClassInfo["bars"]; var classBars = heatMap.getClassifications(); var tempFontSize = fontSize; fontSize = Math.min((classBars[names[0]].height - paddingHeight) * scale, 11); doc.setFontSize(fontSize); doc.text(label.offsetLeft/detClient2PdfWRatio+detImgL, label.offsetTop/detClient2PdfHRatio+paddingTop+fontSize/2, label.innerHTML, null); fontSize = tempFontSize doc.setFontSize(fontSize); } } // col labels and row class bar labels headerSize = 0; var rowHeight = calculateTotalClassBarHeight("row") + detailDendroWidth; if (rowHeight > 0) { headerSize = detImgW * (rowHeight / (detailDataViewWidth + rowHeight)); } skip = (detImgW - headerSize) / dataPerRow; fontSize = Math.min(skip - 2, 11); doc.setFontSize(fontSize); for (var i = 0; i < allLabels.length; i++){ var label = allLabels[i]; if (label.getAttribute("axis") == "Column"){ doc.text(label.offsetLeft/detClient2PdfWRatio-fontSize+detImgL, label.offsetTop/detClient2PdfHRatio+paddingTop, label.innerHTML, null, 270); } else if (label.getAttribute("axis") == "RowClass"){ var scale = detImgW / (detailDataViewWidth + calculateTotalClassBarHeight("row")+detailDendroWidth); var colClassInfo = getClassBarsToDraw("row"); var names = colClassInfo["bars"]; var classBars = heatMap.getClassifications(); var tempFontSize = fontSize; fontSize = Math.min((classBars[names[0]].height - paddingHeight) * scale, 11); doc.setFontSize(fontSize); doc.text(label.offsetLeft/detClient2PdfWRatio-fontSize/2+detImgL, label.offsetTop/detClient2PdfHRatio+paddingTop, label.innerHTML, null, 270); fontSize = tempFontSize doc.setFontSize(fontSize); } } // class bar legends var classBars = heatMap.getClassifications(); var classBarHeaderSize = 20; // these are font sizes var classBarTitleSize = 15; var classBarLegendTextSize = 10; var classBarFigureW = 150; // figure dimensions, unless discrete with 15+ categories var classBarFigureH = 150; var condenseClassBars = isChecked('pdfInputCondensed'); paddingLeft = 5, paddingTop = headerHeight+classBarHeaderSize + 5; // reset the top and left coordinates // row if (isChecked('pdfInputRow')){ doc.addPage(); createHeader(); doc.setFontSize(classBarHeaderSize); doc.text(10, paddingTop, "Row Covariate Bar Legends:" , null); var rowClassInfo = getClassBarsToDraw("row"); var names = rowClassInfo["bars"]; var colorSchemes = rowClassInfo["colors"]; var leftOff=10, topOff = paddingTop + classBarTitleSize; for (var i = 0; i < names.length; i++){ // for each class bar to draw... doc.setFontSize(classBarTitleSize); var currentClassBar = classBars[names[i]]; var colorMap = heatMap.getColorMapManager().getColorMap(colorSchemes[i]); if (currentClassBar.show === 'Y') { // place the figure if it's shown if (colorMap.getType() == "discrete"){ getBarGraphForDiscreteClassBar(currentClassBar,colorMap,names[i]); } else { getBarGraphForContinuousClassBar(currentClassBar,colorMap,names[i]); } } } } // column if (isChecked('pdfInputColumn')){ doc.addPage(); createHeader(); doc.setFontSize(classBarHeaderSize); doc.text(10, paddingTop, "Column Covariate Bar Legends:" , null); var colClassInfo = getClassBarsToDraw("column"); var names = colClassInfo["bars"]; var colorSchemes = colClassInfo["colors"]; var leftOff=10, topOff = paddingTop + classBarTitleSize; for (var i = 0; i < names.length; i++){ // for each class bar to draw... doc.setFontSize(classBarTitleSize); var currentClassBar = classBars[names[i]]; var colorMap = heatMap.getColorMapManager().getColorMap(colorSchemes[i]); if (currentClassBar.show === 'Y') { if (colorMap.getType() == "discrete"){ getBarGraphForDiscreteClassBar(currentClassBar,colorMap,names[i]); } else { getBarGraphForContinuousClassBar(currentClassBar,colorMap,names[i]); } } } } // TODO: in case there is an empty page after the class bar legends, delete it doc.save( heatMap.getChm().name + '.pdf'); //==================// // HELPER FUNCTIONS // //==================// // makes the MDAnderson logo, the HM name, and the red divider line at the top of each page function createHeader() { doc.addImage(headerData, 'PNG',5,5,header.clientWidth,header.clientHeight); doc.setFontSize(20); doc.text(pageWidth/2 - doc.getStringUnitWidth(heatMap.getChm().name)*20/2, headerHeight, heatMap.getChm().name, null); doc.setFillColor(255,0,0); doc.setDrawColor(255,0,0); doc.rect(5, header.clientHeight+10, pageWidth-10, 2, "FD"); } /********************************************************************************** * FUNCTION - getBarGraphForContinousClassBar: places the classBar legend using the * variables leftOff and topOff, which are updated after every classBar legend. * inputs: classBar object, colorMap object, and string for name **********************************************************************************/ function getBarGraphForContinuousClassBar(classBar, colorMap,name){ doc.text(leftOff, topOff , name, null); var thresholds = colorMap.getContinuousThresholdKeys(); var numThresholds = thresholds.length-1; // the last threshold repeats for some reason :\ var barHeight = !condenseClassBars ? classBarFigureH/(thresholds.length) : 10; // get the number N in each threshold var counts = {}, maxCount = 0, maxLabelLength = doc.getStringUnitWidth("Missing Value")*classBarLegendTextSize; // get the continuous thresholds and find the counts for each bucket for(var i = 0; i < classBar.values.length; i++) { var num = classBar.values[i]; for (var k = 0; k < thresholds.length; k++){ var thresh = thresholds[k]; if (k == 0 && num <thresholds[k]){ counts[thresh] = counts[thresh] ? counts[thresh]+1 : 1; } else if (k == thresholds.length-1 && num > thresholds[thresholds.length-1]){ counts[thresh] = counts[thresh] ? counts[thresh]+1 : 1; } else if (num <= thresh){ counts[thresh] = counts[thresh] ? counts[thresh]+1 : 1; break; } } } // find the longest label length for (var val in counts){ maxCount = Math.max(maxCount, counts[val]); maxLabelLength = Math.max(maxLabelLength, doc.getStringUnitWidth(val.length)*classBarLegendTextSize); } var bartop = topOff+5; // top location of first bar var missingCount = classBar.values.length; // start at total number of labels and work down for (var j = 0; j < thresholds.length-1; j++){ var rgb = colorMap.getClassificationColor(thresholds[j]); doc.setFillColor(rgb.r,rgb.g,rgb.b); doc.setDrawColor(0,0,0); if (condenseClassBars){ // square var barW = 10; doc.rect(leftOff, bartop, barW, barHeight, "FD"); // make the square doc.setFontSize(classBarLegendTextSize); doc.text(leftOff +barW + 5, bartop + classBarLegendTextSize, thresholds[j].toString() + " " + "n = " + counts[thresholds[j]] , null); } else { // histogram var barW = counts[thresholds[j]]/maxCount*classBarFigureW; doc.rect(leftOff + maxLabelLength, bartop, barW, barHeight, "FD"); // make the histo bar doc.setFontSize(classBarLegendTextSize); doc.text(leftOff + maxLabelLength - doc.getStringUnitWidth(thresholds[j].toString())*classBarLegendTextSize - 4, bartop + classBarLegendTextSize, thresholds[j].toString() , null); doc.text(leftOff + maxLabelLength +barW + 5, bartop + classBarLegendTextSize, "n = " + counts[thresholds[j]] , null); } missingCount -= counts[thresholds[j]]; bartop+=barHeight; // adjust top position for the next bar } var rgb = colorMap.getClassificationColor("Missing Value"); doc.setFillColor(rgb.r,rgb.g,rgb.b); doc.setDrawColor(0,0,0); if (condenseClassBars){ var barW = 10; doc.rect(leftOff, bartop, barW, barHeight, "FD"); doc.setFontSize(classBarLegendTextSize); doc.text(leftOff +barW + 5, bartop + classBarLegendTextSize, "Missing Value n = " + missingCount , null); } else { var barW = missingCount/maxCount*classBarFigureW; doc.rect(leftOff + maxLabelLength, bartop, barW, barHeight, "FD"); doc.setFontSize(classBarLegendTextSize); doc.text(leftOff + maxLabelLength - doc.getStringUnitWidth("Missing Value")*classBarLegendTextSize - 4, bartop + classBarLegendTextSize, "Missing Value" , null); doc.text(leftOff + maxLabelLength +barW + 5, bartop + classBarLegendTextSize, "n = " + missingCount , null); } // adjust the location for the next class bar figure leftOff+= classBarFigureW + maxLabelLength + 50; if (leftOff + classBarFigureW > pageWidth){ // if we'll go off the width of the page... leftOff = 10; // ...reinitialize the left side topOff += classBarFigureH + classBarHeaderSize; classBarFigureH = 150; // return figure height to original value in case it got changed in the current row if (topOff + classBarFigureH > pageHeight){ // if we'll go off the bottom of the page... doc.addPage(); createHeader(); // ...create a new page and reinitialize the top topOff = paddingTop + 5; } } } /********************************************************************************** * FUNCTION - getBarGraphForDiscreteClassBar: places the classBar legend using the * variables leftOff and topOff, which are updated after every classBar legend. * inputs: classBar object, colorMap object, and string for name **********************************************************************************/ function getBarGraphForDiscreteClassBar(classBar, colorMap,name){ doc.text(leftOff, topOff , name, null); var thresholds = colorMap.getThresholds(); var barHeight = !condenseClassBars ? classBarFigureH/(thresholds.length+1) : 10; var counts = {}, maxCount = 0, maxLabelLength = doc.getStringUnitWidth("Missing Value")*classBarLegendTextSize; // get the number N in each threshold for(var i = 0; i< classBar.values.length; i++) { var num = classBar.values[i]; counts[num] = counts[num] ? counts[num]+1 : 1; } for (var val in counts){ maxCount = Math.max(maxCount, counts[val]); maxLabelLength = Math.max(maxLabelLength, doc.getStringUnitWidth(val.length)*classBarLegendTextSize); } var bartop = topOff+5; // NOTE: missingCount will contain all elements that are not accounted for in the thresholds // ie: thresholds = [type1, type2, type3], typeX will get included in the missingCount var missingCount = classBar.values.length; // draw the bars for (var j = 0; j < thresholds.length; j++){ // make a gradient stop (and also a bucket for continuous) var rgb = colorMap.getClassificationColor(thresholds[j]); doc.setFillColor(rgb.r,rgb.g,rgb.b); doc.setDrawColor(0,0,0); if (condenseClassBars){ var barW = 10; doc.rect(leftOff, bartop, barW, barHeight, "FD"); doc.setFontSize(classBarLegendTextSize); doc.text(leftOff +barW + 5, bartop + classBarLegendTextSize, thresholds[j].toString() + " " + "n = " + counts[thresholds[j]] , null); } else { var barW = counts[thresholds[j]]/maxCount*classBarFigureW; doc.rect(leftOff + maxLabelLength, bartop, barW, barHeight, "FD"); doc.setFontSize(classBarLegendTextSize); doc.text(leftOff + maxLabelLength - doc.getStringUnitWidth(thresholds[j].toString())*classBarLegendTextSize - 4, bartop + barHeight/2, thresholds[j].toString() , null); doc.text(leftOff + maxLabelLength +barW + 5, bartop + barHeight/2, "n = " + counts[thresholds[j]] , null); } missingCount -= counts[thresholds[j]]; bartop+=barHeight; } var rgb = colorMap.getClassificationColor("Missing Value"); doc.setFillColor(rgb.r,rgb.g,rgb.b); doc.setDrawColor(0,0,0); if (condenseClassBars){ var barW = 10; doc.rect(leftOff, bartop, barW, barHeight, "FD"); doc.setFontSize(classBarLegendTextSize); doc.text(leftOff +barW + 5, bartop + classBarLegendTextSize, "Missing Value n = " + missingCount , null); } else { var barW = missingCount/maxCount*classBarFigureW; doc.rect(leftOff + maxLabelLength, bartop, barW, barHeight, "FD"); doc.setFontSize(classBarLegendTextSize); doc.text(leftOff + maxLabelLength - doc.getStringUnitWidth("Missing Value")*classBarLegendTextSize - 4, bartop + barHeight/2, "Missing Value" , null); doc.text(leftOff + maxLabelLength +barW + 5, bartop + barHeight/2, "n = " + missingCount , null); } if (thresholds.length > 15){ // in case a discrete classbar has over 15 categories, make the topOff increment bigger classBarFigureH = (1+thresholds.length)*10; } leftOff+= classBarFigureW + maxLabelLength + 50; if (leftOff + classBarFigureW > pageWidth){ // if the next class bar figure will go beyond the width of the page... leftOff = 10; // ...reset leftOff... topOff += classBarFigureH+classBarHeaderSize; // ... and move the next figure to the line below classBarFigureH = 150; // return class bar height to original value in case it got changed in this row if (topOff + classBarFigureH > pageHeight){ // if the next class bar goes off the page vertically... doc.addPage(); // ... make a new page and reset topOff createHeader(); topOff = paddingTop + 10; } } } function isChecked(el){ if(document.getElementById(el)) return document.getElementById(el).checked; } } function pdfCancelButton(){ var prefspanel = document.getElementById('pdfPrefsPanel'); prefspanel.style.display = "none"; }
