1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package org.utgenome.graphics;
26
27 import java.awt.BasicStroke;
28 import java.awt.Color;
29 import java.awt.Font;
30 import java.awt.FontMetrics;
31 import java.awt.Graphics2D;
32 import java.awt.Polygon;
33 import java.awt.geom.Rectangle2D;
34 import java.awt.image.BufferedImage;
35 import java.io.IOException;
36 import java.io.OutputStream;
37 import java.util.HashMap;
38 import java.util.List;
39
40 import javax.imageio.ImageIO;
41
42 import org.utgenome.gwt.utgb.client.bio.CytoBand;
43 import org.xerial.util.log.Logger;
44
45
46
47
48
49
50
51 public class ChromosomeMapCanvas {
52
53 private static Logger _logger = Logger.getLogger(ChromosomeMapCanvas.class);
54
55 private final HashMap<String, ChromosomeWindow> windows;
56 private BufferedImage image;
57 private Graphics2D g;
58
59 private int canvasWidth;
60 private int canvasHeight;
61 private int chromHeight = 10;
62 private int chromMargin = 10;
63 private int charHeight = 10;
64
65 private boolean isRotate = false;
66
67 private long maxChromosomeLength = 0;
68
69 private boolean isLighter = false;
70 private double lighterRate = 0.7;
71
72 public ChromosomeMapCanvas(int pixelWidth, int pixelHeight, HashMap<String, ChromosomeWindow> windows) {
73 this.windows = windows;
74 setPixelSize(pixelWidth, pixelHeight);
75
76 for (String chrom : windows.keySet()) {
77 if (maxChromosomeLength < windows.get(chrom).getRange()) {
78 maxChromosomeLength = windows.get(chrom).getRange();
79 }
80 }
81 }
82
83 public void setPixelSize(int width, int height) {
84 canvasWidth = width;
85 canvasHeight = height;
86
87 image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
88 g = (Graphics2D) image.createGraphics();
89 }
90
91 public BufferedImage getBufferedImage() {
92 return image;
93 }
94
95 public void setChromHeight(int height) {
96 this.chromHeight = height;
97 }
98
99 public void setChromMargin(int margin) {
100 this.chromMargin = margin;
101 }
102
103 public void setCharHeight(int charHeight) {
104 this.charHeight = charHeight;
105 }
106
107 public void setPixelHeight(int height) {
108 this.canvasHeight = height;
109 setPixelSize(canvasWidth, canvasHeight);
110 }
111
112 public void setPixelWidth(int width) {
113 this.canvasWidth = width;
114 setPixelSize(canvasWidth, canvasHeight);
115 }
116
117 public static int width(int x1, int x2) {
118 return (x1 < x2) ? x2 - x1 : x1 - x2;
119 }
120
121 public boolean isLighter() {
122 return isLighter;
123 }
124
125 public void setLighter(boolean isLighter) {
126 this.isLighter = isLighter;
127 }
128
129 public double getLighterRate() {
130 return lighterRate;
131 }
132
133 public void setLighterRate(double lighterRate) {
134 this.lighterRate = lighterRate;
135 }
136
137 public void setRotate() {
138 this.isRotate = true;
139 }
140
141
142 public <E extends CytoBand> void draw(List<E> cytoBandList) {
143
144
145 int chrNameWidth = 0;
146
147 if (!isRotate) {
148 for (String chromName : windows.keySet()) {
149 Font f = new Font("SansSerif", Font.PLAIN, charHeight);
150 g.setFont(f);
151
152 FontMetrics fontMetrics = g.getFontMetrics();
153 if (chrNameWidth < fontMetrics.stringWidth(chromName)) {
154 chrNameWidth = fontMetrics.stringWidth(chromName);
155 }
156 }
157 }
158 else {
159 chrNameWidth = charHeight * 2;
160 }
161 for (String chromName : windows.keySet()) {
162 windows.get(chromName).setLeftMargin(chrNameWidth);
163 }
164
165 if (!isRotate) {
166 setPixelHeight((windows.size()) * (chromHeight + chromMargin) + chromMargin);
167 }
168 else {
169 chromHeight = ((canvasWidth - chromMargin) / windows.size()) - chromMargin;
170 setPixelWidth((windows.size()) * (chromHeight + chromMargin) + chromMargin);
171 }
172
173
174 for (CytoBand c : cytoBandList) {
175 drawCytoBand(c, windows.get(c.getChrom()));
176 }
177
178
179 for (String chrName : windows.keySet()) {
180 drawChrName(chrName, windows.get(chrName).getRank());
181 }
182 }
183
184
185 public void drawCytoBand(CytoBand c, ChromosomeWindow w) {
186
187 int start = w.getXPosOnWindow(c.getStart(), windowWidth(w));
188 int end = w.getXPosOnWindow(c.getEnd(), windowWidth(w));
189
190 Font f = new Font("SansSerif", Font.PLAIN, charHeight);
191 g.setFont(f);
192 FontMetrics fontMetrics = g.getFontMetrics();
193
194 if (start > end) {
195 int temp = start;
196 start = end;
197 end = temp;
198 }
199
200 if (((!isRotate && start <= canvasWidth) || (isRotate && start <= canvasHeight)) && end >= 0) {
201 int offset = w.getRank() * (chromHeight + chromMargin) + chromMargin;
202
203 if (c.getGieStain() != null && c.getGieStain().equals("acen")) {
204
205 if (c.getName().startsWith("p")) {
206 drawTriangle(start, end, offset, getCytoBandColor(c));
207 }
208 else {
209 drawTriangle(end, start, offset, getCytoBandColor(c));
210 }
211 }
212 else {
213
214
215 fillRect(start, end, offset, getCytoBandColor(c));
216 drawRect(start, end, offset, getFrameColor());
217
218
219
220 if (end - start > fontMetrics.stringWidth(c.getName()) && charHeight <= chromHeight && !isRotate) {
221 drawCytoBandName(c, w);
222 }
223 }
224 }
225 }
226
227
228 public void drawMapping(String[] data) {
229 drawMapping(data[1], Integer.valueOf(data[8]), Integer.valueOf(data[9]));
230 }
231
232 public void drawMapping(String chrom, int startOnChrom, int endOnChrom) {
233 if (startOnChrom <= endOnChrom) {
234 drawMapping(chrom, startOnChrom, endOnChrom, '+');
235 }
236 else {
237 drawMapping(chrom, endOnChrom, startOnChrom, '-');
238 }
239 }
240
241 public void drawMapping(String chrom, int startOnChrom, int endOnChrom, char strand) {
242 assert startOnChrom <= endOnChrom;
243 Color color;
244 if (windows.containsKey(chrom)) {
245 int start = windows.get(chrom).getXPosOnWindow(startOnChrom, windowWidth(windows.get(chrom)));
246 int end = windows.get(chrom).getXPosOnWindow(endOnChrom, windowWidth(windows.get(chrom)));
247 int offset = windows.get(chrom).getRank() * (chromHeight + chromMargin) + chromMargin;
248
249 if (strand == '+') {
250 color = new Color(255, 0, 0, 192);
251 }
252 else {
253 color = new Color(0, 0, 255, 192);
254 }
255
256 if (start > end) {
257 int t = start;
258 start = end;
259 end = t;
260 }
261 int w = end - start;
262 if (w <= 2)
263 w = 2;
264
265 fillRect(start, offset - 3, w, chromHeight + 6, color);
266
267
268 }
269 }
270
271 public void drawGenomeWindow(String chrom, long startOnChrom, long endOnChrom) {
272 if (windows.containsKey(chrom)) {
273 int start = windows.get(chrom).getXPosOnWindow(startOnChrom, windowWidth(windows.get(chrom)));
274 int end = windows.get(chrom).getXPosOnWindow(endOnChrom, windowWidth(windows.get(chrom)));
275 int offset = windows.get(chrom).getRank() * (chromHeight + chromMargin) + chromMargin;
276
277 g.setColor(new Color(128, 64, 255));
278 g.setStroke(new BasicStroke(1.0f));
279
280 if (start > end) {
281 int t = start;
282 start = end;
283 end = t;
284 }
285 int w = end - start;
286 if (w <= 3)
287 w = 3;
288
289 drawRect(start, offset - 3, w, chromHeight + 6, new Color(128, 64, 255));
290
291
292 }
293 }
294
295
296 public void drawCytoBandName(CytoBand c, ChromosomeWindow w) {
297 Font f = new Font("SansSerif", Font.PLAIN, charHeight);
298 g.setFont(f);
299 FontMetrics fontMetrics = g.getFontMetrics();
300 int fontWidth = fontMetrics.stringWidth(c.getName());
301
302 int start = w.getXPosOnWindow(c.getStart(), windowWidth(w));
303 int end = w.getXPosOnWindow(c.getEnd(), windowWidth(w));
304
305 int offset = (w.getRank() + 1) * (chromHeight + chromMargin);
306
307 drawText(c.getName(), (start + end - fontWidth) / 2, offset, getCytoBandNameColor(c));
308 }
309
310 public void drawChrName(String chrName, Integer rank) {
311 if (!isRotate) {
312 drawText(chrName, 0, (rank + 1) * (chromHeight + chromMargin), Color.BLACK);
313 }
314 else {
315 drawText(chrName, charHeight * (rank % 2), rank * (chromHeight + chromMargin) + chromMargin, Color.BLACK);
316 }
317 }
318
319 public void drawText(String text, int x, int y, Color color) {
320 Font f = new Font("SansSerif", Font.PLAIN, charHeight);
321 g.setFont(f);
322 g.setColor(color);
323 if (!isRotate)
324 g.drawString(text, x, y - 1);
325 else
326 g.drawString(text, y - 1, canvasHeight - x);
327 }
328
329 public void fillRect(int x1, int x2, int y, Color c) {
330 fillRect(x1, y, x2 - x1, chromHeight, c);
331 }
332
333 public void fillRect(int x, int y, int width, int height, Color color) {
334 Rectangle2D rect;
335
336 if (!isRotate)
337 rect = new Rectangle2D.Double(x, y, width, height);
338 else
339 rect = new Rectangle2D.Double(y, canvasHeight - x - width, height, width);
340
341 g.setColor(color);
342 g.fill(rect);
343 }
344
345 public void drawRect(int x1, int x2, int y, Color c) {
346 drawRect(x1, y, x2 - x1, chromHeight, c);
347 }
348
349 public void drawRect(int x, int y, int width, int height, Color color) {
350 Rectangle2D rect;
351
352 if (!isRotate)
353 rect = new Rectangle2D.Double(x, y, width, height);
354 else
355 rect = new Rectangle2D.Double(y, canvasHeight - x - width, height, width);
356
357 g.setColor(color);
358 g.draw(rect);
359 }
360
361 public void drawTriangle(int x1, int x2, int y, Color color) {
362 Polygon tri = new Polygon();
363
364 if (!isRotate) {
365 tri.addPoint(x1, y);
366 tri.addPoint(x1, y + chromHeight);
367 tri.addPoint(x2, y + (chromHeight / 2));
368 }
369 else {
370 tri.addPoint(y, canvasHeight - x1);
371 tri.addPoint(y + chromHeight, canvasHeight - x1);
372 tri.addPoint(y + (chromHeight / 2), canvasHeight - x2);
373 }
374
375 g.setColor(color);
376 g.fill(tri);
377 }
378
379 public void toPNG(OutputStream out) throws IOException {
380 ImageIO.write(image, "png", out);
381 }
382
383 public Color getCytoBandColor(CytoBand c) {
384 Color color;
385
386 if (c.getGieStain() == null) {
387 color = Color.WHITE;
388 }
389 else if (c.getGieStain().equals("gpos100")) {
390 color = Color.BLACK;
391 }
392 else if (c.getGieStain().equals("gpos75")) {
393 color = Color.DARK_GRAY;
394 }
395 else if (c.getGieStain().equals("gpos") || c.getGieStain().equals("gpos50")) {
396 color = Color.GRAY;
397 }
398 else if (c.getGieStain().equals("gpos25")) {
399 color = Color.LIGHT_GRAY;
400 }
401 else if (c.getGieStain().equals("gvar")) {
402 color = Color.LIGHT_GRAY;
403 }
404 else if (c.getGieStain().equals("acen")) {
405 color = new Color(150, 25, 25);
406 }
407 else if (c.getGieStain().equals("stalk")) {
408 color = Color.GRAY;
409 }
410 else {
411 color = Color.WHITE;
412 }
413
414 if (isLighter) {
415 return colorLighter(color);
416 }
417 return color;
418 }
419
420 public Color getCytoBandNameColor(CytoBand c) {
421 if (c.getGieStain() == null)
422 return Color.WHITE;
423
424 if (c.getGieStain().equals("gpos100") || c.getGieStain().equals("gpos75")) {
425 return Color.WHITE;
426 }
427 else {
428 if (isLighter) {
429 return colorLighter(Color.BLACK);
430 }
431 return Color.BLACK;
432 }
433 }
434
435 public Color getFrameColor() {
436 if (isLighter) {
437 return colorLighter(Color.BLACK);
438 }
439 return Color.BLACK;
440 }
441
442 public Color colorLighter(Color color) {
443 return new Color(calcLighterValue(color.getRed()), calcLighterValue(color.getGreen()), calcLighterValue(color.getBlue()));
444 }
445
446 public int calcLighterValue(int value) {
447 return value + (int) ((255 - value) * lighterRate);
448 }
449
450
451 private int windowWidth(ChromosomeWindow w) {
452 double rate = 1.0;
453 double ratio = 1.0 - ((1.0 - ((double) w.getRange() / (double) maxChromosomeLength)) / rate);
454
455 if (!isRotate) {
456 return (int) ((canvasWidth - w.getLeftMargin()) * ratio);
457 }
458 else {
459 return (int) ((canvasHeight - w.getLeftMargin()) * ratio);
460 }
461 }
462 }