From 9289ad58228571a63fdf54ef720603a590d58adc Mon Sep 17 00:00:00 2001 From: kiramt Date: Wed, 28 Feb 2018 15:47:06 +0000 Subject: [PATCH] JAL-2909 Wire up coordinates dialog with bam file reader --- resources/lang/Messages.properties | 4 ++ src/jalview/gui/BamFileOptionsChooser.java | 94 +++++++++++++++++++++++++ src/jalview/io/AlignFile.java | 19 +++-- src/jalview/io/AlignmentFileReaderI.java | 13 +++- src/jalview/io/AppletFormatAdapter.java | 19 ++++- src/jalview/io/BamFile.java | 104 ++++++++++++++++++++-------- src/jalview/io/FileLoader.java | 83 +++++++++++++++------- src/jalview/io/FormatAdapter.java | 1 - 8 files changed, 273 insertions(+), 64 deletions(-) diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index f526699..965a4a0 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -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 diff --git a/src/jalview/gui/BamFileOptionsChooser.java b/src/jalview/gui/BamFileOptionsChooser.java index 7f9583f..f456319 100644 --- a/src/jalview/gui/BamFileOptionsChooser.java +++ b/src/jalview/gui/BamFileOptionsChooser.java @@ -20,7 +20,19 @@ */ 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 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); + + } + } diff --git a/src/jalview/io/AlignFile.java b/src/jalview/io/AlignFile.java index 2340283..c931cb8 100755 --- a/src/jalview/io/AlignFile.java +++ b/src/jalview/io/AlignFile.java @@ -304,9 +304,9 @@ public abstract class AlignFile extends FileParse */ protected void initData() { - seqs = new Vector(); - annotations = new Vector(); - seqGroups = new ArrayList(); + 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(); + 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(); + 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; + } + } diff --git a/src/jalview/io/AlignmentFileReaderI.java b/src/jalview/io/AlignmentFileReaderI.java index a471d9b..fe47c8a 100644 --- a/src/jalview/io/AlignmentFileReaderI.java +++ b/src/jalview/io/AlignmentFileReaderI.java @@ -20,12 +20,12 @@ */ 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; + } diff --git a/src/jalview/io/AppletFormatAdapter.java b/src/jalview/io/AppletFormatAdapter.java index 5e209e6..da78420 100755 --- a/src/jalview/io/AppletFormatAdapter.java +++ b/src/jalview/io/AppletFormatAdapter.java @@ -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) { diff --git a/src/jalview/io/BamFile.java b/src/jalview/io/BamFile.java index 26c14a2..1aebbe9 100644 --- a/src/jalview/io/BamFile.java +++ b/src/jalview/io/BamFile.java @@ -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 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 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 refSeqs = fileReader.getFileHeader() + .getSequenceDictionary().getSequences(); + List 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; + } } diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java index 11432bd..96ab8bb 100755 --- a/src/jalview/io/FileLoader.java +++ b/src/jalview/io/FileLoader.java @@ -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) diff --git a/src/jalview/io/FormatAdapter.java b/src/jalview/io/FormatAdapter.java index 7647a16..4fa1f8e 100755 --- a/src/jalview/io/FormatAdapter.java +++ b/src/jalview/io/FormatAdapter.java @@ -266,5 +266,4 @@ public class FormatAdapter extends AppletFormatAdapter source.getDataSourceType()); return readFromFile(fp, format); } - } -- 1.7.10.2