package org.seqcode.viz.genomicplot;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.seqcode.deepseq.experiments.Sample;
import org.seqcode.genome.location.ExonicGene;
import org.seqcode.genome.location.Gene;
import org.seqcode.genome.location.Region;
import org.seqcode.gseutils.Pair;
import org.seqcode.projects.seqview.paintable.NonOverlappingLayout;
import org.seqcode.viz.paintable.AbstractPaintable;
public abstract class FigurePaintable extends AbstractPaintable{
protected FigureOptions options;
protected NonOverlappingLayout<Gene> layout;
protected boolean reverseIt;
protected int topBound, bottomBound, leftBound, rightBound, baseLine;
protected int rstart, rstop;
protected String chr;
protected void drawCoordinates(Graphics2D g2d, int x1, int y, int x2){
g2d.setColor(Color.black);
g2d.drawLine(leftBound, y, rightBound, y);
g2d.setFont(new Font("Ariel", Font.PLAIN, options.fontSize));
FontMetrics metrics = g2d.getFontMetrics();
AffineTransform oldtrans = g2d.getTransform();
AffineTransform newtrans = new AffineTransform();
String text = reverseIt ? new String("chr"+chr+":"+rstop) : new String("chr"+chr+":"+rstart);
newtrans.translate(leftBound, y+metrics.stringWidth(text)+metrics.getHeight()+10);
newtrans.rotate(Math.toRadians(-90));
g2d.setTransform(newtrans);
g2d.drawString(text,0,0);
g2d.setTransform(oldtrans);
newtrans = new AffineTransform();
text = reverseIt ? new String("chr"+chr+":"+rstart) : new String("chr"+chr+":"+rstop);
newtrans.translate(rightBound+(metrics.getHeight()), y+metrics.stringWidth(text)+metrics.getHeight()+10);
newtrans.rotate(Math.toRadians(-90));
g2d.setTransform(newtrans);
g2d.drawString(text,0,0);
g2d.setTransform(oldtrans);
}
protected int drawGenes(Graphics2D g2d, int x1, int y, int x2, List<Gene> genes, boolean drawNames){return(drawGenes(g2d, x1, y, x2, genes, drawNames, ""));}
protected int drawGenes(Graphics2D g2d, int x1, int y, int x2, List<Gene> genes, boolean drawNames, String trackName){
layout = new NonOverlappingLayout<Gene>();
layout.setRegions(genes);
int[] a = new int[7];
int[] b = new int[7];
FontMetrics metrics = g2d.getFontMetrics();
for(Gene g : genes){
if(g instanceof ExonicGene) {
int track = 0;
if(!layout.hasTrack(g)) {
System.err.println("No track assigned to gene: " + g.getName());
} else {
track = layout.getTrack(g);
}
ExonicGene gene = (ExonicGene)g;
int gy1 = y+ (2 * options.geneHeight * track);
int halfGeneHeight = options.geneHeight / 2;
int gmy = gy1 + halfGeneHeight;
int geneStart = gene.getStart(), geneEnd = gene.getEnd();
boolean strand = reverseIt ? (gene.getStrand() == '-'):(gene.getStrand() == '+');
int gx1 = reverseIt ? xcoord(geneEnd):xcoord(geneStart);
int gx2 = reverseIt? xcoord(geneStart):xcoord(geneEnd);
gx1=Math.max(leftBound, gx1);
gx2=Math.min(rightBound, gx2);
g2d.setColor(Color.black);
g2d.drawLine(gx1, gmy, gx2, gmy);
if((g.getStrand()!='.') && (g.getFivePrime()>=options.gRegion.getStart() && g.getFivePrime()<=options.gRegion.getEnd())){
arrangeArrow(a, b, strand, halfGeneHeight, gx1, gx2, gmy);
g2d.drawPolyline(a, b, 7);
}
Iterator<Region> exons = gene.getExons();
while(exons.hasNext()) {
Region exon = exons.next();
if(exon.overlaps(options.gRegion)){
int ex1 = reverseIt ? xcoord(exon.getEnd()) : xcoord(exon.getStart());
int ex2 = reverseIt?xcoord(exon.getStart()): xcoord(exon.getEnd());
int eleft = Math.max(leftBound, ex1);
int eright = Math.min(rightBound, ex2);
int rectwidth = eright - eleft + 1;
if(g.getName().equals(options.specialGeneName)){
g2d.setColor(options.specialGeneColor);
}else{
g2d.setColor(options.geneColor);
}
g2d.fillRect(eleft, gy1, rectwidth, options.geneHeight);
g2d.setColor(Color.black);
g2d.drawRect(eleft, gy1, rectwidth, options.geneHeight);
}
}
//Gene name
if(drawNames){
g2d.setColor(Color.gray);
g2d.setFont(new Font("Ariel", Font.BOLD, options.geneFontSize));
metrics = g2d.getFontMetrics();
AffineTransform oldtrans = g2d.getTransform();
AffineTransform newtrans = new AffineTransform();
int nx = strand ? gx1 : gx2;
int ny = gmy+(options.geneHeight)+(metrics.getHeight()*2);
int str = strand ? 1 : 0;
newtrans.translate(nx, ny);
newtrans.rotate(Math.toRadians(-90));
g2d.setTransform(newtrans);
g2d.drawString(gene.getName(),-1*metrics.stringWidth(gene.getName()),str*(metrics.getHeight()));
g2d.setTransform(oldtrans);
}
}
}
int finalOff = layout.getNumTracks()*2*options.geneHeight+options.geneFontSize;
//Track name
if(options.drawTrackNames){
g2d.setColor(Color.lightGray);
g2d.setFont(new Font("Ariel", Font.BOLD, options.geneFontSize));
metrics = g2d.getFontMetrics();
g2d.drawString(trackName,(leftBound+rightBound)/2-metrics.stringWidth(trackName)/2,y+finalOff);
g2d.setStroke(new BasicStroke(1.0f));
//g2d.drawLine(leftBound, y+finalOff, rightBound, y+finalOff);
}
return(finalOff+10);
}
protected int drawJunctions(Graphics2D g2d, int x1, int y, int x2, List<Pair<Gene, Double>> scoredJunctions){
ArrayList<Gene> juncts = new ArrayList<Gene>();
double maxScore = 0;
for(Pair<Gene,Double> p : scoredJunctions){
juncts.add(p.car());
if(p.cdr()>maxScore)
maxScore = p.cdr();
}
layout = new NonOverlappingLayout<Gene>();
layout.setRegions(juncts);
g2d.setColor(Color.black);
g2d.setFont(new Font("Ariel", Font.PLAIN, options.geneFontSize));
FontMetrics metrics = g2d.getFontMetrics();
int maxY = y+ (2 * options.geneHeight * layout.getNumTracks());
for(Pair<Gene,Double> p : scoredJunctions){
Gene g = p.car();
Double s = p.cdr();
if(g instanceof ExonicGene) {
int track = 0;
if(!layout.hasTrack(g)) {
System.err.println("No track assigned to gene: " + g.getName());
} else {
track = layout.getTrack(g);
}
ExonicGene gene = (ExonicGene)g;
int gy1 = maxY - (2 * options.geneHeight * track) - options.geneHeight;
int halfGeneHeight = options.geneHeight / 2;
int gmy = gy1 + halfGeneHeight;
int geneStart = gene.getStart(), geneEnd = gene.getEnd();
boolean strand = reverseIt ? (gene.getStrand() == '-'):(gene.getStrand() == '+');
int gx1 = reverseIt ? xcoord(geneEnd):xcoord(geneStart);
int gx2 = reverseIt? xcoord(geneStart):xcoord(geneEnd);
gx1=Math.max(leftBound, gx1);
gx2=Math.min(rightBound, gx2);
g2d.setColor(Color.black);
g2d.drawLine(gx1, gmy, gx2, gmy);
//Score
String scoreStr = String.format("%.0f", s);
g2d.drawString(scoreStr, (gx1+gx2)/2-metrics.stringWidth(scoreStr)/2, gy1);
Iterator<Region> exons = gene.getExons();
while(exons.hasNext()) {
Region exon = exons.next();
if(exon.overlaps(options.gRegion)){
int ex1 = reverseIt ? xcoord(exon.getEnd()) : xcoord(exon.getStart());
int ex2 = reverseIt?xcoord(exon.getStart()): xcoord(exon.getEnd());
int eleft = Math.max(x1, ex1);
int eright = Math.min(x2, ex2);
int rectwidth = eright - eleft + 1;
double percScore = s/maxScore;
Color cx = new Color((int)(255-(255*percScore)), (int)(255-(255*percScore)), (int)(255-(255*percScore)));
g2d.setColor(cx);
g2d.fillRect(eleft, gy1, rectwidth, options.geneHeight);
g2d.setColor(Color.black);
g2d.drawRect(eleft, gy1, rectwidth, options.geneHeight);
}
}
}
}
int finalOff = layout.getNumTracks()*2*options.geneHeight;
return(finalOff+10);
}
protected int xcoord(int coord) {
double frac = ((double)(coord-rstart)/(double)(rstop-rstart));
int pos;
if(reverseIt)
pos = leftBound+(int)((double)(rightBound-leftBound)*(1-frac));
else
pos= leftBound+(int)((double)(rightBound-leftBound)*frac);
return(pos);
}
protected void arrangeArrow(int[] a, int[] b, boolean strand, int geneHeight, int gx1, int gx2, int my) {
double arrowHt = 0.15 * geneHeight;
double arrowWd = 1;
int a1, a2, a3;
// forward arrow
if(strand) {
int startX = gx1;
a1 = startX;
a2 = (int) Math.round(startX + (arrowWd * 6));
a3 = (int) Math.round(startX + (arrowWd * 10));
} else {
// backward arrow
int startX = gx2+1;
a1 = startX;
a2 = (int) Math.round(startX - (arrowWd * 6));
a3 = (int) Math.round(startX - (arrowWd * 10));
}
a[0] = a1;
a[1] = a1;
a[2] = a2;
a[3] = a2;
a[4] = a3;
a[5] = a2;
a[6] = a2;
int b1 = (int) Math.round(my);
int b2 = (int) Math.round(my + (arrowHt * 13));
int b3 = (int) Math.round(my + (arrowHt * 10));
int b4 = (int) Math.round(my + (arrowHt * 16));
b[0] = b1;
b[1] = b2;
b[2] = b2;
b[3] = b3;
b[4] = b2;
b[5] = b4;
b[6] = b2;
}
public ArrayList<Gene> loadGenes(File gtf, Region reg){
ArrayList<Gene> theGenes = new ArrayList<Gene>();
try {
if(!gtf.isFile()){System.err.println("Invalid GTF filename");System.exit(1);}
BufferedReader reader = new BufferedReader(new FileReader(gtf));
String currGeneName=null;
String currTransName=null;
String currTrans="";
ArrayList<Region> exons=new ArrayList<Region>();
Integer geneStart=-1, geneStop=-1; String geneChr=""; char geneStrand='+';
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
String[] words = line.split("\\t");
if(words[2].equals("exon")){
String chr = words[0].replaceAll("chr", "");
if(chr.equals("MT"))
chr = "M";
Integer start = new Integer(words[3]);
Integer stop = new Integer(words[4]);
char strand = words[6].charAt(0);
String transID = ""; String transName=null; String geneName=null;
String[] dwords = line.split(";");
for(String d : dwords){
String [] dxwords = d.split("\\s+");
for(int s=0; s<dxwords.length-1; s++){
if(dxwords[s].equals("transcript_id"))
transID = dxwords[s+1].replaceAll("\"", "");
if(dxwords[s].equals("transcript_name"))
transName = dxwords[s+1].replaceAll("\"", "");
if(dxwords[s].equals("gene_name"))
geneName = dxwords[s+1].replaceAll("\"", "");
}
}
Region exon = new Region(options.genome, chr, start, stop);
if(transID.equals(currTrans)){ //Add a new exon
exons.add(exon);
if(stop>geneStop)
geneStop = stop;
if(start<geneStart)
geneStart=start;
}else{ //Check if recorded gene should be saved. Record a new gene.
if(geneStart != -1){
ExonicGene currGene = new ExonicGene(options.genome, geneChr, geneStart, geneStop, currGeneName==null ? currTrans : currGeneName, currTrans, geneStrand, "user");
if(currGene.overlaps(options.gRegion)){
for(Region e : exons)
currGene.addExon(e);
theGenes.add(currGene);
System.out.println(currGene.getName()+"\t"+currGene.getLocationString());
}
}
//New gene
geneStart = start; geneStop = stop; geneChr = chr; geneStrand = strand;
exons=new ArrayList<Region>();
exons.add(exon);
currTrans = transID;
currGeneName = geneName;
currTransName = transName;
}
}
}
if(exons.size()>0){
ExonicGene currGene = new ExonicGene(options.genome, geneChr, geneStart, geneStop, currGeneName==null ? currTrans : currGeneName, currTrans, geneStrand, "user");
if(currGene.overlaps(options.gRegion)){
for(Region e : exons)
currGene.addExon(e);
theGenes.add(currGene);
System.out.println(currGene.getName()+"\t"+currGene.getLocationString());
}
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return theGenes;
}
}