From 17fe82c9d7f9da1b17f3091e4b3992a35e3e411e Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 20 May 2015 14:41:38 +0100 Subject: [PATCH] JAL-1741 nucleotide-aware colour schemes applied to structures --- src/jalview/ext/jmol/JalviewJmolBinding.java | 49 ++++++++--------- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 17 ++---- src/jalview/schemes/ResidueProperties.java | 56 ++++++++++++++++++-- .../structures/models/AAStructureBindingModel.java | 15 ++++-- src/jalview/util/Comparison.java | 34 +++++++++++- test/jalview/schemes/ResiduePropertiesTest.java | 45 ++++++++++++++++ test/jalview/util/ComparisonTest.java | 31 +++++++++-- 7 files changed, 193 insertions(+), 54 deletions(-) diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 68cc792..45fc378 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -20,22 +20,6 @@ */ package jalview.ext.jmol; -import jalview.api.AlignmentViewPanel; -import jalview.api.FeatureRenderer; -import jalview.api.SequenceRenderer; -import jalview.datamodel.AlignmentI; -import jalview.datamodel.ColumnSelection; -import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceI; -import jalview.io.AppletFormatAdapter; -import jalview.schemes.ColourSchemeI; -import jalview.schemes.ResidueProperties; -import jalview.structure.StructureMapping; -import jalview.structure.StructureMappingcommandSet; -import jalview.structure.StructureSelectionManager; -import jalview.structures.models.AAStructureBindingModel; -import jalview.util.MessageManager; - import java.awt.Color; import java.awt.Container; import java.awt.event.ComponentEvent; @@ -44,6 +28,7 @@ import java.io.File; import java.net.URL; import java.security.AccessControlException; import java.util.Hashtable; +import java.util.List; import java.util.Map; import java.util.Vector; @@ -55,6 +40,22 @@ import org.jmol.api.JmolViewer; import org.jmol.constant.EnumCallback; import org.jmol.popup.JmolPopup; +import jalview.api.AlignmentViewPanel; +import jalview.api.FeatureRenderer; +import jalview.api.SequenceRenderer; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceI; +import jalview.io.AppletFormatAdapter; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ResidueProperties; +import jalview.structure.StructureMapping; +import jalview.structure.StructureMappingcommandSet; +import jalview.structure.StructureSelectionManager; +import jalview.structures.models.AAStructureBindingModel; +import jalview.util.MessageManager; + public abstract class JalviewJmolBinding extends AAStructureBindingModel implements JmolStatusListener, JmolSelectionListener, ComponentListener @@ -1327,22 +1328,14 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel return; } - int index; - Color col; jmolHistory(false); - // TODO: Switch between nucleotide or aa selection expressions StringBuilder command = new StringBuilder(128); command.append("select *;color white;"); - for (String res : ResidueProperties.aa3Hash.keySet()) + List residueSet = ResidueProperties.getResidues(isNucleotide(), + false); + for (String res : residueSet) { - index = ResidueProperties.aa3Hash.get(res).intValue(); - if (index > 20) - { - continue; - } - - col = cs.findColour(ResidueProperties.aa[index].charAt(0)); - + Color col = cs.findColour(res.charAt(0)); command.append("select " + res + ";color[" + col.getRed() + "," + col.getGreen() + "," + col.getBlue() + "];"); } diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 543cad4..b649a64 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -1018,23 +1018,16 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel return; } - int index; - Color col; // Chimera expects RBG values in the range 0-1 final double normalise = 255D; viewerCommandHistory(false); - // TODO: Switch between nucleotide or aa selection expressions StringBuilder command = new StringBuilder(128); - command.append("color white;"); - for (String res : ResidueProperties.aa3Hash.keySet()) - { - index = ResidueProperties.aa3Hash.get(res).intValue(); - if (index > 20) - { - continue; - } - col = cs.findColour(ResidueProperties.aa[index].charAt(0)); + List residueSet = ResidueProperties.getResidues(isNucleotide(), + false); + for (String res : residueSet) + { + Color col = cs.findColour(res.charAt(0)); command.append("color " + col.getRed() / normalise + "," + col.getGreen() / normalise + "," + col.getBlue() / normalise + " ::" + res + ";"); diff --git a/src/jalview/schemes/ResidueProperties.java b/src/jalview/schemes/ResidueProperties.java index 0f34824..662a77e 100755 --- a/src/jalview/schemes/ResidueProperties.java +++ b/src/jalview/schemes/ResidueProperties.java @@ -20,10 +20,6 @@ */ package jalview.schemes; -import jalview.analysis.scoremodels.FeatureScoreModel; -import jalview.analysis.scoremodels.PIDScoreModel; -import jalview.api.analysis.ScoreModelI; - import java.awt.Color; import java.util.ArrayList; import java.util.Enumeration; @@ -33,6 +29,10 @@ import java.util.List; import java.util.Map; import java.util.Vector; +import jalview.analysis.scoremodels.FeatureScoreModel; +import jalview.analysis.scoremodels.PIDScoreModel; +import jalview.api.analysis.ScoreModelI; + public class ResidueProperties { public static Hashtable scoreMatrices = new Hashtable(); @@ -1787,4 +1787,52 @@ public class ResidueProperties } // to here + /** + * Returns a list of residue characters for the specified inputs + * + * @param nucleotide + * @param includeAmbiguous + * @return + */ + public static List getResidues(boolean nucleotide, + boolean includeAmbiguous) + { + List result = new ArrayList(); + if (nucleotide) + { + for (String nuc : nucleotideName.keySet()) + { + int val = nucleotideIndex[nuc.charAt(0)]; + if ((!includeAmbiguous && val > 4) || (val >= maxNucleotideIndex)) + { + continue; + } + nuc = nuc.toUpperCase(); + if (!result.contains(nuc)) + { + result.add(nuc); + } + } + } else { + /* + * Peptide + */ + for (String res : aa3Hash.keySet()) + { + int index = aa3Hash.get(res).intValue(); + if ((!includeAmbiguous && index >= 20) || index >= maxProteinIndex) + { + continue; + } + res = res.toUpperCase(); + if (!result.contains(res)) + { + result.add(res); + } + } + } + + return result; + } + } diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index 653ec2d..3602056 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -1,16 +1,17 @@ package jalview.structures.models; +import java.util.ArrayList; +import java.util.List; + import jalview.api.StructureSelectionManagerProvider; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.structure.AtomSpec; import jalview.structure.StructureListener; import jalview.structure.StructureSelectionManager; +import jalview.util.Comparison; import jalview.util.MessageManager; -import java.util.ArrayList; -import java.util.List; - /** * * A base class to hold common function for protein structure model binding. @@ -46,6 +47,8 @@ public abstract class AAStructureBindingModel extends protected boolean colourBySequence = true; + private boolean nucleotide; + /** * Constructor * @@ -74,6 +77,7 @@ public abstract class AAStructureBindingModel extends { this.ssm = ssm; this.sequence = sequenceIs; + this.nucleotide = Comparison.isNucleotide(sequenceIs); this.chains = chains; this.pdbEntry = pdbentry; this.protocol = protocol; @@ -375,8 +379,11 @@ public abstract class AAStructureBindingModel extends } } - // TODO Jmol and Chimera seem to expect pdbFile, javascript listener pdbId protected abstract void highlightAtom(int atomIndex, int pdbResNum, String chain, String pdbFile); + protected boolean isNucleotide() + { + return this.nucleotide; + } } \ No newline at end of file diff --git a/src/jalview/util/Comparison.java b/src/jalview/util/Comparison.java index e224b71..835a1b4 100644 --- a/src/jalview/util/Comparison.java +++ b/src/jalview/util/Comparison.java @@ -20,6 +20,9 @@ */ package jalview.util; +import java.util.ArrayList; +import java.util.List; + import jalview.datamodel.SequenceI; /** @@ -248,7 +251,7 @@ public class Comparison /** * Answers true if more than 85% of the sequence residues (ignoring gaps) are * A, G, C, T or U, else false. This is just a heuristic guess and may give a - * wrong answer (as AGCT are also animo acid codes). + * wrong answer (as AGCT are also amino acid codes). * * @param seqs * @return @@ -263,6 +266,12 @@ public class Comparison int aaCount = 0; for (SequenceI seq : seqs) { + if (seq == null) + { + continue; + } + // TODO could possibly make an informed guess just from the first sequence + // to save a lengthy calculation for (char c : seq.getSequence()) { if ('a' <= c && c <= 'z') @@ -295,4 +304,27 @@ public class Comparison } } + + /** + * Convenience overload of isNucleotide + * + * @param seqs + * @return + */ + public static boolean isNucleotide(SequenceI[][] seqs) + { + if (seqs == null) + { + return false; + } + List flattened = new ArrayList(); + for (SequenceI[] ss : seqs) + { + for (SequenceI s : ss) { + flattened.add(s); + } + } + final SequenceI[] oneDArray = flattened.toArray(new SequenceI[flattened.size()]); + return isNucleotide(oneDArray); + } } diff --git a/test/jalview/schemes/ResiduePropertiesTest.java b/test/jalview/schemes/ResiduePropertiesTest.java index b976e44..b82d338 100644 --- a/test/jalview/schemes/ResiduePropertiesTest.java +++ b/test/jalview/schemes/ResiduePropertiesTest.java @@ -3,6 +3,9 @@ package jalview.schemes; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import java.util.Collections; +import java.util.List; + import org.junit.Test; public class ResiduePropertiesTest @@ -171,4 +174,46 @@ public class ResiduePropertiesTest assertNull(ResidueProperties.codonTranslate("VHD")); assertNull(ResidueProperties.codonTranslate("WSK")); } + + @Test + public void testGetResidues_nucleotide() + { + /* + * Non-ambiguous only; we don't care about the order of the list, it is just + * sorted here to make assertions reliable + */ + List residues = ResidueProperties.getResidues(true, false); + Collections.sort(residues); + assertEquals("[A, C, G, T, U]", residues.toString()); + + /* + * Including ambiguity codes I N R X Y + */ + residues = ResidueProperties.getResidues(true, true); + Collections.sort(residues); + assertEquals("[A, C, G, I, N, R, T, U, X, Y]", residues.toString()); + } + + @Test + public void testGetResidues_peptide() + { + /* + * Non-ambiguous only; we don't care about the order of the list, it is just + * sorted here to make assertions reliable + */ + List residues = ResidueProperties.getResidues(false, false); + Collections.sort(residues); + assertEquals( + "[ALA, ARG, ASN, ASP, CYS, GLN, GLU, GLY, HIS, ILE, LEU, LYS, MET, PHE, PRO, SER, THR, TRP, TYR, VAL]", + residues.toString()); + + /* + * Including ambiguity codes ASX, GLX, XAA + */ + residues = ResidueProperties.getResidues(false, true); + Collections.sort(residues); + assertEquals( + "[ALA, ARG, ASN, ASP, ASX, CYS, GLN, GLU, GLX, GLY, HIS, ILE, LEU, LYS, MET, PHE, PRO, SER, THR, TRP, TYR, VAL, XAA]", + residues.toString()); + } } diff --git a/test/jalview/util/ComparisonTest.java b/test/jalview/util/ComparisonTest.java index bfc2610..57d1ac1 100644 --- a/test/jalview/util/ComparisonTest.java +++ b/test/jalview/util/ComparisonTest.java @@ -3,11 +3,12 @@ package jalview.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import jalview.datamodel.Sequence; -import jalview.datamodel.SequenceI; import org.junit.Test; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; + public class ComparisonTest { @@ -32,6 +33,9 @@ public class ComparisonTest SequenceI seq = new Sequence("eightypercent", "agctuAGCPV"); assertFalse(Comparison.isNucleotide(new SequenceI[] { seq })); + assertFalse(Comparison.isNucleotide(new SequenceI[][] + { new SequenceI[] + { seq } })); seq = new Sequence("eightyfivepercent", "agctuAGCPVagctuAGCUV"); assertFalse(Comparison.isNucleotide(new SequenceI[] @@ -57,17 +61,34 @@ public class ComparisonTest seq = new Sequence("DNA", "ACTugGCCAG"); SequenceI seq2 = new Sequence("Protein", "FLIMVSPTYW"); + /* + * 90% DNA: + */ assertTrue(Comparison.isNucleotide(new SequenceI[] - { seq, seq, seq, seq, seq, seq, seq, seq, seq, seq2 })); // 90% DNA + { seq, seq, seq, seq, seq, seq, seq, seq, seq, seq2 })); + assertTrue(Comparison.isNucleotide(new SequenceI[][] + { new SequenceI[] + { seq }, new SequenceI[] + { seq, seq, seq }, new SequenceI[] + { seq, seq, seq, seq, seq, seq2 } })); + /* + * 80% DNA: + */ assertFalse(Comparison.isNucleotide(new SequenceI[] - { seq, seq, seq, seq, seq, seq, seq, seq, seq2, seq2 })); // 80% DNA + { seq, seq, seq, seq, seq, seq, seq, seq, seq2, seq2 })); + assertFalse(Comparison.isNucleotide(new SequenceI[][] + { new SequenceI[] + { seq }, new SequenceI[] + { seq, seq, seq }, new SequenceI[] + { seq, seq, seq, seq, seq2, seq2, null } })); seq = new Sequence("ProteinThatLooksLikeDNA", "WYATGCCTGAgtcgt"); // 12/14 = 85.7% assertTrue(Comparison.isNucleotide(new SequenceI[] { seq })); - assertFalse(Comparison.isNucleotide(null)); + assertFalse(Comparison.isNucleotide((SequenceI[]) null)); + assertFalse(Comparison.isNucleotide((SequenceI[][]) null)); } /** -- 1.7.10.2