JAL-2909 Wire up coordinates dialog with bam file reader
authorkiramt <k.mourao@dundee.ac.uk>
Wed, 28 Feb 2018 15:47:06 +0000 (15:47 +0000)
committerkiramt <k.mourao@dundee.ac.uk>
Wed, 28 Feb 2018 15:47:06 +0000 (15:47 +0000)
resources/lang/Messages.properties
src/jalview/gui/BamFileOptionsChooser.java
src/jalview/io/AlignFile.java
src/jalview/io/AlignmentFileReaderI.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/BamFile.java
src/jalview/io/FileLoader.java
src/jalview/io/FormatAdapter.java

index f526699..965a4a0 100644 (file)
@@ -1330,3 +1330,7 @@ label.most_bound_molecules = Most Bound Molecules
 label.most_polymer_residues = Most Polymer Residues
 label.cached_structures = Cached Structures
 label.free_text_search = Free Text Search
+label.bam_file_options = BAM File Options
+label.bam_chromosome = Chromosome to load:
+label.bam_range = Region to load:
+warn.bam_params_not_set = .bam file region parameters have not been set
index 7f9583f..f456319 100644 (file)
  */
 package jalview.gui;
 
+import jalview.io.AlignmentFileReaderI;
+import jalview.io.BamFile;
+import jalview.util.MessageManager;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.util.Arrays;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
 import javax.swing.JPanel;
+import javax.swing.JTextField;
 
 /**
  * A dialog where a user can choose import options for a bam file
@@ -28,4 +40,86 @@ import javax.swing.JPanel;
 public class BamFileOptionsChooser extends JPanel
 {
 
+  // text field for start of range
+  // JFormattedTextField startRange = new JFormattedTextField(
+  // new NumberFormatter(NumberFormat.getIntegerInstance()));
+
+  // text field for end of range
+  // JFormattedTextField endRange = new JFormattedTextField(
+  // new NumberFormatter(NumberFormat.getIntegerInstance()));
+
+  JTextField startRange = new JTextField(10);
+
+  JTextField endRange = new JTextField(10);
+
+  // combo box for chromosome list
+  JComboBox<String> chroCombo;
+
+  public BamFileOptionsChooser(AlignmentFileReaderI source)
+  {
+    Object[] preprocessResult = source.preprocess();
+    String[] chromosomes = Arrays.copyOf(preprocessResult,
+            preprocessResult.length, String[].class);
+
+    setLayout(new GridBagLayout());
+    GridBagConstraints c = new GridBagConstraints();
+
+    setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
+
+    // set up chromosome input
+    JLabel chroLabel = new JLabel(
+            MessageManager.getString("label.bam_chromosome"));
+    chroCombo = new JComboBox<>(chromosomes);
+    
+    // set up region input
+    JPanel range = new JPanel();
+    JLabel rangeLabel = new JLabel(
+            MessageManager.getString("label.bam_range"));
+    startRange.setColumns(8);
+    endRange.setColumns(8);
+
+    JLabel rangeSep = new JLabel("-");
+    range.add(startRange);
+    range.add(rangeSep);
+    range.add(endRange);
+
+    // chromosome label top left
+    c.gridx = 0;
+    c.gridy = 0;
+    c.anchor = GridBagConstraints.WEST;
+    c.fill = GridBagConstraints.NONE;
+    this.add(chroLabel, c);
+
+    // chromosome combo top right
+    c.gridx = 1;
+    c.weightx = 1;
+    c.anchor = GridBagConstraints.WEST;
+    c.fill = GridBagConstraints.HORIZONTAL;
+    this.add(chroCombo, c);
+
+    // region label mid left
+    c.gridx = 0;
+    c.gridy = 1;
+    c.anchor = GridBagConstraints.WEST;
+    c.fill = GridBagConstraints.NONE;
+    this.add(rangeLabel, c);
+
+    // region input mid right
+    c.gridx = 1;
+    c.weightx = 1;
+    c.anchor = GridBagConstraints.WEST;
+    c.fill = GridBagConstraints.HORIZONTAL;
+    this.add(range, c);
+  }
+
+  public void update(AlignmentFileReaderI source)
+  {
+
+    int start = Integer.parseInt(startRange.getText());
+    int end = Integer.parseInt(endRange.getText());
+    ((BamFile) source).setOptions((String) chroCombo.getSelectedItem(),
+            start, end);
+
+  }
+
 }
index 2340283..c931cb8 100755 (executable)
@@ -304,9 +304,9 @@ public abstract class AlignFile extends FileParse
    */
   protected void initData()
   {
-    seqs = new Vector<SequenceI>();
-    annotations = new Vector<AlignmentAnnotation>();
-    seqGroups = new ArrayList<SequenceGroup>();
+    seqs = new Vector<>();
+    annotations = new Vector<>();
+    seqGroups = new ArrayList<>();
     parseCalled = false;
   }
 
@@ -319,7 +319,7 @@ public abstract class AlignFile extends FileParse
   @Override
   public void setSeqs(SequenceI[] s)
   {
-    seqs = new Vector<SequenceI>();
+    seqs = new Vector<>();
 
     for (int i = 0; i < s.length; i++)
     {
@@ -390,7 +390,7 @@ public abstract class AlignFile extends FileParse
   {
     if (newickStrings == null)
     {
-      newickStrings = new Vector<String[]>();
+      newickStrings = new Vector<>();
     }
     newickStrings.addElement(new String[] { treeName, newickString });
   }
@@ -414,4 +414,13 @@ public abstract class AlignFile extends FileParse
   {
     seqs.add(seq);
   }
+
+  @Override
+  public Object[] preprocess()
+  {
+    // most AlignFiles will not need to return any preprocessing information
+    // those that do should override this method
+    return null;
+  }
+
 }
index a471d9b..fe47c8a 100644 (file)
  */
 package jalview.io;
 
-import jalview.api.AlignExportSettingI;
-import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureSettingsModelI;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 
+import java.io.IOException;
+
 public interface AlignmentFileReaderI
 {
 
@@ -47,4 +47,13 @@ public interface AlignmentFileReaderI
 
   FeatureSettingsModelI getFeatureColourScheme();
 
+  /**
+   * Get a string array resulting from preprocessing the file
+   * 
+   * @return preprocess result
+   */
+  Object[] preprocess();
+
+  public abstract void parse() throws IOException;
+
 }
index 5e209e6..da78420 100755 (executable)
@@ -147,6 +147,20 @@ public class AppletFormatAdapter
   public AlignmentI readFile(String file, DataSourceType sourceType,
           FileFormatI fileFormat) throws IOException
   {
+    if (alignFile == null)
+    {
+      prepareFileReader(file, sourceType, fileFormat);
+    }
+    else
+    {
+      alignFile.parse();
+    }
+    return buildAlignmentFromFile();
+  }
+  
+  public void prepareFileReader(String file, DataSourceType sourceType,
+          FileFormatI fileFormat) throws IOException
+  {
     this.inFile = file;
     try
     {
@@ -176,10 +190,9 @@ public class AppletFormatAdapter
       }
       else
       {
-        // alignFile = fileFormat.getAlignmentFile(inFile, sourceType);
         alignFile = fileFormat.getReader(new FileParse(inFile, sourceType));
       }
-      return buildAlignmentFromFile();
+      return;
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -200,7 +213,7 @@ public class AppletFormatAdapter
           // Possible sequence is just residues with no label
           alignFile = new FastaFile(">UNKNOWN\n" + inFile,
                   DataSourceType.PASTE);
-          return buildAlignmentFromFile();
+          return;
 
         } catch (Exception ex)
         {
index 26c14a2..1aebbe9 100644 (file)
@@ -26,18 +26,30 @@ import jalview.datamodel.SequenceI;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.SortedMap;
 
 import htsjdk.samtools.SAMRecord;
 import htsjdk.samtools.SAMRecordIterator;
+import htsjdk.samtools.SAMSequenceRecord;
 import htsjdk.samtools.SamReader;
 import htsjdk.samtools.SamReaderFactory;
 import htsjdk.samtools.ValidationStringency;
 
 public class BamFile extends AlignFile
 {
+  // SAM/BAM file reader
+  private SamReader fileReader;
 
-  SamReader fileReader;
+  // start position to read from
+  private int start = -1;
+
+  // end position to read to
+  private int end = -1;
+
+  // chromosome/contig to read
+  private String chromosome = "";
 
   /**
    * Creates a new BamFile object.
@@ -50,17 +62,16 @@ public class BamFile extends AlignFile
    * Creates a new BamFile object.
    * 
    * @param inFile
-   *          DOCUMENT ME!
+   *          Name of file to read
    * @param sourceType
-   *          DOCUMENT ME!
+   *          Whether data source is file, url or other type of data source
    * 
    * @throws IOException
-   *           DOCUMENT ME!
    */
   public BamFile(String inFile, DataSourceType sourceType)
           throws IOException
   {
-    super(inFile, sourceType);
+    super(true, inFile, sourceType);
     final SamReaderFactory factory = SamReaderFactory.makeDefault()
             .enable(SamReaderFactory.Option.INCLUDE_SOURCE_IN_RECORDS,
                     SamReaderFactory.Option.VALIDATE_CRC_CHECKSUMS)
@@ -68,8 +79,16 @@ public class BamFile extends AlignFile
     fileReader = factory.open(new File(inFile));
   }
 
+  /**
+   * Creates a new BamFile object
+   * 
+   * @param source
+   *          wrapper for datasource
+   * @throws IOException
+   */
   public BamFile(FileParse source) throws IOException
   {
+    super(true, source);
     final SamReaderFactory factory = SamReaderFactory.makeDefault()
             .enable(SamReaderFactory.Option.INCLUDE_SOURCE_IN_RECORDS,
                     SamReaderFactory.Option.VALIDATE_CRC_CHECKSUMS)
@@ -77,7 +96,6 @@ public class BamFile extends AlignFile
 
     // File-based bam
     fileReader = factory.open(source.inFile);
-    parse();
   }
   
   @Override
@@ -88,33 +106,65 @@ public class BamFile extends AlignFile
   }
 
   @Override
-  public void parse() throws IOException
+  public void parse()
   {
-    SAMRecordIterator it = fileReader.iterator();
-    CigarParser parser = new CigarParser('-');
-    SortedMap<Integer, Integer> insertions = parser.getInsertions(it);
-    it.close();
-
-    it = fileReader.iterator();
-    while (it.hasNext())
+    // only actually parse if params are set
+    if (chromosome != null && chromosome != "")
     {
-      SAMRecord rec = it.next();
+      SAMRecordIterator it = fileReader.query(chromosome, start, end,
+              false);
+      CigarParser parser = new CigarParser('-');
+      SortedMap<Integer, Integer> insertions = parser.getInsertions(it);
+      it.close();
+
+      it = fileReader.query(chromosome, start, end, false);
+      while (it.hasNext())
+      {
+        SAMRecord rec = it.next();
 
-      // make dataset sequence: start at 1, end at read length
-      SequenceI seq = new Sequence(rec.getReadName(),
-              rec.getReadString().toLowerCase());
-      seq.setStart(1);
-      seq.setEnd(rec.getReadLength());
+        // make dataset sequence: start at 1, end at read length
+        SequenceI seq = new Sequence(rec.getReadName(),
+                rec.getReadString().toLowerCase());
+        seq.setStart(1);
+        seq.setEnd(rec.getReadLength());
 
-      String newRead = parser.parseCigarToSequence(rec, insertions);
+        String newRead = parser.parseCigarToSequence(rec, insertions);
 
-      // make alignment sequences
-      SequenceI alsq = seq.deriveSequence();
-      alsq.setSequence(newRead);
+        // make alignment sequences
+        SequenceI alsq = seq.deriveSequence();
+        alsq.setSequence(newRead);
 
-      // set start relative to soft clip; assume end is set by Sequence code
-      alsq.setStart(rec.getStart() - rec.getUnclippedStart() + 1);
-      seqs.add(alsq);
+        // set start relative to soft clip; assume end is set by Sequence code
+        alsq.setStart(rec.getStart() - rec.getUnclippedStart() + 1);
+        seqs.add(alsq);
+      }
     }
   }
+
+  /**
+   * Get the list of chromosomes or contigs from the file (listed in SQ entries
+   * in BAM file header)
+   * 
+   * @return array of chromosome/contig strings
+   */
+  @Override
+  public Object[] preprocess()
+  {
+    List<SAMSequenceRecord> refSeqs = fileReader.getFileHeader()
+            .getSequenceDictionary().getSequences();
+    List<String> chrs = new ArrayList<>();
+
+    for (SAMSequenceRecord ref : refSeqs)
+    {
+      chrs.add(ref.getSequenceName());
+    }
+    return chrs.toArray();
+  }
+
+  public void setOptions(String chr, int s, int e)
+  {
+    chromosome = chr;
+    start = s;
+    end = e;
+  }
 }
index 11432bd..96ab8bb 100755 (executable)
@@ -32,6 +32,7 @@ import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.AlignViewport;
+import jalview.gui.BamFileOptionsChooser;
 import jalview.gui.Desktop;
 import jalview.gui.Jalview2XML;
 import jalview.gui.JvOptionPane;
@@ -310,7 +311,34 @@ public class FileLoader implements Runnable
 
       if (FileFormat.Bam.equals(format))
       {
+        FormatAdapter fa = new FormatAdapter();
+        if (source == null)
+        {
+          fa.prepareFileReader(file, protocol, format);
+          source = fa.getAlignFile();
+        }
+
+        BamFileOptionsChooser bamoptions = new BamFileOptionsChooser(
+                source);
+
         // ask the user which bit of the bam they want to load
+        int confirm = JvOptionPane.showConfirmDialog(null,
+                bamoptions,
+                MessageManager.getString("label.bam_file_options"),
+                JvOptionPane.OK_CANCEL_OPTION,
+                JvOptionPane.PLAIN_MESSAGE);
+        
+        if (confirm == JvOptionPane.CANCEL_OPTION
+                || confirm == JvOptionPane.CLOSED_OPTION)
+        {
+          Desktop.instance.stopLoading();
+          return;
+        }
+        else
+        {
+          bamoptions.update(source);
+          al = fa.readFile(file, protocol, format);
+        }
       }
 
       if (FileFormat.Jalview.equals(format))
@@ -329,37 +357,40 @@ public class FileLoader implements Runnable
         String error = AppletFormatAdapter.getSupportedFormats();
         try
         {
-          if (source != null)
+          if (al == null)
           {
-            // read from the provided source
-            al = new FormatAdapter().readFromFile(source, format);
-          }
-          else
-          {
-
-            // open a new source and read from it
-            FormatAdapter fa = new FormatAdapter();
-            boolean downloadStructureFile = format.isStructureFile()
-                    && protocol.equals(DataSourceType.URL);
-            if (downloadStructureFile)
+            if (source != null)
             {
-              String structExt = format.getExtensions().split(",")[0];
-              String urlLeafName = file.substring(
-                      file.lastIndexOf(
-                              System.getProperty("file.separator")),
-                      file.lastIndexOf("."));
-              String tempStructureFileStr = createNamedJvTempFile(
-                      urlLeafName, structExt);
-              UrlDownloadClient.download(file, tempStructureFileStr);
-              al = fa.readFile(tempStructureFileStr, DataSourceType.FILE,
-                      format);
-              source = fa.getAlignFile();
+              // read from the provided source
+              al = new FormatAdapter().readFromFile(source, format);
             }
             else
             {
-              al = fa.readFile(file, protocol, format);
-              source = fa.getAlignFile(); // keep reference for later if
-                                          // necessary.
+
+              // open a new source and read from it
+              FormatAdapter fa = new FormatAdapter();
+              boolean downloadStructureFile = format.isStructureFile()
+                      && protocol.equals(DataSourceType.URL);
+              if (downloadStructureFile)
+              {
+                String structExt = format.getExtensions().split(",")[0];
+                String urlLeafName = file.substring(
+                        file.lastIndexOf(
+                                System.getProperty("file.separator")),
+                        file.lastIndexOf("."));
+                String tempStructureFileStr = createNamedJvTempFile(
+                        urlLeafName, structExt);
+                UrlDownloadClient.download(file, tempStructureFileStr);
+                al = fa.readFile(tempStructureFileStr, DataSourceType.FILE,
+                        format);
+                source = fa.getAlignFile();
+              }
+              else
+              {
+                al = fa.readFile(file, protocol, format);
+                source = fa.getAlignFile(); // keep reference for later if
+                                            // necessary.
+              }
             }
           }
         } catch (java.io.IOException ex)
index 7647a16..4fa1f8e 100755 (executable)
@@ -266,5 +266,4 @@ public class FormatAdapter extends AppletFormatAdapter
             source.getDataSourceType());
     return readFromFile(fp, format);
   }
-
 }