View Javadoc

1   /*--------------------------------------------------------------------------
2    *  Copyright 2008 utgenome.org
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *--------------------------------------------------------------------------*/
16  //--------------------------------------
17  // utgb-core Project
18  //
19  // GeneCanvas.java
20  // Since: Jul 8, 2008
21  //
22  // $URL: http://svn.utgenome.org/utgb/trunk/utgb/utgb-core/src/main/java/org/utgenome/gwt/utgb/client/canvas/GeneCanvas.java $ 
23  // $Author: leo $
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   * drawing chromosome map
47   * 
48   * @author yoshimura
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 	// draw Chromosomes
142 	public <E extends CytoBand> void draw(List<E> cytoBandList) {
143 
144 		// calc left margin
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 		// draw CytoBand
174 		for (CytoBand c : cytoBandList) {
175 			drawCytoBand(c, windows.get(c.getChrom()));
176 		}
177 
178 		// draw ChromosomeName
179 		for (String chrName : windows.keySet()) {
180 			drawChrName(chrName, windows.get(chrName).getRank());
181 		}
182 	}
183 
184 	// draw a CytoBand
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 				//draw Centromere
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 				// draw Chromosome
214 				//canvas.setGlobalAlpha(0.5f);
215 				fillRect(start, end, offset, getCytoBandColor(c));
216 				drawRect(start, end, offset, getFrameColor());
217 				//canvas.setGlobalAlpha(1f);
218 
219 				// Then Band Width longer than Name String, drawing Band Name.
220 				if (end - start > fontMetrics.stringWidth(c.getName()) && charHeight <= chromHeight && !isRotate) {
221 					drawCytoBandName(c, w);
222 				}
223 			}
224 		}
225 	}
226 
227 	// draw Mapping data
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 			//			Rectangle2D rect = new Rectangle2D.Double(start, offset - 3, 2, chromHeight + 6);
267 			//			g.fill(rect);
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 			//			Rectangle2D rect = new Rectangle2D.Double(start, offset - 3, w, chromHeight + 6);
291 			//			g.draw(rect);
292 		}
293 	}
294 
295 	// draw Band Name
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 	// adjust window width of each chromosome
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 }