package jalview.schemes;
+import jalview.api.AlignViewportI;
import jalview.datamodel.AnnotatedCollectionI;
import jalview.datamodel.HiddenMarkovModel;
-import jalview.datamodel.SequenceCollectionI;
import jalview.datamodel.SequenceI;
import jalview.util.ColorUtils;
import jalview.util.Comparison;
import java.awt.Color;
+import java.util.List;
import java.util.Map;
/**
* Base class for colour schemes based on a selected Hidden Markov Model. The
- * colour is
+ * colour is with reference to an HMM consensus sequence and HMM profile
* <ul>
* <li>white for a gap</li>
- * <li>red for an insertion</li>
+ * <li>red for an insertion (position is gapped in the HMM consensus)</li>
* <li>orange for negative information content</li>
* <li>white to blue for increasing information content</li>
* </ul>
{
private static final Color INSERTION_COLOUR = new Color(230, 0, 0); // reddish
- private HiddenMarkovModel hmm;
+ /*
+ * the aligned HMM consensus sequence to use as reference for colouring
+ */
+ private List<SequenceI> hmmSeqs=null;
+
private Map<Character, Float> frequencies;
/**
- * Constructor given a Hidden Markov Model
+ * Constructor given a list of Hidden Markov Model consensus sequences. The
+ * first sequence provides the HMM profile from which we can read the emission
+ * probabilities that determine the colour.
*
- * @param markov
+ * @param hmmSeqs
*/
- public HmmerColourScheme(HiddenMarkovModel markov)
+ public HmmerColourScheme(List<SequenceI> hmmSeqs)
{
- hmm = markov;
+ this.hmmSeqs = hmmSeqs.isEmpty() ? null : hmmSeqs;
+ }
+
+ protected String getAlphabetType()
+ {
+ if (hmmSeqs!=null) {
+ for (SequenceI hmmSeq : hmmSeqs)
+ {
+ if (hmmSeq != null)
+ {
+ HiddenMarkovModel hmm = hmmSeq.getHMM();
+ if (hmm != null)
+ {
+ return hmm.getAlphabetType();
+ }
+ }
+ }}
+ return ResidueProperties.ALPHABET_AMINO;
}
/**
}
@Override
- public Color findColour(char symbol, int position, SequenceI seq,
+ public Color findColour(char symbol, int column, SequenceI seq,
String consensusResidue, float pid)
{
- return findColour(symbol, position);
+ return findColour(symbol, column);
}
/**
*/
private Color findColour(char symbol, int column)
{
- if (getHmm() == null || Comparison.isGap(symbol))
+ if (hmmSeqs==null || Comparison.isGap(symbol))
{
+ // todo: could return probability of seeing a gap ?
return Color.white;
}
- if (Character.isLowerCase(symbol))
- {
- symbol = Character.toUpperCase(symbol);
- }
-
- double prob = getHmm().getMatchEmissionProbability(column, symbol);
- Float freq = 0f;
-
- if (!frequencies.containsKey(symbol))
- {
- return Color.WHITE;
- }
- else
- {
- freq = frequencies.get(symbol);
- }
- if (prob == 0D)
+ // locate first hmm with a non-gap at this position
+ for (SequenceI hmmSeq:hmmSeqs)
{
- return INSERTION_COLOUR;
+ if (hmmSeq==null)
+ {
+ continue;
+ }
+ if (!Comparison.isGap(hmmSeq.getCharAt(column)))
+ {
+ if (symbol >= 'a')
+ {
+ symbol += 'A' - 'a';
+ }
+
+ final double prob = hmmSeq.getHMM()
+ .getMatchEmissionProbability(column, symbol);
+
+ Float freq = 0f;
+
+ if (!frequencies.containsKey(symbol))
+ {
+ return Color.WHITE;
+ }
+ else
+ {
+ freq = frequencies.get(symbol);
+ }
+
+ /*
+ * Orange if match emission probability is less than background probability
+ */
+ double infoRatio = prob / freq.floatValue();
+ Color colour = Color.ORANGE;
+ if (infoRatio >= 1)
+ {
+ /*
+ * log-scale graduated shade of blue if prob is greater than background
+ */
+ float infoLog = (float) Math.log(infoRatio);
+ colour = ColorUtils.getGraduatedColour(infoLog, 0, Color.WHITE,
+ getMaxInformationScore(), Color.blue);
+ }
+
+ return colour;
+ }
}
- double value = Math.log(prob / freq.floatValue());
- Color colour = null;
- if (value > 0)
- {
- colour = ColorUtils.getGraduatedColour((float) value, 0,
- Color.WHITE, getMaxInformationScore(), Color.blue);
- }
- else if (value < 0)
- {
- return Color.ORANGE;
- }
- return colour;
+ // no more seqs. so
+ return INSERTION_COLOUR;
}
/**
*/
abstract float getMaxInformationScore();
- @Override
- public void alignmentChanged(AnnotatedCollectionI collection,
- Map<SequenceI, SequenceCollectionI> hiddenReps)
- {
- /*
- * ? no need to do anything if alignment is adjusted
- * since findColour() handles everything
- */
- }
-
/**
* Answers a new colour scheme instance based on the HMM of the first sequence
* in ac that has an HMM
*/
@Override
- public ColourSchemeI getInstance(AnnotatedCollectionI ac,
- Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
+ public ColourSchemeI getInstance(AlignViewportI viewport,
+ AnnotatedCollectionI ac)
{
- SequenceI hmmSeq = ac.getHmmConsensus();
- HiddenMarkovModel model = hmmSeq == null ? null : hmmSeq.getHMM();
-
- return newInstance(ac, model);
+ return newInstance(ac);
}
/**
* Answers a new instance of the colour scheme for the given HMM
*
* @param ac
- * @param model
* @return
*/
- protected abstract HmmerColourScheme newInstance(AnnotatedCollectionI ac,
- HiddenMarkovModel model);
+ protected abstract HmmerColourScheme newInstance(AnnotatedCollectionI ac);
@Override
public boolean isSimple()
@Override
public boolean isApplicableTo(AnnotatedCollectionI ac)
{
- return ac.getHmmConsensus() != null;
+ return !ac.getHmmSequences().isEmpty();
}
protected Map<Character, Float> getFrequencies()
{
this.frequencies = frequencies;
}
-
- protected HiddenMarkovModel getHmm()
- {
- return hmm;
- }
-
}