Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Searchable bigbed #1447

Merged
merged 4 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions src/main/java/org/broad/igv/feature/genome/Genome.java
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ private void addTracks(GenomeConfig config) {
res.setVisibilityWindow(vw);
}

if (trackConfig.searchTrix != null) {
res.setTrixURL(trackConfig.searchTrix);
}

res.setFeatureInfoURL(trackConfig.infoURL);
Boolean indexed = trackConfig.indexed;
if (indexed != null) {
Expand Down Expand Up @@ -351,7 +355,7 @@ public Genome(String id, List<Chromosome> chromosomes) {
* Return the canonical chromosome name for the (possibly) alias
*
* @param str chromosome or alias name
* @return the canonical chromsoome name -- i.e. chromosome name as defined by the reference sequence
* @return the canonical chromsoome name -if the chromosome exists.
*/
public String getCanonicalChrName(String str) {
if (str == null) {
Expand All @@ -361,9 +365,7 @@ public String getCanonicalChrName(String str) {
} else if (chromAliasSource != null) {
try {
ChromAlias aliasRecord = chromAliasSource.search(str);
if (aliasRecord == null) {
return str;
} else {
if (aliasRecord != null) {
String chr = aliasRecord.getChr();
chrAliasCache.put(str, chr);
return chr;
Expand Down Expand Up @@ -487,9 +489,9 @@ public Chromosome getChromosome(String name) {
if (chromosomeMap.containsKey(chrName)) {
return chromosomeMap.get(chrName);
} else {
int idx = this.chromosomeMap.size();
int length = this.sequence.getChromosomeLength(chrName);
if (length > 0) {
int idx = this.chromosomeMap.size();
Chromosome chromosome = new Chromosome(idx, chrName, length);
chromosomeMap.put(chrName, chromosome);
return chromosome;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.broad.igv.ui.PanelName;
import org.broad.igv.ui.commandbar.GenomeListManager;
import org.broad.igv.ui.panel.FrameManager;
import org.broad.igv.ui.panel.ReferenceFrame;
import org.broad.igv.ui.util.MessageUtils;
import org.broad.igv.ui.util.ProgressBar;
import org.broad.igv.ui.util.ProgressMonitor;
Expand All @@ -72,6 +73,7 @@
import java.net.SocketException;
import java.net.URL;
import java.net.URLDecoder;
import java.sql.Ref;
import java.util.*;
import java.util.List;

Expand Down Expand Up @@ -231,8 +233,9 @@ public Genome loadGenome(String genomePath, ProgressMonitor monitor) throws IOEx

// hasInstance() test needed for unit tests
if (IGV.hasInstance()) {
IGV.getInstance().goToLocus(newGenome.getDefaultPos());
IGV.getInstance().goToLocus(newGenome.getHomeChromosome()); // newGenome.getDefaultPos());
loadGenomeAnnotations(newGenome);
IGV.getInstance().resetFrames();
}

if (PreferencesManager.getPreferences().getAsBoolean(Constants.CIRC_VIEW_ENABLED) && CircularViewUtilities.ping()) {
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/org/broad/igv/feature/genome/Sequence.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,26 @@
*/
public interface Sequence {

byte[] getSequence(String chr, int start, int end);
/**
* Return the sequence for the given range. If sequence named "seq" does not exist returns null.
* @param chr
* @param start
* @param end
* @return The sequence in bytes, or null if no sequence exists
*/
byte[] getSequence(String seq, int start, int end) ;

byte getBase(String chr, int position);
byte getBase(String seq, int position);

List<String> getChromosomeNames();

int getChromosomeLength(String chrname);
/**
* Return the given sequence length. If no sequence exists with name "seq" return -1.
*
* @param seq
* @return
*/
int getChromosomeLength(String seq);

List<Chromosome> getChromosomes();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.broad.igv.feature.genome;

public class SequenceNotFoundException extends RuntimeException {
public SequenceNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public List<String> getChromosomeNames() {

@Override
public int getChromosomeLength(String chrname) {
return index.getSequenceSize(chrname);
return index.getSequenceSize(chrname);
}

@Override
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/broad/igv/session/History.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.broad.igv.ui.IGV;
import org.broad.igv.ui.action.SearchCommand;
import org.broad.igv.ui.panel.FrameManager;
import org.broad.igv.util.LongRunningTask;

import java.util.ArrayList;
import java.util.LinkedList;
Expand Down Expand Up @@ -121,7 +122,7 @@ public void processItem(Entry entry) {
if (FrameManager.isGeneListMode()) {
IGV.getInstance().setGeneList(null, false);
}
(new SearchCommand(FrameManager.getDefaultFrame(), locus, false)).execute();
LongRunningTask.submit(new SearchCommand(FrameManager.getDefaultFrame(), locus, false));
//Zoom should be implicit in the locus
//FrameManager.getDefaultFrame().setZoom(entry.getZoom());
}
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/broad/igv/track/FeatureSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package org.broad.igv.track;

import htsjdk.tribble.Feature;
import htsjdk.tribble.NamedFeature;
import org.broad.igv.feature.LocusScore;
import org.broad.igv.ui.panel.ReferenceFrame;

Expand Down Expand Up @@ -92,4 +93,17 @@ default void close() {
default Object getHeader() {
return null;
}

/**
* Return true if the source can be searched for a feature by name
*
* @return
*/
default boolean isSearchable() {
return false;
}

default NamedFeature search(String name) {
return null;
}
}
11 changes: 11 additions & 0 deletions src/main/java/org/broad/igv/track/FeatureTrack.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package org.broad.igv.track;

import htsjdk.tribble.Feature;
import htsjdk.tribble.NamedFeature;
import htsjdk.tribble.TribbleException;
import org.broad.igv.event.IGVEvent;
import org.broad.igv.logging.*;
Expand Down Expand Up @@ -1056,6 +1057,16 @@ public void marshalXML(Document document, Element element) {

}

@Override
public boolean isSearchable() {
return source.isSearchable();
}

@Override
public NamedFeature search(String token) {
return source.search(token);
}

@Override
public void unmarshalXML(Element element, Integer version) {

Expand Down
21 changes: 19 additions & 2 deletions src/main/java/org/broad/igv/track/Track.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@


import htsjdk.tribble.Feature;
import htsjdk.tribble.NamedFeature;
import org.broad.igv.renderer.ContinuousColorScale;
import org.broad.igv.renderer.DataRange;
import org.broad.igv.renderer.Renderer;
Expand Down Expand Up @@ -272,9 +273,12 @@ default Color getExplicitAltColor() {

void setAutoScale(boolean autoScale);

default boolean isShowFeatureNames() {return true;}
default boolean isShowFeatureNames() {
return true;
}

default void setShowFeatureNames(boolean b) {}
default void setShowFeatureNames(boolean b) {
}

/**
* Return the java property or attribute for the feature display name. Default is "null", in which case the
Expand All @@ -286,6 +290,19 @@ default String getLabelField() {
return null;
}

/**
* Return true if the track can be searched for a feature by name.
*
* @return
*/
default boolean isSearchable() {
return false;
}

default NamedFeature search(String token) {
return null;
}

default void repaint() {
IGV.getInstance().repaint(this);
}
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/org/broad/igv/track/TrackLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import org.broad.igv.sam.reader.IndexNotFoundException;
import org.broad.igv.tdf.TDFDataSource;
import org.broad.igv.tdf.TDFReader;
import org.broad.igv.ucsc.Trix;
import org.broad.igv.ucsc.bb.BBDataSource;
import org.broad.igv.ucsc.bb.BBFeatureSource;
import org.broad.igv.ucsc.bb.BBFile;
Expand Down Expand Up @@ -792,9 +793,9 @@ public void loadBWFile(ResourceLocator locator, List<Track> newTracks, Genome ge
String trackId = locator.getPath();

String path = locator.getPath();
BBFile reader = new BBFile(path, genome);
Track track = null;

String trixURL = locator.getTrixURL();
BBFile reader = trixURL == null ? new BBFile(path, genome) : new BBFile(path, genome, trixURL);
Track track;
if (reader.isBigWigFile()) {
BBDataSource bigwigSource = new BBDataSource(reader, genome);
track = new DataSourceTrack(locator, trackId, trackName, bigwigSource);
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/broad/igv/ucsc/Trix.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ String _getBuffer(String searchWord) throws IOException {
int end = 65536;
List<IndexEntry> indexes = this.getIndex();
for (IndexEntry entry : indexes) {
String trimmedKey = entry.key.substring(0, searchWord.length());
int trimEnd = Math.min(entry.key.length(), searchWord.length());
String trimmedKey = entry.key.substring(0, trimEnd);
if (trimmedKey.compareTo(searchWord) < 0) {
start = entry.value;
end = entry.value + 65536;
Expand Down
28 changes: 20 additions & 8 deletions src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@

package org.broad.igv.ucsc.bb;

import htsjdk.tribble.NamedFeature;
import org.broad.igv.feature.BasicFeature;
import org.broad.igv.feature.LocusScore;
import org.broad.igv.feature.genome.Genome;
import org.broad.igv.logging.LogManager;
import org.broad.igv.logging.Logger;
import org.broad.igv.track.FeatureSource;
import org.broad.igv.track.WindowFunction;

Expand All @@ -42,10 +45,9 @@
*/
public class BBFeatureSource implements FeatureSource {

final int screenWidth = 1000; // TODO use actual screen width
private static Logger log = LogManager.getLogger(BBFeatureSource.class);
private final Genome genome;


Collection<WindowFunction> availableWindowFunctions =
Arrays.asList(WindowFunction.min, WindowFunction.mean, WindowFunction.max, WindowFunction.none);

Expand All @@ -56,9 +58,6 @@ public class BBFeatureSource implements FeatureSource {

private Map<WindowFunction, List<LocusScore>> wholeGenomeScores;

// Lookup table to support chromosome aliasing.
private Map<String, String> chrNameMap = new HashMap();

public BBFeatureSource(BBFile reader, Genome genome) throws IOException {

super();
Expand Down Expand Up @@ -108,6 +107,20 @@ public List<LocusScore> getCoverageScores(String chr, int start, int end, int zo
return null;
}

public boolean isSearchable() {
return reader.isSearchable();
}

@Override
public NamedFeature search(String term) {
try {
return reader.search(term);
} catch (IOException e) {
log.error("Error searching for: " + term, e);
return null;
}
}

static class FeatureIterator implements Iterator<BasicFeature> {

List<BasicFeature> features;
Expand All @@ -125,10 +138,9 @@ public FeatureIterator(List<BasicFeature> features, int start, int end) {
}

private void advance() {
if(idx == features.size()) {
if (idx == features.size()) {
next = null;
}
else {
} else {
while (idx < features.size()) {
next = features.get(idx++);
if (next.getStart() > end) {
Expand Down
27 changes: 26 additions & 1 deletion src/main/java/org/broad/igv/ucsc/bb/BBFile.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.broad.igv.ucsc.bb;

import htsjdk.samtools.seekablestream.SeekableStream;
import htsjdk.tribble.NamedFeature;
import org.broad.igv.data.BasicScore;
import org.broad.igv.feature.BasicFeature;
import org.broad.igv.feature.LocusScore;
Expand Down Expand Up @@ -112,6 +113,9 @@ public Set<String> getChromosomeNames() {
return chrNames;
}

public void setTrix(Trix trix) {
}


enum Type {BIGWIG, BIGBED}

Expand Down Expand Up @@ -384,6 +388,9 @@ BBZoomHeader zoomLevelForScale(double bpPerPixel) {
return lastLevel.reductionLevel / 2 < bpPerPixel ? lastLevel : null;
}

public boolean isSearchable() {
return header.extraIndexCount > 0;
}

/**
* Search the extended BP tree for the search term, and return any matching features. This only works
Expand All @@ -394,6 +401,7 @@ BBZoomHeader zoomLevelForScale(double bpPerPixel) {
* @param term
* @returns {Promise<void>}
*/

public BasicFeature search(String term) throws IOException {

if (this.header == null) {
Expand All @@ -403,6 +411,16 @@ public BasicFeature search(String term) throws IOException {
return null;
}

if (this.trix != null) {
String termLower = term.toLowerCase();
Map<String, String[]> results = trix.search(termLower);
if (results != null) {
String[] exactMatches = results.get(termLower);
if (exactMatches.length > 0) term = exactMatches[0];
}
}


long[] region = this.searchForRegions(term); // Either 1 or no (undefined) reginos returned for now
if (region != null) {
long start = region[0];
Expand All @@ -412,11 +430,18 @@ public BasicFeature search(String term) throws IOException {
is.seek(start);
is.readFully(buffer);
List<BasicFeature> features = decodeFeatures(buffer, -1, -1, -1);
BasicFeature largest = features.stream().reduce((f1, f2) -> {

// Filter features to those matching term
final String searchTerm = term;

BasicFeature largest = features.stream().filter(f -> {
return f.getName().equalsIgnoreCase(searchTerm) || f.getAttributes().values().stream().anyMatch(v -> v.equalsIgnoreCase(searchTerm));
}).reduce((f1, f2) -> {
int l1 = f1.getEnd() - f1.getStart();
int l2 = f2.getEnd() - f2.getStart();
return l1 > l2 ? f1 : f2;
}).get();

return largest;
}
}
Expand Down
Loading