/* * Author: tdanford * Date: Sep 24, 2008 */ package org.seqcode.viz.genomicplot; import java.util.*; import java.util.List; import java.awt.*; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import org.seqcode.deepseq.StrandedBaseCount; import org.seqcode.deepseq.experiments.Sample; import org.seqcode.genome.GenomeConfig; import org.seqcode.genome.location.Region; import org.seqcode.gsebricks.verbs.*; import org.seqcode.gseutils.Closeable; import org.seqcode.viz.paintable.*; public class DiffSeqExptPaintable extends AbstractPaintable { private GenomeConfig gconfig; private Sample signalSamp = null; private Sample controlSamp = null; private boolean axis =true; private boolean filledColumns=true; private Region region; private Vector<Region> highlighted; private int binWidth, binStep; private double scalingFactor; private Color negColor, posColor; private boolean reverse=false; private int maxLogFold=8; private int minLogFold=-8; private int sigPerBaseMax=2; private int ctrlPerBaseMax=2; private boolean needleFiltering=true; private double LOG2 = Math.log(2); private double[] diff; private int[] dataProfile; // y pixel values of where the data line is drawn. //Load data from ReadDB sources public DiffSeqExptPaintable(GenomeConfig gcon, Region basereg, Sample sig, Sample ctrl, int win, int step, double scaling) { this.gconfig = gcon; region = basereg; signalSamp = sig; controlSamp = ctrl; binWidth = win; binStep = step; scalingFactor = scaling; negColor = Color.blue; posColor = Color.yellow; reverse = false; loadData(); } //Load data from preformatted file public DiffSeqExptPaintable(GenomeConfig gcon, Region basereg, File data, int win, int step, double scaling) { this.gconfig = gcon; region = basereg; binWidth = win; binStep = step; scalingFactor = scaling; negColor = Color.blue; posColor = Color.yellow; reverse = false; loadDataFromFile(data); } public void setNegColor(Color c){negColor = c;} public void setPosColor(Color c){posColor = c;} public void setSigPerBaseMax(int pb){sigPerBaseMax = pb;} public void setCtrlPerBaseMax(int pb){ctrlPerBaseMax = pb;} public void setReverse(boolean r) { reverse = r; dispatchChangedEvent(); } public void setMaxLogFold(int m){maxLogFold=m;} public void setMinLogFold(int m){minLogFold=m;} public void synchronizeMaxLogFold(DiffSeqExptPaintable p) { int maxo = Math.max(maxLogFold, p.maxLogFold); maxLogFold = maxo; p.maxLogFold = maxo; int mino = Math.max(minLogFold, p.minLogFold); minLogFold = mino; p.minLogFold = mino; dispatchChangedEvent(); p.dispatchChangedEvent(); } public void loadData() { System.err.println("Loading data in "+region); List<StrandedBaseCount> AHits = new ArrayList<StrandedBaseCount>(); List<StrandedBaseCount> BHits = new ArrayList<StrandedBaseCount>(); if(signalSamp !=null) AHits.addAll(signalSamp.getBases(region)); if(controlSamp !=null) BHits.addAll(controlSamp.getBases(region)); double [] ABinnedReads = makeHitLandscape(AHits, region, sigPerBaseMax, '.'); double [] BBinnedReads = makeHitLandscape(BHits, region, ctrlPerBaseMax, '.'); int numBins = (int)(region.getWidth()/binStep); diff = new double[numBins+1]; for(int x=0; x<=numBins; x++){ if(ABinnedReads[x]==0 && BBinnedReads[x]==0) diff[x] = 0; else if(BBinnedReads[x]==0) diff[x] = Math.min(Math.log(ABinnedReads[x]), Math.log(maxLogFold)/LOG2); else if(ABinnedReads[x]==0) diff[x] = -1* Math.min(Math.log(BBinnedReads[x]*scalingFactor), -Math.log(minLogFold)/LOG2); else diff[x] = Math.log(ABinnedReads[x]/(BBinnedReads[x]*scalingFactor))/LOG2; } } public void loadDataFromFile(File data) { //Load data HashMap<String, Double> dataHash = new HashMap<String,Double>(); try { if(!data.isFile()){System.err.println("Invalid file name: "+data.getAbsolutePath());System.exit(1);} BufferedReader reader = new BufferedReader(new FileReader(data)); String line; while ((line = reader.readLine()) != null) { line = line.trim(); String[] words = line.split("\\s+"); if(!words[0].equals("Position")) dataHash.put(words[0], new Double(words[1])); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //format int numBins = (int)(region.getWidth()/binStep); diff = new double[numBins+1]; String chr = region.getChrom(); int start = region.getStart(); for(int x=0; x<=numBins; x++){ int curr =start +(x*binStep); String currPos = chr+":"+curr; if(dataHash.containsKey(currPos)) diff[x] = dataHash.get(currPos); else diff[x] = 0; } } //Makes integer arrays corresponding to the read landscape over the current region private double [] makeHitLandscape(List<StrandedBaseCount> hits, Region currReg, int perBaseMax, char strand){ int numBins = (int)(currReg.getWidth()/binStep); int [] counts = new int[currReg.getWidth()+1]; double [] landscape = new double[numBins+1]; for(int i=0; i<=numBins; i++){ landscape[i]=0;} for(int i=0; i<=currReg.getWidth(); i++){counts[i]=0;} int ext = binWidth/2; for(StrandedBaseCount r : hits){ if(strand=='.' || r.getStrand()==strand){ int rpos = r.getCoordinate(); int offset=inBounds(rpos-currReg.getStart(),0,currReg.getWidth()); counts[offset]++;//small issue here... counts will not be corrected for scalingFactor in control if(!needleFiltering || (counts[offset] <= perBaseMax)){ int binstart = inBounds((int)((double)(offset-ext)/binStep), 0, numBins); int binend = inBounds((int)((double)(offset+ext)/binStep), 0, numBins); for(int i=binstart; i<=binend; i++){ landscape[i]+=r.getCount(); } } } } return(landscape); } private final int inBounds(int x, int min, int max){ if(x<min){return min;} if(x>max){return max;} return x; } public void paintItem(Graphics g, int x1, int y1, int x2, int y2) { Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); int w = x2-x1; dataProfile = new int[w+2]; g2.setColor(Color.white); g2.fillRect(x1, y1, w, y2-y1); //Split the area into two sections: positive & negative. int posDiffH = (y2-y1)/ ((maxLogFold-minLogFold)/maxLogFold); int posDiffY1 = y1; int posDiffY2 = y1+posDiffH; int negDiffH = (y2-y1)-posDiffH; int negDiffY1 = posDiffY2; int negDiffY2 = y2; //Map out the points that must be drawn int numBins = (int)(region.getWidth()/binStep); for(int a=0; a<=numBins; a++){ int bp = region.getStart() + (a*binStep)+(binWidth/2); int currX1 = x1+Math.min(Math.max(getX(bp-(binStep/2), w), 0), w); int currX2 = x1+Math.min(Math.max(getX(bp+(binStep/2), w), 0), w); double foldDiff = diff[a]; if(foldDiff > (double)maxLogFold) foldDiff = (double)maxLogFold; else if(foldDiff < (double)minLogFold) foldDiff = (double)minLogFold; double yf = foldDiff<0 ? Math.abs(foldDiff / -(double)minLogFold) : Math.abs(foldDiff / (double)maxLogFold); int currY1 =foldDiff<0 ? negDiffY1 : posDiffY2 - (int)Math.round(yf * (double)posDiffH); int currY2 = foldDiff<0 ? negDiffY1 + (int)Math.round(yf * (double)negDiffH) : posDiffY2; Color currColor = foldDiff<0 ? negColor : posColor; //Columns g2.setColor(currColor); int x = Math.min(currX1, currX2); int y = Math.min(currY1, currY2); int width = Math.abs(currX2-currX1); int height = Math.abs(currY2-currY1); g2.fillRect(x, y, width, height); } if(axis){ g2.setColor(Color.black); g2.setStroke(new BasicStroke(1.0f)); g2.drawLine(x1, posDiffY1, x1,negDiffY2); g2.setFont(new Font("Ariel", Font.PLAIN, 16)); FontMetrics metrics = g2.getFontMetrics(); g2.drawString(String.format("%d",maxLogFold), x1+1, y1+(metrics.getHeight()/2)); g2.drawString(String.format("%d",minLogFold), x1+1, y2+(metrics.getHeight()/2)); } } public int[] getDataProfile() { return dataProfile; } private void setDataProfile(int x, int y) { if(x >= 0 && x < dataProfile.length) { dataProfile[x] = Math.max(dataProfile[x], y); } else { System.err.println(String.format("%d not in %d profile", x, dataProfile.length)); } } private boolean isHighlighted(int bp) { for(Region r : highlighted) { if(r.getStart() <= bp && r.getEnd() >= bp) { return true; } } return false; } private int getX(int bp, int pixwidth) { double f = (double)(bp-region.getStart()) / (double)region.getWidth(); if(reverse) { f = 1.0 - f; } return (int)Math.round(f * (double)pixwidth); } /** * A one-shot (i.e., one use) expander, that automatically closes its inner expander * when it's been called the first time. Only used in the constructor, above. * * @author tdanford * * @param <R> */ private static class ClosingRegionExpanderWrapper<R extends Region> implements Expander<Region,Region> { private Expander<Region,R> exp; private Mapper<R,R> mapper; public ClosingRegionExpanderWrapper(Expander<Region,R> e) { exp = e; mapper = null; } public ClosingRegionExpanderWrapper(Expander<Region,R> e, Mapper<R,R> m) { exp = e; mapper = m; } public Iterator<Region> execute(Region a) { Iterator<R> itr = exp.execute(a); if(mapper != null) { itr = new MapperIterator<R,R>(mapper,itr); } Iterator<Region> regs = new MapperIterator<R,Region>(new CastingMapper<R,Region>(), itr); LinkedList<Region> regList = new LinkedList<Region>(); while(regs.hasNext()) { regList.addLast(regs.next()); } if(exp instanceof Closeable) { ((Closeable)exp).close(); } System.out.println(String.format("ClosingRegionExpanderWrapper: %d", regList.size())); return regList.iterator(); } } }