href="../features/varna.html">VARNA</a>.
</em></li>
<li><a name="hideinserts"><strong>Hide Insertions</strong></a><br />
- <em>Hides columns containing gaps in the current sequence or
- selected region, and reveals columns not including gaps.</em>
+ <em>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)</em></li>
<li><strong>Hide Sequences</strong><br> <em>Hides the
currently selected sequences in this alignment view.</em><strong><br>
</strong></li>
</li>
<li><!-- JAL-1256 -->Trackpad horizontal scroll gesture adjusts start position in wrap mode</li>
<li><!-- JAL-2563 -->Status bar doesn't show positions for ambiguous amino acids</li>
+ <li><!-- JAL-2291 -->Hide insertions in PopUp menu excludes gaps in selection, current sequence and only within selected columns</li>
</ul>
<em>Applet</em>
<ul>
web pages for database cross-references</a> via the UK Elixir's
EMBL-EBI's MIRIAM database and identifiers.org services.
</li>
+ <li><em>Showing and hiding regions</em>
+ <ul>
+ <li><a href="menus/popupMenu.html#hideinserts">Hide
+ insertions</a> in the PopUp menu has changed its behaviour.
+ Prior to 2.10.2, columns were only shown or hidden according
+ to gaps in the sequence under the popup menu. Now, only
+ columns that are gapped in all selected sequences as well as
+ the sequence under the popup menu are hidden, and column
+ visibility outside the selected region is left as is. This
+ makes it easy to filter insertions from the alignment view
+ (just select the region containing insertions to remove)
+ without affecting the rest of the hidden columns.</li>
+ </ul></li>
</ul>
<p>
<strong><a name="experimental">Experimental Features</a></strong>
import jalview.util.ShiftList;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
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);
+ }
+ }
+
}
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
}
@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;
import jalview.datamodel.features.SequenceFeaturesI;
+import java.util.BitSet;
import java.util.List;
import java.util.Vector;
* positions to be invalidated.
*/
void sequenceChanged();
+
+ /**
+ *
+ * @return BitSet corresponding to index [0,length) where Comparison.isGap()
+ * returns true.
+ */
+ BitSet getInsertionsAsBits();
}
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
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}$");
/**
* 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
setOldHiddenColumns(av.getAlignment().getHiddenColumns());
adjusting = true;
- setAnnotations(new JComboBox<String>(getAnnotationItems(false)));
+ setAnnotations(new JComboBox<>(getAnnotationItems(false)));
populateThresholdComboBox(threshold);
AnnotationColumnChooser lastChooser = av
.getAnnotationColumnSelectionState();
}
}
+ // 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();
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;
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();
}
int oldstartres = this.startRes;
if (start > getVisibleAlignmentWidth() - 1)
{
- startRes = getVisibleAlignmentWidth() - 1;
+ startRes = Math.max(getVisibleAlignmentWidth() - 1, 0);
}
else if (start < 0)
{
}
else if (end > getVisibleAlignmentWidth() - 1)
{
- endRes = getVisibleAlignmentWidth() - 1;
+ endRes = Math.max(getVisibleAlignmentWidth() - 1, 0);
}
else
{
int oldstartseq = this.startSeq;
if (start > getVisibleAlignmentHeight() - 1)
{
- startSeq = getVisibleAlignmentHeight() - 1;
+ startSeq = Math.max(getVisibleAlignmentHeight() - 1, 0);
}
else if (start < 0)
{
int oldendseq = this.endSeq;
if (end >= getVisibleAlignmentHeight())
{
- endSeq = getVisibleAlignmentHeight() - 1;
+ endSeq = Math.max(getVisibleAlignmentHeight() - 1, 0);
}
else if (end < 0)
{
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);
}
/*
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))
{
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;
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);
+ }
+ }
}
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.List;
import java.util.Vector;
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"))
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" })
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" })
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" })