From: gmungoc Date: Mon, 5 Jun 2017 07:54:11 +0000 (+0100) Subject: Merge branch 'features/JAL-2446NCList' into X-Git-Tag: Release_2_10_3b1~214^2 X-Git-Url: http://source.jalview.org/gitweb/?p=jalview.git;a=commitdiff_plain;h=refs%2Fheads%2Ffeatures%2FJAL-2526sequenceCursor;hp=ba61fc7a3397ce6b57640e0b19c76392a983768a Merge branch 'features/JAL-2446NCList' into features/JAL-2526sequenceCursor Conflicts: src/jalview/datamodel/SequenceI.java --- diff --git a/help/html/menus/popupMenu.html b/help/html/menus/popupMenu.html index d42f854..7625606 100755 --- a/help/html/menus/popupMenu.html +++ b/help/html/menus/popupMenu.html @@ -189,8 +189,10 @@ href="../features/varna.html">VARNA.
  • Hide Insertions
    - Hides columns containing gaps in the current sequence or - selected region, and reveals columns not including gaps. + Hides columns containing gaps in both the current + sequence and selected region, and reveals columns not including + gaps. (before 2.10.2, this option hid or revealed columns + according to gaps in just the current sequence)
  • Hide Sequences
    Hides the currently selected sequences in this alignment view.
  • diff --git a/help/html/releases.html b/help/html/releases.html index 1ac87af..cb46024 100755 --- a/help/html/releases.html +++ b/help/html/releases.html @@ -184,6 +184,7 @@ li:before {
  • Trackpad horizontal scroll gesture adjusts start position in wrap mode
  • Status bar doesn't show positions for ambiguous amino acids
  • +
  • Hide insertions in PopUp menu excludes gaps in selection, current sequence and only within selected columns
  • Applet

    Experimental Features diff --git a/src/jalview/datamodel/HiddenColumns.java b/src/jalview/datamodel/HiddenColumns.java index 3685ab0..f0d99e5 100644 --- a/src/jalview/datamodel/HiddenColumns.java +++ b/src/jalview/datamodel/HiddenColumns.java @@ -4,6 +4,7 @@ import jalview.util.Comparison; import jalview.util.ShiftList; import java.util.ArrayList; +import java.util.BitSet; import java.util.Collections; import java.util.List; import java.util.Vector; @@ -1262,4 +1263,37 @@ public class HiddenColumns return hashCode; } + /** + * Hide columns corresponding to the marked bits + * + * @param inserts + * - columns map to bits starting from zero + */ + public void hideMarkedBits(BitSet inserts) + { + for (int firstSet = inserts.nextSetBit(0), lastSet = 0; firstSet >= 0; firstSet = inserts + .nextSetBit(lastSet)) + { + lastSet = inserts.nextClearBit(firstSet); + hideColumns(firstSet, lastSet - 1); + } + } + + /** + * + * @param inserts + * BitSet where hidden columns will be marked + */ + public void markHiddenRegions(BitSet inserts) + { + if (hiddenColumns == null) + { + return; + } + for (int[] range : hiddenColumns) + { + inserts.set(range[0], range[1] + 1); + } + } + } diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index 24f904c..ab6639a 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -31,6 +31,7 @@ import jalview.util.StringUtils; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collections; import java.util.Enumeration; import java.util.List; @@ -1165,6 +1166,40 @@ public class Sequence extends ASequence implements SequenceI } @Override + public BitSet getInsertionsAsBits() + { + BitSet map = new BitSet(); + int lastj = -1, j = 0; + int pos = start; + int seqlen = sequence.length; + while ((j < seqlen)) + { + if (jalview.util.Comparison.isGap(sequence[j])) + { + if (lastj == -1) + { + lastj = j; + } + } + else + { + if (lastj != -1) + { + map.set(lastj, j); + lastj = -1; + } + } + j++; + } + if (lastj != -1) + { + map.set(lastj, j); + lastj = -1; + } + return map; + } + + @Override public void deleteChars(int i, int j) { int newstart = start, newend = end; diff --git a/src/jalview/datamodel/SequenceI.java b/src/jalview/datamodel/SequenceI.java index 18f0948..38be37f 100755 --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@ -22,6 +22,7 @@ package jalview.datamodel; import jalview.datamodel.features.SequenceFeaturesI; +import java.util.BitSet; import java.util.List; import java.util.Vector; @@ -527,4 +528,11 @@ public interface SequenceI extends ASequenceI * positions to be invalidated. */ void sequenceChanged(); + + /** + * + * @return BitSet corresponding to index [0,length) where Comparison.isGap() + * returns true. + */ + BitSet getInsertionsAsBits(); } diff --git a/src/jalview/ext/ensembl/EnsemblGene.java b/src/jalview/ext/ensembl/EnsemblGene.java index 2d4d61a..915fa0a 100644 --- a/src/jalview/ext/ensembl/EnsemblGene.java +++ b/src/jalview/ext/ensembl/EnsemblGene.java @@ -191,7 +191,22 @@ public class EnsemblGene extends EnsemblSeqProxy geneIds.add(geneId); } } + else if (isProteinIdentifier(acc)) + { + String tscriptId = new EnsemblLookup(getDomain()).getParent(acc); + if (tscriptId != null) + { + String geneId = new EnsemblLookup(getDomain()) + .getParent(tscriptId); + if (geneId != null && !geneIds.contains(geneId)) + { + geneIds.add(geneId); + } + } + // NOTE - acc is lost if it resembles an ENS.+ ID but isn't actually + // resolving to one... e.g. ENSMICP00000009241 + } /* * if given a gene or other external name, lookup and fetch * the corresponding gene for all model organisms diff --git a/src/jalview/ext/ensembl/EnsemblRestClient.java b/src/jalview/ext/ensembl/EnsemblRestClient.java index 5960f81..2437588 100644 --- a/src/jalview/ext/ensembl/EnsemblRestClient.java +++ b/src/jalview/ext/ensembl/EnsemblRestClient.java @@ -76,6 +76,9 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher private final static long VERSION_RETEST_INTERVAL = 1000L * 3600; // 1 hr + private static final Regex PROTEIN_REGEX = new Regex( + "(ENS)([A-Z]{3}|)P[0-9]{11}$"); + private static final Regex TRANSCRIPT_REGEX = new Regex( "(ENS)([A-Z]{3}|)T[0-9]{11}$"); @@ -125,6 +128,18 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher /** * Answers true if the query matches the regular expression pattern for an + * Ensembl protein stable identifier + * + * @param query + * @return + */ + public boolean isProteinIdentifier(String query) + { + return query == null ? false : PROTEIN_REGEX.search(query); + } + + /** + * Answers true if the query matches the regular expression pattern for an * Ensembl gene stable identifier * * @param query diff --git a/src/jalview/gui/AnnotationColumnChooser.java b/src/jalview/gui/AnnotationColumnChooser.java index a15f605..9c2a1b9 100644 --- a/src/jalview/gui/AnnotationColumnChooser.java +++ b/src/jalview/gui/AnnotationColumnChooser.java @@ -112,7 +112,7 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements setOldHiddenColumns(av.getAlignment().getHiddenColumns()); adjusting = true; - setAnnotations(new JComboBox(getAnnotationItems(false))); + setAnnotations(new JComboBox<>(getAnnotationItems(false))); populateThresholdComboBox(threshold); AnnotationColumnChooser lastChooser = av .getAnnotationColumnSelectionState(); @@ -386,10 +386,13 @@ public class AnnotationColumnChooser extends AnnotationRowFilter implements } } + // show hidden columns here, before changing the column selection in + // filterAnnotations, because showing hidden columns has the side effect of + // adding them to the selection + av.showAllHiddenColumns(); av.getColumnSelection().filterAnnotations( getCurrentAnnotation().annotations, filterParams); - av.showAllHiddenColumns(); if (getActionOption() == ACTION_OPTION_HIDE) { av.hideSelectedColumns(); diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index c27aeee..a8e2f7e 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -57,6 +57,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Hashtable; @@ -1444,24 +1445,59 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener protected void hideInsertions_actionPerformed(ActionEvent actionEvent) { - if (sequence != null) + + HiddenColumns hidden = new HiddenColumns(); + BitSet inserts = new BitSet(), mask = new BitSet(); + + // set mask to preserve existing hidden columns outside selected group + if (ap.av.hasHiddenColumns()) + { + ap.av.getAlignment().getHiddenColumns().markHiddenRegions(mask); + } + + boolean markedPopup = false; + // mark inserts in current selection + if (ap.av.getSelectionGroup() != null) { - /* ColumnSelection cs = ap.av.getColumnSelection(); - if (cs == null) - { - cs = new ColumnSelection(); - } - cs.hideInsertionsFor(sequence); - ap.av.setColumnSelection(cs);*/ + // mark just the columns in the selection group to be hidden + inserts.set(ap.av.getSelectionGroup().getStartRes(), ap.av + .getSelectionGroup().getEndRes() + 1); + + // and clear that part of the mask + mask.andNot(inserts); - HiddenColumns hidden = ap.av.getAlignment().getHiddenColumns(); - if (hidden == null) + // now clear columns without gaps + for (SequenceI sq : ap.av.getSelectionGroup().getSequences()) { - hidden = new HiddenColumns(); + if (sq == sequence) + { + markedPopup = true; + } + inserts.and(sq.getInsertionsAsBits()); } - hidden.hideInsertionsFor(sequence); - ap.av.getAlignment().setHiddenColumns(hidden); } + else + { + // initially, mark all columns to be hidden + inserts.set(0, ap.av.getAlignment().getWidth()); + + // and clear out old hidden regions completely + mask.clear(); + } + + // now mark for sequence under popup if we haven't already done it + if (!markedPopup && sequence != null) + { + inserts.and(sequence.getInsertionsAsBits()); + } + + // finally, preserve hidden regions outside selection + inserts.or(mask); + + // and set hidden columns accordingly + hidden.hideMarkedBits(inserts); + + ap.av.getAlignment().setHiddenColumns(hidden); refresh(); } diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java index 743d212..4eb8c95 100644 --- a/src/jalview/viewmodel/ViewportRanges.java +++ b/src/jalview/viewmodel/ViewportRanges.java @@ -123,7 +123,7 @@ public class ViewportRanges extends ViewportProperties int oldstartres = this.startRes; if (start > getVisibleAlignmentWidth() - 1) { - startRes = getVisibleAlignmentWidth() - 1; + startRes = Math.max(getVisibleAlignmentWidth() - 1, 0); } else if (start < 0) { @@ -141,7 +141,7 @@ public class ViewportRanges extends ViewportProperties } else if (end > getVisibleAlignmentWidth() - 1) { - endRes = getVisibleAlignmentWidth() - 1; + endRes = Math.max(getVisibleAlignmentWidth() - 1, 0); } else { @@ -208,7 +208,7 @@ public class ViewportRanges extends ViewportProperties int oldstartseq = this.startSeq; if (start > getVisibleAlignmentHeight() - 1) { - startSeq = getVisibleAlignmentHeight() - 1; + startSeq = Math.max(getVisibleAlignmentHeight() - 1, 0); } else if (start < 0) { @@ -222,7 +222,7 @@ public class ViewportRanges extends ViewportProperties int oldendseq = this.endSeq; if (end >= getVisibleAlignmentHeight()) { - endSeq = getVisibleAlignmentHeight() - 1; + endSeq = Math.max(getVisibleAlignmentHeight() - 1, 0); } else if (end < 0) { diff --git a/src/jalview/workers/ColumnCounterSetWorker.java b/src/jalview/workers/ColumnCounterSetWorker.java index 6c5707d..2422748 100644 --- a/src/jalview/workers/ColumnCounterSetWorker.java +++ b/src/jalview/workers/ColumnCounterSetWorker.java @@ -182,19 +182,19 @@ class ColumnCounterSetWorker extends AlignCalcWorker for (int anrow = 0; anrow < rows; anrow++) { Annotation[] anns = new Annotation[width]; + long rmax = 0; /* - * add non-zero counts as annotations + * add counts as annotations. zeros are needed since select-by-annotation ignores empty annotation positions */ for (int i = 0; i < counts.length; i++) { int count = counts[i][anrow]; - if (count > 0) - { - Color color = ColorUtils.getGraduatedColour(count, 0, minColour, - max[anrow], maxColour); - String str = String.valueOf(count); - anns[i] = new Annotation(str, str, '0', count, color); - } + + Color color = ColorUtils.getGraduatedColour(count, 0, minColour, + max[anrow], maxColour); + String str = String.valueOf(count); + anns[i] = new Annotation(str, str, '0', count, color); + rmax = Math.max(count, rmax); } /* @@ -212,7 +212,8 @@ class ColumnCounterSetWorker extends AlignCalcWorker ann.scaleColLabel = true; ann.graph = AlignmentAnnotation.BAR_GRAPH; ann.annotations = anns; - setGraphMinMax(ann, anns); + ann.graphMin = 0f; // minimum always zero count + ann.graphMax = rmax; // maximum count from loop over feature columns ann.validateRangeAndDisplay(); if (!ourAnnots.contains(ann)) { diff --git a/test/jalview/datamodel/HiddenColumnsTest.java b/test/jalview/datamodel/HiddenColumnsTest.java index b767cf7..10808d6 100644 --- a/test/jalview/datamodel/HiddenColumnsTest.java +++ b/test/jalview/datamodel/HiddenColumnsTest.java @@ -29,7 +29,9 @@ import jalview.analysis.AlignmentGenerator; import jalview.gui.JvOptionPane; import java.util.Arrays; +import java.util.BitSet; import java.util.List; +import java.util.Random; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -517,4 +519,70 @@ public class HiddenColumnsTest assertEquals("[60, 70]", Arrays.toString(hidden.get(1))); } + @Test(groups = { "Functional" }) + public void testHideBitset() + { + HiddenColumns cs; + + BitSet one = new BitSet(); + + // one hidden range + one.set(1); + cs = new HiddenColumns(); + cs.hideMarkedBits(one); + assertEquals(1, cs.getHiddenRegions().size()); + + one.set(2); + cs = new HiddenColumns(); + cs.hideMarkedBits(one); + assertEquals(1, cs.getHiddenRegions().size()); + + one.set(3); + cs = new HiddenColumns(); + cs.hideMarkedBits(one); + assertEquals(1, cs.getHiddenRegions().size()); + + // split + one.clear(2); + cs = new HiddenColumns(); + cs.hideMarkedBits(one); + assertEquals(2, cs.getHiddenRegions().size()); + + assertEquals(0, cs.adjustForHiddenColumns(0)); + assertEquals(2, cs.adjustForHiddenColumns(1)); + assertEquals(4, cs.adjustForHiddenColumns(2)); + + // one again + one.clear(1); + cs = new HiddenColumns(); + cs.hideMarkedBits(one); + + assertEquals(1, cs.getHiddenRegions().size()); + + assertEquals(0, cs.adjustForHiddenColumns(0)); + assertEquals(1, cs.adjustForHiddenColumns(1)); + assertEquals(2, cs.adjustForHiddenColumns(2)); + assertEquals(4, cs.adjustForHiddenColumns(3)); + } + + @Test(groups = { "Functional" }) + public void testGetBitset() + { + BitSet toMark, fromMark; + long seed = -3241532; + Random number = new Random(seed); + for (int n = 0; n < 1000; n++) + { + // create a random bitfield + toMark = BitSet.valueOf(new long[] { number.nextLong(), + number.nextLong(), number.nextLong() }); + toMark.set(n * number.nextInt(10), n * (25 + number.nextInt(25))); + HiddenColumns hc = new HiddenColumns(); + hc.hideMarkedBits(toMark); + + // see if we can recover bitfield + hc.markHiddenRegions(fromMark = new BitSet()); + assertEquals(toMark, fromMark); + } + } } diff --git a/test/jalview/datamodel/SequenceTest.java b/test/jalview/datamodel/SequenceTest.java index 4f42947..c5850dc 100644 --- a/test/jalview/datamodel/SequenceTest.java +++ b/test/jalview/datamodel/SequenceTest.java @@ -37,6 +37,7 @@ import jalview.util.MapList; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.List; import java.util.Vector; @@ -78,6 +79,18 @@ public class SequenceTest assertEquals("Gap interval 1 end wrong", 4, gapInt.get(0)[1]); assertEquals("Gap interval 2 start wrong", 6, gapInt.get(1)[0]); assertEquals("Gap interval 2 end wrong", 8, gapInt.get(1)[1]); + + BitSet gapfield = aseq.getInsertionsAsBits(); + BitSet expectedgaps = new BitSet(); + expectedgaps.set(2, 5); + expectedgaps.set(6, 9); + + assertEquals(6, expectedgaps.cardinality()); + + assertEquals("getInsertionsAsBits didn't mark expected number of gaps", + 6, gapfield.cardinality()); + + assertEquals("getInsertionsAsBits not correct.", expectedgaps, gapfield); } @Test(groups = ("Functional")) diff --git a/test/jalview/viewmodel/ViewportRangesTest.java b/test/jalview/viewmodel/ViewportRangesTest.java index 80bd4db..636f8dd 100644 --- a/test/jalview/viewmodel/ViewportRangesTest.java +++ b/test/jalview/viewmodel/ViewportRangesTest.java @@ -25,12 +25,14 @@ public class ViewportRangesTest { AlignmentI smallAl = gen.generate(7, 2, 2, 5, 5); - @BeforeMethod + @BeforeMethod(alwaysRun = true) public void cleanUp() { ColumnSelection sel = new ColumnSelection(); al.getHiddenColumns().revealAllHiddenColumns(sel); al.getHiddenSequences().showAll(null); + smallAl.getHiddenColumns().revealAllHiddenColumns(sel); + smallAl.getHiddenSequences().showAll(null); } @Test(groups = { "Functional" }) @@ -133,6 +135,12 @@ public class ViewportRangesTest { ViewportRanges vrsmall = new ViewportRanges(smallAl); vrsmall.setStartEndRes(al.getWidth(), al.getWidth()); assertEquals(vrsmall.getEndRes(), 6); + + // make visible alignment width = 0 + smallAl.getHiddenColumns().hideColumns(0, 6); + vrsmall.setStartEndRes(0, 4); + assertEquals(vrsmall.getStartRes(), 0); + assertEquals(vrsmall.getEndRes(), 0); } @Test(groups = { "Functional" }) @@ -149,6 +157,14 @@ public class ViewportRangesTest { vr.setStartEndSeq(al.getHeight(), al.getHeight()); assertEquals(vr.getEndSeq(), al.getHeight() - 1); + + // make visible alignment height = 0 + smallAl.getHiddenSequences().hideSequence(smallAl.getSequenceAt(0)); + smallAl.getHiddenSequences().hideSequence(smallAl.getSequenceAt(0)); + ViewportRanges vrsmall = new ViewportRanges(smallAl); + vrsmall.setStartEndSeq(0, 3); + assertEquals(vrsmall.getStartSeq(), 0); + assertEquals(vrsmall.getEndSeq(), 0); } @Test(groups = { "Functional" })