From 0a8274f491ee9ef03b6bede0bd7cecd3a7df5a93 Mon Sep 17 00:00:00 2001 From: jrobinso <933148+jrobinso@users.noreply.github.com> Date: Thu, 20 Feb 2025 08:36:43 -0800 Subject: [PATCH 1/9] Support "interact" format. Simplify feature hierarchy --- src/main/java/org/broad/igv/bedpe/BedPE.java | 42 +++- .../org/broad/igv/bedpe/BedPEFeature.java | 100 +++++---- .../broad/igv/bedpe/BedPEInterFeature.java | 88 -------- .../java/org/broad/igv/bedpe/BedPEParser.java | 53 +++-- .../org/broad/igv/bedpe/BedPERenderer.java | 1 + .../java/org/broad/igv/bedpe/BedPESource.java | 134 ++++++++++++ .../java/org/broad/igv/bedpe/BedPEUtils.java | 70 ------- .../org/broad/igv/bedpe/InteractCodec.java | 59 ++++++ .../org/broad/igv/bedpe/InteractFeature.java | 81 ++++++++ .../org/broad/igv/bedpe/InteractParser.java | 44 ++++ .../org/broad/igv/bedpe/InteractionTrack.java | 192 ++++++------------ .../broad/igv/bedpe/NestedArcRenderer.java | 6 +- .../org/broad/igv/bedpe/PEBlockRenderer.java | 10 +- .../igv/bedpe/ProportionalArcRenderer.java | 51 +++-- .../java/org/broad/igv/bedpe/WGFeature.java | 104 ++++++++++ .../java/org/broad/igv/data/WiggleParser.java | 6 +- .../broad/igv/feature/AbstractFeature.java | 2 +- .../org/broad/igv/feature/FeatureType.java | 2 +- .../org/broad/igv/feature/IGVFeature.java | 6 + .../broad/igv/feature/IGVNamedFeature.java | 2 - .../igv/feature/genome/ChromAliasBB.java | 3 +- .../igv/feature/tribble/CodecFactory.java | 3 + .../broad/igv/feature/tribble/DGVCodec.java | 2 +- .../igv/feature/tribble/EncodePeakCodec.java | 13 +- .../igv/feature/tribble/IGVBEDCodec.java | 40 +--- .../broad/igv/feature/tribble/PSLCodec.java | 20 +- .../broad/igv/feature/tribble/UCSCCodec.java | 36 +++- .../feature/tribble/UCSCGeneTableCodec.java | 14 +- .../igv/feature/tribble/UCSCSnpCodec.java | 7 +- .../broad/igv/htsget/HtsgetVariantSource.java | 5 - .../java/org/broad/igv/jbrowse/Chord.java | 16 +- .../tools/motiffinder/MotifFinderSource.java | 6 - .../org/broad/igv/track/AbstractTrack.java | 1 + .../igv/track/FeatureCollectionSource.java | 5 - .../org/broad/igv/track/FeatureDirSource.java | 4 - .../org/broad/igv/track/FeatureSource.java | 10 +- .../org/broad/igv/track/FeatureTrack.java | 1 + .../org/broad/igv/track/FileFormatUtils.java | 5 +- .../igv/track/MutationFeatureSource.java | 5 - src/main/java/org/broad/igv/track/Track.java | 4 + .../java/org/broad/igv/track/TrackLoader.java | 29 +-- .../org/broad/igv/track/TrackMenuUtils.java | 3 +- .../broad/igv/track/TribbleFeatureSource.java | 22 -- .../broad/igv/ucsc/bb/BBFeatureSource.java | 4 - .../java/org/broad/igv/ucsc/bb/BBFile.java | 13 +- .../org/broad/igv/ucsc/bb/codecs/BBCodec.java | 3 +- .../igv/ucsc/bb/codecs/BBInteractCodec.java | 101 +++++++++ .../java/org/broad/igv/ui/IGVMenuBar.java | 4 +- .../igv/ui/action/LoadFromURLMenuAction.java | 2 +- .../broad/igv/ui/color/ColorUtilities.java | 2 +- .../java/org/broad/igv/util/FeatureCache.java | 7 +- .../feature/tribble/InteractCodecTest.java | 82 ++++++++ .../org/broad/igv/ucsc/bb/BBFileTest.java | 21 +- .../org/broad/igv/util/FeatureCacheTest.java | 9 +- test/data/bedpe/interactExample1.txt | 7 + test/data/bedpe/interactExample2.txt | 6 + 56 files changed, 1006 insertions(+), 562 deletions(-) delete mode 100644 src/main/java/org/broad/igv/bedpe/BedPEInterFeature.java create mode 100644 src/main/java/org/broad/igv/bedpe/BedPESource.java create mode 100644 src/main/java/org/broad/igv/bedpe/InteractCodec.java create mode 100644 src/main/java/org/broad/igv/bedpe/InteractFeature.java create mode 100644 src/main/java/org/broad/igv/bedpe/InteractParser.java create mode 100644 src/main/java/org/broad/igv/bedpe/WGFeature.java create mode 100644 src/main/java/org/broad/igv/ucsc/bb/codecs/BBInteractCodec.java create mode 100644 src/test/java/org/broad/igv/feature/tribble/InteractCodecTest.java create mode 100644 test/data/bedpe/interactExample1.txt create mode 100644 test/data/bedpe/interactExample2.txt diff --git a/src/main/java/org/broad/igv/bedpe/BedPE.java b/src/main/java/org/broad/igv/bedpe/BedPE.java index 8819b2e43b..deba7fb031 100644 --- a/src/main/java/org/broad/igv/bedpe/BedPE.java +++ b/src/main/java/org/broad/igv/bedpe/BedPE.java @@ -1,21 +1,28 @@ package org.broad.igv.bedpe; - -import htsjdk.samtools.util.Locatable; +import org.broad.igv.feature.IGVFeature; import java.awt.*; -public interface BedPE extends Locatable { +public interface BedPE extends IGVFeature { - BedPEFeature get(); + default String getName() { + return null; + } - double getScore(); + String getChr1(); - boolean isSameChr(); + int getStart1(); + + int getEnd1(); - void setRow(int row); + String getChr2(); - int getRow(); + int getStart2(); + + int getEnd2(); + + boolean isSameChr(); Color getColor(); @@ -27,5 +34,22 @@ public interface BedPE extends Locatable { String getValueString(); - double getCenterDistance(); + default double getMidStart() { + return Math.min ((getStart1() + getEnd1()) / 2.0, (getStart2() + getEnd2()) / 2.0); + } + + default double getMidEnd() { + return Math.max ((getStart1() + getEnd1()) / 2.0, (getStart2() + getEnd2()) / 2.0); + } + + default double getCenterDistance() { + return Math.abs((getStart1() + getEnd1()) / 2.0 - (getStart2() + getEnd2()) / 2.0); + } + + /** + * @return true if this feature is a shadow complement of an inter-chr feature. + */ + default boolean isComplement() { + return false; + } } diff --git a/src/main/java/org/broad/igv/bedpe/BedPEFeature.java b/src/main/java/org/broad/igv/bedpe/BedPEFeature.java index 765845ad25..8ba6a5face 100644 --- a/src/main/java/org/broad/igv/bedpe/BedPEFeature.java +++ b/src/main/java/org/broad/igv/bedpe/BedPEFeature.java @@ -1,5 +1,8 @@ package org.broad.igv.bedpe; +import org.broad.igv.feature.BasicFeature; +import org.broad.igv.feature.IGVFeature; + import java.awt.*; import java.util.Map; @@ -8,21 +11,25 @@ */ public class BedPEFeature implements BedPE { - public String chr1; - public int start1; - public int end1; - public String chr2; - public int start2; - public int end2; - String name; - String scoreString = ""; - double score; - Color color; - int thickness = 1; - String type; + protected String chr1; + protected int start1; + protected int end1; + + protected String chr2; + protected int start2; + protected int end2; + protected String name; + protected String scoreString = ""; + protected float score; + protected Color color; + protected int thickness = 1; + protected String type; Map attributes; - int row; BedPEShape shape; + private boolean isComplement = false; + + public BedPEFeature() { + } public BedPEFeature(String chr1, int start1, int end1, String chr2, int start2, int end2) { this.chr1 = chr1; @@ -33,15 +40,17 @@ public BedPEFeature(String chr1, int start1, int end1, String chr2, int start2, this.end2 = end2; } - public BedPEFeature get() { - return this; + public BedPEFeature getComplement() { + BedPEFeature complement = new BedPEFeature(chr1, start1, end1, chr2, start2, end2); + complement.isComplement = true; + return complement; } public String getChr() { - if(isSameChr()) { + if (isSameChr()) { return chr1; } else { - return null; + return isComplement ? chr2 : chr1; } } @@ -54,30 +63,25 @@ public int getEnd() { } public double getMidStart() { - return Math.min ((start1 + end1) / 2.0, (start2 + end2) / 2.0); + return Math.min((start1 + end1) / 2.0, (start2 + end2) / 2.0); } public double getMidEnd() { - return Math.max ((start1 + end1) / 2.0, (start2 + end2) / 2.0); + return Math.max((start1 + end1) / 2.0, (start2 + end2) / 2.0); } @Override - public double getScore() { - return score; - } - - public boolean isSameChr() { - return chr1.equals(chr2); + public boolean isComplement() { + return this.isComplement; } @Override - public void setRow(int row) { - this.row = row; + public float getScore() { + return score; } - @Override - public int getRow() { - return row; + public boolean isSameChr() { + return chr1.equals(chr2); } @Override @@ -110,13 +114,13 @@ public String getValueString() { String locus1 = chr1 + ":" + start1 + "-" + end1; String locus2 = chr2 + ":" + start2 + "-" + end2; - if(name != null && name.length() > 0 && !name.equals(".")) { + if (name != null && name.length() > 0 && !name.equals(".")) { buf.append(name + "
"); } buf.append(locus1); buf.append("
" + locus2); buf.append("
Score: " + score); - if(attributes != null) { + if (attributes != null) { buf.append("

"); for (Map.Entry entry : attributes.entrySet()) { buf.append("
" + entry.getKey() + ": " + entry.getValue()); @@ -126,8 +130,36 @@ public String getValueString() { return buf.toString(); } + public String getChr1() { + return chr1; + } + + public int getStart1() { + return start1; + } + + public int getEnd1() { + return end1; + } + + public String getChr2() { + return chr2; + } + + public int getStart2() { + return start2; + } + + public int getEnd2() { + return end2; + } + + public void setColor(Color color) { + this.color = color; + } + @Override - public double getCenterDistance() { - return Math.abs((start1 + end1) / 2.0 - (start2 + end2) / 2.0); + public String getName() { + return name; } } diff --git a/src/main/java/org/broad/igv/bedpe/BedPEInterFeature.java b/src/main/java/org/broad/igv/bedpe/BedPEInterFeature.java deleted file mode 100644 index 8255c235eb..0000000000 --- a/src/main/java/org/broad/igv/bedpe/BedPEInterFeature.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.broad.igv.bedpe; - -import java.awt.*; - -public class BedPEInterFeature implements BedPE { - - - private final BedPEFeature wrappedFeature; - private final int order; - int row; - BedPEShape shape; - - public BedPEInterFeature(BedPEFeature wrappedFeature, int order) { - this.wrappedFeature = wrappedFeature; - this.order = order; - } - - public BedPEFeature get() { - return wrappedFeature; - } - - public String getChr() { - return this.order == 1 ? wrappedFeature.chr1 : wrappedFeature.chr2; - } - - public int getStart() { - return this.order == 1 ? wrappedFeature.start1 : wrappedFeature.start2; - } - - public int getEnd() { - return this.order == 1 ? wrappedFeature.end1 : wrappedFeature.end2; - } - - @Override - public double getScore() { - return wrappedFeature.score; - } - - public boolean isSameChr() { - return false; - } - - @Override - public void setRow(int row) { - this.row = row; - } - - @Override - public int getRow() { - return row; - } - - @Override - public Color getColor() { - return wrappedFeature.getColor(); - } - - @Override - public int getThickness() { - return wrappedFeature.getThickness(); - } - - @Override - public void setShape(BedPEShape s) { - this.shape = s; - } - - @Override - public BedPEShape getShape() { - return shape; - } - - @Override - public String getValueString() { - return wrappedFeature.getValueString(); - } - - @Override - public double getCenterDistance() { - return 0; - } - - public String getContig() { - return getChr(); - } - - -} diff --git a/src/main/java/org/broad/igv/bedpe/BedPEParser.java b/src/main/java/org/broad/igv/bedpe/BedPEParser.java index b50a015987..2094f6bd39 100644 --- a/src/main/java/org/broad/igv/bedpe/BedPEParser.java +++ b/src/main/java/org/broad/igv/bedpe/BedPEParser.java @@ -21,23 +21,21 @@ public class BedPEParser { private static Logger log = LogManager.getLogger(BedPEParser.class); - enum DatasetType {TENX, CLUSTER, UNKNOWN} - - public static Dataset parse(ResourceLocator locator, Genome genome) throws IOException { + public static List parse(ResourceLocator locator, Genome genome) throws IOException { int colorColumn = -1; int thicknessColumn = -1; - DatasetType type = DatasetType.UNKNOWN; - boolean parsedHeader = true; // Default column headers from BedPE spec. Can be overriden String[] columns = {"chrom1", "start1", "stop1", "chrom2", "start2", "stop2", "name", "score", "strand1", "strand2"}; boolean col7isNumeric = true; // Until proven otherwise Map colorCache = new HashMap<>(); - List features = new ArrayList<>(); + List features = new ArrayList<>(); BufferedReader br = null; + Map featureCounts = new HashMap<>(); + try { br = ParsingUtils.openBufferedReader(locator.getPath()); String nextLine; @@ -63,8 +61,6 @@ public static Dataset parse(ResourceLocator locator, Genome genome) throws IOExc } catch (NumberFormatException e) { log.error("Error parsing #column line.", e); } - } else if (nextLine.trim().equals("#chrom1\tstart1\tstop1\tchrom2\tstart2\tstop2\tname\tqual\tstrand1\tstrand2\tfilters\tinfo")) { - type = DatasetType.TENX; } if (nextLine.startsWith("#") || nextLine.startsWith("chr1\tx1\tx2")) { @@ -112,6 +108,11 @@ public static Dataset parse(ResourceLocator locator, Genome genome) throws IOExc BedPEFeature feature = new BedPEFeature(chr1, start1, end1, chr2, start2, end2); + if(chr1.equals(chr2)) { + Integer counts = featureCounts.containsKey(chr1) ? featureCounts.get(chr1) : 0; + featureCounts.put(chr1, counts + 1); + } + if (tokens.length > 6) { feature.name = tokens[6]; col7isNumeric = col7isNumeric && isNumeric(tokens[6]); @@ -123,7 +124,7 @@ public static Dataset parse(ResourceLocator locator, Genome genome) throws IOExc if (tokens.length > 7) { feature.scoreString = tokens[7]; try { - feature.score = Double.parseDouble(tokens[7]); + feature.score = Float.parseFloat(tokens[7]); } catch (NumberFormatException e) { feature.score = 0; } @@ -160,14 +161,18 @@ public static Dataset parse(ResourceLocator locator, Genome genome) throws IOExc c = ColorUtilities.stringToColor(colorString); colorCache.put(colorString, c); } - feature.color = c; + feature.setColor(c); } if (thicknessColumn > 0 && tokens.length > thicknessColumn) { - feature.thickness = Integer.parseInt(tokens[thicknessColumn]); + feature.thickness = Math.max(1, (int) Float.parseFloat(tokens[thicknessColumn])); } // Skipping remaining fields for now + if (!feature.getChr1().equals(feature.getChr2())) { + // Add complement feature + features.add(feature.getComplement()); + } features.add(feature); } @@ -177,17 +182,17 @@ public static Dataset parse(ResourceLocator locator, Genome genome) throws IOExc // A hack to detect "interaction" bedpe files, which are not spec compliant. Interaction score is column 7 if (col7isNumeric) { - for (BedPEFeature f : features) { - f.score = Double.parseDouble(f.name); - f.scoreString = f.name; - f.name = null; - } - if (type == DatasetType.UNKNOWN) { - type = DatasetType.CLUSTER; // A guess + for (BedPE bedpe : features) { + if(bedpe instanceof BedPEFeature) { + BedPEFeature f = (BedPEFeature) bedpe; + f.score = Float.parseFloat(f.name); + f.scoreString = f.name; + f.name = null; + } } } - return new Dataset(type, features); + return features; } finally { br.close(); } @@ -200,15 +205,5 @@ public static boolean isNumeric(String strNum) { return strNum.matches("-?\\d+(\\.\\d+)?"); } - public static class Dataset { - - public DatasetType type; - public List features; - - public Dataset(DatasetType type, List features) { - this.type = type; - this.features = features; - } - } } diff --git a/src/main/java/org/broad/igv/bedpe/BedPERenderer.java b/src/main/java/org/broad/igv/bedpe/BedPERenderer.java index d51b09a268..a157175941 100644 --- a/src/main/java/org/broad/igv/bedpe/BedPERenderer.java +++ b/src/main/java/org/broad/igv/bedpe/BedPERenderer.java @@ -7,4 +7,5 @@ public interface BedPERenderer { void render(List features, RenderContext context, Rectangle trackRectangle, InteractionTrack.ArcOption arcOption); + } diff --git a/src/main/java/org/broad/igv/bedpe/BedPESource.java b/src/main/java/org/broad/igv/bedpe/BedPESource.java new file mode 100644 index 0000000000..0b3a5a0b06 --- /dev/null +++ b/src/main/java/org/broad/igv/bedpe/BedPESource.java @@ -0,0 +1,134 @@ +package org.broad.igv.bedpe; + +import org.broad.igv.Globals; +import org.broad.igv.feature.Chromosome; +import org.broad.igv.feature.genome.Genome; +import org.broad.igv.track.FeatureSource; +import org.broad.igv.util.Downsampler; +import org.broad.igv.util.FeatureCache; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +public class BedPESource implements FeatureSource { + + public static final int MAX_WG_COUNT = 10000; + private FeatureCache featureCache; + private List allFeatures; + private List wgFeatures; + private int visibilityWindow; + + public BedPESource(List allFeatures, Genome genome) { + this.allFeatures = allFeatures; + init(allFeatures, genome); + } + + @Override + public Iterator getFeatures(String chr, int start, int end) throws IOException { + + if (chr.equals(Globals.CHR_ALL)) { + return wgFeatures.iterator(); + } else { + return featureCache.getFeatures(chr, start, end).iterator(); + } + } + + + @Override + public int getFeatureWindowSize() { + return this.visibilityWindow; + } + + private void init(List featureList, Genome genome) { + + featureCache = new FeatureCache<>(featureList, 50); + + wgFeatures = createWGFeatures(featureList, genome); + + allFeatures = featureList; + + Map featureCounts = new HashMap<>(); + for(BedPE f : featureList) { + int count = featureCounts.containsKey(f.getChr()) ? featureCounts.get(f.getChr()) : 0; + featureCounts.put(f.getChr(), count + 1); + } + + // Compute viz window based on feature density + int vw = Integer.MAX_VALUE; + for (Map.Entry entry : featureCounts.entrySet()) { + String chr = entry.getKey(); + Chromosome chromosome = genome.getChromosome(chr); + if (chromosome != null) { + double f = 100000.0 / entry.getValue(); + vw = Math.min(vw, (int) (f * chromosome.getLength())); + } + } + + this.visibilityWindow = vw; + } + + private List createWGFeatures(List features, Genome genome) { + + int size = Math.min(features.size(), MAX_WG_COUNT); + List wgFeatures = new ArrayList<>(size); + + List sampledFeatures; + if (features.size() < MAX_WG_COUNT) { + sampledFeatures = features; + } else { + sampledFeatures = downsampleFeatures(features); + } + + for (BedPE f : sampledFeatures) { + + BedPE wgFeature = new WGFeature(f, genome); + + wgFeatures.add(wgFeature); + + } + return wgFeatures; + } + + public static List downsampleFeatures(List features) { + + if (features.isEmpty()) { + return Collections.EMPTY_LIST; + } + + BedPE maxScoreFeature = features.stream() + .max(Comparator.comparing(BedPE::getScore)).get(); + + int nBins = maxScoreFeature.getScore() > 0 ? 5 : 1; // TODO make a function of total # of features & maxCount? + double binSize = nBins > 1 ? Math.log10(maxScoreFeature.getScore()) / nBins : Integer.MAX_VALUE; + + // Divide features into bins + List[] binnedFeatures = new List[nBins]; + int counts[] = new int[nBins]; + for (int i = 0; i < nBins; i++) { + binnedFeatures[i] = new ArrayList<>(); + counts[i] = 0; + } + for (BedPE f : features) { + if (f.isComplement()) continue; + int bin = f.getScore() <= 0 ? 0 : (int) Math.min(nBins - 1, Math.floor(Math.log10(f.getScore()) / binSize)); + binnedFeatures[bin].add(f); + counts[bin]++; + } + + // Add sampled features from each bin + int featuresPerBin = MAX_WG_COUNT / nBins; + List sampledFeatures = new ArrayList<>(MAX_WG_COUNT); + for (int i = 0; i < nBins; i++) { + List bfs = binnedFeatures[i]; + sampledFeatures.addAll(Arrays.asList(new Downsampler().sample(bfs.toArray(BedPEFeature[]::new), featuresPerBin))); + } + + // Be sure we keep the maximum feature + if (maxScoreFeature != null) { + sampledFeatures.add(maxScoreFeature); + } + + return sampledFeatures; + } +} diff --git a/src/main/java/org/broad/igv/bedpe/BedPEUtils.java b/src/main/java/org/broad/igv/bedpe/BedPEUtils.java index 00ef020382..10d4da68c5 100644 --- a/src/main/java/org/broad/igv/bedpe/BedPEUtils.java +++ b/src/main/java/org/broad/igv/bedpe/BedPEUtils.java @@ -105,74 +105,4 @@ public static void juiceboxToBedPE(String ifile, String ofile) throws IOExceptio ; } } - - public static void packFeatures(List features, int maxRows) { - - int gap = 2; - - int start = features.get(0).getStart(); - int end = features.get(features.size() - 1).getStart(); - - int bucketStart = start; - int nextStart = bucketStart; - - Map> bucketList = new HashMap<>(); - for(BedPE alignment : features) { - - int buckListIndex = Math.max(0, alignment.getStart() - bucketStart); - if (bucketList.get(buckListIndex) == null) { - bucketList.put(buckListIndex, new Stack<>()); - } - bucketList.get(buckListIndex).add(alignment); - } - - - int row = 0; - int allocatedCount = 0; - int lastAllocatedCount = -1; - - while (allocatedCount < features.size() && row <= maxRows) { - - while (nextStart <= end) { - - Stack bucket = null; - - int index = 0; - while (bucket == null && nextStart <= end) { - index = nextStart - bucketStart; - if (bucketList.get(index) == null) { - ++nextStart; // No buckets at this index - } else { - bucket = bucketList.get(index); - } - } // while (bucket) - - if (bucket == null) { - break; - } - - BedPE feature = bucket.pop(); - if (0 == bucket.size()) { - bucketList.remove(index); - } - - feature.setRow(row); - - nextStart = feature.getEnd() + gap; - ++allocatedCount; - - } // while (nextStart) - - row++; - nextStart = bucketStart; - - if (allocatedCount == lastAllocatedCount) break; // Protect from infinite loops - - lastAllocatedCount = allocatedCount; - - } // while (allocatedCount) - - } - - } diff --git a/src/main/java/org/broad/igv/bedpe/InteractCodec.java b/src/main/java/org/broad/igv/bedpe/InteractCodec.java new file mode 100644 index 0000000000..0dae01a8dc --- /dev/null +++ b/src/main/java/org/broad/igv/bedpe/InteractCodec.java @@ -0,0 +1,59 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2007-2015 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.broad.igv.bedpe; + +import org.broad.igv.bedpe.InteractFeature; +import org.broad.igv.feature.*; +import org.broad.igv.feature.genome.Genome; +import org.broad.igv.feature.tribble.UCSCCodec; + +/** + * Decode an UCSC interact file + * + * Reference: https://genome.ucsc.edu/goldenpath/help/interact.html + * + */ +public class InteractCodec extends UCSCCodec { + + private Genome genome; + + public InteractCodec(Genome genome, FeatureType featureType) { + super(InteractFeature.class, featureType); + this.genome = genome; + } + + + //@Override + public InteractFeature decode(String[] tokens) { + return InteractFeature.fromTokens(tokens, genome); + } + + public boolean canDecode(String path) { + return true; + } +} + + diff --git a/src/main/java/org/broad/igv/bedpe/InteractFeature.java b/src/main/java/org/broad/igv/bedpe/InteractFeature.java new file mode 100644 index 0000000000..d575201627 --- /dev/null +++ b/src/main/java/org/broad/igv/bedpe/InteractFeature.java @@ -0,0 +1,81 @@ +package org.broad.igv.bedpe; + +import org.broad.igv.feature.genome.Genome; +import org.broad.igv.ui.color.ColorUtilities; + +import java.awt.*; + +/** + * 0 string chrom; "Chromosome (or contig, scaffold, etc.). For interchromosomal, use 2 records" + * 1 uint chromStart; "Start position of lower region. For interchromosomal, set to chromStart of this region" + * 2 uint chromEnd; "End position of upper region. For interchromosomal, set to chromEnd of this region" + * 3 string name; "Name of item, for display. Usually 'sourceName/targetName/exp' or empty" + * 4 uint score; "Score (0-1000)" + * 5 double value; "Strength of interaction or other data value. Typically basis for score" + * 6 string exp; "Experiment name (metadata for filtering). Use . if not applicable" + * 7 string color; "Item color. Specified as r,g,b or hexadecimal #RRGGBB or html color name, as in //www.w3.org/TR/css3-color/#html4. Use 0 and spectrum setting to shade by score" + * 8 string sourceChrom; "Chromosome of source region (directional) or lower region. For non-directional interchromosomal, chrom of this region." + * 9 uint sourceStart; "Start position in chromosome of source/lower/this region" + * 10 uint sourceEnd; "End position in chromosome of source/lower/this region" + * 11 string sourceName; "Identifier of source/lower/this region" + * 12 string sourceStrand; "Orientation of source/lower/this region: + or -. Use . if not applicable" + * 13 string targetChrom; "Chromosome of target region (directional) or upper region. For non-directional interchromosomal, chrom of other region" + * 14 uint targetStart; "Start position in chromosome of target/upper/this region" + * 15 uint targetEnd; "End position in chromosome of target/upper/this region" + * 16 string targetName; "Identifier of target/upper/this region" + * 17 string targetStrand; "Orientation of target/upper/this region: + or -. Use . if not applicable" + */ +public class InteractFeature extends BedPEFeature { + + String chr; + int start; + int end; + private double value; + + public InteractFeature() { + + } + + public static InteractFeature fromTokens(String[] tokens, Genome genome) { + + if (tokens.length < 12) { + return null; + } + + InteractFeature feature = new InteractFeature(); + + feature.chr = genome == null ? tokens[0] : genome.getCanonicalChrName(tokens[0]); + + feature.start = Integer.parseInt(tokens[1]); + feature.end = Integer.parseInt(tokens[2]); + feature.name = tokens[3]; + feature.score = Float.parseFloat(tokens[4]); + feature.value = Double.parseDouble(tokens[5]); + feature.color = ColorUtilities.stringToColor(tokens[7]); + + feature.chr1 = genome == null ? tokens[8] : genome.getCanonicalChrName(tokens[8]); + feature.start1 = Integer.parseInt(tokens[9]); + feature.end1 = Integer.parseInt(tokens[10]); + + feature.chr2 = genome == null ? tokens[13] : genome.getCanonicalChrName(tokens[13]); + feature.start2 = Integer.parseInt(tokens[14]); + feature.end2 = Integer.parseInt(tokens[15]); + + return feature; + } + + @Override + public String getChr() { + return chr; + } + + @Override + public int getStart() { + return start; + } + + @Override + public int getEnd() { + return end; + } +} diff --git a/src/main/java/org/broad/igv/bedpe/InteractParser.java b/src/main/java/org/broad/igv/bedpe/InteractParser.java new file mode 100644 index 0000000000..36a677da1f --- /dev/null +++ b/src/main/java/org/broad/igv/bedpe/InteractParser.java @@ -0,0 +1,44 @@ +package org.broad.igv.bedpe; + +import org.broad.igv.Globals; +import org.broad.igv.feature.genome.Genome; +import org.broad.igv.logging.LogManager; +import org.broad.igv.logging.Logger; +import org.broad.igv.util.ParsingUtils; +import org.broad.igv.util.ResourceLocator; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.List; +import java.util.*; + +/** + * Created by jrobinso on 6/29/18. + */ +public class InteractParser { + + private static Logger log = LogManager.getLogger(InteractParser.class); + + public static List parse(ResourceLocator locator, Genome genome) throws IOException { + + List features = new ArrayList<>(); + BufferedReader br = null; + + try { + br = ParsingUtils.openBufferedReader(locator.getPath()); + String nextLine; + while ((nextLine = br.readLine()) != null) { + if (nextLine.startsWith("#") || nextLine.startsWith("track") || nextLine.startsWith("browser")) continue; + String [] tokens = Globals.whitespacePattern.split(nextLine); + InteractFeature f = InteractFeature.fromTokens(tokens, genome); + if(f != null) { + features.add(f); + } + } + return features; + } finally { + br.close(); + } + } + +} diff --git a/src/main/java/org/broad/igv/bedpe/InteractionTrack.java b/src/main/java/org/broad/igv/bedpe/InteractionTrack.java index 6e04214119..32607592bf 100644 --- a/src/main/java/org/broad/igv/bedpe/InteractionTrack.java +++ b/src/main/java/org/broad/igv/bedpe/InteractionTrack.java @@ -1,6 +1,10 @@ package org.broad.igv.bedpe; import org.broad.igv.Globals; +import org.broad.igv.event.IGVEvent; +import org.broad.igv.event.IGVEventObserver; +import org.broad.igv.feature.Chromosome; +import org.broad.igv.feature.FeatureUtils; import org.broad.igv.feature.Range; import org.broad.igv.feature.genome.Genome; import org.broad.igv.jbrowse.CircularViewUtilities; @@ -8,10 +12,8 @@ import org.broad.igv.logging.Logger; import org.broad.igv.prefs.Constants; import org.broad.igv.prefs.PreferencesManager; -import org.broad.igv.track.AbstractTrack; -import org.broad.igv.track.RenderContext; -import org.broad.igv.track.TrackClickEvent; -import org.broad.igv.track.TrackMenuUtils; +import org.broad.igv.renderer.GraphicUtils; +import org.broad.igv.track.*; import org.broad.igv.ui.FontManager; import org.broad.igv.ui.panel.FrameManager; import org.broad.igv.ui.panel.IGVPopupMenu; @@ -25,30 +27,20 @@ import javax.swing.*; import java.awt.*; +import java.io.IOException; import java.util.List; import java.util.*; import java.util.function.Function; -import java.util.stream.Collectors; import static org.broad.igv.bedpe.InteractionTrack.Direction.UP; /** * Created by jrobinso on 6/29/18. */ -public class InteractionTrack extends AbstractTrack { +public class InteractionTrack extends AbstractTrack implements IGVEventObserver { - public static final int MAX_WG_COUNT = 1000; private static Logger log = LogManager.getLogger(InteractionTrack.class); - protected static final int AXIS_AREA_WIDTH = 60; - protected static Color axisLineColor = new Color(255, 180, 180); - private JCheckBoxMenuItem autoscaleCB; - private JMenuItem maxScoreItem; - private List wgFeatures; - - // TODO -- for jbrowse experiment - private List allFeatures; - enum Direction {UP, DOWN} @@ -56,7 +48,10 @@ enum GraphType {BLOCK, NESTED_ARC, PROPORTIONAL_ARC} enum ArcOption {ALL, ONE_END, BOTH_ENDS} - private Genome genome; + private FeatureSource featureSource; + private JCheckBoxMenuItem autoscaleCB; + private JMenuItem maxScoreItem; + InteractionTrack.Direction direction = UP; //DOWN; GraphType graphType; // GraphType.block; // private ArcOption arcOption = ArcOption.ALL; @@ -65,20 +60,17 @@ enum ArcOption {ALL, ONE_END, BOTH_ENDS} double maxScore = -1; int gap = 5; boolean showBlocks = false; - - - //private Map> featureMap; private Map renderers; - private FeatureCache featureCache; + + transient Map> lastRenderedFeatures = new HashMap<>(); public InteractionTrack() { } - public InteractionTrack(ResourceLocator locator, BedPEParser.Dataset dataset, Genome genome) { + public InteractionTrack(ResourceLocator locator, FeatureSource src) { super(locator); - init(dataset.features, genome); - this.genome = genome; + this.featureSource = src; setHeight(250, true); setColor(new Color(180, 25, 137)); @@ -96,7 +88,7 @@ public InteractionTrack(ResourceLocator locator, BedPEParser.Dataset dataset, Ge graphType = GraphType.NESTED_ARC; // default } } else { - graphType = dataset.type == BedPEParser.DatasetType.TENX ? GraphType.PROPORTIONAL_ARC : GraphType.NESTED_ARC; + graphType = GraphType.PROPORTIONAL_ARC; } @@ -122,27 +114,16 @@ public InteractionTrack(ResourceLocator locator, BedPEParser.Dataset dataset, Ge } } - private void init(List featureList, Genome genome) { - List newList = new ArrayList<>((int) (1.2 * featureList.size())); + protected boolean isShowFeatures(ReferenceFrame frame) { - for (BedPEFeature f : featureList) { - String key = genome == null ? f.chr1 : genome.getCanonicalChrName(f.chr1); - if (f.chr1.equals(f.chr2)) { - newList.add(f); - } else { - newList.add(new BedPEInterFeature(f, 1)); - newList.add(new BedPEInterFeature(f, 2)); - } + if (frame.getChrName().equals(Globals.CHR_ALL)) { + return true; + } else { + double windowSize = frame.getEnd() - frame.getOrigin(); + int vw = getVisibilityWindow(); + return (vw <= 0 || windowSize <= vw); } - - featureCache = new FeatureCache<>(newList, 50); - - wgFeatures = createWGFeatures(featureList, genome); - - allFeatures = featureList; - - } @@ -156,14 +137,6 @@ public void load(ReferenceFrame frame) { // Nothing to do, this track is pre-loaded } - private List getFeaturesOverlapping(String chr, double start, double end) { - - if (chr.equals(Globals.CHR_ALL)) { - return wgFeatures; - } else { - return featureCache.getFeatures(chr, (int) start, (int) end); - } - } @Override public void render(RenderContext context, Rectangle trackRectangle) { @@ -173,10 +146,23 @@ public void render(RenderContext context, Rectangle trackRectangle) { g2d.setClip(trackRectangle.intersection(clip.getBounds())); context.clearGraphicsCache(); + if (!isShowFeatures(context.getReferenceFrame())) { + String message = "Zoom in to see features, or right-click to increase Feature Visibility Window."; + GraphicUtils.drawCenteredText(message, trackRectangle, context.getGraphics()); + return; + } try { String chr = context.getReferenceFrame().getChrName(); - List features = getFeaturesOverlapping(chr, context.getOrigin(), context.getEndLocation()); + + + // TODO Convert iterator to list. This is very wasteful, but neccessary due to the feature source interface. + List features = new ArrayList<>(); + Iterator iter = featureSource.getFeatures(chr, (int) context.getOrigin(), (int) context.getEndLocation()); + while (iter.hasNext()) { + features.add(iter.next()); + } + if (features != null && features.size() > 0) { if (graphType == GraphType.PROPORTIONAL_ARC) { @@ -192,6 +178,10 @@ public void render(RenderContext context, Rectangle trackRectangle) { renderers.get(GraphType.BLOCK).render(features, context, trackRectangle, this.arcOption); } + lastRenderedFeatures.put(context.getReferenceFrame(), features); + + } catch (IOException e) { + log.error("Error fetching features. ", e); } finally { context.clearGraphicsCache(); g2d.setClip(clip); @@ -383,6 +373,9 @@ public IGVPopupMenu getPopupMenu(TrackClickEvent te) { }); menu.add(item); + menu.addSeparator(); + menu.add(TrackMenuUtils.getChangeFeatureWindow(Arrays.asList(this))); + // final JCheckBoxMenuItem cbItem = new JCheckBoxMenuItem("Hide Large Features"); // cbItem.setSelected(hideLargeFeatures); // cbItem.addActionListener(e -> { @@ -404,7 +397,16 @@ public String getValueStringAt(String chr, double position, int mouseX, int mous // Expand range a little bit -- this should be done in pixels double tolerance = frame.getScale() * 3; - List candidates = getFeaturesOverlapping(frame.getChrName(), (int) position - tolerance, (int) position + 1 + tolerance); + + List features = lastRenderedFeatures.get(frame); + if (features == null) return ""; + + List candidates = new ArrayList<>(); + for (BedPE bedPE : features) { + if (bedPE.getEnd() < position - tolerance) continue; + if (bedPE.getStart() > position + tolerance) break; + candidates.add(bedPE); + } // Sort candidate features smallest to largest Comparator sorter = graphType == GraphType.PROPORTIONAL_ARC ? @@ -487,13 +489,7 @@ public void unmarshalXML(Element element, Integer version) { public List getVisibleFeatures(List frames) { Function> frameFeatures = (f) -> { - String chr = f.getChrName(); - if (chr.equals(Globals.CHR_ALL)) { - return downsampleWGFeatures(allFeatures); - } else { - Range r = f.getCurrentRange(); - return getFeaturesOverlapping(chr, r.getStart(), r.getEnd()); - } + return lastRenderedFeatures.get(f); }; if (frames.size() == 0) { @@ -509,79 +505,13 @@ public List getVisibleFeatures(List frames) { } } - - private List createWGFeatures(List features, Genome genome) { - - int size = Math.min(features.size(), MAX_WG_COUNT); - List wgFeatures = new ArrayList<>(size); - - List sampledFeatures; - if (features.size() < MAX_WG_COUNT) { - sampledFeatures = features; - } else { - sampledFeatures = downsampleWGFeatures(features); - } - - for (BedPEFeature f : sampledFeatures) { - - int start1 = genome.getGenomeCoordinate(f.chr1, f.start1); - int end1 = genome.getGenomeCoordinate(f.chr1, f.end1); - int start2 = genome.getGenomeCoordinate(f.chr2, f.start2); - int end2 = genome.getGenomeCoordinate(f.chr2, f.end2); - BedPEFeature wgFeature = new BedPEFeature(Globals.CHR_ALL, start1, end1, Globals.CHR_ALL, start2, end2); - - wgFeature.name = f.name; - wgFeature.score = f.score; - wgFeature.thickness = f.thickness; - wgFeature.color = f.color; - wgFeature.attributes = f.attributes; - - wgFeatures.add(wgFeature); - + @Override + public void receiveEvent(IGVEvent event) { + if (event instanceof FrameManager.ChangeEvent) { + lastRenderedFeatures.clear(); } - return wgFeatures; } - private List downsampleWGFeatures(List features) { - - // First pass -- find the max score feature - BedPEFeature maxScoreFeature = features.get(0); - for (BedPEFeature f : features) { - if (f.score > maxScoreFeature.score) { - maxScoreFeature = f; - } - } - - int nBins = maxScoreFeature.score > 0 ? 5 : 1; // TODO make a function of total # of features & maxCount? - double binSize = nBins > 1 ? Math.log10(maxScoreFeature.score) / nBins : Integer.MAX_VALUE; - - // Divide features into bins\ - Object[] binnedFeatures = new Object[nBins]; - int counts[] = new int[nBins]; - for (int i = 0; i < nBins; i++) { - binnedFeatures[i] = new ArrayList(); - counts[i] = 0; - } - for (BedPEFeature f : features) { - int bin = f.score <= 0 ? 0 : (int) Math.min(nBins - 1, Math.floor(Math.log10(f.score) / binSize)); - ((List) binnedFeatures[bin]).add(f); - counts[bin]++; - } - - // Add sampled features from each bin - int featuresPerBin = MAX_WG_COUNT / nBins; - List sampledFeatures = new ArrayList<>(MAX_WG_COUNT); - for (int i = 0; i < nBins; i++) { - List bfs = (List) binnedFeatures[i]; - sampledFeatures.addAll(Arrays.asList(new Downsampler().sample(bfs.toArray(BedPEFeature[]::new), featuresPerBin))); - } - - // Be sure we keep the maximum feature - if (maxScoreFeature != null) { - sampledFeatures.add(maxScoreFeature); - } - - return sampledFeatures; - } } + diff --git a/src/main/java/org/broad/igv/bedpe/NestedArcRenderer.java b/src/main/java/org/broad/igv/bedpe/NestedArcRenderer.java index 86085c2b02..92a077e922 100644 --- a/src/main/java/org/broad/igv/bedpe/NestedArcRenderer.java +++ b/src/main/java/org/broad/igv/bedpe/NestedArcRenderer.java @@ -70,8 +70,8 @@ public void render(List features, RenderContext context, Rectangle trackR if (bedPE.isSameChr()) { - BedPEFeature feature = bedPE.get(); - Color fcolor = feature.color == null ? trackColor : feature.color; + BedPE feature = bedPE; + Color fcolor = feature.getColor() == null ? trackColor : feature.getColor(); double pixelStart = (feature.getMidStart() - origin) / locScale; double pixelEnd = (feature.getMidEnd() - origin) / locScale; @@ -113,7 +113,7 @@ public void render(List features, RenderContext context, Rectangle trackR feature.setShape(new NAShape(xc, yc, r)); } else { - Color fcolor = bedPE.get().color == null ? Color.black : bedPE.get().color; + Color fcolor = bedPE.getColor() == null ? Color.black : bedPE.getColor(); g.setColor(fcolor); int h = trackRectangle.height / 2; double ps = ((bedPE.getStart() + bedPE.getEnd()) / 2 - origin) / locScale; diff --git a/src/main/java/org/broad/igv/bedpe/PEBlockRenderer.java b/src/main/java/org/broad/igv/bedpe/PEBlockRenderer.java index a9a7223d64..4c35b06147 100644 --- a/src/main/java/org/broad/igv/bedpe/PEBlockRenderer.java +++ b/src/main/java/org/broad/igv/bedpe/PEBlockRenderer.java @@ -38,15 +38,15 @@ public void render(List features, RenderContext context, Rectangle trackR if (bedPE.isSameChr()) { - BedPEFeature feature = bedPE.get(); - int ps1 = (int) ((feature.start1 - origin) / locScale); - int pe1 = (int) ((feature.end1 - origin) / locScale); + BedPE feature = bedPE; + int ps1 = (int) ((feature.getStart1() - origin) / locScale); + int pe1 = (int) ((feature.getEnd1() - origin) / locScale); if (pe1 >= trackRectangle.getX() && ps1 <= trackRectangle.getMaxX()) { drawBlock(ps1, pe1, blockY, g); } - int ps2 = (int) ((feature.start2 - origin) / locScale); - int pe2 = (int) ((feature.end2 - origin) / locScale); + int ps2 = (int) ((feature.getStart2() - origin) / locScale); + int pe2 = (int) ((feature.getEnd2() - origin) / locScale); if (pe2 >= trackRectangle.getX() && ps2 <= trackRectangle.getMaxX()) { drawBlock(ps2, pe2, blockY, g); } diff --git a/src/main/java/org/broad/igv/bedpe/ProportionalArcRenderer.java b/src/main/java/org/broad/igv/bedpe/ProportionalArcRenderer.java index 4b86727dc4..2fc7666374 100644 --- a/src/main/java/org/broad/igv/bedpe/ProportionalArcRenderer.java +++ b/src/main/java/org/broad/igv/bedpe/ProportionalArcRenderer.java @@ -1,9 +1,12 @@ package org.broad.igv.bedpe; import org.broad.igv.track.RenderContext; +import org.broad.igv.ui.util.MessageUtils; +import org.broad.igv.util.Pair; import java.awt.*; import java.awt.geom.Arc2D; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -36,7 +39,27 @@ public void render(List features, RenderContext context, Rectangle trackR g.setStroke(new BasicStroke(track.thickness)); } + // Filter features to in-view + List filteredFeatures = new ArrayList<>(); for (BedPE bedPE : features) { + double p1 = (bedPE.getStart() - origin) / locScale; + double p2 = (bedPE.getEnd() - origin) / locScale; + if (p2 >= trackRectangle.getX() && p1 <= trackRectangle.getMaxX()) { + // Optionally filter arcs with one or both ends out of view + double pixelStart = (bedPE.getMidStart() - origin) / locScale; + double pixelEnd = (bedPE.getMidEnd() - origin) / locScale; + if (arcOption == InteractionTrack.ArcOption.ONE_END) { + if (pixelStart < trackRectangle.x && pixelEnd > trackRectangle.x + trackRectangle.width) + continue; + } else if (arcOption == InteractionTrack.ArcOption.BOTH_ENDS) { + if (pixelStart < trackRectangle.x || pixelEnd > trackRectangle.x + trackRectangle.width) + continue; + } + filteredFeatures.add(bedPE); + } + } + + for (BedPE bedPE : filteredFeatures) { double p1 = (bedPE.getStart() - origin) / locScale; double p2 = (bedPE.getEnd() - origin) / locScale; @@ -54,9 +77,9 @@ public void render(List features, RenderContext context, Rectangle trackR if (bedPE.isSameChr()) { - BedPEFeature feature = bedPE.get(); + BedPE feature = bedPE; - Color fcolor = feature.color == null ? trackColor : feature.color; + Color fcolor = feature.getColor() == null ? trackColor : feature.getColor(); if (fcolor != null) { g.setColor(fcolor); } @@ -64,13 +87,6 @@ public void render(List features, RenderContext context, Rectangle trackR double pixelStart = (feature.getMidStart() - origin) / locScale; double pixelEnd = (feature.getMidEnd() - origin) / locScale; - // Optionally filter arcs with one or both ends out of view - if(arcOption == InteractionTrack.ArcOption.ONE_END) { - if(pixelStart < trackRectangle.x && pixelEnd > trackRectangle.x + trackRectangle.width) continue; - } else if(arcOption == InteractionTrack.ArcOption.BOTH_ENDS) { - if(pixelStart < trackRectangle.x || pixelEnd > trackRectangle.x + trackRectangle.width) continue; - } - int w = (int) (pixelEnd - pixelStart); if (w < 3) { w = 3; @@ -94,17 +110,16 @@ public void render(List features, RenderContext context, Rectangle trackR g.setColor(shadedColor); g.fill(arcPath); - bedPE.setShape(new PAShape(pixelStart + w/2, y + h, w/2, h)); + bedPE.setShape(new PAShape(pixelStart + w / 2, y + h, w / 2, h)); } else { - Color fcolor = bedPE.get().color == null ? Color.black : bedPE.get().color; + Color fcolor = bedPE.getColor() == null ? Color.black : bedPE.getColor(); g.setColor(fcolor); double ps = ((bedPE.getStart() + bedPE.getEnd()) / 2 - origin) / locScale; int yBase = direction == UP ? trackRectangle.y + trackRectangle.height - h : trackRectangle.y + gap; g.drawLine((int) ps, yBase, (int) ps, yBase + h); } - } - else { + } else { bedPE.setShape(null); } } @@ -126,7 +141,7 @@ private Color getAlphaColor(Color fcolor, float alpha) { } //(x-h)^2/a^2 + (y-k)^2/b^2 <= 1 - public static class PAShape implements BedPEShape{ + public static class PAShape implements BedPEShape { double h; //xc double k; //yc @@ -136,8 +151,8 @@ public static class PAShape implements BedPEShape{ public PAShape(double h, double k, double a, double b) { this.h = h; this.k = k; - this.a2 = a*a; - this.b2 = b*b; + this.a2 = a * a; + this.b2 = b * b; } @Override @@ -145,8 +160,8 @@ public boolean contains(double x, double y) { double dx = x - h; double dy = y - k; - double e = dx*dx / a2 + dy*dy / b2; - return e < 1.0; + double e = dx * dx / a2 + dy * dy / b2; + return e < 1.0; } } } diff --git a/src/main/java/org/broad/igv/bedpe/WGFeature.java b/src/main/java/org/broad/igv/bedpe/WGFeature.java new file mode 100644 index 0000000000..f8896ef029 --- /dev/null +++ b/src/main/java/org/broad/igv/bedpe/WGFeature.java @@ -0,0 +1,104 @@ +package org.broad.igv.bedpe; + +import org.broad.igv.feature.genome.Genome; + +import java.awt.*; + +class WGFeature implements BedPE { + + private final int start1; + private final int end1; + private final int start2; + private final int end2; + private final BedPE feature; + + + WGFeature(BedPE f, Genome genome) { + this.feature = f; + this.start1 = genome.getGenomeCoordinate(f.getChr1(), f.getStart1()); + this.end1 = genome.getGenomeCoordinate(f.getChr1(), f.getEnd1()); + this.start2 = genome.getGenomeCoordinate(f.getChr2(), f.getStart2()); + this.end2 = genome.getGenomeCoordinate(f.getChr2(), f.getEnd2()); + + } + + @Override + public String getChr1() { + return "All"; + } + + @Override + public int getStart1() { + return start1; + } + + @Override + public int getEnd1() { + return end1; + } + + @Override + public String getChr2() { + return "All"; + } + + @Override + public int getStart2() { + return start2; + } + + @Override + public int getEnd2() { + return end2; + } + + @Override + public boolean isSameChr() { + return true; + } + + @Override + public Color getColor() { + return feature.getColor(); + } + + @Override + public int getThickness() { + return feature.getThickness(); + } + + @Override + public void setShape(BedPEShape s) { + feature.setShape(s); + } + + @Override + public BedPEShape getShape() { + return feature.getShape(); + } + + @Override + public String getValueString() { + return feature.getValueString(); + } + + @Override + public float getScore() { + return feature.getScore(); + } + + @Override + public String getContig() { + return feature.getContig(); + } + + @Override + public int getStart() { + return Math.min(start1, start2); + } + + @Override + public int getEnd() { + return Math.max(end1, end2); + } +} diff --git a/src/main/java/org/broad/igv/data/WiggleParser.java b/src/main/java/org/broad/igv/data/WiggleParser.java index cce9acc4f4..b84b6d0e3d 100644 --- a/src/main/java/org/broad/igv/data/WiggleParser.java +++ b/src/main/java/org/broad/igv/data/WiggleParser.java @@ -160,6 +160,8 @@ protected void parseFile(ResourceLocator locator) { if (type == Type.EXPR) { reader.readLine(); // Skip header line + } else if(type == null) { + type = Type.BED_GRAPH; // Until proven otherwise (e.g. by fixedStep line) } int position = -1; @@ -172,9 +174,7 @@ protected void parseFile(ResourceLocator locator) { // Skip } - - if (nextLine.startsWith("track") && type != Type.CPG) { - type = Type.BED_GRAPH; + if (nextLine.startsWith("track")) { ParsingUtils.parseTrackLine(nextLine, dataset.getTrackProperties()); if (dataset.getTrackProperties().getBaseCoord() == TrackProperties.BaseCoord.ZERO) { this.startBase = 0; diff --git a/src/main/java/org/broad/igv/feature/AbstractFeature.java b/src/main/java/org/broad/igv/feature/AbstractFeature.java index 1118ff5eb5..e49e2a9647 100644 --- a/src/main/java/org/broad/igv/feature/AbstractFeature.java +++ b/src/main/java/org/broad/igv/feature/AbstractFeature.java @@ -39,7 +39,7 @@ /** * @author jrobinso */ -abstract public class AbstractFeature implements IGVFeature, htsjdk.tribble.Feature { +abstract public class AbstractFeature implements IGVFeature { private static Logger log = LogManager.getLogger(AbstractFeature.class); protected Strand strand = Strand.NONE; diff --git a/src/main/java/org/broad/igv/feature/FeatureType.java b/src/main/java/org/broad/igv/feature/FeatureType.java index ade18e3bc0..ef00a1d68e 100644 --- a/src/main/java/org/broad/igv/feature/FeatureType.java +++ b/src/main/java/org/broad/igv/feature/FeatureType.java @@ -30,7 +30,7 @@ * @author jrobinso */ public enum FeatureType { - OTHER, GENE, PROMOTER, MISC_RNA, REPEAT_REGION, LTR, MUTATION, BED, GAPPED_PEAK, SPLICE_JUNCTION, BED_METHYL + OTHER, GENE, PROMOTER, MISC_RNA, REPEAT_REGION, LTR, MUTATION, BED, GAPPED_PEAK, SPLICE_JUNCTION, BED_METHYL, INTERACT } diff --git a/src/main/java/org/broad/igv/feature/IGVFeature.java b/src/main/java/org/broad/igv/feature/IGVFeature.java index 6e1d02cb8c..6283c4fd3b 100644 --- a/src/main/java/org/broad/igv/feature/IGVFeature.java +++ b/src/main/java/org/broad/igv/feature/IGVFeature.java @@ -26,9 +26,12 @@ package org.broad.igv.feature; +import org.broad.igv.feature.genome.ChromAlias; + import java.awt.*; import java.util.Collections; import java.util.List; +import java.util.Map; /** * Interface for features in IGV annotation tracks (FeatureTrack and derived classes). @@ -95,4 +98,7 @@ default String getURL() { return null; } + default Map getAttributes() { + return Collections.EMPTY_MAP; + } } diff --git a/src/main/java/org/broad/igv/feature/IGVNamedFeature.java b/src/main/java/org/broad/igv/feature/IGVNamedFeature.java index b5a44e989e..8ad1ebbd39 100644 --- a/src/main/java/org/broad/igv/feature/IGVNamedFeature.java +++ b/src/main/java/org/broad/igv/feature/IGVNamedFeature.java @@ -31,8 +31,6 @@ */ public interface IGVNamedFeature extends htsjdk.tribble.NamedFeature { - String getName(); - default String getDisplayName(String property) { return getName(); } diff --git a/src/main/java/org/broad/igv/feature/genome/ChromAliasBB.java b/src/main/java/org/broad/igv/feature/genome/ChromAliasBB.java index 27260fbbae..ab43fcf6ca 100644 --- a/src/main/java/org/broad/igv/feature/genome/ChromAliasBB.java +++ b/src/main/java/org/broad/igv/feature/genome/ChromAliasBB.java @@ -1,6 +1,7 @@ package org.broad.igv.feature.genome; import org.broad.igv.feature.BasicFeature; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.ucsc.bb.BBFile; import java.io.IOException; @@ -34,7 +35,7 @@ public String getChromosomeAlias(String chr, String nameSet) { */ public ChromAlias search(String alias) throws IOException { if (!this.aliasCache.containsKey(alias)) { - BasicFeature f = this.reader.search(alias); + IGVFeature f = this.reader.search(alias); if (f != null) { String chr = f.getChr(); ChromAlias aliasRecord = new ChromAlias(chr); diff --git a/src/main/java/org/broad/igv/feature/tribble/CodecFactory.java b/src/main/java/org/broad/igv/feature/tribble/CodecFactory.java index 9fec4c82a0..3e2b2777a7 100644 --- a/src/main/java/org/broad/igv/feature/tribble/CodecFactory.java +++ b/src/main/java/org/broad/igv/feature/tribble/CodecFactory.java @@ -34,6 +34,7 @@ import org.broad.igv.feature.FeatureType; import org.broad.igv.feature.dsi.DSICodec; import org.broad.igv.feature.genome.Genome; +import org.broad.igv.bedpe.InteractCodec; import org.broad.igv.gwas.EQTLCodec; import org.broad.igv.logging.LogManager; import org.broad.igv.logging.Logger; @@ -140,6 +141,8 @@ public static FeatureCodec getCodec(ResourceLocator locator, Genome genome) { return new UCSCGeneTableCodec(UCSCGeneTableCodec.Type.GENEPRED_EXT, genome); case "bedmethyl": return new IGVBEDCodec(genome, FeatureType.BED_METHYL); +// case "interact": +// return new InteractCodec(genome, FeatureType.INTERACT); default: if (MUTCodec.isMutationAnnotationFile(locator)) { diff --git a/src/main/java/org/broad/igv/feature/tribble/DGVCodec.java b/src/main/java/org/broad/igv/feature/tribble/DGVCodec.java index b77a39b7b0..a032138056 100644 --- a/src/main/java/org/broad/igv/feature/tribble/DGVCodec.java +++ b/src/main/java/org/broad/igv/feature/tribble/DGVCodec.java @@ -72,7 +72,7 @@ /** * */ -public class DGVCodec extends UCSCCodec { +public class DGVCodec extends UCSCCodec { static final Pattern BR_PATTERN = Pattern.compile("
"); static final Pattern EQ_PATTERN = Pattern.compile("="); diff --git a/src/main/java/org/broad/igv/feature/tribble/EncodePeakCodec.java b/src/main/java/org/broad/igv/feature/tribble/EncodePeakCodec.java index b37efb38ac..592acc3d24 100644 --- a/src/main/java/org/broad/igv/feature/tribble/EncodePeakCodec.java +++ b/src/main/java/org/broad/igv/feature/tribble/EncodePeakCodec.java @@ -66,18 +66,7 @@ public EncodePeakCodec(Genome genome) { } @Override - public BasicFeature decode(String nextLine) { - - - if (nextLine.trim().length() == 0) { - return null; - } - - if (nextLine.startsWith("#") || nextLine.startsWith("track") || nextLine.startsWith("browser")) { - return null; - } - - String[] tokens = Globals.tabPattern.split(nextLine); + public BasicFeature decode(String [] tokens) { int tokenCount = tokens.length; diff --git a/src/main/java/org/broad/igv/feature/tribble/IGVBEDCodec.java b/src/main/java/org/broad/igv/feature/tribble/IGVBEDCodec.java index a7c2c2e970..d51e0c9625 100644 --- a/src/main/java/org/broad/igv/feature/tribble/IGVBEDCodec.java +++ b/src/main/java/org/broad/igv/feature/tribble/IGVBEDCodec.java @@ -34,7 +34,6 @@ import org.broad.igv.util.StringUtils; import htsjdk.tribble.Feature; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -56,8 +55,6 @@ public class IGVBEDCodec extends UCSCCodec { private Genome genome; - private Pattern delimiter = null; - private int maxColumnCount = Integer.MAX_VALUE; public IGVBEDCodec() { @@ -73,6 +70,14 @@ public IGVBEDCodec(Genome genome, FeatureType featureType) { this.genome = genome; } + @Override + public BasicFeature decode(String nextLine) { + BasicFeature feature = super.decode(nextLine); + if(feature != null) { + feature.setRepresentation(nextLine); + } + return feature; + } //@Override public BasicFeature decode(String[] tokens) { @@ -282,35 +287,6 @@ public static boolean isCoding(BasicFeature feature) { && feature.getStrand() != Strand.NONE; } - private String[] tokens = new String[50]; - - @Override - public BasicFeature decode(String nextLine) { - - String trimLine = nextLine.trim(); - if (trimLine.length() == 0) { - return null; - } - - if (nextLine.startsWith("#") || nextLine.startsWith("track") || nextLine.startsWith("browser")) { - return null; - } - - // Bed files can be tab or whitespace delimited - if (delimiter == null) { - if(featureType == FeatureType.BED_METHYL) { - delimiter = Globals.whitespacePattern; - } else { - delimiter = trimLine.contains("\t") ? Globals.multiTabPattern : Globals.whitespacePattern; - } - } - - tokens = delimiter.split(trimLine); - BasicFeature feature = decode(tokens); - feature.setRepresentation(nextLine); - return feature; - } - /** * This function returns true iff the File potentialInput can be parsed by this diff --git a/src/main/java/org/broad/igv/feature/tribble/PSLCodec.java b/src/main/java/org/broad/igv/feature/tribble/PSLCodec.java index 38643ce58d..dfb5fd0533 100644 --- a/src/main/java/org/broad/igv/feature/tribble/PSLCodec.java +++ b/src/main/java/org/broad/igv/feature/tribble/PSLCodec.java @@ -26,10 +26,7 @@ package org.broad.igv.feature.tribble; import org.broad.igv.Globals; -import org.broad.igv.feature.PSLRecord; -import org.broad.igv.feature.BasicFeature; -import org.broad.igv.feature.Exon; -import org.broad.igv.feature.Strand; +import org.broad.igv.feature.*; import org.broad.igv.feature.genome.Genome; /** @@ -60,12 +57,12 @@ * 20. qStarts - Comma-separated list of starting positions of each block in query * 21. tStarts - Comma-separated list of starting positions of each block in target */ -public class PSLCodec extends UCSCCodec { +public class PSLCodec extends UCSCCodec { Genome genome; boolean keepText; - public PSLCodec(){ + public PSLCodec() { this(null); } @@ -82,7 +79,6 @@ public PSLCodec(Genome genome, boolean keepText) { public PSLRecord decode(String line) { - PSLRecord f = null; try { if (line.trim().length() == 0 || line.startsWith("#") || @@ -95,17 +91,19 @@ public PSLRecord decode(String line) { } String[] tokens = Globals.tabPattern.split(line); - f = getPslRecord(tokens, genome); - if (f == null) return null; + return decode(tokens); } catch (NumberFormatException e) { return null; } + } - - return f; + public PSLRecord decode(String[] tokens) { + return getPslRecord(tokens, genome); } + + /* "matches", "misMatches", diff --git a/src/main/java/org/broad/igv/feature/tribble/UCSCCodec.java b/src/main/java/org/broad/igv/feature/tribble/UCSCCodec.java index 4b6e347e94..1488b0e0c2 100644 --- a/src/main/java/org/broad/igv/feature/tribble/UCSCCodec.java +++ b/src/main/java/org/broad/igv/feature/tribble/UCSCCodec.java @@ -25,7 +25,10 @@ package org.broad.igv.feature.tribble; +import org.broad.igv.Globals; +import org.broad.igv.feature.BasicFeature; import org.broad.igv.feature.FeatureType; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.renderer.SpliceJunctionRenderer; import org.broad.igv.track.TrackProperties; import org.broad.igv.track.TrackType; @@ -35,11 +38,13 @@ import htsjdk.tribble.exception.CodecLineParsingException; import htsjdk.tribble.readers.LineIterator; +import java.util.regex.Pattern; + /** * @author jrobinso * @date Aug 5, 2010 */ -public abstract class UCSCCodec extends AsciiFeatureCodec { +public abstract class UCSCCodec extends AsciiFeatureCodec { GFFCodec.GFF3Helper tagHelper = new GFFCodec.GFF3Helper(); protected boolean gffTags = false; @@ -47,6 +52,8 @@ public abstract class UCSCCodec extends AsciiFeatureCodec FeatureFileHeader header; FeatureType featureType; + private Pattern delimiter = null; + protected UCSCCodec(Class myClass) { super(myClass); } @@ -139,4 +146,31 @@ public void setFeatureType(org.broad.igv.feature.FeatureType featureType) { this.featureType = featureType; } + @Override + public T decode(String nextLine) { + + String trimLine = nextLine.trim(); + if (trimLine.length() == 0) { + return null; + } + + if (nextLine.startsWith("#") || nextLine.startsWith("track") || nextLine.startsWith("browser")) { + return null; + } + + // Bed files can be tab or whitespace delimited + if (delimiter == null) { + if(featureType == FeatureType.BED_METHYL) { + delimiter = Globals.whitespacePattern; + } else { + delimiter = trimLine.contains("\t") ? Globals.multiTabPattern : Globals.whitespacePattern; + } + } + + String [] tokens = delimiter.split(trimLine); + T feature = decode(tokens); + return feature; + } + + public abstract T decode(String[] tokens); } diff --git a/src/main/java/org/broad/igv/feature/tribble/UCSCGeneTableCodec.java b/src/main/java/org/broad/igv/feature/tribble/UCSCGeneTableCodec.java index f7026439db..5b9286e11b 100644 --- a/src/main/java/org/broad/igv/feature/tribble/UCSCGeneTableCodec.java +++ b/src/main/java/org/broad/igv/feature/tribble/UCSCGeneTableCodec.java @@ -28,6 +28,7 @@ import org.broad.igv.Globals; import org.broad.igv.feature.BasicFeature; import org.broad.igv.feature.Exon; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.feature.Strand; import org.broad.igv.feature.genome.Genome; @@ -37,7 +38,7 @@ * @author Jim Robinson * @date 11/19/11 */ -public class UCSCGeneTableCodec extends UCSCCodec { +public class UCSCGeneTableCodec extends UCSCCodec { private int nameColumn = 0; private int idColumn = 1; @@ -120,6 +121,13 @@ public BasicFeature decode(String line) { line = line.replaceAll("\"", ""); String[] tokens = Globals.tabPattern.split(line); + BasicFeature gene = decode(tokens); + gene.setRepresentation(line); + return gene; + } + + public BasicFeature decode(String[] tokens) { + int tokenCount = tokens.length; if (tokenCount <= strandColumn) { @@ -152,7 +160,7 @@ public BasicFeature decode(String line) { } BasicFeature gene = new BasicFeature(chr, start, end, strand); - gene.setRepresentation(line); + gene.setName(name); gene.setIdentifier(identifier); @@ -162,7 +170,7 @@ public BasicFeature decode(String line) { gene.setThickEnd(Integer.parseInt(tokens[7])); } - if(scoreColumn > 0 && tokenCount > scoreColumn) { + if (scoreColumn > 0 && tokenCount > scoreColumn) { gene.setScore((float) Double.parseDouble(tokens[scoreColumn])); } diff --git a/src/main/java/org/broad/igv/feature/tribble/UCSCSnpCodec.java b/src/main/java/org/broad/igv/feature/tribble/UCSCSnpCodec.java index 61af988973..1298b9422d 100644 --- a/src/main/java/org/broad/igv/feature/tribble/UCSCSnpCodec.java +++ b/src/main/java/org/broad/igv/feature/tribble/UCSCSnpCodec.java @@ -26,13 +26,14 @@ package org.broad.igv.feature.tribble; import org.broad.igv.Globals; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.feature.UCSCSnpFeature; import org.broad.igv.feature.genome.Genome; /** * Created by jrobinso on 5/26/15. */ -public class UCSCSnpCodec extends UCSCCodec { +public class UCSCSnpCodec extends UCSCCodec { private Genome genome; @@ -42,9 +43,7 @@ public UCSCSnpCodec(Genome genome) { } @Override - public UCSCSnpFeature decode(String s) { - - String[] tokens = Globals.tabPattern.split(s); + public UCSCSnpFeature decode(String [] tokens) { if (tokens.length < 25) return null; diff --git a/src/main/java/org/broad/igv/htsget/HtsgetVariantSource.java b/src/main/java/org/broad/igv/htsget/HtsgetVariantSource.java index 5305fd77ee..72e424c0d6 100644 --- a/src/main/java/org/broad/igv/htsget/HtsgetVariantSource.java +++ b/src/main/java/org/broad/igv/htsget/HtsgetVariantSource.java @@ -86,11 +86,6 @@ public Iterator getFeatures(String chr, int start, int end) throws IOException { return features.iterator(); } - @Override - public List getCoverageScores(String chr, int start, int end, int zoom) { - return null; - } - @Override public int getFeatureWindowSize() { return featureWindowSize; diff --git a/src/main/java/org/broad/igv/jbrowse/Chord.java b/src/main/java/org/broad/igv/jbrowse/Chord.java index cdbfd7982c..ec82b78103 100644 --- a/src/main/java/org/broad/igv/jbrowse/Chord.java +++ b/src/main/java/org/broad/igv/jbrowse/Chord.java @@ -24,18 +24,16 @@ class Chord { private Chord() { } - public static Chord fromBedPE(BedPE bedPE) { - - BedPEFeature f = bedPE.get(); + public static Chord fromBedPE(BedPE f) { Chord c = new Chord(); - String chr1 = shortName(f.chr1); - String chr2 = shortName(f.chr2); - c.uniqueId = chr1 + ":" + f.start1 + "-" + f.end1 + "_" + chr2 + ":" + f.start2 + "-" + f.end2; + String chr1 = shortName(f.getChr1()); + String chr2 = shortName(f.getChr2()); + c.uniqueId = chr1 + ":" + f.getStart1() + "-" + f.getEnd1() + "_" + chr2 + ":" + f.getStart2() + "-" + f.getEnd2(); c.refName = chr1; - c.start = f.start1; - c.end = f.end1; - c.mate = new Mate(chr2, f.start2, f.end2); + c.start = f.getStart1(); + c.end = f.getEnd1(); + c.mate = new Mate(chr2, f.getStart2(), f.getEnd2()); return c; } diff --git a/src/main/java/org/broad/igv/tools/motiffinder/MotifFinderSource.java b/src/main/java/org/broad/igv/tools/motiffinder/MotifFinderSource.java index 382d7b63ba..1d3681961f 100644 --- a/src/main/java/org/broad/igv/tools/motiffinder/MotifFinderSource.java +++ b/src/main/java/org/broad/igv/tools/motiffinder/MotifFinderSource.java @@ -160,12 +160,6 @@ public Iterator getFeatures(String chr, int start, int end) throws IOEx return search(this.pattern, this.strand, chr, start, seq); } - @Override - public List getCoverageScores(String chr, int start, int end, int zoom) { - //TODO Precalculate and/or store? - return null; - } - @Override public int getFeatureWindowSize() { return this.featureWindowSize; diff --git a/src/main/java/org/broad/igv/track/AbstractTrack.java b/src/main/java/org/broad/igv/track/AbstractTrack.java index e844923153..2e04b5076f 100644 --- a/src/main/java/org/broad/igv/track/AbstractTrack.java +++ b/src/main/java/org/broad/igv/track/AbstractTrack.java @@ -424,6 +424,7 @@ public int getMinimumHeight() { return minimumHeight < 0 ? getDefaultMinimumHeight() : minimumHeight; } + @Override public void setTrackType(TrackType type) { this.trackType = type; } diff --git a/src/main/java/org/broad/igv/track/FeatureCollectionSource.java b/src/main/java/org/broad/igv/track/FeatureCollectionSource.java index 75513f8675..d0c6da3750 100644 --- a/src/main/java/org/broad/igv/track/FeatureCollectionSource.java +++ b/src/main/java/org/broad/igv/track/FeatureCollectionSource.java @@ -93,11 +93,6 @@ public List getFeatureList(String chr, int start, int end) { return filteredFeatures; } - @Override - public boolean isLoaded(ReferenceFrame frame) { - return true; // All features are loaded by definition - } - public List getFeatures(String chr) { return featureMap.get(chr); } diff --git a/src/main/java/org/broad/igv/track/FeatureDirSource.java b/src/main/java/org/broad/igv/track/FeatureDirSource.java index 4418780395..714ec87606 100644 --- a/src/main/java/org/broad/igv/track/FeatureDirSource.java +++ b/src/main/java/org/broad/igv/track/FeatureDirSource.java @@ -114,10 +114,6 @@ public List getFeatures(final String chr) { return featureCache.get(chr); } - public List getCoverageScores(String chr, int i, int i1, int zoom) { - return null; - } - public Iterator getFeatures(String chr, int start, int end) { List features = getFeatures(chr); return features == null ? Collections.emptyList().iterator() : features.iterator(); diff --git a/src/main/java/org/broad/igv/track/FeatureSource.java b/src/main/java/org/broad/igv/track/FeatureSource.java index 0e0f9be3c0..cf0ea52c8e 100644 --- a/src/main/java/org/broad/igv/track/FeatureSource.java +++ b/src/main/java/org/broad/igv/track/FeatureSource.java @@ -27,10 +27,12 @@ import htsjdk.tribble.Feature; import htsjdk.tribble.NamedFeature; +import org.broad.igv.bedpe.BedPE; import org.broad.igv.feature.LocusScore; import org.broad.igv.ui.panel.ReferenceFrame; import java.io.IOException; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -42,9 +44,6 @@ */ public interface FeatureSource { - default boolean isLoaded(ReferenceFrame frame) { - return false; - } /** * Return an iterator over all features that overlap the interval. The coordinates are in the "UCSC" convention, @@ -69,8 +68,9 @@ default boolean isLoaded(ReferenceFrame frame) { * @param zoom the zoom level * @return */ - List getCoverageScores(String chr, int start, int end, int zoom); - + default List getCoverageScores(String chr, int start, int end, int zoom) { + return null; + } /** * The featureWindowSize is the genomic interval, in bases, at which the associated feature track should start diff --git a/src/main/java/org/broad/igv/track/FeatureTrack.java b/src/main/java/org/broad/igv/track/FeatureTrack.java index b402fc6059..afe2de15eb 100644 --- a/src/main/java/org/broad/igv/track/FeatureTrack.java +++ b/src/main/java/org/broad/igv/track/FeatureTrack.java @@ -1018,6 +1018,7 @@ public List getVisibleFeatures(ReferenceFrame frame) { } + @Override public void setTrackLine(String trackLine) { this.trackLine = trackLine; } diff --git a/src/main/java/org/broad/igv/track/FileFormatUtils.java b/src/main/java/org/broad/igv/track/FileFormatUtils.java index 2f021ed8c7..343930517a 100644 --- a/src/main/java/org/broad/igv/track/FileFormatUtils.java +++ b/src/main/java/org/broad/igv/track/FileFormatUtils.java @@ -100,8 +100,8 @@ public static String determineFormat(String path) throws IOException { // Read maximum of first 100 lines searching for format indication. int n = 0; - String nextLine; - while((nextLine = reader.readLine()) != null && n++ < 100) { + String nextLine = firstLine; + while(nextLine != null && n++ < 100) { if(nextLine.startsWith("#")) continue; if(nextLine.startsWith("track")) { TrackProperties properties = new TrackProperties(); @@ -115,6 +115,7 @@ public static String determineFormat(String path) throws IOException { if(nextLine.startsWith("fixedStep") || nextLine.startsWith("variableStep")) { return "wig"; } + nextLine = reader.readLine(); } if (maybeSampleInfo(bytes)) { diff --git a/src/main/java/org/broad/igv/track/MutationFeatureSource.java b/src/main/java/org/broad/igv/track/MutationFeatureSource.java index 84c0e4dbfc..da7ad02a8e 100644 --- a/src/main/java/org/broad/igv/track/MutationFeatureSource.java +++ b/src/main/java/org/broad/igv/track/MutationFeatureSource.java @@ -66,11 +66,6 @@ public Iterator getFeatures(String chr, int start, int end) throws IOE return dataManager.getFeatures(sample, chr, start, end); } - @Override - public List getCoverageScores(String chr, int start, int end, int zoom) { - return null; //Not supported for mutation tracks - } - static public class MutationDataManager { Range currentRange; diff --git a/src/main/java/org/broad/igv/track/Track.java b/src/main/java/org/broad/igv/track/Track.java index 553828b516..9147cab116 100644 --- a/src/main/java/org/broad/igv/track/Track.java +++ b/src/main/java/org/broad/igv/track/Track.java @@ -163,6 +163,8 @@ default boolean isAlignment() { void setOverlayed(boolean overlayVisible); + void setTrackType(TrackType type); + TrackType getTrackType(); void setHeight(int preferredHeight); @@ -294,6 +296,8 @@ default String getLabelField() { return null; } + default void setTrackLine(String trackLine) {} + /** * Return true if the track can be searched for a feature by name. * diff --git a/src/main/java/org/broad/igv/track/TrackLoader.java b/src/main/java/org/broad/igv/track/TrackLoader.java index 3637a9997e..61ed58ce76 100644 --- a/src/main/java/org/broad/igv/track/TrackLoader.java +++ b/src/main/java/org/broad/igv/track/TrackLoader.java @@ -28,8 +28,7 @@ import htsjdk.tribble.AsciiFeatureCodec; import htsjdk.tribble.Feature; import htsjdk.variant.vcf.VCFHeader; -import org.broad.igv.bedpe.BedPEParser; -import org.broad.igv.bedpe.InteractionTrack; +import org.broad.igv.bedpe.*; import org.broad.igv.blast.BlastMapping; import org.broad.igv.blast.BlastParser; import org.broad.igv.data.*; @@ -129,7 +128,7 @@ public List load(ResourceLocator locator, Genome genome) throws DataLoadE if ("bed".equals(format)) { try { String tmp = FileFormatUtils.determineFormat(locator.getPath()); - if(tmp != null && !tmp.equals("sampleinfo")) { + if (tmp != null && !tmp.equals("sampleinfo")) { format = tmp; locator.setFormat(format); } @@ -224,7 +223,7 @@ public List load(ResourceLocator locator, Genome genome) throws DataLoadE loadSMAPFile(locator, newTracks, genome); } else if (format.equals("dsi")) { loadDSIFile(locator, newTracks, genome); - } else if (format.equals("bedpe")) { + } else if (format.equals("bedpe") || format.equals("interact")) { loadBedPEFile(locator, newTracks, genome); } else if (format.equals("clusters")) { loadClusterFile(locator, newTracks, genome); @@ -420,8 +419,11 @@ private void loadDRangerFile(ResourceLocator locator, List newTracks, Gen private void loadBedPEFile(ResourceLocator locator, List newTracks, Genome genome) throws IOException { - BedPEParser.Dataset features = BedPEParser.parse(locator, genome); - newTracks.add(new InteractionTrack(locator, features, genome)); + List features = "interact".equals(locator.format) ? + InteractParser.parse(locator, genome) : + BedPEParser.parse(locator, genome); + BedPESource featureSource = new BedPESource(features, genome); + newTracks.add(new InteractionTrack(locator, featureSource)); } private void loadClusterFile(ResourceLocator locator, List newTracks, Genome genome) throws IOException { @@ -470,7 +472,10 @@ private void loadTribbleFile(ResourceLocator locator, List newTracks, Gen } // Create feature source and track - FeatureTrack t = new FeatureTrack(locator, src); + AbstractTrack t = + "interact".equals(locator.format) ? + new InteractionTrack(locator, src) : + new FeatureTrack(locator, src); //t.setRendererClass(BasicTribbleRenderer.class); @@ -490,11 +495,11 @@ private void loadTribbleFile(ResourceLocator locator, List newTracks, Gen t.setHeight(15); } } - - if (format.equals(".narrowpeak") || - locator.getPath().equals(".broadpeak") || - locator.getPath().equals(".gappedpeak") || - locator.getPath().equals(".regionpeak")) { + String path = locator.getPath().toLowerCase(); + if (path.contains(".narrowpeak") || + locator.getPath().contains(".broadpeak") || + locator.getPath().contains(".gappedpeak") || + locator.getPath().contains(".regionpeak")) { t.setUseScore(true); } newTracks.add(t); diff --git a/src/main/java/org/broad/igv/track/TrackMenuUtils.java b/src/main/java/org/broad/igv/track/TrackMenuUtils.java index ad2c757a8d..93b3c3cc07 100644 --- a/src/main/java/org/broad/igv/track/TrackMenuUtils.java +++ b/src/main/java/org/broad/igv/track/TrackMenuUtils.java @@ -29,6 +29,7 @@ import com.google.common.collect.Lists; import htsjdk.tribble.Feature; import org.apache.commons.math3.stat.StatUtils; +import org.broad.igv.bedpe.InteractionTrack; import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.data.AbstractDataSource; @@ -1032,7 +1033,7 @@ public static void changeFeatureVisibilityWindow(final Collection selecte Collection featureTracks = new ArrayList(selectedTracks.size()); for (Track t : selectedTracks) { - if (t instanceof FeatureTrack) { + if (t instanceof FeatureTrack || t instanceof InteractionTrack) { featureTracks.add(t); } } diff --git a/src/main/java/org/broad/igv/track/TribbleFeatureSource.java b/src/main/java/org/broad/igv/track/TribbleFeatureSource.java index 76fb6d28b4..273a8b0ed6 100644 --- a/src/main/java/org/broad/igv/track/TribbleFeatureSource.java +++ b/src/main/java/org/broad/igv/track/TribbleFeatureSource.java @@ -226,28 +226,11 @@ public Iterator getFeatures(String chr, int start, int end) throws IOEx return reader.query(seqName, start, end); } - /** - * Return coverage values overlapping the query interval. At this time Tribble sources do not provide - * coverage values - * - * @param chr - * @param start - * @param end - * @param zoom - * @return - */ - @Override - public List getCoverageScores(String chr, int start, int end, int zoom) { - return null; - } - - @Override protected Collection getSequenceNames() { return reader.getSequenceNames(); } - /** * Estimate an appropriate feature window size. * @@ -368,11 +351,6 @@ public boolean isIndexed() { return false; } - @Override - public boolean isLoaded(ReferenceFrame frame) { - return true; - } - @Override public List getCoverageScores(String chr, int start, int end, int zoom) { return coverageData == null ? Collections.emptyList() : diff --git a/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java b/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java index 9b6cccbb5a..f01568cca5 100644 --- a/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java +++ b/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java @@ -107,10 +107,6 @@ public Iterator getFeatures(String chr, int start, int end) throws } } - public List getCoverageScores(String chr, int start, int end, int zoom) { - return null; - } - public boolean isSearchable() { return reader.isSearchable(); } diff --git a/src/main/java/org/broad/igv/ucsc/bb/BBFile.java b/src/main/java/org/broad/igv/ucsc/bb/BBFile.java index f13756c8c2..771b3a31ad 100644 --- a/src/main/java/org/broad/igv/ucsc/bb/BBFile.java +++ b/src/main/java/org/broad/igv/ucsc/bb/BBFile.java @@ -3,6 +3,7 @@ import htsjdk.samtools.seekablestream.SeekableStream; import org.broad.igv.data.BasicScore; import org.broad.igv.feature.BasicFeature; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.feature.LocusScore; import org.broad.igv.feature.genome.ChromAlias; import org.broad.igv.feature.genome.Genome; @@ -427,7 +428,7 @@ public boolean isSearchable() { * @returns {Promise} */ - public BasicFeature search(String term) throws IOException { + public IGVFeature search(String term) throws IOException { if (this.header == null) { this.readHeader(); @@ -454,12 +455,12 @@ public BasicFeature search(String term) throws IOException { byte[] buffer = new byte[size]; is.seek(start); is.readFully(buffer); - List features = decodeFeatures(buffer, -1, -1, -1); + List features = decodeFeatures(buffer, -1, -1, -1); // Filter features to those matching term final String searchTerm = term; - BasicFeature largest = features.stream().filter(f -> { + IGVFeature 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(); @@ -473,8 +474,8 @@ public BasicFeature search(String term) throws IOException { return null; } - List decodeFeatures(byte[] buffer, int chrIdx, int start, int end) { - List features = new ArrayList<>(); + List decodeFeatures(byte[] buffer, int chrIdx, int start, int end) { + List features = new ArrayList<>(); byte[] uncompressed; if (this.header.uncompressBuffSize > 0) { uncompressed = (new CompressionUtils()).decompress(buffer, this.header.uncompressBuffSize); @@ -498,7 +499,7 @@ List decodeFeatures(byte[] buffer, int chrIdx, int start, int end) String chr = getChrForId(chromId); final BedData bedData = new BedData(chr, chromStart, chromEnd, restOfFields); - final BasicFeature feature = bedCodec.decode(bedData); + final IGVFeature feature = bedCodec.decode(bedData); features.add(feature); } return features; diff --git a/src/main/java/org/broad/igv/ucsc/bb/codecs/BBCodec.java b/src/main/java/org/broad/igv/ucsc/bb/codecs/BBCodec.java index a645b91796..f5bbd13a96 100644 --- a/src/main/java/org/broad/igv/ucsc/bb/codecs/BBCodec.java +++ b/src/main/java/org/broad/igv/ucsc/bb/codecs/BBCodec.java @@ -1,9 +1,10 @@ package org.broad.igv.ucsc.bb.codecs; import org.broad.igv.feature.BasicFeature; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.ucsc.bb.BedData; public interface BBCodec { - BasicFeature decode(BedData feat); + IGVFeature decode(BedData feat); } diff --git a/src/main/java/org/broad/igv/ucsc/bb/codecs/BBInteractCodec.java b/src/main/java/org/broad/igv/ucsc/bb/codecs/BBInteractCodec.java new file mode 100644 index 0000000000..1729c928ba --- /dev/null +++ b/src/main/java/org/broad/igv/ucsc/bb/codecs/BBInteractCodec.java @@ -0,0 +1,101 @@ +package org.broad.igv.ucsc.bb.codecs; + +import org.broad.igv.Globals; +import org.broad.igv.bedpe.BedPEFeature; +import org.broad.igv.feature.BasicFeature; +import org.broad.igv.feature.Exon; +import org.broad.igv.feature.tribble.IGVBEDCodec; +import org.broad.igv.ucsc.bb.BBUtils; +import org.broad.igv.ucsc.bb.BedData; + +import java.util.List; + + +public class BBInteractCodec implements BBCodec { + + + private final BBUtils.ASTable astable; + + int standardFieldCount; + IGVBEDCodec igvBedCodec; + + public BBInteractCodec(int standardFieldCount, BBUtils.ASTable autosql) { + this.astable = autosql; + this.standardFieldCount = standardFieldCount; + this.igvBedCodec = new IGVBEDCodec(); // Backing "tribble" codec + } + + /* + feature.chr1 = tokens[5] + feature.start1 = Number.parseInt(tokens[6]) + feature.end1 = Number.parseInt(tokens[7]) + + feature.chr2 = tokens[10] + feature.start2 = Number.parseInt(tokens[11]) + feature.end2 = Number.parseInt(tokens[12]) + + feature.name = tokens[0] + feature.score = Number(tokens[1]) + feature.value = Number(tokens[2]) + feature.color = tokens[4] === '.' ? undefined : tokens[4] === "0" ? "rgb(0,0,0)" : tokens[4] + + */ + + public BedPEFeature decode(BedData bedData) { + + String[] restOfFields = Globals.tabPattern.split(bedData.restOfFields, -1); + String[] tokens = new String[this.standardFieldCount]; + + String chr1 = tokens[5]; + int start1 = Integer.parseInt(tokens[6]); + int end1 = Integer.parseInt(tokens[7]); + + String chr2 = tokens[10]; + int start2 = Integer.parseInt(tokens[11]); + int end2 = Integer.parseInt(tokens[12]); + + String name = tokens[0]; + double score = Double.parseDouble(tokens[1]); + double value = Double.parseDouble(tokens[2]); + String color = tokens[4].equals(".") ? null : tokens[4].equals("0") ? "rgb(0,0,0)" : tokens[4]; + + BedPEFeature feature = new BedPEFeature(chr1, start1, end1, chr2, start2, end2); + + return feature; + + // public BedPEFeature(String chr1, int start1, int end1, String chr2, int start2, int end2) { + + + + // "Non standard" fields +// if (astable != null && astable.fields.size() > standardFieldCount) { +// LinkedHashMap attributes = new LinkedHashMap<>(); +// List fields = astable.fields; +// for (int i = this.standardFieldCount; i < fields.size(); i++) { +// BBUtils.ASField field = fields.get(i); +// attributes.put(field.name, restOfFields[i - 3]); +// } +// feature.setAttributes(attributes); +// +// if (attributes.containsKey("exonFrames")) { +// computeExonFrames(feature, attributes.get("exonFrames")); +// } +// } + + } + + private void computeExonFrames(BasicFeature feature, String exonFrames) { + String[] frameBuffer = Globals.commaPattern.split(exonFrames); + List exons = feature.getExons(); + for (int i = 0; i < frameBuffer.length; i++) { + int exonFrame = Integer.parseInt(frameBuffer[i].trim()); + if (exonFrame == -1) { + exons.get(i).setNonCoding(true); + } else { + exons.get(i).setReadingFrame(exonFrame); + } + } + } + + +} diff --git a/src/main/java/org/broad/igv/ui/IGVMenuBar.java b/src/main/java/org/broad/igv/ui/IGVMenuBar.java index 3c6982550c..32d1c249e5 100644 --- a/src/main/java/org/broad/igv/ui/IGVMenuBar.java +++ b/src/main/java/org/broad/igv/ui/IGVMenuBar.java @@ -285,7 +285,7 @@ JMenu createFileMenu(Genome genome) { menuItems.add(new JSeparator()); // Load menu items - menuAction = new LoadFilesMenuAction("Load Tracks from File...", KeyEvent.VK_L, igv); + menuAction = new LoadFilesMenuAction("Load from File...", KeyEvent.VK_L, igv); menuAction.setToolTipText(UIConstants.LOAD_TRACKS_TOOLTIP); menuItems.add(MenuAndToolbarUtils.createMenuItem(menuAction)); @@ -296,7 +296,7 @@ JMenu createFileMenu(Genome genome) { if (genomeId != null && genome.getTrackHubs().isEmpty() && LoadFromServerAction.getNodeURLs(genomeId) != null) { - menuAction = new LoadFromServerAction("Load Tracks From IGV Server...", KeyEvent.VK_S, igv); + menuAction = new LoadFromServerAction("Load from IGV Server...", KeyEvent.VK_S, igv); menuAction.setToolTipText(UIConstants.LOAD_SERVER_DATA_TOOLTIP); JMenuItem loadTracksFromServerMenuItem = MenuAndToolbarUtils.createMenuItem(menuAction); menuItems.add(loadTracksFromServerMenuItem); diff --git a/src/main/java/org/broad/igv/ui/action/LoadFromURLMenuAction.java b/src/main/java/org/broad/igv/ui/action/LoadFromURLMenuAction.java index 5bdda10fca..1049ca999f 100644 --- a/src/main/java/org/broad/igv/ui/action/LoadFromURLMenuAction.java +++ b/src/main/java/org/broad/igv/ui/action/LoadFromURLMenuAction.java @@ -54,7 +54,7 @@ */ public class LoadFromURLMenuAction extends MenuAction { - public static final String LOAD_FROM_URL = "Load Track from URL..."; + 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..."; public static final String LOAD_TRACKHUB = "Load Track Hub..."; diff --git a/src/main/java/org/broad/igv/ui/color/ColorUtilities.java b/src/main/java/org/broad/igv/ui/color/ColorUtilities.java index e2b1c8ea2d..6c8aa64a52 100644 --- a/src/main/java/org/broad/igv/ui/color/ColorUtilities.java +++ b/src/main/java/org/broad/igv/ui/color/ColorUtilities.java @@ -180,7 +180,7 @@ public static Color stringToColor(String string) { public static Color stringToColor(String string, Color defaultColor) { - if (string == null) return defaultColor; + if (string == null || string.equals(".")) return defaultColor; try { Color c = stringToColorNoDefault(string); diff --git a/src/main/java/org/broad/igv/util/FeatureCache.java b/src/main/java/org/broad/igv/util/FeatureCache.java index 64fd6600ec..245315c1d2 100644 --- a/src/main/java/org/broad/igv/util/FeatureCache.java +++ b/src/main/java/org/broad/igv/util/FeatureCache.java @@ -104,9 +104,8 @@ private void init(List features, int batchSize) { } IntervalTree> tree = featureMap.get(lastChr); - tree.insert(new Interval(currentMin, currentMax, currentFeatureList)); - - + if(tree != null) { + tree.insert(new Interval(currentMin, currentMax, currentFeatureList)); + } } - } diff --git a/src/test/java/org/broad/igv/feature/tribble/InteractCodecTest.java b/src/test/java/org/broad/igv/feature/tribble/InteractCodecTest.java new file mode 100644 index 0000000000..dae5285093 --- /dev/null +++ b/src/test/java/org/broad/igv/feature/tribble/InteractCodecTest.java @@ -0,0 +1,82 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2007-2015 Broad Institute + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.broad.igv.feature.tribble; + +import org.broad.igv.AbstractHeadlessTest; +import org.broad.igv.bedpe.InteractFeature; +import org.broad.igv.feature.FeatureType; +import org.broad.igv.bedpe.InteractCodec; +import org.broad.igv.util.TestUtils; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.ArrayList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author jrobinso + * Date: 4/8/13 + * Time: 2:51 PM + */ +public class InteractCodecTest extends AbstractHeadlessTest { + + + /** + * #chrom chromStart chromEnd name score value exp color sourceChrom sourceStart sourceEnd sourceName sourceStrand targetChrom targetStart targetEnd targetName targetStrand + * chr12 40572709 40618813 rs7974522/LRRK2/muscleSkeletal 0 0.624 muscleSkeletal #7A67EE chr12 40572709 40572710 rs7974522 . chr12 40618812 40618813 LRRK2 + + * chr12 40579899 40618813 rs17461492/LRRK2/muscleSkeletal 0 0.624 muscleSkeletal #7A67EE chr12 40579899 40579900 rs17461492 . chr12 40618812 40618813 LRRK2 + + * chr12 40614433 40618813 rs76904798/LRRK2/nerveTibial 0 0.625 nerveTibial #FFD700 chr12 40614433 40614434 rs76904798 . chr12 40618812 40618813 LRRK2 + + * chr12 40618812 40652520 rs2723264/LRRK2/lung 0 1.839 lung #9ACD32 chr12 40652519 40652520 rs2723264 . chr12 40618812 40618813 LRRK2 + + */ + @Test + public void testInteract1() throws Exception { + + String path = TestUtils.DATA_DIR + "bedpe/interactExample1.txt"; + InteractCodec codec = new InteractCodec(genome, FeatureType.INTERACT); + BufferedReader bufferedReader = null; + try { + bufferedReader = new BufferedReader(new FileReader(path)); + String nextLine; + ArrayList features = new ArrayList<>(); + while ((nextLine = bufferedReader.readLine()) != null) { + InteractFeature f = codec.decode(nextLine); + if (f != null) { + assertEquals("chr12", f.getChr()); + features.add(f); + } + } + assertEquals(4, features.size()); + } finally { + bufferedReader.close(); + } + } + + + +} diff --git a/src/test/java/org/broad/igv/ucsc/bb/BBFileTest.java b/src/test/java/org/broad/igv/ucsc/bb/BBFileTest.java index 4ae89067e6..51526ab200 100644 --- a/src/test/java/org/broad/igv/ucsc/bb/BBFileTest.java +++ b/src/test/java/org/broad/igv/ucsc/bb/BBFileTest.java @@ -1,6 +1,7 @@ package org.broad.igv.ucsc.bb; import org.broad.igv.feature.BasicFeature; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.util.TestUtils; import org.junit.Test; @@ -82,12 +83,12 @@ public void testExtraIndexSearch() throws IOException { // There are 5 extra indexes, 1 for each alias String ncbiName = "3"; - BasicFeature f1 = bbReader.search(ncbiName); + IGVFeature f1 = bbReader.search(ncbiName); assertNotNull(f1); assertEquals(ncbiName, f1.getAttribute("ncbi")); String ucscName = "chr2"; - BasicFeature f2 = bbReader.search(ucscName); + IGVFeature f2 = bbReader.search(ucscName); assertEquals(ucscName, f2.getAttribute("ucsc")); assertNull(bbReader.search("zzzz")); @@ -102,17 +103,29 @@ public void testExtraIndexTrixSearch() throws IOException { // Search by name, which is the index parameter, does not require trix String name = "NP_389226.1"; - BasicFeature f = bbReader.search(name); + IGVFeature f = bbReader.search(name); assertEquals(name, f.getName()); // Search by alternate name, does require trix String name2 = "ykoX"; - BasicFeature f2 = bbReader.search(name2); + IGVFeature f2 = bbReader.search(name2); assertEquals(name, f2.getName()); assertNull(bbReader.search("zzzz")); } + /** + * Test a BW file with an unusual layout (chromTree after full data). + */ + @Test + public void testBigInteract() throws IOException { + String bbFile = TestUtils.DATA_DIR + "bb/interactExample3.inter.bb"; + BBFile reader = new BBFile(bbFile, null); + reader.readHeader(); + String [] chrNames = reader.getChromosomeNames(); + assertEquals(6, chrNames.length); + } + } \ No newline at end of file diff --git a/src/test/java/org/broad/igv/util/FeatureCacheTest.java b/src/test/java/org/broad/igv/util/FeatureCacheTest.java index c1e2ab2394..a95bbcc23d 100644 --- a/src/test/java/org/broad/igv/util/FeatureCacheTest.java +++ b/src/test/java/org/broad/igv/util/FeatureCacheTest.java @@ -1,5 +1,6 @@ package org.broad.igv.util; +import org.broad.igv.bedpe.BedPE; import org.broad.igv.bedpe.BedPEFeature; import org.broad.igv.bedpe.BedPEParser; import org.junit.Test; @@ -15,7 +16,7 @@ public void testGetFeatures() throws Exception { String p = TestUtils.DATA_DIR + "bedpe/interleaved_chrs.bedpe"; - List features = BedPEParser.parse(new ResourceLocator(p), null).features; + List features = BedPEParser.parse(new ResourceLocator(p), null); //chr5:135,300,000-135,399,999 String chr = "chr5"; @@ -24,12 +25,12 @@ public void testGetFeatures() throws Exception { // Manually count overlaps int count = 0; - for (BedPEFeature f : features) { + for (BedPE f : features) { if (chr.equals(f.getChr()) && f.getEnd() >= start && f.getStart() <= end) count++; } - FeatureCache cache = new FeatureCache<>(features); - List subset = cache.getFeatures(chr, start, end); + FeatureCache cache = new FeatureCache<>(features); + List subset = cache.getFeatures(chr, start, end); assertEquals(count, subset.size()); } diff --git a/test/data/bedpe/interactExample1.txt b/test/data/bedpe/interactExample1.txt new file mode 100644 index 0000000000..3f018931fb --- /dev/null +++ b/test/data/bedpe/interactExample1.txt @@ -0,0 +1,7 @@ +track type=interact name="interact Example One" description="An interact file" interactDirectional=true maxHeightPixels=200:100:50 visibility=full +browser position chr12:40,560,500-40,660,499 +#chrom chromStart chromEnd name score value exp color sourceChrom sourceStart sourceEnd sourceName sourceStrand targetChrom targetStart targetEnd targetName targetStrand +chr12 40572709 40618813 rs7974522/LRRK2/muscleSkeletal 0 0.624 muscleSkeletal #7A67EE chr12 40572709 40572710 rs7974522 . chr12 40618812 40618813 LRRK2 + +chr12 40579899 40618813 rs17461492/LRRK2/muscleSkeletal 0 0.624 muscleSkeletal #7A67EE chr12 40579899 40579900 rs17461492 . chr12 40618812 40618813 LRRK2 + +chr12 40614433 40618813 rs76904798/LRRK2/nerveTibial 0 0.625 nerveTibial #FFD700 chr12 40614433 40614434 rs76904798 . chr12 40618812 40618813 LRRK2 + +chr12 40618812 40652520 rs2723264/LRRK2/lung 0 1.839 lung #9ACD32 chr12 40652519 40652520 rs2723264 . chr12 40618812 40618813 LRRK2 + diff --git a/test/data/bedpe/interactExample2.txt b/test/data/bedpe/interactExample2.txt new file mode 100644 index 0000000000..82ea1a9d9d --- /dev/null +++ b/test/data/bedpe/interactExample2.txt @@ -0,0 +1,6 @@ +track type=interact name="interact Example Two" description="Chromatin interactions" useScore=on maxHeightPixels=200:100:50 visibility=full +browser position chr3:64,562,440-64,642,288 +chr3 64496901 64584378 . 375 3 . 0 chr3 64496901 64498901 . . chr3 64581378 64584378 . . +chr3 64568052 64569134 . 400 3 . 0 chr20 52552477 52556062 . . chr3 64568052 64569134 . . +chr3 64596901 64615378 . 175 3 . 0 chr3 64596901 64600855 . . chr3 64612677 64615378 . . +chr3 64623042 64636663 . 800 4 . 0 chr3 64623042 64625153 . . chr3 64632961 64636663 . . From 9f3e7d276dbb6f75cbc29ab32875f946bd63f4a6 Mon Sep 17 00:00:00 2001 From: jrobinso <933148+jrobinso@users.noreply.github.com> Date: Tue, 15 Jul 2025 20:03:40 -0700 Subject: [PATCH 2/9] support interact and bigInteract --- .../org/broad/igv/bedpe/BedPEFeature.java | 8 ++ .../java/org/broad/igv/bedpe/BedPEParser.java | 14 ++- .../org/broad/igv/bedpe/InteractionTrack.java | 2 - .../org/broad/igv/feature/FeatureType.java | 3 +- .../igv/feature/genome/CytobandSourceBB.java | 5 +- .../java/org/broad/igv/track/TrackLoader.java | 14 ++- .../broad/igv/ucsc/bb/BBFeatureSource.java | 15 +-- .../java/org/broad/igv/ucsc/bb/BBFile.java | 29 ++++- .../igv/ucsc/bb/codecs/BBCodecFactory.java | 2 + .../igv/ucsc/bb/codecs/BBInteractCodec.java | 77 ++----------- test/data/bedpe/interactExample4.inter.bed | 102 ++++++++++++++++++ 11 files changed, 181 insertions(+), 90 deletions(-) create mode 100644 test/data/bedpe/interactExample4.inter.bed diff --git a/src/main/java/org/broad/igv/bedpe/BedPEFeature.java b/src/main/java/org/broad/igv/bedpe/BedPEFeature.java index 8ba6a5face..f87ff77d7e 100644 --- a/src/main/java/org/broad/igv/bedpe/BedPEFeature.java +++ b/src/main/java/org/broad/igv/bedpe/BedPEFeature.java @@ -1,9 +1,13 @@ package org.broad.igv.bedpe; +import org.broad.igv.Globals; import org.broad.igv.feature.BasicFeature; import org.broad.igv.feature.IGVFeature; +import org.broad.igv.feature.Strand; +import org.broad.igv.ui.color.ColorUtilities; import java.awt.*; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -14,10 +18,13 @@ public class BedPEFeature implements BedPE { protected String chr1; protected int start1; protected int end1; + protected Strand strand1 = Strand.NONE; // Default to NONE, can be set later protected String chr2; protected int start2; protected int end2; + protected Strand strand2 = Strand.NONE; // Default to NONE, can be set later + protected String name; protected String scoreString = ""; protected float score; @@ -162,4 +169,5 @@ public void setColor(Color color) { public String getName() { return name; } + } diff --git a/src/main/java/org/broad/igv/bedpe/BedPEParser.java b/src/main/java/org/broad/igv/bedpe/BedPEParser.java index 2094f6bd39..b8f11a0c3d 100644 --- a/src/main/java/org/broad/igv/bedpe/BedPEParser.java +++ b/src/main/java/org/broad/igv/bedpe/BedPEParser.java @@ -1,5 +1,6 @@ package org.broad.igv.bedpe; +import org.broad.igv.feature.Strand; import org.broad.igv.logging.*; import org.broad.igv.Globals; import org.broad.igv.feature.genome.Genome; @@ -131,9 +132,20 @@ public static List parse(ResourceLocator locator, Genome genome) throws I } if (tokens.length > 8) { + + int extraFieldsStart = 8; + if(tokens[8].trim().equals("+") || tokens[8].trim().equals("-")) { + feature.strand1 = tokens[8].trim().equals("+") ? Strand.POSITIVE : Strand.NEGATIVE; + extraFieldsStart = 9; + } + if(tokens[9].trim().equals("+") || tokens[9].trim().equals("-")) { + feature.strand2 = tokens[9].trim().equals("+") ? Strand.POSITIVE : Strand.NEGATIVE; + extraFieldsStart = 10; + } + Map attributes = new LinkedHashMap<>(); - for (int i = 8; i < tokens.length; i++) { + for (int i = extraFieldsStart; i < tokens.length; i++) { String t = tokens[i]; String c = columns != null && columns.length > i ? columns[i] : String.valueOf(i); diff --git a/src/main/java/org/broad/igv/bedpe/InteractionTrack.java b/src/main/java/org/broad/igv/bedpe/InteractionTrack.java index 32607592bf..b49241ed6e 100644 --- a/src/main/java/org/broad/igv/bedpe/InteractionTrack.java +++ b/src/main/java/org/broad/igv/bedpe/InteractionTrack.java @@ -155,7 +155,6 @@ public void render(RenderContext context, Rectangle trackRectangle) { try { String chr = context.getReferenceFrame().getChrName(); - // TODO Convert iterator to list. This is very wasteful, but neccessary due to the feature source interface. List features = new ArrayList<>(); Iterator iter = featureSource.getFeatures(chr, (int) context.getOrigin(), (int) context.getEndLocation()); @@ -234,7 +233,6 @@ public IGVPopupMenu getPopupMenu(TrackClickEvent te) { IGVPopupMenu menu = new IGVPopupMenu(); - // Experimental JBrowse. if (PreferencesManager.getPreferences().getAsBoolean(Constants.CIRC_VIEW_ENABLED) && CircularViewUtilities.ping()) { menu.addSeparator(); diff --git a/src/main/java/org/broad/igv/feature/FeatureType.java b/src/main/java/org/broad/igv/feature/FeatureType.java index ef00a1d68e..d6938f55e8 100644 --- a/src/main/java/org/broad/igv/feature/FeatureType.java +++ b/src/main/java/org/broad/igv/feature/FeatureType.java @@ -30,7 +30,8 @@ * @author jrobinso */ public enum FeatureType { - OTHER, GENE, PROMOTER, MISC_RNA, REPEAT_REGION, LTR, MUTATION, BED, GAPPED_PEAK, SPLICE_JUNCTION, BED_METHYL, INTERACT + OTHER, GENE, PROMOTER, MISC_RNA, REPEAT_REGION, LTR, MUTATION, BED, GAPPED_PEAK, SPLICE_JUNCTION, BED_METHYL, + INTERACT, BEDPE, WIG, RMSK, NARROW_PEAK, PEAK } diff --git a/src/main/java/org/broad/igv/feature/genome/CytobandSourceBB.java b/src/main/java/org/broad/igv/feature/genome/CytobandSourceBB.java index 2f3d27dac5..882592e50e 100644 --- a/src/main/java/org/broad/igv/feature/genome/CytobandSourceBB.java +++ b/src/main/java/org/broad/igv/feature/genome/CytobandSourceBB.java @@ -2,6 +2,7 @@ import org.broad.igv.feature.BasicFeature; import org.broad.igv.feature.Cytoband; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.ucsc.bb.BBFeatureSource; import org.broad.igv.ucsc.bb.BBFile; @@ -22,9 +23,9 @@ public CytobandSourceBB(String path, Genome genome) throws IOException { @Override public List getCytobands(String chr) throws IOException { List cytobands = new ArrayList<>(); - Iterator features = featureSource.getFeatures(chr, 0, Integer.MAX_VALUE); + Iterator features = featureSource.getFeatures(chr, 0, Integer.MAX_VALUE); while (features.hasNext()) { - BasicFeature f = features.next(); + IGVFeature f = features.next(); cytobands.add(new Cytoband(f.getChr(), f.getStart(), f.getEnd(), f.getName(), f.getAttribute("gieStain"))); } return cytobands; diff --git a/src/main/java/org/broad/igv/track/TrackLoader.java b/src/main/java/org/broad/igv/track/TrackLoader.java index 61ed58ce76..946d90b6a1 100644 --- a/src/main/java/org/broad/igv/track/TrackLoader.java +++ b/src/main/java/org/broad/igv/track/TrackLoader.java @@ -199,7 +199,7 @@ public List load(ResourceLocator locator, Genome genome) throws DataLoadE loadEwigIBFFile(locator, newTracks, genome); } else if (format.equals("bw") || format.equals("bb") || format.equals("bigwig") || format.equals("bigbed") || format.equals("biggenepred") || format.equals("bigrepmsk")) { - loadBWFile(locator, newTracks, genome); + loadBBFile(locator, newTracks, genome); } else if (format.equals("ibf") || format.equals("tdf")) { loadTDFFile(locator, newTracks, genome); } else if (WiggleParser.isWiggle(locator)) { @@ -807,7 +807,7 @@ public void loadTDFFile(ResourceLocator locator, List newTracks, Genome g } - public void loadBWFile(ResourceLocator locator, List newTracks, Genome genome) throws IOException { + public void loadBBFile(ResourceLocator locator, List newTracks, Genome genome) throws IOException { String trackName = locator.getTrackName(); String trackId = locator.getPath(); @@ -821,7 +821,15 @@ public void loadBWFile(ResourceLocator locator, List newTracks, Genome ge track = new DataSourceTrack(locator, trackId, trackName, bigwigSource); } else if (reader.isBigBedFile()) { BBFeatureSource featureSource = new BBFeatureSource(reader, genome); - track = new FeatureTrack(locator, trackId, trackName, featureSource); + switch(reader.getFeatureType()) { + case INTERACT: + track = new InteractionTrack(locator,featureSource); + break; + default: + track = new FeatureTrack(locator, trackId, trackName, featureSource); + } + + } else { throw new RuntimeException("Unknown BIGWIG type: " + locator.getPath()); diff --git a/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java b/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java index f01568cca5..944094d014 100644 --- a/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java +++ b/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java @@ -27,6 +27,7 @@ import htsjdk.tribble.NamedFeature; import org.broad.igv.feature.BasicFeature; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.feature.LocusScore; import org.broad.igv.feature.genome.Genome; import org.broad.igv.logging.LogManager; @@ -91,7 +92,7 @@ public void close() { * @return * @throws IOException */ - public Iterator getFeatures(String chr, int start, int end) throws IOException { + public Iterator getFeatures(String chr, int start, int end) throws IOException { long rTreeOffset = reader.getHeader().fullIndexOffset; Integer chrIdx = reader.getIdForChr(chr); @@ -121,16 +122,16 @@ public NamedFeature search(String term) { } } - static class FeatureIterator implements Iterator { + static class FeatureIterator implements Iterator { - List features; + List features; int idx; int start; int end; - BasicFeature next; + IGVFeature next; - public FeatureIterator(List features, int start, int end) { + public FeatureIterator(List features, int start, int end) { this.features = features; this.start = start; this.end = end; @@ -159,8 +160,8 @@ public boolean hasNext() { } @Override - public BasicFeature next() { - BasicFeature retValue = next; + public IGVFeature next() { + IGVFeature retValue = next; advance(); return retValue; } diff --git a/src/main/java/org/broad/igv/ucsc/bb/BBFile.java b/src/main/java/org/broad/igv/ucsc/bb/BBFile.java index 771b3a31ad..df35ab3c26 100644 --- a/src/main/java/org/broad/igv/ucsc/bb/BBFile.java +++ b/src/main/java/org/broad/igv/ucsc/bb/BBFile.java @@ -3,6 +3,7 @@ import htsjdk.samtools.seekablestream.SeekableStream; import org.broad.igv.data.BasicScore; import org.broad.igv.feature.BasicFeature; +import org.broad.igv.feature.FeatureType; import org.broad.igv.feature.IGVFeature; import org.broad.igv.feature.LocusScore; import org.broad.igv.feature.genome.ChromAlias; @@ -10,9 +11,8 @@ import org.broad.igv.track.WindowFunction; import org.broad.igv.ucsc.BPTree; import org.broad.igv.ucsc.Trix; +import org.broad.igv.ucsc.bb.codecs.*; import org.broad.igv.ucsc.twobit.UnsignedByteBuffer; -import org.broad.igv.ucsc.bb.codecs.BBCodec; -import org.broad.igv.ucsc.bb.codecs.BBCodecFactory; import org.broad.igv.ucsc.twobit.UnsignedByteBufferImpl; import org.broad.igv.util.CompressionUtils; import org.broad.igv.util.stream.IGVSeekableStreamFactory; @@ -83,7 +83,7 @@ */ public class BBFile { - enum Type {BIGWIG, BIGBED} + public enum Type {BIGWIG, BIGBED} static public final int BBFILE_HEADER_SIZE = 64; static public final long BIGWIG_MAGIC = 2291137574l; // BigWig Magic @@ -101,6 +101,7 @@ enum Type {BIGWIG, BIGBED} private BBCodec bedCodec; private String path; private Type type; + private FeatureType featureType; private BBHeader header = null; private BBZoomHeader[] zoomHeaders; private ByteOrder byteOrder; @@ -131,6 +132,28 @@ public Type getType() { return type; } + public FeatureType getFeatureType() { + if (featureType == null) { + if (isBigWigFile()) { + featureType = FeatureType.WIG; + } else if (isBigBedFile()) { + String as = getAutoSQL(); + if (as != null) { + BBUtils.ASTable astable = BBUtils.parseAutosql(autosql); + if (astable != null && "bigRmskBed".equals(astable.name)) { + featureType = FeatureType.RMSK; + } else if (astable != null && astable.name.toLowerCase().contains("interact")) { + featureType = FeatureType.INTERACT; + } + } + if (featureType == null) { + featureType = FeatureType.BED; + } + } + } + return featureType; + } + public BBHeader getHeader() { return header; } diff --git a/src/main/java/org/broad/igv/ucsc/bb/codecs/BBCodecFactory.java b/src/main/java/org/broad/igv/ucsc/bb/codecs/BBCodecFactory.java index 6187dbaf1c..38658f1c5a 100644 --- a/src/main/java/org/broad/igv/ucsc/bb/codecs/BBCodecFactory.java +++ b/src/main/java/org/broad/igv/ucsc/bb/codecs/BBCodecFactory.java @@ -11,6 +11,8 @@ public static BBCodec getCodec(String autosql, int standardFieldCount) { if (astable != null && "bigRmskBed".equals(astable.name)) { return new BBRmskCodec(standardFieldCount, astable); + } else if (astable != null && astable.name.toLowerCase().contains("interact")) { + return new BBInteractCodec(standardFieldCount, astable); } else { return new BBBedCodec(standardFieldCount, astable); } diff --git a/src/main/java/org/broad/igv/ucsc/bb/codecs/BBInteractCodec.java b/src/main/java/org/broad/igv/ucsc/bb/codecs/BBInteractCodec.java index 1729c928ba..a54f99d080 100644 --- a/src/main/java/org/broad/igv/ucsc/bb/codecs/BBInteractCodec.java +++ b/src/main/java/org/broad/igv/ucsc/bb/codecs/BBInteractCodec.java @@ -2,11 +2,13 @@ import org.broad.igv.Globals; import org.broad.igv.bedpe.BedPEFeature; +import org.broad.igv.bedpe.InteractFeature; import org.broad.igv.feature.BasicFeature; import org.broad.igv.feature.Exon; import org.broad.igv.feature.tribble.IGVBEDCodec; import org.broad.igv.ucsc.bb.BBUtils; import org.broad.igv.ucsc.bb.BedData; +import software.amazon.awssdk.services.s3.endpoints.internal.Value; import java.util.List; @@ -16,86 +18,19 @@ public class BBInteractCodec implements BBCodec { private final BBUtils.ASTable astable; - int standardFieldCount; - IGVBEDCodec igvBedCodec; + private final int standardFieldCount; public BBInteractCodec(int standardFieldCount, BBUtils.ASTable autosql) { this.astable = autosql; this.standardFieldCount = standardFieldCount; - this.igvBedCodec = new IGVBEDCodec(); // Backing "tribble" codec } - /* - feature.chr1 = tokens[5] - feature.start1 = Number.parseInt(tokens[6]) - feature.end1 = Number.parseInt(tokens[7]) + public InteractFeature decode(BedData bedData) { - feature.chr2 = tokens[10] - feature.start2 = Number.parseInt(tokens[11]) - feature.end2 = Number.parseInt(tokens[12]) + String[] tokens = bedData.toString().split("\t"); - feature.name = tokens[0] - feature.score = Number(tokens[1]) - feature.value = Number(tokens[2]) - feature.color = tokens[4] === '.' ? undefined : tokens[4] === "0" ? "rgb(0,0,0)" : tokens[4] - - */ - - public BedPEFeature decode(BedData bedData) { - - String[] restOfFields = Globals.tabPattern.split(bedData.restOfFields, -1); - String[] tokens = new String[this.standardFieldCount]; - - String chr1 = tokens[5]; - int start1 = Integer.parseInt(tokens[6]); - int end1 = Integer.parseInt(tokens[7]); - - String chr2 = tokens[10]; - int start2 = Integer.parseInt(tokens[11]); - int end2 = Integer.parseInt(tokens[12]); - - String name = tokens[0]; - double score = Double.parseDouble(tokens[1]); - double value = Double.parseDouble(tokens[2]); - String color = tokens[4].equals(".") ? null : tokens[4].equals("0") ? "rgb(0,0,0)" : tokens[4]; - - BedPEFeature feature = new BedPEFeature(chr1, start1, end1, chr2, start2, end2); - - return feature; - - // public BedPEFeature(String chr1, int start1, int end1, String chr2, int start2, int end2) { - - - - // "Non standard" fields -// if (astable != null && astable.fields.size() > standardFieldCount) { -// LinkedHashMap attributes = new LinkedHashMap<>(); -// List fields = astable.fields; -// for (int i = this.standardFieldCount; i < fields.size(); i++) { -// BBUtils.ASField field = fields.get(i); -// attributes.put(field.name, restOfFields[i - 3]); -// } -// feature.setAttributes(attributes); -// -// if (attributes.containsKey("exonFrames")) { -// computeExonFrames(feature, attributes.get("exonFrames")); -// } -// } + return InteractFeature.fromTokens(tokens, null); } - private void computeExonFrames(BasicFeature feature, String exonFrames) { - String[] frameBuffer = Globals.commaPattern.split(exonFrames); - List exons = feature.getExons(); - for (int i = 0; i < frameBuffer.length; i++) { - int exonFrame = Integer.parseInt(frameBuffer[i].trim()); - if (exonFrame == -1) { - exons.get(i).setNonCoding(true); - } else { - exons.get(i).setReadingFrame(exonFrame); - } - } - } - - } diff --git a/test/data/bedpe/interactExample4.inter.bed b/test/data/bedpe/interactExample4.inter.bed new file mode 100644 index 0000000000..a987b30554 --- /dev/null +++ b/test/data/bedpe/interactExample4.inter.bed @@ -0,0 +1,102 @@ +track type=interact name="interact Example Four" description="A inter-chr interact file" useScore=on maxHeightPixels=200:100:50 +chr3 63702628 63705638 . 584 10 . 0 chr17 58878552 58880897 . . chr3 63702628 63705638 . . +chr3 63741418 63978511 . 350 6 . 0 chr3 63741418 63743120 . . chr3 63976338 63978511 . . +chr3 63807692 63850723 . 350 6 . 0 chr3 63807692 63812972 . . chr3 63845430 63850723 . . +chr3 63811608 63822750 . 409 7 . 0 chr3 63811608 63815899 . . chr3 63817183 63822750 . . +chr3 63812972 63851457 . 175 3 . 0 chr3 63812972 63815116 . . chr3 63848971 63851457 . . +chr3 63815972 63825185 . 175 3 . 0 chr3 63815972 63819524 . . chr3 63821646 63825185 . . +chr3 63816028 63850178 . 175 3 . 0 chr3 63816028 63819408 . . chr3 63848457 63850178 . . +chr3 63824431 63834652 . 175 3 . 0 chr3 63824431 63827315 . . chr3 63832117 63834652 . . +chr3 63845488 63895612 . 935 16 . 0 chr3 63845488 63852271 . . chr3 63889273 63895612 . . +chr3 63847908 63851113 . 175 3 . 0 chr1 106770547 106773987 . . chr3 63847908 63851113 . . +chr3 63848065 63927103 . 350 6 . 0 chr3 63848065 63850949 . . chr3 63924261 63927103 . . +chr3 63848074 63903527 . 1000 22 . 0 chr3 63848074 63852099 . . chr3 63895812 63903527 . . +chr3 63848746 63932798 . 233 4 . 0 chr3 63848746 63850674 . . chr3 63929679 63932798 . . +chr3 63849543 63920825 . 175 3 . 0 chr3 63849543 63851298 . . chr3 63918128 63920825 . . +chr3 63862344 63899163 . 292 5 . 0 chr3 63862344 63865741 . . chr3 63896165 63899163 . . +chr3 63869747 63879302 . 233 4 . 0 chr3 63869747 63873691 . . chr3 63875945 63879302 . . +chr3 63871958 63898999 . 175 3 . 0 chr3 63871958 63875351 . . chr3 63897249 63898999 . . +chr3 63876666 63894115 . 233 4 . 0 chr3 63876666 63879961 . . chr3 63891527 63894115 . . +chr3 63880561 63893500 . 175 3 . 0 chr3 63880561 63883208 . . chr3 63891284 63893500 . . +chr3 63881103 63899059 . 175 3 . 0 chr3 63881103 63885377 . . chr3 63897172 63899059 . . +chr3 63931046 63934786 . 292 5 . 0 chr1 106734104 106736624 . . chr3 63931046 63934786 . . +chr3 63963771 63974650 . 233 4 . 0 chr3 63963771 63967133 . . chr3 63970981 63974650 . . +chr3 63967573 63969852 . 175 3 . 0 chr17 56707778 56709679 . . chr3 63967573 63969852 . . +chr3 63973083 63981558 . 292 5 . 0 chr3 63973083 63977048 . . chr3 63979036 63981558 . . +chr3 63978257 63982674 . 233 4 . 0 chr17 58796861 58799991 . . chr3 63978257 63982674 . . +chr3 63978715 63982258 . 233 4 . 0 chr17 56707661 56711378 . . chr3 63978715 63982258 . . +chr3 63991508 63995886 . 175 3 . 0 chr17 56707758 56710392 . . chr3 63991508 63995886 . . +chr3 64002517 64475046 . 175 3 . 0 chr3 64002517 64005948 . . chr3 64473057 64475046 . . +chr3 64004820 64091839 . 350 6 . 0 chr3 64004820 64009182 . . chr3 64087150 64091839 . . +chr3 64005818 64245239 . 175 3 . 0 chr3 64005818 64009070 . . chr3 64242644 64245239 . . +chr3 64006035 64010454 . 233 4 . 0 chr17 58753497 58756759 . . chr3 64006035 64010454 . . +chr3 64006269 64009310 . 233 4 . 0 chr17 56707751 56710415 . . chr3 64006269 64009310 . . +chr3 64006461 64161808 . 175 3 . 0 chr3 64006461 64009792 . . chr3 64159396 64161808 . . +chr3 64006646 64009985 . 233 4 . 0 chr17 58802736 58806064 . . chr3 64006646 64009985 . . +chr3 64006869 64145072 . 350 6 . 0 chr3 64006869 64010826 . . chr3 64141698 64145072 . . +chr3 64007005 64320845 . 175 3 . 0 chr3 64007005 64009350 . . chr3 64316822 64320845 . . +chr3 64007405 64026085 . 350 6 . 0 chr3 64007405 64010888 . . chr3 64022085 64026085 . . +chr3 64007591 64478040 . 233 4 . 0 chr3 64007591 64010395 . . chr3 64474573 64478040 . . +chr3 64007893 64472912 . 292 5 . 0 chr3 64007893 64010590 . . chr3 64469435 64472912 . . +chr3 64008465 64433300 . 175 3 . 0 chr3 64008465 64010139 . . chr3 64430286 64433300 . . +chr3 64012522 64149149 . 292 5 . 0 chr3 64012522 64017910 . . chr3 64147316 64149149 . . +chr3 64014971 64256982 . 175 3 . 0 chr3 64014971 64018080 . . chr3 64253190 64256982 . . +chr3 64015157 64146777 . 233 4 . 0 chr3 64015157 64017714 . . chr3 64143118 64146777 . . +chr3 64018287 64022779 . 409 7 . 0 chr20 52609763 52615074 . . chr3 64018287 64022779 . . +chr3 64020009 64022030 . 175 3 . 0 chr20 52868969 52871919 . . chr3 64020009 64022030 . . +chr3 64021415 64031712 . 350 6 . 0 chr3 64021415 64026878 . . chr3 64028521 64031712 . . +chr3 64022627 64035893 . 233 4 . 0 chr3 64022627 64025842 . . chr3 64032632 64035893 . . +chr3 64022932 64144245 . 175 3 . 0 chr3 64022932 64025833 . . chr3 64141668 64144245 . . +chr3 64035758 64050395 . 233 4 . 0 chr3 64035758 64039952 . . chr3 64046789 64050395 . . +chr3 64047625 64049831 . 233 4 . 0 chr20 55839840 55842797 . . chr3 64047625 64049831 . . +chr3 64058070 64097401 . 175 3 . 0 chr3 64058070 64059964 . . chr3 64094824 64097401 . . +chr3 64093663 64097859 . 292 5 . 0 chr20 46412424 46415897 . . chr3 64093663 64097859 . . +chr3 64095692 64125430 . 175 3 . 0 chr3 64095692 64097906 . . chr3 64122527 64125430 . . +chr3 64097877 64103506 . 584 10 . 0 chr20 46410273 46414943 . . chr3 64097877 64103506 . . +chr3 64098052 64101134 . 175 3 . 0 chr20 52552477 52556062 . . chr3 64098052 64101134 . . +chr3 64105701 64121819 . 292 5 . 0 chr3 64105701 64110013 . . chr3 64117316 64121819 . . +chr3 64113988 64128443 . 233 4 . 0 chr3 64113988 64116622 . . chr3 64124656 64128443 . . +chr3 64121332 64138636 . 409 7 . 0 chr3 64121332 64125629 . . chr3 64134634 64138636 . . +chr3 64122783 64431860 . 175 3 . 0 chr3 64122783 64126250 . . chr3 64429396 64431860 . . +chr3 64123966 64144701 . 175 3 . 0 chr3 64123966 64127602 . . chr3 64141764 64144701 . . +chr3 64124948 64133404 . 350 6 . 0 chr3 64124948 64129268 . . chr3 64129542 64133404 . . +chr3 64128056 64145392 . 175 3 . 0 chr3 64128056 64131078 . . chr3 64141290 64145392 . . +chr3 64135050 64197000 . 175 3 . 0 chr3 64135050 64138046 . . chr3 64193423 64197000 . . +chr3 64141116 64178808 . 233 4 . 0 chr3 64141116 64144725 . . chr3 64174576 64178808 . . +chr3 64141287 64155317 . 233 4 . 0 chr3 64141287 64144265 . . chr3 64152811 64155317 . . +chr3 64144833 64157417 . 175 3 . 0 chr3 64144833 64148021 . . chr3 64154630 64157417 . . +chr3 64167276 64176914 . 175 3 . 0 chr3 64167276 64171618 . . chr3 64174317 64176914 . . +chr3 64169678 64200073 . 993 17 . 0 chr3 64169678 64182784 . . chr3 64192956 64200073 . . +chr3 64170984 64211814 . 175 3 . 0 chr3 64170984 64173122 . . chr3 64207791 64211814 . . +chr3 64172103 64186117 . 175 3 . 0 chr3 64172103 64174298 . . chr3 64183951 64186117 . . +chr3 64172519 64192464 . 292 5 . 0 chr3 64172519 64176550 . . chr3 64189067 64192464 . . +chr3 64174339 64185816 . 350 6 . 0 chr3 64174339 64178023 . . chr3 64182791 64185816 . . +chr3 64193228 64252788 . 175 3 . 0 chr3 64193228 64196645 . . chr3 64250068 64252788 . . +chr3 64248354 64295587 . 175 3 . 0 chr3 64248354 64251412 . . chr3 64292988 64295587 . . +chr3 64252639 64337094 . 175 3 . 0 chr3 64252639 64255371 . . chr3 64333557 64337094 . . +chr3 64256493 64386371 . 175 3 . 0 chr3 64256493 64259166 . . chr3 64383513 64386371 . . +chr3 64259497 64273548 . 175 3 . 0 chr3 64259497 64262146 . . chr3 64269811 64273548 . . +chr3 64259525 64268550 . 175 3 . 0 chr3 64259525 64261676 . . chr3 64265304 64268550 . . +chr3 64275288 64285044 . 292 5 . 0 chr3 64275288 64278076 . . chr3 64282172 64285044 . . +chr3 64303511 64322555 . 1000 19 . 0 chr3 64303511 64313379 . . chr3 64313475 64322555 . . +chr3 64315571 64332451 . 818 14 . 0 chr3 64315571 64323705 . . chr3 64325673 64332451 . . +chr3 64317671 64341288 . 175 3 . 0 chr3 64317671 64321742 . . chr3 64337514 64341288 . . +chr3 64321141 64336648 . 175 3 . 0 chr3 64321141 64323562 . . chr3 64333935 64336648 . . +chr3 64329128 64347824 . 233 4 . 0 chr3 64329128 64333230 . . chr3 64344560 64347824 . . +chr3 64329830 64369138 . 233 4 . 0 chr3 64329830 64332863 . . chr3 64365026 64369138 . . +chr3 64335401 64345035 . 233 4 . 0 chr3 64335401 64339525 . . chr3 64341417 64345035 . . +chr3 64360098 64368794 . 233 4 . 0 chr3 64360098 64362290 . . chr3 64365434 64368794 . . +chr3 64366760 64378654 . 175 3 . 0 chr3 64366760 64368863 . . chr3 64375994 64378654 . . +chr3 64369310 64386258 . 175 3 . 0 chr3 64369310 64372646 . . chr3 64382820 64386258 . . +chr3 64405843 64432343 . 526 9 . 0 chr3 64405843 64411279 . . chr3 64426624 64432343 . . +chr3 64415977 64429293 . 350 6 . 0 chr3 64415977 64418738 . . chr3 64423176 64429293 . . +chr3 64415987 64432842 . 233 4 . 0 chr3 64415987 64418531 . . chr3 64429843 64432842 . . +chr3 64429588 64446305 . 175 3 . 0 chr3 64429588 64431688 . . chr3 64443897 64446305 . . +chr3 64432560 64451350 . 233 4 . 0 chr3 64432560 64436743 . . chr3 64446845 64451350 . . +chr3 64438719 64452973 . 467 8 . 0 chr3 64438719 64444043 . . chr3 64446974 64452973 . . +chr3 64447931 64458035 . 409 7 . 0 chr3 64447931 64454851 . . chr3 64454897 64458035 . . +chr3 64475927 64484685 . 409 7 . 0 chr3 64475927 64480534 . . chr3 64481189 64484685 . . +chr3 64582336 64593646 . 409 7 . 0 chr3 64582336 64587085 . . chr3 64588789 64593646 . . +chr3 64596901 64615378 . 175 3 . 0 chr3 64596901 64600855 . . chr3 64612677 64615378 . . +chr3 64623042 64636663 . 233 4 . 0 chr3 64623042 64625153 . . chr3 64632961 64636663 . . +chr3 64694246 64704675 . 175 3 . 0 chr3 64694246 64697620 . . chr3 64700741 64704675 . . From d33f7a8dd72e5709d3e673dcf0eee28e6e468a21 Mon Sep 17 00:00:00 2001 From: jrobinso <933148+jrobinso@users.noreply.github.com> Date: Tue, 15 Jul 2025 20:21:14 -0700 Subject: [PATCH 3/9] merge bugs --- .../igv/feature/genome/ChromAliasBB.java | 20 ++++++++++--------- .../broad/igv/ucsc/bb/BBFeatureSource.java | 2 +- .../java/org/broad/igv/ucsc/bb/BBFile.java | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/broad/igv/feature/genome/ChromAliasBB.java b/src/main/java/org/broad/igv/feature/genome/ChromAliasBB.java index cf42ed8c55..27aa1fce48 100644 --- a/src/main/java/org/broad/igv/feature/genome/ChromAliasBB.java +++ b/src/main/java/org/broad/igv/feature/genome/ChromAliasBB.java @@ -47,15 +47,17 @@ public String getChromosomeAlias(String chr, String nameSet) { */ public ChromAlias search(String alias) throws IOException { if (!this.aliasCache.containsKey(alias)) { - IGVFeature f = this.reader.search(alias); - if (f != null) { - String chr = f.getChr(); - ChromAlias aliasRecord = new ChromAlias(chr); - this.aliasCache.put(chr, aliasRecord); - for (String key : f.getAttributeKeys()){ - final String a = f.getAttribute(key); - aliasRecord.put(key, a); - this.aliasCache.put(a, aliasRecord); // One entry for each alias + List results = this.reader.search(alias); + if (results != null) { + for (IGVFeature f : results) { + String chr = f.getChr(); + ChromAlias aliasRecord = new ChromAlias(chr); + this.aliasCache.put(chr, aliasRecord); + for (String key : f.getAttributeKeys()) { + final String a = f.getAttribute(key); + aliasRecord.put(key, a); + this.aliasCache.put(a, aliasRecord); // One entry for each alias + } } } } diff --git a/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java b/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java index 0c020c4670..eb78d62044 100644 --- a/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java +++ b/src/main/java/org/broad/igv/ucsc/bb/BBFeatureSource.java @@ -113,7 +113,7 @@ public boolean isSearchable() { } @Override - public List search(String term) { + public List search(String term) { try { return reader.search(term); } catch (IOException e) { diff --git a/src/main/java/org/broad/igv/ucsc/bb/BBFile.java b/src/main/java/org/broad/igv/ucsc/bb/BBFile.java index d844836dce..7adef2f588 100644 --- a/src/main/java/org/broad/igv/ucsc/bb/BBFile.java +++ b/src/main/java/org/broad/igv/ucsc/bb/BBFile.java @@ -449,7 +449,7 @@ public boolean isSearchable() { */ - public IGVFeature search(String term) throws IOException { + public List search(String term) throws IOException { if (this.header == null) { this.readHeader(); From 1234dc78b55ad36f0fde66a98a7def2afd140fb4 Mon Sep 17 00:00:00 2001 From: Jim Robinson <933148+jrobinso@users.noreply.github.com> Date: Tue, 15 Jul 2025 20:49:14 -0700 Subject: [PATCH 4/9] Update src/main/java/org/broad/igv/feature/IGVFeature.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/main/java/org/broad/igv/feature/IGVFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/broad/igv/feature/IGVFeature.java b/src/main/java/org/broad/igv/feature/IGVFeature.java index 6283c4fd3b..6be283610c 100644 --- a/src/main/java/org/broad/igv/feature/IGVFeature.java +++ b/src/main/java/org/broad/igv/feature/IGVFeature.java @@ -99,6 +99,6 @@ default String getURL() { } default Map getAttributes() { - return Collections.EMPTY_MAP; + return Collections.emptyMap(); } } From 6a726b410ca019fb4061cc740c9dcd9fa193d23a Mon Sep 17 00:00:00 2001 From: Jim Robinson <933148+jrobinso@users.noreply.github.com> Date: Tue, 15 Jul 2025 20:50:10 -0700 Subject: [PATCH 5/9] Update src/main/java/org/broad/igv/bedpe/BedPESource.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/main/java/org/broad/igv/bedpe/BedPESource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/broad/igv/bedpe/BedPESource.java b/src/main/java/org/broad/igv/bedpe/BedPESource.java index 0b3a5a0b06..23790fff2d 100644 --- a/src/main/java/org/broad/igv/bedpe/BedPESource.java +++ b/src/main/java/org/broad/igv/bedpe/BedPESource.java @@ -93,7 +93,7 @@ private List createWGFeatures(List features, Genome genome) { public static List downsampleFeatures(List features) { if (features.isEmpty()) { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } BedPE maxScoreFeature = features.stream() From a2a3b6480cb43d79fb272dd55058f5c6236b83b8 Mon Sep 17 00:00:00 2001 From: Jim Robinson <933148+jrobinso@users.noreply.github.com> Date: Tue, 15 Jul 2025 20:50:42 -0700 Subject: [PATCH 6/9] Update src/main/java/org/broad/igv/bedpe/BedPESource.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/main/java/org/broad/igv/bedpe/BedPESource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/broad/igv/bedpe/BedPESource.java b/src/main/java/org/broad/igv/bedpe/BedPESource.java index 23790fff2d..6c108d4483 100644 --- a/src/main/java/org/broad/igv/bedpe/BedPESource.java +++ b/src/main/java/org/broad/igv/bedpe/BedPESource.java @@ -121,7 +121,7 @@ public static List downsampleFeatures(List features) { List sampledFeatures = new ArrayList<>(MAX_WG_COUNT); for (int i = 0; i < nBins; i++) { List bfs = binnedFeatures[i]; - sampledFeatures.addAll(Arrays.asList(new Downsampler().sample(bfs.toArray(BedPEFeature[]::new), featuresPerBin))); + sampledFeatures.addAll(Arrays.asList(new Downsampler().sample(bfs.toArray(BedPE[]::new), featuresPerBin))); } // Be sure we keep the maximum feature From 85037a6d920b0ecde17801f530eb7ae4b0b84c29 Mon Sep 17 00:00:00 2001 From: jrobinso <933148+jrobinso@users.noreply.github.com> Date: Tue, 15 Jul 2025 20:52:54 -0700 Subject: [PATCH 7/9] code review --- src/main/java/org/broad/igv/util/FeatureCache.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/broad/igv/util/FeatureCache.java b/src/main/java/org/broad/igv/util/FeatureCache.java index 245315c1d2..82c551382a 100644 --- a/src/main/java/org/broad/igv/util/FeatureCache.java +++ b/src/main/java/org/broad/igv/util/FeatureCache.java @@ -104,8 +104,11 @@ private void init(List features, int batchSize) { } IntervalTree> tree = featureMap.get(lastChr); - if(tree != null) { - tree.insert(new Interval(currentMin, currentMax, currentFeatureList)); + if (tree == null) { + tree = new IntervalTree<>(); + featureMap.put(lastChr, tree); } + tree.insert(new Interval(currentMin, currentMax, currentFeatureList)); + } } From f76239322aa2107778b533cd85c73fcde5867179 Mon Sep 17 00:00:00 2001 From: jrobinso <933148+jrobinso@users.noreply.github.com> Date: Wed, 16 Jul 2025 10:58:03 -0700 Subject: [PATCH 8/9] fix unit tests --- .../igv/ucsc/bb/BBFeatureSourceTest.java | 38 +++++++++---------- .../org/broad/igv/ucsc/bb/BBFileTest.java | 14 ------- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/src/test/java/org/broad/igv/ucsc/bb/BBFeatureSourceTest.java b/src/test/java/org/broad/igv/ucsc/bb/BBFeatureSourceTest.java index c643d65105..7b9df8d88a 100644 --- a/src/test/java/org/broad/igv/ucsc/bb/BBFeatureSourceTest.java +++ b/src/test/java/org/broad/igv/ucsc/bb/BBFeatureSourceTest.java @@ -1,7 +1,7 @@ package org.broad.igv.ucsc.bb; import htsjdk.tribble.Feature; -import org.broad.igv.feature.BasicFeature; +import org.broad.igv.feature.IGVFeature; import org.broad.igv.feature.genome.Genome; import org.broad.igv.util.TestUtils; import org.junit.Ignore; @@ -27,9 +27,9 @@ public void testbed9_2() throws IOException { BBFile reader = new BBFile(path, genome); BBFeatureSource bwSource = new BBFeatureSource(reader, genome); - Iterator iter = bwSource.getFeatures(chr, start, end); + Iterator iter = bwSource.getFeatures(chr, start, end); - List features = new ArrayList<>(); + List features = new ArrayList<>(); while (iter.hasNext()) { features.add(iter.next()); } @@ -37,7 +37,7 @@ public void testbed9_2() throws IOException { assertEquals(3339, features.size()); // Verified in iPad app //chr7 773975 792642 uc003sjb.2 0 + 776710 791816 0,255,0 HEATR2 Q86Y56-3 - BasicFeature f = features.get(20); + IGVFeature f = features.get(20); assertEquals(f.getStart(), 773975); assertEquals(f.getAttribute("geneSymbol"), "HEATR2"); assertEquals(f.getAttribute("spID"), "Q86Y56-3"); @@ -55,9 +55,9 @@ public void testbed9_2_prelod() throws IOException { reader.preload(); BBFeatureSource bwSource = new BBFeatureSource(reader, genome); - Iterator iter = bwSource.getFeatures(chr, start, end); + Iterator iter = bwSource.getFeatures(chr, start, end); - List features = new ArrayList<>(); + List features = new ArrayList<>(); while (iter.hasNext()) { features.add(iter.next()); } @@ -65,7 +65,7 @@ public void testbed9_2_prelod() throws IOException { assertEquals(3339, features.size()); // Verified in iPad app //chr7 773975 792642 uc003sjb.2 0 + 776710 791816 0,255,0 HEATR2 Q86Y56-3 - BasicFeature f = features.get(20); + IGVFeature f = features.get(20); assertEquals(f.getStart(), 773975); assertEquals(f.getAttribute("geneSymbol"), "HEATR2"); assertEquals(f.getAttribute("spID"), "Q86Y56-3"); @@ -82,9 +82,9 @@ public void testChromAlias() throws IOException { BBFile reader = new BBFile(path, genome); BBFeatureSource bwSource = new BBFeatureSource(reader, genome); - Iterator iter = bwSource.getFeatures(chr, start, end); + Iterator iter = bwSource.getFeatures(chr, start, end); - List features = new ArrayList<>(); + List features = new ArrayList<>(); while (iter.hasNext()) { features.add(iter.next()); } @@ -92,7 +92,7 @@ public void testChromAlias() throws IOException { assertEquals(3339, features.size()); // Verified in iPad app //chr7 773975 792642 uc003sjb.2 0 + 776710 791816 0,255,0 HEATR2 Q86Y56-3 - BasicFeature f = features.get(20); + IGVFeature f = features.get(20); assertEquals(f.getStart(), 773975); assertEquals(f.getAttribute("geneSymbol"), "HEATR2"); assertEquals(f.getAttribute("spID"), "Q86Y56-3"); @@ -112,10 +112,10 @@ public void testBigBed() throws IOException { int end = 42182827; - Iterator iter = bbSource.getFeatures(chr, start, end); + Iterator iter = bbSource.getFeatures(chr, start, end); int count = 0; while (iter.hasNext()) { - BasicFeature f = iter.next(); + IGVFeature f = iter.next(); assertEquals(chr, f.getChr()); assertTrue(f.getStart() <= end && f.getEnd() >= start); count++; @@ -139,10 +139,10 @@ public void testBigBed_preload() throws IOException { int end = 42182827; - Iterator iter = bbSource.getFeatures(chr, start, end); + Iterator iter = bbSource.getFeatures(chr, start, end); int count = 0; while (iter.hasNext()) { - BasicFeature f = iter.next(); + IGVFeature f = iter.next(); assertEquals(chr, f.getChr()); assertTrue(f.getStart() <= end && f.getEnd() >= start); count++; @@ -167,7 +167,7 @@ public void testDataCount() throws IOException { BBFeatureSource bbSource = new BBFeatureSource(bbReader, null); int count = 0; - Iterator iter = bbSource.getFeatures("chr21", 0, Integer.MAX_VALUE); + Iterator iter = bbSource.getFeatures("chr21", 0, Integer.MAX_VALUE); while (iter.hasNext()) { iter.next(); count++; @@ -191,7 +191,7 @@ public void testBigGenePred() throws IOException { int start = 26490012; int end = 42182827; - Iterator iter = bbSource.getFeatures(chr, start, end); + Iterator iter = bbSource.getFeatures(chr, start, end); int count = 0; while (iter.hasNext()) { @@ -215,7 +215,7 @@ public void testBigNarrowpeak() throws IOException { int start = 26490012; int end = 42182827; - Iterator iter = bbSource.getFeatures(chr, start, end); + Iterator iter = bbSource.getFeatures(chr, start, end); int count = 0; while (iter.hasNext()) { @@ -239,7 +239,7 @@ public void testBigInteract() throws IOException { int start = 63081504; int end = 64501215; - Iterator iter = bbSource.getFeatures(chr, start, end); + Iterator iter = bbSource.getFeatures(chr, start, end); int count = 0; while (iter.hasNext()) { @@ -289,7 +289,7 @@ public void testBigRmsk() throws IOException { int start = 145481787; int end = 145482000; - Iterator iter = bbSource.getFeatures(chr, start, end); + Iterator iter = bbSource.getFeatures(chr, start, end); int count = 0; while (iter.hasNext()) { diff --git a/src/test/java/org/broad/igv/ucsc/bb/BBFileTest.java b/src/test/java/org/broad/igv/ucsc/bb/BBFileTest.java index f067c2f31e..be3fa9d9fd 100644 --- a/src/test/java/org/broad/igv/ucsc/bb/BBFileTest.java +++ b/src/test/java/org/broad/igv/ucsc/bb/BBFileTest.java @@ -128,18 +128,4 @@ public void testExtraIndexTrixSearch() throws IOException { assertNull(bbReader.search("zzzz")); } - - /** - * Test a BW file with an unusual layout (chromTree after full data). - */ - @Test - public void testBigInteract() throws IOException { - String bbFile = TestUtils.DATA_DIR + "bb/interactExample3.inter.bb"; - BBFile reader = new BBFile(bbFile, null); - reader.readHeader(); - String [] chrNames = reader.getChromosomeNames(); - assertEquals(6, chrNames.length); - } - - } \ No newline at end of file From 74782497050de412efcbf9fcafd3af68bde64c82 Mon Sep 17 00:00:00 2001 From: Jim Robinson <933148+jrobinso@users.noreply.github.com> Date: Wed, 16 Jul 2025 11:14:14 -0700 Subject: [PATCH 9/9] Update test/data/bedpe/interactExample4.inter.bed Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- test/data/bedpe/interactExample4.inter.bed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/data/bedpe/interactExample4.inter.bed b/test/data/bedpe/interactExample4.inter.bed index a987b30554..640e86ef53 100644 --- a/test/data/bedpe/interactExample4.inter.bed +++ b/test/data/bedpe/interactExample4.inter.bed @@ -1,4 +1,4 @@ -track type=interact name="interact Example Four" description="A inter-chr interact file" useScore=on maxHeightPixels=200:100:50 +track type=interact name="interact Example Four" description="An inter-chr interact file" useScore=on maxHeightPixels=200:100:50 chr3 63702628 63705638 . 584 10 . 0 chr17 58878552 58880897 . . chr3 63702628 63705638 . . chr3 63741418 63978511 . 350 6 . 0 chr3 63741418 63743120 . . chr3 63976338 63978511 . . chr3 63807692 63850723 . 350 6 . 0 chr3 63807692 63812972 . . chr3 63845430 63850723 . .