Skip to content

Commit

Permalink
Htsget (#1402)
Browse files Browse the repository at this point in the history
* Add explicit menu item for htsget URLs -- avoids attempts to guess
  • Loading branch information
jrobinso authored Oct 2, 2023
1 parent dd16fc5 commit 2d82d8e
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 112 deletions.
3 changes: 3 additions & 0 deletions src/main/java/org/broad/igv/htsget/HtsgetUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public static Metadata getMetadata(final String url) throws IOException {
return null;
} else {
String format = json.get("htsget").getAsJsonObject().get("format").getAsString();
// if(!(format.toUpperCase().equals("BAM")) || format.toUpperCase().equals("VCF")) {
// throw new RuntimeException(("Format"))
// }
return new Metadata(url, format);
}
}
Expand Down
105 changes: 38 additions & 67 deletions src/main/java/org/broad/igv/track/TrackLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ public List<Track> load(ResourceLocator locator, Genome genome) throws DataLoadE

log.info("Loading resource: " + (locator.isDataURL() ? "<data url>" : path));
try {

String format = locator.getFormat();

if (format.equals("tbi")) {
Expand All @@ -134,9 +135,23 @@ public List<Track> load(ResourceLocator locator, Genome genome) throws DataLoadE
//This list will hold all new tracks created for this locator
List<Track> newTracks = new ArrayList<Track>();


// Determine track type, if possible, and add new tracks
if (locator.isHtsget()) {
tryHtsget(locator, newTracks, genome);
} else if (format.equals("gmt")) {
HtsgetUtils.Metadata htsgetMeta = HtsgetUtils.getMetadata(locator.getPath());
locator.setFormat(htsgetMeta.getFormat().toLowerCase());
if (htsgetMeta.getFormat().equals("VCF")) {
locator.setHtsget(true);
HtsgetVariantSource source = new HtsgetVariantSource(htsgetMeta, genome);
loadVCFWithSource(locator, source, newTracks);
} else if (htsgetMeta.getFormat().equals("BAM") || htsgetMeta.getFormat().equals("CRAM")) {
locator.setHtsget(true);
loadAlignmentsTrack(locator, newTracks, genome);
} else {
throw new RuntimeException("Format: '" + htsgetMeta.getFormat() + "' is not supported for htsget servers.");
}

} else if (format.equals("gmt")) {
loadGMT(locator);
} else if (format.equals("vcf.list")) {
loadVCFListFile(locator, newTracks, genome);
Expand Down Expand Up @@ -208,33 +223,29 @@ public List<Track> load(ResourceLocator locator, Genome genome) throws DataLoadE
loadMutFile(locator, newTracks, genome); // Must be tried before ".maf" test below
} else if (format.equals("maf")) {
loadMultipleAlignmentTrack(locator, newTracks, genome);
} else {
//if a url, try htsget
boolean isHtsget = tryHtsget(locator, newTracks, genome);
if (!isHtsget) {

// If the file is too large, give up
// TODO -- ftp test
final int tenMB = 10000000;
long fileLength = ParsingUtils.getContentLength(locator.getPath());
if (fileLength > tenMB) {
MessageUtils.confirm("<html>Cannot determine file type of: " + locator.getPath());
}
} else {
// If the file is too large, give up
// TODO -- ftp test
final int tenMB = 10000000;
long fileLength = ParsingUtils.getContentLength(locator.getPath());
if (fileLength > tenMB) {
MessageUtils.confirm("<html>Cannot determine file type of: " + locator.getPath());
}

// Read file contents and try to sort it out
String contents = FileUtils.getContents(locator.getPath());
BufferedReader reader = new BufferedReader(new StringReader(contents));

if (CytoBandFileParser.isValid(reader, locator.getPath())) {
Track track = new CytobandTrack(locator, new BufferedReader(new StringReader(contents)), genome);
newTracks.add(track);
} else if (AttributeManager.isSampleInfoFile(reader)) {
// This might be a sample information file.
AttributeManager.getInstance().loadSampleInfo(locator);
} else {
MessageUtils.showMessage("<html>Unknown file type: " + path + "<br>Check file extension");
}
// Read file contents and try to sort it out
String contents = FileUtils.getContents(locator.getPath());
BufferedReader reader = new BufferedReader(new StringReader(contents));

if (CytoBandFileParser.isValid(reader, locator.getPath())) {
Track track = new CytobandTrack(locator, new BufferedReader(new StringReader(contents)), genome);
newTracks.add(track);
} else if (AttributeManager.isSampleInfoFile(reader)) {
// This might be a sample information file.
AttributeManager.getInstance().loadSampleInfo(locator);
} else {
MessageUtils.showMessage("<html>Unknown file type: " + path + "<br>Check file extension");
}

}

// Track line
Expand Down Expand Up @@ -275,46 +286,6 @@ public List<Track> load(ResourceLocator locator, Genome genome) throws DataLoadE

}

/**
* Try to load as an htsget resource. As most (all?) htsget endpoints use an https:// scheme URL, there is
* no way to detect other than try.
*
* @param locator
* @param newTracks
* @param genome
* @return
*/
private boolean tryHtsget(ResourceLocator locator, List<Track> newTracks, Genome genome) {
boolean isHtsget = false;
if (locator.getPath().startsWith("https://") ||
locator.getPath().startsWith("http://") ||
locator.getPath().startsWith("htsget://")) {
try {
HtsgetUtils.Metadata htsgetMeta = HtsgetUtils.getMetadata(locator.getPath());
if (htsgetMeta != null) {
isHtsget = true;
locator.setFormat(htsgetMeta.getFormat().toLowerCase());
if (htsgetMeta.getFormat().equals("VCF")) {
locator.setHtsget(true);
HtsgetVariantSource source = new HtsgetVariantSource(htsgetMeta, genome);
loadVCFWithSource(locator, source, newTracks);
} else if (htsgetMeta.getFormat().equals("BAM") || htsgetMeta.getFormat().equals("CRAM")) {
locator.setHtsget(true);
loadAlignmentsTrack(locator, newTracks, genome);
} else {
throw new RuntimeException("Format: '" + htsgetMeta.getFormat() + "' is not supported for htsget servers.");
}
}
} catch (IOException e) {
// Not neccessarily an error, might just indicate its not an htsget server. Not sure
// if this should be logged or not, it will be a common and expected occurence when loading
// sample information, which is checked after htsget
return false;
}
}
return isHtsget;
}

public static boolean isAlignmentTrack(String typeString) {
if (typeString == null) {
return false;
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/broad/igv/ui/IGVMenuBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -284,10 +284,14 @@ JMenu createFileMenu() {
menuAction.setToolTipText(UIConstants.LOAD_TRACKS_TOOLTIP);
menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));

menuAction = new LoadFromServerAction("Load from Server...", KeyEvent.VK_S, igv);
menuAction = new LoadFromServerAction("Load IGV Hosted Tracks...", KeyEvent.VK_S, igv);
menuAction.setToolTipText(UIConstants.LOAD_SERVER_DATA_TOOLTIP);
menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));

menuAction = new LoadFromURLMenuAction(LoadFromURLMenuAction.LOAD_FROM_HTSGET, 0, igv);
menuAction.setToolTipText(UIConstants.LOAD_HTSGET_TOOLTOP);
menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));

if (PreferencesManager.getPreferences().getAsBoolean(DB_ENABLED)) {
menuAction = new LoadFromDatabaseAction("Load from Database...", 0, igv);
menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction));
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/broad/igv/ui/UIConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class UIConstants {

// Menu tooltips
static final public String LOAD_TRACKS_TOOLTIP = "Load tracks or sample information";

static final public String LOAD_HTSGET_TOOLTOP = "Load BAM or VCF tracks from an htsget server";
static final public String LOAD_SERVER_DATA_TOOLTIP = "Load tracks or sample information from a server";
static final public String SAVE_PNG_IMAGE_TOOLTIP = "Capture and save a PNG image";
static final public String SAVE_SVG_IMAGE_TOOLTIP = "Capture and save an SVG image";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ public static LinkedHashSet<String> getNodeURLs(String genomeURL) {
nodeURLs = getResourceUrls(bufferedReader);
} catch (IOException e) {
//This is pretty common, if there is no data registry file for the genome the file won't exist
log.error("Error loading genome registry file", e);
//log.error("Error loading genome registry file", e);
log.warn("No data found for current genome.");
} finally {
if (is != null) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public class LoadFromURLMenuAction extends MenuAction {
static Logger log = LogManager.getLogger(LoadFilesMenuAction.class);
public static final String LOAD_FROM_URL = "Load from URL...";
public static final String LOAD_GENOME_FROM_URL = "Load Genome from URL...";
public static final String LOAD_FROM_HTSGET = "Load from htsget Server...";
private IGV igv;

public LoadFromURLMenuAction(String label, int mnemonic, IGV igv) {
Expand All @@ -71,9 +72,10 @@ public void actionPerformed(ActionEvent e) {

JPanel ta = new JPanel();
ta.setPreferredSize(new Dimension(600, 20));
if (e.getActionCommand().equalsIgnoreCase(LOAD_FROM_URL)) {
boolean isHtsGet = e.getActionCommand().equalsIgnoreCase(LOAD_FROM_HTSGET);
if (e.getActionCommand().equalsIgnoreCase(LOAD_FROM_URL) || isHtsGet) {

LoadFromURLDialog dlg = new LoadFromURLDialog(IGV.getInstance().getMainFrame());
LoadFromURLDialog dlg = new LoadFromURLDialog(IGV.getInstance().getMainFrame(), isHtsGet);
dlg.setVisible(true);

if (!dlg.isCanceled()) {
Expand Down Expand Up @@ -114,6 +116,9 @@ public void actionPerformed(ActionEvent e) {
String indexUrl = indexes[i];
rl.setIndexPath(indexUrl);
}
if(isHtsGet) {
rl.setHtsget(true);
}
locators.add(rl);
}
igv.loadTracks(locators);
Expand Down
88 changes: 47 additions & 41 deletions src/main/java/org/broad/igv/ui/util/LoadFromURLDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@
/**
* @author James Robinson
*/
public class LoadFromURLDialog extends org.broad.igv.ui.IGVDialog {
public class LoadFromURLDialog extends org.broad.igv.ui.IGVDialog {

boolean canceled = false;
String fileURL;
String indexURL;

public LoadFromURLDialog(Frame owner) {
super(owner, "Load from URL");
initComponents();
public LoadFromURLDialog(Frame owner, boolean isHtsget) {
super(owner, isHtsget ? "htsget URL": "Load from URL");
initComponents(isHtsget);
}


Expand Down Expand Up @@ -82,7 +82,7 @@ public String getIndexURL() {
return indexURL;
}

private void initComponents() {
private void initComponents(boolean isHtsget) {
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents
// Generated using JFormDesigner non-commercial license
dialogPane = new JPanel();
Expand Down Expand Up @@ -112,69 +112,75 @@ private void initComponents() {
{
contentPanel.setBorder(new EmptyBorder(10, 10, 5, 5));
contentPanel.setLayout(new GridBagLayout());
((GridBagLayout)contentPanel.getLayout()).columnWidths = new int[] {0, 0, 0};
((GridBagLayout)contentPanel.getLayout()).rowHeights = new int[] {0, 0, 0, 0, 0, 0, 0};
((GridBagLayout)contentPanel.getLayout()).columnWeights = new double[] {0.0, 0.0, 1.0E-4};
((GridBagLayout)contentPanel.getLayout()).rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0E-4};
((GridBagLayout) contentPanel.getLayout()).columnWidths = new int[]{0, 0, 0};
((GridBagLayout) contentPanel.getLayout()).rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0};
((GridBagLayout) contentPanel.getLayout()).columnWeights = new double[]{0.0, 0.0, 1.0E-4};
((GridBagLayout) contentPanel.getLayout()).rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0E-4};
contentPanel.add(vSpacer2, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 0), 0, 0));
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 0), 0, 0));

//---- label1 ----
label1.setText("File URL:");
contentPanel.add(label1, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 5), 0, 0));

GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 5), 0, 0));
fileField.setPreferredSize(new Dimension(720, 28));
fileField.setMinimumSize(new Dimension(720, 28));
//---- label2 ----
label2.setText("Index URL:");
contentPanel.add(label2, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 5), 0, 0));

contentPanel.add(fileField, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 0), 0, 0));
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 0), 0, 0));
contentPanel.add(vSpacer1, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 5), 0, 0));
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 5), 0, 0));
contentPanel.add(vSpacer3, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 5), 0, 0));
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 5), 0, 0));

//---- indexField ----
indexField.setPreferredSize(new Dimension(720, 28));
indexField.setMinimumSize(new Dimension(720, 28));
contentPanel.add(indexField, new GridBagConstraints(1, 5, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));

//---- label3 ----
label3.setText("<html><i>Specify url to an index file. <b>Required for BAM and indexed files</b></i>");
contentPanel.add(label3, new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 0), 0, 0));
if (!isHtsget) {
label2.setText("Index URL:");
contentPanel.add(label2, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 5), 0, 0));

indexField.setPreferredSize(new Dimension(720, 28));
indexField.setMinimumSize(new Dimension(720, 28));
contentPanel.add(indexField, new GridBagConstraints(1, 5, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));


//---- label3 ----
label3.setText("<html><i>Specify url to an index file. <b>Required for BAM and indexed files</b></i>");
contentPanel.add(label3, new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 10, 0), 0, 0));
}
}
dialogPane.add(contentPanel, BorderLayout.CENTER);

//======== buttonBar ========
{
buttonBar.setLayout(new GridBagLayout());
((GridBagLayout)buttonBar.getLayout()).columnWidths = new int[] {0, 85, 80};
((GridBagLayout)buttonBar.getLayout()).columnWeights = new double[] {1.0, 0.0, 0.0};
((GridBagLayout) buttonBar.getLayout()).columnWidths = new int[]{0, 85, 80};
((GridBagLayout) buttonBar.getLayout()).columnWeights = new double[]{1.0, 0.0, 0.0};

//---- okButton ----
okButton.setText("OK");
okButton.addActionListener(e -> okButtonActionPerformed(e));
buttonBar.add(okButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 5), 0, 0));
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 5), 0, 0));

//---- cancelButton ----
cancelButton.setText("Cancel");
cancelButton.addActionListener(e -> cancelButtonActionPerformed(e));
buttonBar.add(cancelButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0));
}
dialogPane.add(buttonBar, BorderLayout.SOUTH);
}
Expand Down

0 comments on commit 2d82d8e

Please sign in to comment.