package org.seqcode.viz.genomicplot;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.seqcode.deepseq.experiments.Sample;
import org.seqcode.genome.location.ExonicGene;
import org.seqcode.genome.location.Region;
import org.seqcode.genome.location.StrandedRegion;
import org.seqcode.gseutils.Pair;
import org.seqcode.viz.paintable.AbstractPaintable;
public class MultidataSpatialPaintable extends AbstractPaintable{
private boolean reverseIt=true;
private int screenSizeX, screenSizeY;
private final int geneHeight=15;
private final int boxHeight=30, boxWidth = 18, siteWidth=10, litWidth = 4, motifWidth=5, motifHeight=10;
private final int exptTrackHeight=120;
private final int colorbarHeight=14, colorbarWidth=200;
private final int rTrack=75;
private final double maxExp=6.0, minExp=-3.0;
private final double maxSiteZ = 200, minSiteZ=60;
private Color geneColor = new Color(232,232,232);
private Color motifColor = new Color(51,102,0);
private Color siteMaxColor = new Color(92,0,184);
private Color siteMinColor = new Color(184,0,184);
private Color rareColDR5 = new Color(0,153,0);
private Color rareColDR2 = new Color(153,153,255);
private final int topBorder=50, bottomBorder=50;
private final int leftBorder=25, rightBorder=25;
private int topBound, bottomBound, leftBound, rightBound, baseLine;
private List<String> timeLabels;
private List<Sample> times;
private List<ExonicGene> genes;
private Map<String, List<Double>> expression;
private Map<String, List<Region>> sites=null;
private List<Pair<String, StrandedRegion>> motifs = new ArrayList<Pair<String, StrandedRegion>>();
private List<Region> literature = new ArrayList<Region>();
private Region gRegion;
private int rstart, rstop, rwidth;
private String chr;
private Map<Sample, ThinOverlapPaintable> exptPainters = new HashMap<Sample, ThinOverlapPaintable>();
private List<ThinOverlapPaintable> thinpaints = new ArrayList<ThinOverlapPaintable>();
public MultidataSpatialPaintable(List<String> timepointLabels, List<Sample> timepoints, Region genomeRegion, List<ExonicGene> genes, Map<String, List<Double>> expression, Map<String, List<Region>> sites, List<Pair<String, StrandedRegion>> mHits, List<Region> lits){
timeLabels = timepointLabels;
times = timepoints;
rstart = genomeRegion.getStart();
rstop = genomeRegion.getEnd();
chr= genomeRegion.getChrom();
gRegion = genomeRegion;
rwidth = rstop - rstart;
this.genes = genes;
this.expression = expression;
this.sites = sites;
motifs = mHits;
literature = lits;
for(Sample t : times){
ThinOverlapPaintable tp = new ThinOverlapPaintable(gRegion, sites.get(t), t, 200, false);
tp.setReverse(reverseIt);
tp.setMaxOverlap(220);
exptPainters.put(t, tp);
thinpaints.add(tp);
}
}
public void paintItem (Graphics g, int x1, int y1, int x2, int y2){
Graphics2D g2d = (Graphics2D)g;
FontMetrics metrics = g2d.getFontMetrics();
screenSizeX = x2-x1;
screenSizeY = y2-y1;
g2d.setColor(Color.white);
g2d.fillRect(0, 0, screenSizeX, screenSizeY);
topBound = topBorder;
bottomBound = screenSizeY-bottomBorder;
leftBound = leftBorder+30;
rightBound = screenSizeX-rightBorder;
baseLine = (topBound+bottomBound)/2;
//AlignedMotifPaintable timMotifPaint = new AlignedMotifPaintable(gRegion, motifs, thinpaints.get(1), thinpaints.get(0), reverseIt, motifWidth);
//Draw the genes
boolean drawGeneNum=true;
int offset=0, time=0;
for(Sample t : times){
ThinOverlapPaintable p = exptPainters.get(t);
//p.paintItem(g, leftBound, baseLine-offset-motifHeight-exptTrackHeight, rightBound, baseLine-offset-motifHeight-1);
p.paintItem(g, leftBound, baseLine-offset-exptTrackHeight, rightBound, baseLine-offset);
//Draw the RAREs
//drawRAREs(g2d, baseLine-offset-motifHeight);
//Draw the axis
g2d.setColor(Color.lightGray);
g2d.setStroke(new BasicStroke(1.0f));
//g2d.drawLine(leftBound, baseLine-offset-motifHeight, rightBound, baseLine-offset-motifHeight);
//Draw the timepoint name
g2d.setColor(Color.darkGray);
g2d.setFont(new Font("Ariel", Font.BOLD, 14));
metrics = g2d.getFontMetrics();
String currT = timeLabels.get(time);
int ypos = baseLine-offset-(metrics.getHeight()/2);
offset+=(exptTrackHeight+motifHeight+10);
time++;
}
offset=0; time=0;
for(Sample t : times){
drawGenes(g2d, x1, baseLine-offset, x2, time, drawGeneNum);
offset+=(exptTrackHeight+motifHeight+(geneHeight+14));
time++;
drawGeneNum=false;
}
//Draw some coordinates
g2d.setColor(Color.black);
g2d.setFont(new Font("Ariel", Font.PLAIN, 14));
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, baseLine+30+metrics.stringWidth(text));
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()/2), baseLine+30+metrics.stringWidth(text));
newtrans.rotate(Math.toRadians(-90));
g2d.setTransform(newtrans);
g2d.drawString(text,0,0);
g2d.setTransform(oldtrans);
//draw the colorbars
// drawExpColorBar(g2d, (leftBound+rightBound)/2-(colorbarWidth/2), topBound);
/*
//Special
g2d.setFont(new Font("Ariel", Font.BOLD, 18));
g2d.setColor(rareColDR2);
g2d.drawString("DR2", leftBound+10, currLine+(boxHeight/2)+(metrics.getHeight()/2));
g2d.setColor(Color.black);
g2d.drawString(" & ", leftBound+10+metrics.stringWidth("DR2"), currLine+(boxHeight/2)+(metrics.getHeight()/2));
g2d.setColor(rareColDR5);
g2d.drawString("DR5", leftBound+10+metrics.stringWidth("DR2 & "), currLine+(boxHeight/2)+(metrics.getHeight()/2));
//Draw the literature sites
currLine = baseLine+rTrack+boxHeight;
for(Region r : literature){
int gx1 = xcoord(r.getStart())-(litWidth/2);
g2d.setColor(motifColor);
g2d.fillRect(gx1, currLine, litWidth, boxHeight);
}//Special
g2d.setColor(motifColor);
g2d.drawString("Literature", leftBound+10, currLine+(boxHeight/2)+(metrics.getHeight()/2));
*/
// drawLegend(g2d, (leftBound+rightBound)/2+colorbarWidth, topBound);
}
private 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);
}
private Color expColor(double v){
Color c;
if(v>0){
Color maxColor = Color.yellow;
Color minColor = Color.black;
double sVal = v>maxExp ? 1 : (v)/(maxExp);
int red = (int)(maxColor.getRed() * sVal + minColor.getRed() * (1 - sVal));
int green = (int)(maxColor.getGreen() * sVal + minColor.getGreen() * (1 - sVal));
int blue = (int)(maxColor.getBlue() *sVal + minColor.getBlue() * (1 - sVal));
c = new Color(red, green, blue);
}else{
Color maxColor = Color.black;
Color minColor = Color.blue;
double sVal = v<minExp ? 1 : (minExp-v)/(minExp);
int red = (int)(maxColor.getRed() * sVal + minColor.getRed() * (1 - sVal));
int green = (int)(maxColor.getGreen() * sVal + minColor.getGreen() * (1 - sVal));
int blue = (int)(maxColor.getBlue() *sVal + minColor.getBlue() * (1 - sVal));
c = new Color(red, green, blue);
}
return(c);
}
private Color getSiteColor(double v){
Color c;
Color maxColor = siteMaxColor;
Color minColor = siteMinColor;
Color zeroColor = Color.white;
if(v>maxSiteZ)
v=maxSiteZ;
if(v<minSiteZ)
v=minSiteZ;
double sVal =(v-minSiteZ)/(maxSiteZ-minSiteZ);
if(v==0){return(zeroColor);}
int red = (int)(maxColor.getRed() * sVal + minColor.getRed() * (1 - sVal));
int green = (int)(maxColor.getGreen() * sVal + minColor.getGreen() * (1 - sVal));
int blue = (int)(maxColor.getBlue() *sVal + minColor.getBlue() * (1 - sVal));
c = new Color(red, green, blue);
return(c);
}
private void drawGenes(Graphics2D g2d, int x1, int y, int x2, int exprTime, boolean drawNames){
int[] a = new int[7];
int[] b = new int[7];
FontMetrics metrics = g2d.getFontMetrics();
for(ExonicGene gene : genes) {
int trackHeight = 200;
int gy1 = y;
int halfGeneHeight = geneHeight / 2;
int gmy = y + 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);
g2d.setColor(Color.black);
g2d.drawLine(gx1, gmy, gx2, gmy);
arrangeArrow(a, b, strand, trackHeight, gx1, gx2, gmy);
g2d.drawPolyline(a, b, 7);
Iterator<Region> exons = gene.getExons();
while(exons.hasNext()) {
Region exon = exons.next();
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;
//Expression filled rectangles (if they exist)
g2d.setColor(geneColor);
if(expression.containsKey(gene.getName())){
Double val = expression.get(gene.getName()).get(exprTime);
//Convert color
Color eCol = expColor(val.doubleValue());
g2d.setColor(eCol);
}
g2d.fillRect(eleft, gy1, rectwidth, geneHeight);
g2d.setColor(Color.black);
g2d.drawRect(eleft, gy1, rectwidth, geneHeight);
}
//Gene name
if(drawNames){
g2d.setFont(new Font("Ariel", Font.BOLD, 24));
metrics = g2d.getFontMetrics();
int nx = strand ? gx1 : gx2;
int ny = gmy+(3*geneHeight);
g2d.setColor(Color.black);
Rectangle2D textrect = metrics.getStringBounds(gene.getName(), g2d);
int diff = metrics.getHeight()/2;
String cleanName = gene.getName();
cleanName = cleanName.replaceAll("Hoxa", "");
cleanName = cleanName.replaceAll("Hoxb", "");
cleanName = cleanName.replaceAll("Hoxc", "");
cleanName = cleanName.replaceAll("Hoxd", "");
g2d.drawString(cleanName,nx-(metrics.stringWidth(cleanName))/2,ny);
}
//Draw the axis
//g2d.setColor(Color.lightGray);
//g2d.setStroke(new BasicStroke(1.0f));
//g2d.drawLine(leftBound, y, rightBound, y);
}
}
private void drawRAREs(Graphics2D g2d, int currLine){
HashMap<String, Color> motifsProcessed= new HashMap<String, Color>();
for(Pair<String, StrandedRegion> pair : motifs){
if(!motifsProcessed.containsKey(pair.car())){
if(pair.car().equals("DR2"))
motifsProcessed.put(pair.car(), rareColDR2);
else if(pair.car().equals("DR5"))
motifsProcessed.put(pair.car(), rareColDR5);
else
motifsProcessed.put(pair.car(), new Color((int)(Math.random()*256),(int)(Math.random()*256),(int)(Math.random()*256)));
}
Color currCol = motifsProcessed.get(pair.car());
StrandedRegion sr = pair.cdr();
int start = sr.getStart();
if(sr.getStrand()=='-'){start = sr.getEnd();}
int gx1 = xcoord(start)-(motifWidth/2);
g2d.setColor(currCol);
if((sr.getStrand()=='+' && !reverseIt) || (sr.getStrand()=='-' && reverseIt)){
int [] xPoints = {gx1, gx1, gx1+motifWidth};
int [] yPoints = {currLine, currLine+motifHeight, currLine+(motifHeight/2)};
g2d.fillPolygon(xPoints, yPoints, 3);
}else{
gx1 = xcoord(sr.getEnd())+(motifWidth/2);
int [] xPoints = {gx1, gx1, gx1-motifWidth};
int [] yPoints = {currLine, currLine+motifHeight, currLine+(motifHeight/2)};
g2d.fillPolygon(xPoints, yPoints, 3);
}
}
}
private void arrangeArrow(int[] a, int[] b, boolean strand, int geneHeight, int gx1, int gx2, int my) {
double arrowHt = 0.005 * 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;
}
private void drawLegend(Graphics2D g2d, int x, int y){
g2d.setColor(rareColDR5);
int [] x1Points = {x, x, x+16};
int [] y1Points = {y, y+16, y+8};
g2d.fillPolygon(x1Points, y1Points, 3);
g2d.setFont(new Font("Ariel", Font.BOLD, 16));
FontMetrics metrics = g2d.getFontMetrics();
int textY = y+4+(metrics.getHeight()/2);
g2d.drawString("DR5 RARE Motif", x+15, textY);
g2d.setColor(rareColDR2);
int [] x2Points = {x, x, x+16};
int [] y2Points = {y+16, y+32, y+24};
g2d.fillPolygon(x2Points, y2Points, 3);
textY = y+20+ (metrics.getHeight()/2);
g2d.drawString("DR2 RARE Motif", x+15, textY);
}
private void drawExpColorBar(Graphics2D g2d, int x, int y){
//Draw colors
GradientPaint colorbar = new GradientPaint(x, y, Color.blue, x+colorbarWidth/2, y, Color.black, false);
g2d.setPaint(colorbar);
g2d.fillRect(x, y, colorbarWidth/2, colorbarHeight);
colorbar = new GradientPaint(x+colorbarWidth/2, y, Color.black, x+colorbarWidth, y, Color.yellow, false);
g2d.setPaint(colorbar);
g2d.fillRect(x+(colorbarWidth/2), y, colorbarWidth/2, colorbarHeight);
//Draw border
g2d.setPaint(Color.black);
g2d.setColor(Color.black);
g2d.setStroke(new BasicStroke(1.0f));
g2d.drawRect(x, y, colorbarWidth, colorbarHeight);
//Legend
g2d.setFont(new Font("Ariel", Font.PLAIN, 14));
FontMetrics metrics = g2d.getFontMetrics();
int textY = y+colorbarHeight+ (metrics.getHeight());
g2d.drawString("0", x+(colorbarWidth/2)-(metrics.stringWidth("0")/2), textY);
g2d.drawString(String.format("%.1f",minExp), x-(metrics.stringWidth(String.format(".1f",minExp))/2), textY);
g2d.drawString(String.format("%.1f",maxExp), x+colorbarWidth-(metrics.stringWidth(String.format(".1f",maxExp))/2), textY);
//Title
g2d.setFont(new Font("Ariel", Font.ITALIC, 14));
metrics = g2d.getFontMetrics();
g2d.drawString("log2-foldchange", x+(colorbarWidth/2)-(metrics.stringWidth("log2-foldchange")/2), y- (metrics.getHeight())/2);
}
private void drawSiteColorBar(Graphics2D g2d, int x, int y){
//Draw colors
GradientPaint colorbar = new GradientPaint(x, y, siteMinColor, x+colorbarWidth, y, siteMaxColor, false);
g2d.setPaint(colorbar);
g2d.fillRect(x, y, colorbarWidth, colorbarHeight);
//Draw border
g2d.setPaint(Color.black);
g2d.setColor(Color.black);
g2d.setStroke(new BasicStroke(3.0f));
g2d.drawRect(x, y, colorbarWidth, colorbarHeight);
//Legend
g2d.setFont(new Font("Ariel", Font.PLAIN, 16));
FontMetrics metrics = g2d.getFontMetrics();
int textY = y+colorbarHeight+ (metrics.getHeight());
g2d.drawString(String.format("%.1f",minSiteZ), x-(metrics.stringWidth(String.format("%.1f",minSiteZ))), textY);
g2d.drawString(String.format("%.1f",maxSiteZ), x+colorbarWidth-(metrics.stringWidth(String.format(".1f",maxSiteZ))/2), textY);
//Title
g2d.setFont(new Font("Ariel", Font.ITALIC, 16));
metrics = g2d.getFontMetrics();
g2d.drawString("RAR Peak (-10 log(p-value))", x+(colorbarWidth/2)-(metrics.stringWidth("RAR Peak (-10 log(p-value))")/2), y- (metrics.getHeight())/2);
}
}