import jalview.renderer.ResidueShader;
import jalview.renderer.ResidueShaderI;
import jalview.schemes.ColourSchemeI;
+import jalview.util.MessageManager;
+import jalview.workers.InformationThread;
import java.awt.Color;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class SequenceGroup implements AnnotatedCollectionI
{
// TODO ideally this event notification functionality should be separated into
- // a
- // subclass of ViewportProperties similarly to ViewportRanges. Done here as
- // quick fix for JAL-2665
+ // a subclass of ViewportProperties similarly to ViewportRanges.
+ // Done here as a quick fix for JAL-2665
public static final String SEQ_GROUP_CHANGED = "Sequence group changed";
-
+
protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
this);
String groupName;
String description;
-
+
Conservation conserve;
+ Conservation conservationData;
+
+ ProfilesI consensusProfiles;
+
+ ProfilesI hmmProfiles;
+
boolean displayBoxes = true;
boolean displayText = true;
boolean colourText = false;
- /**
- * True if the group is defined as a group on the alignment, false if it is
- * just a selection.
+ /*
+ * true if the group is defined as a group on the alignment, false if it is
+ * just a selection
*/
boolean isDefined = false;
- /**
+ /*
* after Olivier's non-conserved only character display
*/
boolean showNonconserved = false;
- /**
- * group members
+ /*
+ * sequences in the group
*/
- private List<SequenceI> sequences = new ArrayList<>();
+ private List<SequenceI> sequences;
- /**
+ /*
* representative sequence for this group (if any)
*/
private SequenceI seqrep = null;
int width = -1;
- /**
- * Colourscheme applied to group if any
+ /*
+ * colour scheme applied to group if any
*/
public ResidueShaderI cs;
- // start column (base 0)
+ /**
+ * start column (base 0)
+ */
private int startRes = 0;
- // end column (base 0)
- int endRes = 0;
+ /**
+ * end column (base 0)
+ */
+ private int endRes = 0;
public Color outlineColour = Color.black;
public Color textColour2 = Color.white;
- /**
- * consensus calculation property
+ /*
+ * properties for consensus annotation
*/
private boolean ignoreGapsInConsensus = true;
- /**
- * consensus calculation property
- */
private boolean showSequenceLogo = false;
- /**
- * flag indicating if logo should be rendered normalised
- */
private boolean normaliseSequenceLogo;
/*
+ * properties for HMM information annotation
+ */
+ private boolean hmmIgnoreBelowBackground = true;
+
+ private boolean hmmUseInfoLetterHeight;
+
+ private boolean hmmShowSequenceLogo;
+
+ private boolean hmmNormaliseSequenceLogo;
+
+ private boolean hmmShowHistogram;
+
+ /*
* visibility of rows or represented rows covered by group
*/
private boolean hidereps = false;
/*
* visibility of columns intersecting this group
*/
- private boolean hidecols = false;
+ private boolean hidecols;
AlignmentAnnotation consensus = null;
AlignmentAnnotation conservation = null;
+ private AlignmentAnnotation hmmInformation;
+
private boolean showConsensusHistogram;
-
+
private AnnotatedCollectionI context;
+
/**
- * Creates a new SequenceGroup object.
+ * Constructor, assigning a generated default name of "JGroup:" with object
+ * hashcode appended
*/
public SequenceGroup()
{
groupName = "JGroup:" + this.hashCode();
cs = new ResidueShader();
+ sequences = new ArrayList<>();
}
/**
* copy constructor
*
* @param seqsel
+ * @param keepsequences
+ * if false do not add sequences from seqsel to new instance
*/
public SequenceGroup(SequenceGroup seqsel)
{
this();
+
if (seqsel != null)
{
sequences = new ArrayList<>();
showSequenceLogo = seqsel.showSequenceLogo;
normaliseSequenceLogo = seqsel.normaliseSequenceLogo;
showConsensusHistogram = seqsel.showConsensusHistogram;
+ hmmShowSequenceLogo = seqsel.hmmShowSequenceLogo;
+ hmmNormaliseSequenceLogo = seqsel.hmmNormaliseSequenceLogo;
+ hmmShowHistogram = seqsel.hmmShowHistogram;
idColour = seqsel.idColour;
outlineColour = seqsel.outlineColour;
seqrep = seqsel.seqrep;
thresholdTextColour = seqsel.thresholdTextColour;
width = seqsel.width;
ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus;
+ hmmIgnoreBelowBackground = seqsel.hmmIgnoreBelowBackground;
+ hmmUseInfoLetterHeight = seqsel.hmmUseInfoLetterHeight;
if (seqsel.conserve != null)
{
+ // todo avoid doing this if we don't actually want derived calculations
+ // !
recalcConservation(); // safer than
// aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
}
}
}
+ /**
+ * Constructor that copies the given list of sequences
+ *
+ * @param seqs
+ */
+ public SequenceGroup(List<SequenceI> seqs)
+ {
+ this();
+ this.sequences.addAll(seqs);
+ }
+
public boolean isShowSequenceLogo()
{
return showSequenceLogo;
}
/**
- * calculate residue conservation for group - but only if necessary. returns
- * true if the calculation resulted in a visible change to group
+ * Recalculates column consensus, conservation, and HMM annotation for the
+ * group (as applicable). Returns true if the calculation resulted in a
+ * visible change to group.
*
* @param defer
* when set, colourschemes for this group are not refreshed after
*/
public boolean recalcConservation(boolean defer)
{
- if (cs == null && consensus == null && conservation == null)
+ if (cs == null && consensus == null && conservation == null
+ && hmmInformation == null)
{
return false;
}
{
ProfilesI cnsns = AAFrequency.calculate(sequences, startRes,
endRes + 1, showSequenceLogo);
+ if (hmmInformation != null)
+ {
+ HiddenMarkovModel hmm = hmmInformation.sequenceRef.getHMM();
+
+ ProfilesI info = AAFrequency.calculateHMMProfiles(hmm,
+ (endRes + 1) - startRes, startRes, endRes + 1,
+ hmmIgnoreBelowBackground, hmmUseInfoLetterHeight);
+ _updateInformationRow(info);
+ upd = true;
+ }
if (consensus != null)
{
_updateConsensusRow(cnsns, sequences.size());
}
/**
+ * Recalculates the information content on the HMM annotation
+ *
+ * @param cnsns
+ */
+ private void _updateInformationRow(ProfilesI cnsns)
+ {
+ if (hmmInformation == null)
+ {
+ createInformationAnnotation();
+ }
+ hmmInformation.description = MessageManager
+ .getString("label.information_description");
+ setHmmProfiles(cnsns);
+ // preserve width if already set
+ int aWidth = (hmmInformation.annotations != null)
+ ? (endRes < hmmInformation.annotations.length
+ ? hmmInformation.annotations.length : endRes + 1)
+ : endRes + 1;
+ hmmInformation.annotations = null;
+ hmmInformation.annotations = new Annotation[aWidth]; // should be alignment
+ // width
+ hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
+ AAFrequency.completeInformation(hmmInformation, cnsns, startRes,
+ endRes + 1);
+ }
+
+ /**
* @param s
* sequence to either add or remove from group
* @param recalc
/**
* Set the first column selected by this group. Runs from 0<=i<N_cols
*
- * @param i
+ * @param newStart
*/
public void setStartRes(int newStart)
{
- newStart= Math.max(0,newStart); // sanity check for negative start column positions
- int before = startRes;
- startRes = newStart;
-
+ int before = startRes;
+ startRes= Math.max(0,newStart); // sanity check for negative start column positions
changeSupport.firePropertyChange(SEQ_GROUP_CHANGED, before, startRes);
}
/**
+ * Creates the Hidden Markov Model annotation for this group
+ */
+ void createInformationAnnotation()
+ {
+ hmmInformation = new AlignmentAnnotation("", "", new Annotation[1], 0f,
+ 6.25f, AlignmentAnnotation.BAR_GRAPH);
+ hmmInformation.hasText = true;
+ hmmInformation.autoCalculated = false;
+ hmmInformation.groupRef = this;
+ hmmInformation.label = getName();
+ hmmInformation.description = MessageManager
+ .getString("label.information_description");
+ hmmInformation.setCalcId(InformationThread.HMM_CALC_ID);
+ }
+
+ /**
* set this alignmentAnnotation object as the one used to render consensus
* annotation
*
{
if (consensus.annotations[i] != null)
{
- if (consensus.annotations[i].description.charAt(0) == '[')
+ String desc = consensus.annotations[i].description;
+ if (desc.length() > 1 && desc.charAt(0) == '[')
{
- seqs.append(consensus.annotations[i].description.charAt(1));
+ seqs.append(desc.charAt(1));
}
else
{
return ignoreGapsInConsensus;
}
+ public void setIgnoreBelowBackground(boolean state)
+ {
+ hmmIgnoreBelowBackground = state;
+ }
+
+ public boolean isIgnoreBelowBackground()
+ {
+ return hmmIgnoreBelowBackground;
+ }
+
+ public void setInfoLetterHeight(boolean state)
+ {
+ hmmUseInfoLetterHeight = state;
+ }
+
+ public boolean isUseInfoLetterHeight()
+ {
+ return hmmUseInfoLetterHeight;
+ }
+
/**
* @param showSequenceLogo
* indicates if a sequence logo is shown for consensus annotation
@Override
public Iterable<AlignmentAnnotation> findAnnotation(String calcId)
{
- List<AlignmentAnnotation> aa = new ArrayList<>();
- if (calcId == null)
- {
- return aa;
- }
- for (AlignmentAnnotation a : getAlignmentAnnotation())
- {
- if (calcId.equals(a.getCalcId()))
- {
- aa.add(a);
- }
- }
- return aa;
+ return AlignmentAnnotation.findAnnotation(
+ Arrays.asList(getAlignmentAnnotation()), calcId);
}
@Override
public Iterable<AlignmentAnnotation> findAnnotations(SequenceI seq,
String calcId, String label)
{
- ArrayList<AlignmentAnnotation> aa = new ArrayList<>();
- for (AlignmentAnnotation ann : getAlignmentAnnotation())
- {
- if ((calcId == null || (ann.getCalcId() != null
- && ann.getCalcId().equals(calcId)))
- && (seq == null || (ann.sequenceRef != null
- && ann.sequenceRef == seq))
- && (label == null
- || (ann.label != null && ann.label.equals(label))))
- {
- aa.add(ann);
- }
- }
- return aa;
+ return AlignmentAnnotation.findAnnotations(
+ Arrays.asList(getAlignmentAnnotation()), seq, calcId, label);
}
/**
*/
public boolean hasAnnotation(String calcId)
{
- if (calcId != null && !"".equals(calcId))
- {
- for (AlignmentAnnotation a : getAlignmentAnnotation())
- {
- if (a.getCalcId() == calcId)
- {
- return true;
- }
- }
- }
- return false;
+ return AlignmentAnnotation
+ .hasAnnotation(Arrays.asList(getAlignmentAnnotation()), calcId);
}
/**
{
return (startRes <= apos && endRes >= apos) && sequences.contains(seq);
}
+
+ public boolean isShowInformationHistogram()
+ {
+ return hmmShowHistogram;
+ }
+
+ public void setShowInformationHistogram(boolean state)
+ {
+ if (hmmShowHistogram != state && hmmInformation != null)
+ {
+ this.hmmShowHistogram = state;
+ // recalcConservation(); TODO don't know what to do here next
+ }
+ this.hmmShowHistogram = state;
+ }
+
+ public boolean isShowHMMSequenceLogo()
+ {
+ return hmmShowSequenceLogo;
+ }
+
+ public void setShowHMMSequenceLogo(boolean state)
+ {
+ hmmShowSequenceLogo = state;
+ }
+
+ public boolean isNormaliseHMMSequenceLogo()
+ {
+ return hmmNormaliseSequenceLogo;
+ }
+
+ public void setNormaliseHMMSequenceLogo(boolean state)
+ {
+ hmmNormaliseSequenceLogo = state;
+ }
+
+ public ProfilesI getConsensusData()
+ {
+ return consensusProfiles;
+ }
+
+ public ProfilesI getHmmProfiles()
+ {
+ return hmmProfiles;
+ }
+
+ public void setHmmProfiles(ProfilesI hmmProfiles)
+ {
+ this.hmmProfiles = hmmProfiles;
+ }
+
+ @Override
+ public List<SequenceI> getHmmSequences()
+ {
+ List<SequenceI> result = new ArrayList<>();
+ for (int i = 0; i < sequences.size(); i++)
+ {
+ SequenceI seq = sequences.get(i);
+ if (seq.hasHMMProfile())
+ {
+ result.add(seq);
+ }
+ }
+ return result;
+ }
+
}