UNPKG

ideogram

Version:

Chromosome visualization for the web

260 lines (216 loc) 7.35 kB
import {add2dAnnotsForChr} from './heatmap-2d'; import {setAnnotRanks} from './annotations'; // Default colors for tracks of annotations var colorMap = [ ['F00'], // If there is 1 track, then color it red. ['F00', '88F'], // If 2 tracks, color one red and one light blue. ['F00', 'CCC', '88F'], // If 3, color one red, one grey, one light blue. ['F00', 'FA0', '0AF', '88F'], // And so on. ['F00', 'FA0', 'CCC', '0AF', '88F'], ['F00', 'FA0', '875', '578', '0AF', '88F'], ['F00', 'FA0', '875', 'CCC', '578', '0AF', '88F'], ['F00', 'FA0', '7A0', '875', '0A7', '578', '0AF', '88F'], ['F00', 'FA0', '7A0', '875', 'CCC', '0A7', '578', '0AF', '88F'], ['F00', 'FA0', '7A0', '875', '552', '255', '0A7', '578', '0AF', '88F'] ]; /** * Ensure annotation containers are ordered by chromosome. */ function orderAnnotContainers(annots, ideo) { var unorderedAnnots, i, j, annot, chr, chrs; unorderedAnnots = annots; annots = []; chrs = ideo.chromosomesArray; for (i = 0; i < chrs.length; i++) { chr = chrs[i].name; for (j = 0; j < unorderedAnnots.length; j++) { annot = unorderedAnnots[j]; if (annot.chr === chr) { annots.push(annot); } } } return annots; } /** * Add client annotations, as in annotations-tracks.html */ function addClientAnnot(annots, annot, ra, m, ideo) { var annotTrack; annot.trackIndex = ra[3]; annotTrack = ideo.config.annotationTracks[annot.trackIndex]; if (annotTrack.color) { annot.color = annotTrack.color; } if (annotTrack.shape) { annot.shape = annotTrack.shape; } annots[m].annots.push(annot); return annots; } /** * Add sparse server annotations, as in annotations-track-filters.html */ function addSparseServerAnnot(annot, ra, omittedAnnots, annots, m, ideo) { var colors = colorMap[ideo.numAvailTracks - 1]; annot.trackIndex = ra[3]; annot.trackIndexOriginal = ra[4]; annot.color = '#' + colors[annot.trackIndexOriginal]; // Catch annots that will be omitted from display if (annot.trackIndex > ideo.config.numTracks - 1) { if (annot.trackIndex in omittedAnnots) { omittedAnnots[annot.trackIndex].push(annot); } else { omittedAnnots[annot.trackIndex] = [annot]; } return [annots, omittedAnnots]; } annots[m].annots.push(annot); return [annots, omittedAnnots]; } /** * Basic client annotations, as in annotations-basic.html * and annotations-external.html */ function addBasicClientAnnot(annots, annot, m, ideo) { annot.trackIndex = 0; if (!annot.color) { annot.color = ideo.config.annotationsColor; } if (!annot.shape) { annot.shape = 'triangle'; } annots[m].annots.push(annot); return annots; } function addAnnot(annot, keys, ra, omittedAnnots, annots, m, ideo) { if (ideo.config.annotationTracks) { annots = addClientAnnot(annots, annot, ra, m, ideo); } else if (keys[3] === 'trackIndex' && ideo.numAvailTracks !== 1) { [annots, omittedAnnots] = addSparseServerAnnot(annot, ra, omittedAnnots, annots, m, ideo); // } else if ( // keys.length > 3 && // keys[3] in {trackIndex: 1, color: 1, shape: 1} === false && // keys[4] === 'trackIndexOriginal' // ) { // annots = addDenseServerAnnot(keys, annots, annot, m); } else { annots = addBasicClientAnnot(annots, annot, m, ideo); } return [annots, omittedAnnots]; } function getAnnotDomId(chrIndex, annotIndex) { return '_c' + chrIndex + '_a' + annotIndex; } function addAnnotsForChr(annots, omittedAnnots, annotsByChr, chrModel, m, keys, ideo) { var j, k, annot, ra; // Assign DOM ID if annots are rendered as individual DOM elements const shouldAssignDomId = ( !ideo.config.annotationsLayout || ideo.config.annotationsLayout === 'tracks' ); for (j = 0; j < annotsByChr.annots.length; j++) { ra = annotsByChr.annots[j]; annot = {}; for (k = 0; k < keys.length; k++) { annot[keys[k]] = ra[k]; } annot.stop = annot.start + annot.length; annot.chr = annotsByChr.chr; annot.chrIndex = m; annot.startPx = ideo.convertBpToPx(chrModel, annot.start); annot.stopPx = ideo.convertBpToPx(chrModel, annot.stop); annot.px = Math.round((annot.startPx + annot.stopPx) / 2); if (shouldAssignDomId) annot.domId = getAnnotDomId(m, j); [annots, omittedAnnots] = addAnnot(annot, keys, ra, omittedAnnots, annots, m, ideo); } if (shouldAssignDomId) { if (ideo.annotSortFunction) { annots[m].annots = setAnnotRanks(annots[m].annots, ideo); annots[m].annots.sort((a, b) => { // Reverse-sort, so first annots are drawn last, and thus at top layer return -ideo.annotSortFunction(a, b); }); } else { // Sort by genomic position, in ascending order annots[m].annots.sort((a, b) => a[1] - b[1]); } for (j = 0; j < annots[m].annots.length; j++) { annots[m].annots[j].domId = getAnnotDomId(m, j); } } return [annots, omittedAnnots]; } function warnOfUndefinedChromosome(annotsByChr) { console.warn( 'Chromosome "' + annotsByChr.chr + '" undefined in ideogram; ' + annotsByChr.annots.length + ' annotations not shown' ); } function addAnnots(rawAnnots, keys, ideo) { var m, i, annotsByChr, chrModel, annots = [], omittedAnnots = {}; m = -1; for (i = 0; i < rawAnnots.length; i++) { annotsByChr = rawAnnots[i]; chrModel = ideo.chromosomes[ideo.config.taxid][annotsByChr.chr]; if (typeof chrModel === 'undefined') { warnOfUndefinedChromosome(annotsByChr); continue; } m++; annots.push({chr: annotsByChr.chr, annots: []}); if (ideo.config.annotationsLayout !== 'heatmap-2d') { [annots, omittedAnnots] = addAnnotsForChr(annots, omittedAnnots, annotsByChr, chrModel, m, keys, ideo); } else { [annots, omittedAnnots] = add2dAnnotsForChr(annots, omittedAnnots, annotsByChr, chrModel, m, keys, ideo); } } return [annots, omittedAnnots]; } function sendTrackAndAnnotWarnings(omittedAnnots, ideo) { var numOmittedTracks, layout = ideo.config.annotationsLayout, numTracks = ideo.config.numAnnotTracks; if (!/heatmap/.test(layout) && numTracks > 10) { console.error( 'Ideogram only displays up to 10 tracks at a time. ' + 'You specified ' + numTracks + ' tracks. ' + 'Perhaps consider a different way to visualize your data.' ); } numOmittedTracks = Object.keys(omittedAnnots).length; if (numOmittedTracks) { console.warn( 'Ideogram configuration specified ' + numTracks + ' tracks, ' + 'but loaded annotations contain ' + numOmittedTracks + ' ' + 'extra tracks.' ); } } /** * Proccesses genome annotation data. * * This method converts raw annotation data from server, which is structured as * an array of arrays, into a more verbose data structure consisting of an * array of objects. It also adds pixel offset information. */ function processAnnotData(rawAnnots) { var keys, annots, omittedAnnots, ideo = this; keys = rawAnnots.keys; rawAnnots = rawAnnots.annots; [annots, omittedAnnots] = addAnnots(rawAnnots, keys, ideo); annots = orderAnnotContainers(annots, ideo); sendTrackAndAnnotWarnings(omittedAnnots, ideo); return annots; } export {processAnnotData, getAnnotDomId};