diff --git a/uk/ac/sanger/artemis/components/alignment/BamView.java b/uk/ac/sanger/artemis/components/alignment/BamView.java index 4802e10f4687fa948c4d4d079593d1f5c5249b79..3a37de284b06847b0ecd9c38c1cb9d302780946f 100644 --- a/uk/ac/sanger/artemis/components/alignment/BamView.java +++ b/uk/ac/sanger/artemis/components/alignment/BamView.java @@ -70,6 +70,9 @@ import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Vector; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import javax.swing.BorderFactory; import javax.swing.Box; @@ -238,6 +241,8 @@ public class BamView extends JPanel private JPopupMenu popup; private PopupMessageFrame popFrame = new PopupMessageFrame(); private PopupMessageFrame waitingFrame = new PopupMessageFrame("waiting..."); + private ExecutorService bamReadTaskExecutor; + private int MAX_COVERAGE = Integer.MAX_VALUE; public static org.apache.log4j.Logger logger4j = org.apache.log4j.Logger.getLogger(BamView.class); @@ -291,6 +296,22 @@ public class BamView extends JPanel } } + if(Options.getOptions().getIntegerProperty("bam_read_thread") != null) + { + logger4j.debug("BAM READ THREADS="+Options.getOptions().getIntegerProperty("bam_read_thread")); + bamReadTaskExecutor = Executors.newFixedThreadPool( + Options.getOptions().getIntegerProperty("bam_read_thread")); + } + else + bamReadTaskExecutor = Executors.newFixedThreadPool(1); + + + if(Options.getOptions().getIntegerProperty("bam_max_coverage") != null) + { + logger4j.debug("BAM MAX COVERAGE="+Options.getOptions().getIntegerProperty("bam_max_coverage")); + MAX_COVERAGE = Options.getOptions().getIntegerProperty("bam_max_coverage"); + } + try { readHeaderPicard(); @@ -537,6 +558,43 @@ public class BamView extends JPanel seqNames.add(seq.getSequenceName()); } } + + class BamReadTask implements Runnable + { + private int start; + private int end; + private short bamIndex; + private float pixPerBase; + private CountDownLatch latch; + BamReadTask(int start, int end, short bamIndex, float pixPerBase, CountDownLatch latch) + { + this.start = start; + this.end = end; + this.bamIndex = bamIndex; + this.pixPerBase = pixPerBase; + this.latch = latch; + } + + public void run() + { + try + { + readFromBamPicard(start, end, bamIndex, pixPerBase) ; + } + catch (OutOfMemoryError ome) + { + throw ome; + } + catch(IOException me) + { + me.printStackTrace(); + } + finally + { + latch.countDown(); + } + } + } /** * Read a SAM or BAM file. @@ -592,76 +650,116 @@ public class BamView extends JPanel * @param end */ private void iterateOverBam(final SAMFileReader inputSam, - final String refName, final int start, final int end, - final short bamIndex, final float pixPerBase, - final String bam) - { - final CloseableIterator<SAMRecord> it = inputSam.queryOverlapping(refName, start, end); - MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); + final String refName, final int start, final int end, + final short bamIndex, final float pixPerBase, + final String bam) + { + final MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); final int checkMemAfter = 8000; - int cnt = 0; final int seqOffset = getSequenceOffset(refName); final int offset = seqOffset- getBaseAtStartOfView(); final boolean isCoverageView = isCoverageView(pixPerBase); + + int cnt = 0; + + int nbins = 600; + int binSize = ((end-start)/nbins)+1; + if(binSize < 1) + { + binSize = 1; + nbins = end-start+1; + } + int max = MAX_COVERAGE*binSize; + if(max < 1) + max = Integer.MAX_VALUE; + final int cov[] = new int[nbins]; + for(int i=0; i<nbins; i++) + cov[i] = 0; - while ( it.hasNext() ) + final CloseableIterator<SAMRecord> it = inputSam.queryOverlapping(refName, start, end); + try { - try + while ( it.hasNext() ) { - cnt++; - SAMRecord samRecord = it.next(); - - if( samRecordFlagPredicate == null || - !samRecordFlagPredicate.testPredicate(samRecord)) + try { - if(samRecordMapQPredicate == null || - samRecordMapQPredicate.testPredicate(samRecord)) + cnt++; + SAMRecord samRecord = it.next(); + + if( samRecordFlagPredicate == null || + !samRecordFlagPredicate.testPredicate(samRecord)) { - if(isCoverageView) - coverageView.addRecord(samRecord, offset, bam); - - if(isCoverage) - coveragePanel.addRecord(samRecord, offset, bam); - - if(isSNPplot) - snpPanel.addRecord(samRecord, seqOffset); - - if(!isCoverageView) - readsInView.add(new BamViewRecord(samRecord, bamIndex)); + if(samRecordMapQPredicate == null || + samRecordMapQPredicate.testPredicate(samRecord)) + { + int abeg = samRecord.getAlignmentStart(); + int aend = samRecord.getAlignmentEnd(); + boolean over = false; + + for(int i=abeg; i<aend; i++) + { + + int bin = ((i-start)/binSize)-1; + if(bin < 0) + bin = 0; + else if(bin > nbins-1) + bin = nbins-1; + cov[bin]++; + if(cov[bin] > max) + { + over = true; + break; + } + } + + if(over) + continue; + + if(isCoverageView) + coverageView.addRecord(samRecord, offset, bam); + if(isCoverage) + coveragePanel.addRecord(samRecord, offset, bam); + if(isSNPplot) + snpPanel.addRecord(samRecord, seqOffset); + if(!isCoverageView) + readsInView.add(new BamViewRecord(samRecord, bamIndex)); + } } - } - if(cnt > checkMemAfter) - { - cnt = 0; - float heapFraction = - (float)((float)memory.getHeapMemoryUsage().getUsed()/ - (float)memory.getHeapMemoryUsage().getMax()); - logger4j.debug("Heap memory usage (used/max): "+heapFraction); - - if(readsInView.size() > checkMemAfter*2 && !waitingFrame.isVisible()) - waitingFrame.showWaiting("loading...", mainPanel); - - if(heapFraction > 0.90) + if(cnt > checkMemAfter) { - popFrame.show( - "Using > 90 % of the maximum memory limit:"+ - (memory.getHeapMemoryUsage().getMax()/1000000.f)+" Mb.\n"+ - "Not all reads in this range have been read in. Zoom in or\n"+ - "consider increasing the memory for this application.", - mainPanel, - 15000); - break; + cnt = 0; + float heapFraction = + (float)((float)memory.getHeapMemoryUsage().getUsed()/ + (float)memory.getHeapMemoryUsage().getMax()); + logger4j.debug("Heap memory usage (used/max): "+heapFraction); + + if(readsInView.size() > checkMemAfter*2 && !waitingFrame.isVisible()) + waitingFrame.showWaiting("loading...", mainPanel); + + if(heapFraction > 0.90) + { + popFrame.show( + "Using > 90 % of the maximum memory limit:"+ + (memory.getHeapMemoryUsage().getMax()/1000000.f)+" Mb.\n"+ + "Not all reads in this range have been read in. Zoom in or\n"+ + "consider increasing the memory for this application.", + mainPanel, + 15000); + break; + } } } - } - catch(Exception e) - { - System.err.println(e.getMessage()); + catch(Exception e) + { + System.err.println(e.getMessage()); + } } } - - it.close(); + finally + { + it.close(); + } } private int getSequenceLength() @@ -751,7 +849,7 @@ public class BamView extends JPanel } return offsetLengths.get(refName); } - + /** * Override */ @@ -807,11 +905,24 @@ public class BamView extends JPanel else readsInView.clear(); + final CountDownLatch latch = new CountDownLatch(bamList.size()); + //long ms = System.currentTimeMillis(); for(short i=0; i<bamList.size(); i++) { if(!hideBamList.contains(i)) - readFromBamPicard(start, end, i, pixPerBase); + bamReadTaskExecutor.execute( + new BamReadTask(start, end, i, pixPerBase, latch)); + } + + try + { + latch.await(); } + catch (InterruptedException e) {} // TODO + + //System.out.println("===== NO. THREADS="+ + // ((java.util.concurrent.ThreadPoolExecutor)bamReadTaskExecutor).getPoolSize()+" TIME="+(System.currentTimeMillis()-ms)); + float heapFractionUsedAfter = (float) ((float) memory.getHeapMemoryUsage().getUsed() / (float) memory.getHeapMemoryUsage().getMax()); @@ -843,10 +954,6 @@ public class BamView extends JPanel readsInView.clear(); return; } - catch(IOException me) - { - me.printStackTrace(); - } catch(net.sf.samtools.util.RuntimeIOException re) { JOptionPane.showMessageDialog(this, re.getMessage()); @@ -1171,7 +1278,7 @@ public class BamView extends JPanel } /** - * Draw zoomed-out view. + * Draw inferred size view. * @param g2 * @param seqLength * @param pixPerBase @@ -1682,6 +1789,14 @@ public class BamView extends JPanel } int hgt = jspView.getVisibleRect().height-scaleHeight; + if(!cbCoverageStrandView.isSelected()) + { + int y = getHeight()-10-( (hgt* MAX_COVERAGE)/(coverageView.max/coverageView.windowSize) ); + g2.setColor(Color.YELLOW); + // draw the threshold for the coverage max read cut-off + g2.fillRect(0, y, getWidth(), 10); + } + g2.translate(0, getHeight()-hgt-scaleHeight); coverageView.drawSelectionRange(g2, pixPerBase, start, end, getHeight(), Color.PINK); coverageView.draw(g2, getWidth(), hgt); @@ -2624,6 +2739,28 @@ public class BamView extends JPanel } }); + JMenuItem maxReadCoverage = new JMenuItem("Read Coverage Threshold ..."); + menu.add(maxReadCoverage); + maxReadCoverage.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + final TextFieldInt maxRead = new TextFieldInt(); + maxRead.setValue(MAX_COVERAGE); + int status = JOptionPane.showConfirmDialog(null, maxRead, + "Read Coverage Threshold", JOptionPane.OK_CANCEL_OPTION); + if(status == JOptionPane.OK_OPTION && + maxRead.getValue() != MAX_COVERAGE) + { + MAX_COVERAGE = maxRead.getValue(); + if(MAX_COVERAGE < 1) + MAX_COVERAGE = Integer.MAX_VALUE; + laststart = -1; + repaint(); + } + } + }); + JMenuItem readList = new JMenuItem("List Reads ..."); menu.add(readList); readList.addActionListener(new ActionListener()