From 60230a4056ce0094603ca68c6596129e1048dc85 Mon Sep 17 00:00:00 2001 From: tzvanaalten Date: Mon, 28 Aug 2017 17:21:39 +0100 Subject: [PATCH] JAL-2629 add option to set HMM Logo letter height to info content --- resources/lang/Messages.properties | 1 + src/jalview/analysis/AAFrequency.java | 39 +++++++++++++++-------- src/jalview/api/AlignViewportI.java | 2 ++ src/jalview/datamodel/SequenceGroup.java | 24 ++++++++++++-- src/jalview/gui/AnnotationLabels.java | 44 ++++++++++++++++++++++++-- src/jalview/renderer/AnnotationRenderer.java | 5 +-- src/jalview/viewmodel/AlignmentViewport.java | 18 +++++++++++ src/jalview/workers/InformationThread.java | 15 +++++---- test/jalview/analysis/AAFrequencyTest.java | 14 ++++---- 9 files changed, 127 insertions(+), 35 deletions(-) diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index be20995..b5547cb 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1379,3 +1379,4 @@ label.alignment = Alignment label.groups_and_alignment = All groups and alignment label.groups = All groups label.selected_group = Selected group +label.use_info_for_height = Use Information Content as Letter Height diff --git a/src/jalview/analysis/AAFrequency.java b/src/jalview/analysis/AAFrequency.java index 72b3d00..cc4f8f4 100755 --- a/src/jalview/analysis/AAFrequency.java +++ b/src/jalview/analysis/AAFrequency.java @@ -224,7 +224,7 @@ public class AAFrequency */ public static ProfilesI calculateHMMProfiles(final HiddenMarkovModel hmm, int width, int start, int end, boolean saveFullProfile, - boolean removeBelowBackground) + boolean removeBelowBackground, boolean infoLetterHeight) { ProfileI[] result = new ProfileI[width]; int symbolCount = hmm.getNumberOfSymbols(); @@ -234,7 +234,7 @@ public class AAFrequency for (char symbol : hmm.getSymbols()) { int value = getAnalogueCount(hmm, column, symbol, - removeBelowBackground); + removeBelowBackground, infoLetterHeight); counts.put(symbol, value); } int maxCount = counts.getModalCount(); @@ -372,10 +372,9 @@ public class AAFrequency * @param nseq * number of sequences */ - public static void completeInformation(AlignmentAnnotation information, - ProfilesI profiles, int startCol, int endCol, - boolean ignoreBelowBackground, - boolean showSequenceLogo, long nseq) + public static float completeInformation(AlignmentAnnotation information, + ProfilesI profiles, int startCol, int endCol, long nseq, + Float currentMax) { // long now = System.currentTimeMillis(); if (information == null || information.annotations == null @@ -385,7 +384,7 @@ public class AAFrequency * called with a bad alignment annotation row * wait for it to be initialised properly */ - return; + return 0; } Float max = 0f; @@ -400,7 +399,7 @@ public class AAFrequency * shorter than alignment width */ information.annotations[i] = null; - return; + return 0; } HiddenMarkovModel hmm; @@ -422,9 +421,16 @@ public class AAFrequency .toUpperCase(hmm.getConsensusAtAlignColumn(i))), description, ' ', value); } - information.graphMax = max; - // long elapsed = System.currentTimeMillis() - now; - // System.out.println(-elapsed); + if (max > currentMax) + { + information.graphMax = max; + return max; + } + else + { + information.graphMax = currentMax; + return currentMax; + } } /** @@ -898,7 +904,7 @@ public class AAFrequency * @return */ public static int[] extractHMMProfile(HiddenMarkovModel hmm, int column, - boolean removeBelowBackground) + boolean removeBelowBackground, boolean infoHeight) { if (hmm != null) @@ -914,7 +920,7 @@ public class AAFrequency char symbol = charList.get(i); symbols[i] = symbol; int value = getAnalogueCount(hmm, column, symbol, - removeBelowBackground); + removeBelowBackground, infoHeight); values[i] = value; totalCount += value; } @@ -967,7 +973,7 @@ public class AAFrequency * @return */ static int getAnalogueCount(HiddenMarkovModel hmm, int column, - char symbol, boolean removeBelowBackground) + char symbol, boolean removeBelowBackground, boolean infoHeight) { Double value; @@ -981,6 +987,11 @@ public class AAFrequency return 0; } + if (infoHeight) + { + value = value * (Math.log(value / freq) / Math.log(2)); + } + value = value * 10000; return Math.round(value.floatValue()); } diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index a35c2a4..96f9313 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -529,4 +529,6 @@ public interface AlignViewportI extends ViewStyleI */ void updateInformation(AlignmentViewPanel ap); + boolean isInfoLetterHeight(); + } diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java index 6db870c..c1443ef 100755 --- a/src/jalview/datamodel/SequenceGroup.java +++ b/src/jalview/datamodel/SequenceGroup.java @@ -103,6 +103,8 @@ public class SequenceGroup implements AnnotatedCollectionI private boolean ignoreBelowBackground = true; + private boolean infoLetterHeight = false; + /** * consensus calculation property */ @@ -222,6 +224,7 @@ public class SequenceGroup implements AnnotatedCollectionI width = seqsel.width; ignoreGapsInConsensus = seqsel.ignoreGapsInConsensus; ignoreBelowBackground = seqsel.ignoreBelowBackground; + infoLetterHeight = seqsel.infoLetterHeight; if (seqsel.conserve != null) { recalcConservation(); // safer than @@ -581,7 +584,8 @@ public class SequenceGroup implements AnnotatedCollectionI ProfilesI info = AAFrequency.calculateHMMProfiles(hmm, (endRes + 1) - startRes, startRes, endRes + 1, - showHMMSequenceLogo, ignoreBelowBackground); + showHMMSequenceLogo, ignoreBelowBackground, + infoLetterHeight); _updateInformationRow(info, sequences.size()); upd = true; } @@ -711,7 +715,7 @@ public class SequenceGroup implements AnnotatedCollectionI // width information.calcId = "HMM"; AAFrequency.completeInformation(information, cnsns, startRes, - endRes + 1, ignoreBelowBackground, showSequenceLogo, nseq); // TODO: + endRes + 1, nseq, 0f); // TODO: // setting // container // for @@ -1291,7 +1295,21 @@ public class SequenceGroup implements AnnotatedCollectionI public boolean getIgnoreBelowBackground() { - return true; + return ignoreBelowBackground; + } + + public void setInfoLetterHeight(boolean state) + { + if (this.infoLetterHeight != state) + { + infoLetterHeight = state; + } + infoLetterHeight = state; + } + + public boolean getInfoLetterHeight() + { + return infoLetterHeight; } /** diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index f1d8d7c..f7b48fa 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -624,21 +624,59 @@ public class AnnotationLabels extends JPanel @Override public void actionPerformed(ActionEvent e) { + + if (aaa.groupRef != null) + { + // TODO: pass on reference to ap so the view can be updated. + if (aaa.groupRef.getInfoLetterHeight() == false) + { + aaa.groupRef.setIgnoreBelowBackground(cbmi.getState()); + ap.getAnnotationPanel() + .paint(ap.getAnnotationPanel().getGraphics()); + } + } + else if (ap.av.isInfoLetterHeight() == false) + { + ap.av.setIgnoreBelowBackground(cbmi.getState(), ap); + } + ap.alignmentChanged(); + } + }); + pop.add(cbmi); + final JCheckBoxMenuItem letteHeight = new JCheckBoxMenuItem( + MessageManager.getString("label.use_info_for_height"), + (aa[selectedRow].groupRef != null) + ? aa[selectedRow].groupRef.getInfoLetterHeight() + : ap.av.isInfoLetterHeight()); + + letteHeight.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { if (aaa.groupRef != null) { // TODO: pass on reference to ap so the view can be updated. - aaa.groupRef.setIgnoreBelowBackground(cbmi.getState()); + aaa.groupRef.setInfoLetterHeight((letteHeight.getState())); + if (aaa.groupRef.getIgnoreBelowBackground() == false) + { + aaa.groupRef.setIgnoreBelowBackground(true); + } ap.getAnnotationPanel() .paint(ap.getAnnotationPanel().getGraphics()); } else { - ap.av.setIgnoreBelowBackground(cbmi.getState(), ap); + ap.av.setInfoLetterHeight(letteHeight.getState(), ap); + if (ap.av.isIgnoreBelowBackground() == false) + { + ap.av.setIgnoreBelowBackground(true, ap); + } } ap.alignmentChanged(); } }); - pop.add(cbmi); + pop.add(letteHeight); if (aaa.groupRef != null) { final JCheckBoxMenuItem chist = new JCheckBoxMenuItem( diff --git a/src/jalview/renderer/AnnotationRenderer.java b/src/jalview/renderer/AnnotationRenderer.java index 6099a85..70d534c 100644 --- a/src/jalview/renderer/AnnotationRenderer.java +++ b/src/jalview/renderer/AnnotationRenderer.java @@ -75,7 +75,7 @@ public class AnnotationRenderer av_normaliseProfile = false; boolean av_renderInformationHistogram = true, av_renderHMMProfile = true, - av_normaliseHMMProfile = false; + av_normaliseHMMProfile = false, av_infoHeight = false; ResidueShaderI profcolour = null; @@ -353,6 +353,7 @@ public class AnnotationRenderer hStrucConsensus = av.getRnaStructureConsensusHash(); av_ignoreGapsConsensus = av.isIgnoreGapsConsensus(); av_ignoreBelowBackground = av.isIgnoreBelowBackground(); + av_infoHeight = av.isInfoLetterHeight(); } @@ -376,7 +377,7 @@ public class AnnotationRenderer { HiddenMarkovModel hmm = aa.sequenceRef.getHMM(); return AAFrequency.extractHMMProfile(hmm, column, - av_ignoreBelowBackground); // TODO check if this follows standard + av_ignoreBelowBackground, av_infoHeight); // TODO check if this follows standard // pipeline } if (aa.autoCalculated diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index ad12035..c2c2c5d 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -616,6 +616,8 @@ public abstract class AlignmentViewport protected boolean ignoreBelowBackGroundFrequencyCalculation = false; + protected boolean infoLetterHeight = false; + protected ResidueShaderI residueShading = new ResidueShader(); @Override @@ -1370,6 +1372,16 @@ public abstract class AlignmentViewport } + public void setInfoLetterHeight(boolean b, AlignmentViewPanel ap) + { + infoLetterHeight = b; + if (ap != null) + { + updateInformation(ap); + } + + } + private long sgrouphash = -1, colselhash = -1; /** @@ -1430,6 +1442,12 @@ public abstract class AlignmentViewport return ignoreBelowBackGroundFrequencyCalculation; } + @Override + public boolean isInfoLetterHeight() + { + return infoLetterHeight; + } + // property change stuff // JBPNote Prolly only need this in the applet version. private PropertyChangeSupport changeSupport = new PropertyChangeSupport( diff --git a/src/jalview/workers/InformationThread.java b/src/jalview/workers/InformationThread.java index 2c92879..2abfc69 100644 --- a/src/jalview/workers/InformationThread.java +++ b/src/jalview/workers/InformationThread.java @@ -15,6 +15,9 @@ import java.util.List; public class InformationThread extends AlignCalcWorker { + + Float max = 0f; + /** * Constructor for information thread. * @@ -125,11 +128,13 @@ public class InformationThread extends AlignCalcWorker int width = alignment.getWidth(); List hmmSeqs = alignment.getHMMConsensusSequences(false); int index = 0; + for (SequenceI seq : hmmSeqs) { HiddenMarkovModel hmm = seq.getHMM(); ProfilesI hinformation = AAFrequency.calculateHMMProfiles(hmm, width, - 0, width, true, alignViewport.isIgnoreBelowBackground()); + 0, width, true, alignViewport.isIgnoreBelowBackground(), + alignViewport.isInfoLetterHeight()); alignViewport.setSequenceInformationHash(hinformation, index); // setColourSchemeInformation(hinformation); index++; @@ -219,12 +224,10 @@ public class InformationThread extends AlignCalcWorker protected void deriveInformation( AlignmentAnnotation informationAnnotation, ProfilesI hinformation) { - long nseq = getSequences().length; - AAFrequency.completeInformation(informationAnnotation, hinformation, - hinformation.getStartColumn(), hinformation.getEndColumn() + 1, - alignViewport.isIgnoreBelowBackground(), - alignViewport.isShowHMMSequenceLogo(), nseq); + max = AAFrequency.completeInformation(informationAnnotation, + hinformation, hinformation.getStartColumn(), + hinformation.getEndColumn() + 1, nseq, max); } diff --git a/test/jalview/analysis/AAFrequencyTest.java b/test/jalview/analysis/AAFrequencyTest.java index 9a0b33f..05a72e4 100644 --- a/test/jalview/analysis/AAFrequencyTest.java +++ b/test/jalview/analysis/AAFrequencyTest.java @@ -254,7 +254,7 @@ public class AAFrequencyTest "test/jalview/io/test_MADE1_hmm.txt", DataSourceType.FILE)); hmm = hmmFile.getHMM(); int[] expected = { 0, 4, 100, 'T', 71, 'C', 12, 'G', 9, 'A', 9 }; - int[] actual = AAFrequency.extractHMMProfile(hmm, 17, false); + int[] actual = AAFrequency.extractHMMProfile(hmm, 17, false, false); for (int i = 0; i < actual.length; i++) { if (i == 2) @@ -268,7 +268,7 @@ public class AAFrequencyTest } int[] expected2 = { 0, 4, 100, 'A', 85, 'C', 0, 'G', 0, 'T', 0 }; - int[] actual2 = AAFrequency.extractHMMProfile(hmm, 2, true); + int[] actual2 = AAFrequency.extractHMMProfile(hmm, 2, true, false); for (int i = 0; i < actual2.length; i++) { if (i == 2) @@ -281,18 +281,18 @@ public class AAFrequencyTest } } - assertNull(AAFrequency.extractHMMProfile(null, 98978867, true)); + assertNull(AAFrequency.extractHMMProfile(null, 98978867, true, false)); } @Test(groups = { "Functional" }, priority = 2) public void testGetAnalogueCount() { int count; - count = AAFrequency.getAnalogueCount(hmm, 0, 'T', false); + count = AAFrequency.getAnalogueCount(hmm, 0, 'T', false, false); assertEquals(7859, count); - count = AAFrequency.getAnalogueCount(hmm, 20, 'G', false); + count = AAFrequency.getAnalogueCount(hmm, 20, 'G', false, false); assertEquals(7546, count); - count = AAFrequency.getAnalogueCount(hmm, 1077, 'G', true); + count = AAFrequency.getAnalogueCount(hmm, 1077, 'G', true, false); assertEquals(0, count); } @@ -310,7 +310,7 @@ public class AAFrequencyTest seq.setHMM(hmm); AlignmentAnnotation annot = new AlignmentAnnotation("", "", annots); annot.setSequenceRef(seq); - AAFrequency.completeInformation(annot, profs, 0, 1, false, true, 1); + AAFrequency.completeInformation(annot, profs, 0, 1, 1, 1f); float ic = annot.annotations[0].value; assertEquals(0.91532f, ic, 0.0001f); ic = annot.annotations[1].value; -- 1.7.10.2