UNPKG

ideogram

Version:

Chromosome visualization with D3.js

235 lines (203 loc) 6.15 kB
import * as d3selection from 'd3-selection'; var d3 = Object.assign({}, d3selection); /** * Draws annotations defined by user */ function drawAnnots(friendlyAnnots) { var i, j, annot, rawAnnot, keys, chr, rawAnnots = [], ideo = this, chrs = ideo.chromosomes[ideo.config.taxid]; // TODO: multiorganism // Occurs when filtering if ('annots' in friendlyAnnots[0]) { return ideo.drawProcessedAnnots(friendlyAnnots); } for (chr in chrs) { rawAnnots.push({chr: chr, annots: []}); } for (i = 0; i < friendlyAnnots.length; i++) { annot = friendlyAnnots[i]; for (j = 0; j < rawAnnots.length; j++) { if (annot.chr === rawAnnots[j].chr) { rawAnnot = [ annot.name, annot.start, annot.stop - annot.start ]; if ('color' in annot) { rawAnnot.push(annot.color); } if ('shape' in annot) { rawAnnot.push(annot.shape); } rawAnnots[j].annots.push(rawAnnot); break; } } } keys = ['name', 'start', 'length']; if ('color' in friendlyAnnots[0]) { keys.push('color'); } if ('shape' in friendlyAnnots[0]) { keys.push('shape'); } ideo.rawAnnots = {keys: keys, annots: rawAnnots}; ideo.annots = ideo.processAnnotData(ideo.rawAnnots); ideo.drawProcessedAnnots(ideo.annots); } /** * Draws genome annotations on chromosomes. * Annotations can be rendered as either overlaid directly * on a chromosome, or along one or more "tracks" * running parallel to each chromosome. */ function drawProcessedAnnots(annots) { var chrWidth, chrWidths, layout, annotHeight, triangle, circle, rectangle, chr, chrs, r, chrAnnot, i, numAnnots, x1, x2, y1, y2, filledAnnots, ideo = this; d3.selectAll(ideo.selector + ' .annot').remove(); chrWidth = this.config.chrWidth; chrWidths = {}; chrs = ideo.chromosomes[ideo.config.taxid]; for (chr in chrs) { chrWidths[chr] = chrs[chr].width; } layout = 'tracks'; if (this.config.annotationsLayout) { layout = this.config.annotationsLayout; } if (layout === 'histogram') { annots = ideo.getHistogramBars(annots); } if (layout === 'heatmap') { ideo.drawHeatmaps(annots); return; } if ( layout !== 'heatmap' && layout !== 'histogram' ) { numAnnots = 0; for (i = 0; i < annots.length; i++) { numAnnots += annots[i].annots.length; } if (numAnnots > 2000) { console.warn( 'Rendering more than 2000 annotations in Ideogram?\n' + 'Try setting "annotationsLayout" to "heatmap" or "histogram" in your ' + 'Ideogram configuration object for better layout and performance.' ); } } annotHeight = ideo.config.annotationHeight; triangle = 'm0,0 l -' + annotHeight + ' ' + (2 * annotHeight) + ' l ' + (2 * annotHeight) + ' 0 z'; // From http://stackoverflow.com/a/10477334, with a minor change ("m -r, r") // Circles are supported natively via <circle>, but having it as a path // simplifies handling triangles, circles and other shapes in the same // D3 call r = annotHeight; circle = 'm -' + r + ', ' + r + 'a ' + r + ',' + r + ' 0 1,0 ' + (r * 2) + ',0' + 'a ' + r + ',' + r + ' 0 1,0 -' + (r * 2) + ',0'; rectangle = 'm0,0 l 0 ' + (2 * annotHeight) + 'l ' + annotHeight + ' 0' + 'l 0 -' + (2 * annotHeight) + 'z'; filledAnnots = ideo.fillAnnots(annots); chrAnnot = d3.selectAll(ideo.selector + ' .chromosome') .data(filledAnnots) .selectAll('path.annot') .data(function(d) { return d.annots; }) .enter(); if (layout === 'tracks') { chrAnnot .append('g') .attr('id', function(d) { return d.id; }) .attr('class', 'annot') .attr('transform', function(d) { var y = ideo.config.chrWidth + (d.trackIndex * annotHeight * 2); return 'translate(' + d.px + ',' + y + ')'; }) .append('path') .attr('d', function(d) { if (!d.shape || d.shape === 'triangle') { return triangle; } else if (d.shape === 'circle') { return circle; } else if (d.shape === 'rectangle') { return rectangle; } else { return d.shape; } }) .attr('fill', function(d) { return d.color; }) .on('mouseover', function(d) { ideo.showAnnotTooltip(d, this); }) .on('mouseout', function() { ideo.startHideAnnotTooltipTimeout(); }); } else if (layout === 'overlay') { // Overlaid annotations appear directly on chromosomes chrAnnot.append('polygon') .attr('id', function(d) { return d.id; }) .attr('class', 'annot') .attr('points', function(d) { if (d.stopPx - d.startPx > 1) { x1 = d.startPx; x2 = d.stopPx; } else { x1 = d.px - 0.5; x2 = d.px + 0.5; } y1 = chrWidth; y2 = 0; return ( x1 + ',' + y1 + ' ' + x2 + ',' + y1 + ' ' + x2 + ',' + y2 + ' ' + x1 + ',' + y2 ); }) .attr('fill', function(d) { return d.color; }) .on('mouseover', function(d) { ideo.showAnnotTooltip(d, this); }) .on('mouseout', function() { ideo.startHideAnnotTooltipTimeout(); }); } else if (layout === 'histogram') { chrAnnot.append('polygon') // .attr('id', function(d, i) { return d.id; }) .attr('class', 'annot') .attr('points', function(d) { x1 = d.px + ideo.bump; x2 = d.px + ideo.config.barWidth + ideo.bump; y1 = chrWidth; y2 = chrWidth + d.height; var thisChrWidth = chrWidths[d.chrName].width; if (x2 > thisChrWidth) { x2 = thisChrWidth; } return ( x1 + ',' + y1 + ' ' + x2 + ',' + y1 + ' ' + x2 + ',' + y2 + ' ' + x1 + ',' + y2 ); }) .attr('fill', function(d) { return d.color; }); } if (ideo.onDrawAnnotsCallback) { ideo.onDrawAnnotsCallback(); } } export {drawAnnots, drawProcessedAnnots}