• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eweitz / ideogram / 12131714885

03 Dec 2024 02:21AM UTC coverage: 82.273% (-1.1%) from 83.356%
12131714885

push

github

web-flow
Merge pull request #375 from eweitz/smooth-repeat-hover

Avoid hiding tooltip on first hover of previous clicked annotation

2359 of 3225 branches covered (73.15%)

Branch coverage included in aggregate %.

10 of 15 new or added lines in 3 files covered. (66.67%)

455 existing lines in 18 files now uncovered.

5410 of 6218 relevant lines covered (87.01%)

27653.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.86
/src/js/annotations/heatmap.js
1
/**
2
 * @fileoverview Functions for 2D heatmaps of genome annotations.
3
 * Heatmaps provide an easy way to visualize very dense annotation data.
4
 * Unlike the rest of Ideogram's graphics, which use SVG, heatmaps are
5
 * rendered using the Canvas element.
6
 */
7

8
import {d3} from '../lib';
9
import {drawHeatmapsCollinear} from './heatmap-collinear';
10
import {drawHeatmaps2d} from './heatmap-2d';
11
import {getHeatmapAnnotColor} from './heatmap-lib';
12

13
import {
14
  startHideTrackLabelTimeout, writeTrackLabelContainer, showTrackLabel
15
} from './track-labels';
16

17
/**
18
 * Add canvases that will contain annotations.  One canvas per track.
19
 */
20
function writeCanvases(chr, chrLeft, ideoHeight, ideo) {
21
  var j, trackLeft, trackWidth, canvas, context, id,
22
    contextArray = [],
120✔
23
    numAnnotTracks = ideo.config.numAnnotTracks;
120✔
24

25
  var marginHack = 7; // TODO: Make this dynamic
120✔
26

27
  // Create a canvas for each annotation track on this chromosome
28
  for (j = 0; j < numAnnotTracks; j++) {
120✔
29
    trackWidth = ideo.config.annotationHeight;
288✔
30
    id = chr.id + '-canvas-' + j; // e.g. chr1-9606-canvas-0
288✔
31
    trackLeft = chrLeft - trackWidth * (numAnnotTracks - j) - marginHack;
288✔
32
    canvas = d3.select(ideo.config.container + ' #_ideogramInnerWrap')
288✔
33
      .append('canvas')
34
      .attr('id', id)
35
      .attr('width', trackWidth)
36
      .attr('height', ideoHeight)
37
      .style('position', 'absolute')
38
      .style('left', trackLeft + 'px');
39
    context = canvas.nodes()[0].getContext('2d');
288✔
40
    contextArray.push(context);
288✔
41
  }
42

43
  return contextArray;
120✔
44
}
45

46
/**
47
 * Render annotations on the canvas
48
 */
49
function fillCanvasAnnots(annots, contextArray, chrWidth, ideoMarginTop) {
50
  var j, annot, context, x;
51

52
  // Fill in the canvas(es) with annotation colors to draw a heatmap
53
  for (j = 0; j < annots.length; j++) {
120✔
54
    annot = annots[j];
126,641✔
55
    context = contextArray[annot.trackIndex];
126,641✔
56
    context.fillStyle = annot.color;
126,641✔
57
    x = annot.trackIndex - 1;
126,641✔
58
    context.fillRect(x, annot.startPx + ideoMarginTop, chrWidth, 0.5);
126,641✔
59
  }
60
}
61

62
/**
63
 * Draw a 1D heatmap of annotations along each chromosome.
64
 * Ideal for representing very dense annotation sets in a granular manner
65
 * without subsampling.
66
 *
67
 * TODO:
68
 * - Support in 'horizontal' orientation
69
 * - Support after rotating chromosome on click
70
 */
71
function drawHeatmaps(annotContainers) {
72
  var annots, chrLeft, contextArray, chrWidth, i, chr,
73
    ideo = this,
7✔
74
    config = ideo.config,
7✔
75
    ideoMarginTop = ideo._layout.margin.top,
7✔
76
    ideoHeight = config.chrHeight + ideoMarginTop;
7✔
77

78
  if (config.geometry === 'collinear') {
7✔
79
    return drawHeatmapsCollinear(annotContainers, ideo);
2✔
80
  } else if (config.annotationsLayout === 'heatmap-2d') {
5!
UNCOV
81
    return drawHeatmaps2d(annotContainers, ideo);
×
82
  }
83

84
  d3.selectAll(ideo.config.container + ' canvas').remove();
5✔
85

86
  writeTrackLabelContainer(ideo);
5✔
87

88
  // Each "annotationContainer" represents annotations for a chromosome
89
  for (i = 0; i < annotContainers.length; i++) {
5✔
90

91
    annots = annotContainers[i].annots;
120✔
92
    chr = ideo.chromosomesArray[i];
120✔
93
    chrWidth = ideo.config.chrWidth;
120✔
94
    chrLeft = ideo._layout.getChromosomeSetYTranslate(i);
120✔
95

96
    contextArray = writeCanvases(chr, chrLeft, ideoHeight, ideo);
120✔
97
    fillCanvasAnnots(annots, contextArray, chrWidth, ideoMarginTop);
120✔
98
  }
99

100
  d3.selectAll(ideo.config.container + ' canvas')
5✔
101
    .on('mouseover', function() {showTrackLabel(this, ideo);})
2✔
102
    .on('mouseout', function() {startHideTrackLabelTimeout(ideo);});
×
103

104
  if (ideo.onDrawAnnotsCallback) {
5!
105
    ideo.onDrawAnnotsCallback();
5✔
106
  }
107
}
108

109
/**
110
 * Set color and track index for raw annotation objects.
111
 */
112
function getNewRawAnnots(heatmapKeyIndexes, rawAnnots, ideo) {
113
  var j, k, ra, newRa, value, thresholds, color, trackIndex,
114
    newRas = [];
72✔
115

116
  for (j = 0; j < rawAnnots.length; j++) {
72✔
117
    ra = rawAnnots[j];
70,746✔
118
    for (k = 0; k < heatmapKeyIndexes.length; k++) {
70,746✔
119
      newRa = ra.slice(0, 3); // name, start, length
158,392✔
120

121
      value = ra[heatmapKeyIndexes[k]];
158,392✔
122
      thresholds = ideo.config.heatmaps[k].thresholds;
158,392✔
123
      color = getHeatmapAnnotColor(thresholds, value);
158,392✔
124

125
      trackIndex = k;
158,392✔
126
      newRa.push(trackIndex, color, value);
158,392✔
127
      newRas.push(newRa);
158,392✔
128
    }
129
  }
130

131
  return newRas;
72✔
132
}
133

134
function getNewRawAnnotContainers(heatmapKeyIndexes, rawAnnotBoxes, ideo) {
135
  var raContainer, chr, rawAnnots, newRas, i,
136
    newRaContainers = [];
3✔
137

138
  for (i = 0; i < rawAnnotBoxes.length; i++) {
3✔
139
    raContainer = rawAnnotBoxes[i];
72✔
140
    chr = raContainer.chr;
72✔
141

142
    rawAnnots = raContainer.annots;
72✔
143
    newRas = getNewRawAnnots(heatmapKeyIndexes, rawAnnots, ideo);
72✔
144

145
    newRaContainers.push({chr: chr, annots: newRas});
72✔
146
  }
147
  return newRaContainers;
3✔
148
}
149

150
function reportPerformance(t0, ideo) {
151
  var t1 = new Date().getTime();
3✔
152
  if (ideo.config.debug) {
3!
153
    console.log('Time in deserializeAnnotsForHeatmap: ' + (t1 - t0) + ' ms');
×
154
  }
155
}
156

157
/**
158
 * Deserialize compressed annotation data into a format suited for heatmaps.
159
 *
160
 * This enables the annotations to be downloaded from a server without the
161
 * requested annotations JSON needing to explicitly specify track index or
162
 * color.  The track index and color are inferred from the "heatmaps" Ideogram
163
 * configuration option defined before ideogram initialization.
164
 *
165
 * This saves time for the user.
166
 *
167
 * @param rawAnnotsContainer {Object} Raw annotations as passed from server
168
 */
169
function deserializeAnnotsForHeatmap(rawAnnotsContainer) {
170
  var newRaContainers, heatmapKey, heatmapKeyIndexes, i,
171
    t0 = new Date().getTime(),
3✔
172
    keys = rawAnnotsContainer.keys,
3✔
173
    rawAnnotBoxes = rawAnnotsContainer.annots,
3✔
174
    ideo = this;
3✔
175

176
  heatmapKeyIndexes = [];
3✔
177
  for (i = 0; i < ideo.config.heatmaps.length; i++) {
3✔
178
    heatmapKey = ideo.config.heatmaps[i].key;
8✔
179
    heatmapKeyIndexes.push(keys.indexOf(heatmapKey));
8✔
180
  }
181

182
  newRaContainers =
3✔
183
    getNewRawAnnotContainers(heatmapKeyIndexes, rawAnnotBoxes, ideo);
184

185
  keys.splice(3, 0, 'trackIndex');
3✔
186
  keys.splice(4, 0, 'color');
3✔
187

188
  ideo.rawAnnots.keys = keys;
3✔
189
  ideo.rawAnnots.annots = newRaContainers;
3✔
190

191
  reportPerformance(t0, ideo);
3✔
192
}
193

194
export {drawHeatmaps, deserializeAnnotsForHeatmap};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc