From 8b55eedb9d76a8c65b80f756c4412bf029906bf7 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Thu, 29 Dec 2016 11:59:17 +0000 Subject: [PATCH] JAL-2630 first pass groovy colour scheme (with slight refactoring) --- examples/groovy/colourSchemes.groovy | 125 ++++++++++++++++++++ src/jalview/gui/UserDefinedColours.java | 68 ++++++----- src/jalview/schemes/ColourSchemes.java | 6 +- src/jalview/schemes/ResidueColourScheme.java | 44 +++++-- src/jalview/schemes/UserColourScheme.java | 23 ++-- test/jalview/schemes/ClustalxColourSchemeTest.java | 46 +++++++ test/jalview/schemes/ResidueColourSchemeTest.java | 6 +- 7 files changed, 262 insertions(+), 56 deletions(-) create mode 100644 examples/groovy/colourSchemes.groovy diff --git a/examples/groovy/colourSchemes.groovy b/examples/groovy/colourSchemes.groovy new file mode 100644 index 0000000..84eabbf --- /dev/null +++ b/examples/groovy/colourSchemes.groovy @@ -0,0 +1,125 @@ +import java.awt.Color; +import jalview.schemes.ResidueColourScheme; +import jalview.schemes.ColourSchemes; +import jalview.datamodel.AnnotatedCollectionI; +import java.util.Map; +import jalview.datamodel.SequenceI; + +/* + * Example script that registers two new alignment colour schemes + */ + +/* + * Class that defines a colour scheme where odd columns are red, + * even numbered columns are blue, and gaps are yellow + */ +class Stripy extends ResidueColourScheme { + Stripy() { } + String getSchemeName() { "stripy" } + Stripy getInstance(AnnotatedCollectionI coll, Map map) { new Stripy() } + Color findColour(char res, int col, SequenceI seq) { + // determine the colour + Color colour = findColour(res, col) + // let Jalview apply conservation or consensus shading + adjustColour(res, col, colour); + } + Color findColour(char res, int col) { + if (res == ' ' || res == '-' || res == '.') + { + Color.yellow + } else if (col % 2 == 0) + { + Color.blue + } else + { + Color.red + } + } +} + +/* + * Class that defines a colour scheme graduated + * (approximately) by amino acid weight + */ +class ByWeight extends ResidueColourScheme { + int min = 75 + int max = 204 + ByWeight() { } + boolean isPeptideSpecific() {true} + String getSchemeName() { "By Weight" } + ByWeight getInstance(AnnotatedCollectionI coll, Map map) { new ByWeight() } + Color makeColour(int weight) { + int i = 255 * (weight - min) / (max - min); + new Color(i, 0, i); + } + Color findColour(char res, int col, SequenceI seq) { + // determine the colour + Color colour = findColour(res, col) + // let Jalview apply any conservation or consensus shading + adjustColour(res, col, colour); + } + Color findColour(char res, int col) { + switch (res) { + case ' ': + case '-': + case '.': + Color.white + break + case 'A': + makeColour(89) + break + case 'R': + makeColour(174) + break + case 'N': + case 'D': + case 'B': + case 'I': + case 'L': + makeColour(132) + break + case 'C': + makeColour(121) + break + case 'Q': + case 'E': + case 'Z': + case 'K': + case 'M': + makeColour(146) + break + case 'G': + makeColour(75) + break + case 'H': + makeColour(155) + break + case 'F': + makeColour(165) + break + case 'P': + makeColour(115) + break + case 'S': + makeColour(105) + break + case 'T': + makeColour(119) + break + case 'W': + makeColour(204) + break + case 'Y': + makeColour(181) + break + case 'V': + makeColour(117) + break + default: + makeColour(150) + } + } +} + +ColourSchemes.instance.registerColourScheme(new Stripy()) +ColourSchemes.instance.registerColourScheme(new ByWeight()) diff --git a/src/jalview/gui/UserDefinedColours.java b/src/jalview/gui/UserDefinedColours.java index b182c28..012edb6 100755 --- a/src/jalview/gui/UserDefinedColours.java +++ b/src/jalview/gui/UserDefinedColours.java @@ -118,7 +118,7 @@ public class UserDefinedColours extends GUserDefinedColours implements if (oldColourScheme instanceof UserColourScheme) { - schemeName.setText(((UserColourScheme) oldColourScheme).getSchemeName()); + schemeName.setText(oldColourScheme.getSchemeName()); if (((UserColourScheme) oldColourScheme).getLowerCaseColours() != null) { caseSensitive.setSelected(true); @@ -174,7 +174,15 @@ public class UserDefinedColours extends GUserDefinedColours implements } } - void resetButtonPanel(boolean caseSensitive) + /** + * Rebuilds the panel with coloured buttons for residues. If not case + * sensitive colours, show 3-letter amino acid code as button text. If case + * sensitive, just show the single letter code, in order to make space for the + * additional buttons. + * + * @param isCaseSensitive + */ + void resetButtonPanel(boolean isCaseSensitive) { buttonPanel.removeAll(); @@ -183,23 +191,13 @@ public class UserDefinedColours extends GUserDefinedColours implements upperCaseButtons = new ArrayList(); } - JButton button; - String label; for (int i = 0; i < 20; i++) { - if (caseSensitive) - { - label = ResidueProperties.aa[i]; - } - else - { - label = ResidueProperties.aa2Triplet.get(ResidueProperties.aa[i]) - .toString(); - } - - button = makeButton(label, ResidueProperties.aa[i], upperCaseButtons, - i); - + String label = isCaseSensitive ? ResidueProperties.aa[i] + : ResidueProperties.aa2Triplet.get(ResidueProperties.aa[i]) + .toString(); + JButton button = makeButton(label, ResidueProperties.aa[i], + upperCaseButtons, i); buttonPanel.add(button); } @@ -208,7 +206,7 @@ public class UserDefinedColours extends GUserDefinedColours implements buttonPanel.add(makeButton("X", "X", upperCaseButtons, 22)); buttonPanel.add(makeButton("Gap", "-", upperCaseButtons, 23)); - if (!caseSensitive) + if (!isCaseSensitive) { gridLayout.setRows(6); gridLayout.setColumns(4); @@ -228,14 +226,14 @@ public class UserDefinedColours extends GUserDefinedColours implements { int row = i / cols + 1; int index = (row * cols) + i; - button = makeButton(ResidueProperties.aa[i].toLowerCase(), + JButton button = makeButton(ResidueProperties.aa[i].toLowerCase(), ResidueProperties.aa[i].toLowerCase(), lowerCaseButtons, i); buttonPanel.add(button, index); } } - if (caseSensitive) + if (isCaseSensitive) { buttonPanel.add(makeButton("b", "b", lowerCaseButtons, 20)); buttonPanel.add(makeButton("z", "z", lowerCaseButtons, 21)); @@ -246,7 +244,7 @@ public class UserDefinedColours extends GUserDefinedColours implements // codes if (this.frame != null) { - int newWidth = caseSensitive ? MY_FRAME_WIDTH_CASE_SENSITIVE + int newWidth = isCaseSensitive ? MY_FRAME_WIDTH_CASE_SENSITIVE : MY_FRAME_WIDTH; this.frame.setSize(newWidth, this.frame.getHeight()); } @@ -430,15 +428,14 @@ public class UserDefinedColours extends GUserDefinedColours implements buttons.add(button); + /* + * make initial button colour that of the current colour scheme, + * if it is a simple per-residue colouring, else white + */ col = Color.white; - if (oldColourScheme != null) + if (oldColourScheme != null && oldColourScheme.isSimple()) { - try - { - col = oldColourScheme.findColour(residue.charAt(0), -1, null); - } catch (Exception ex) - { - } + col = oldColourScheme.findColour(residue.charAt(0)); } } @@ -720,7 +717,8 @@ public class UserDefinedColours extends GUserDefinedColours implements Cache.getProperty(LAST_DIRECTORY), "jc", "Jalview User Colours"); - chooser.setFileView(new JalviewFileView()); + JalviewFileView fileView = new JalviewFileView(); + chooser.setFileView(fileView); chooser.setDialogTitle(MessageManager .getString("label.save_colour_scheme")); chooser.setToolTipText(MessageManager.getString("action.save")); @@ -729,9 +727,9 @@ public class UserDefinedColours extends GUserDefinedColours implements if (value == JalviewFileChooser.APPROVE_OPTION) { - String choice = chooser.getSelectedFile().getPath(); - addNewColourScheme(choice); - saveToFile(choice); + File file = chooser.getSelectedFile(); + addNewColourScheme(file.getPath()); + saveToFile(file); } } @@ -779,9 +777,9 @@ public class UserDefinedColours extends GUserDefinedColours implements /** * Saves the colour scheme to file in XML format * - * @param filePath + * @param path */ - protected void saveToFile(String filePath) + protected void saveToFile(File toFile) { /* * build a Java model of colour scheme as XML, and @@ -792,7 +790,7 @@ public class UserDefinedColours extends GUserDefinedColours implements try { PrintWriter out = new PrintWriter(new OutputStreamWriter( - new FileOutputStream(filePath), "UTF-8")); + new FileOutputStream(toFile), "UTF-8")); for (int i = 0; i < buttonPanel.getComponentCount(); i++) { diff --git a/src/jalview/schemes/ColourSchemes.java b/src/jalview/schemes/ColourSchemes.java index 5707876..817fb01 100644 --- a/src/jalview/schemes/ColourSchemes.java +++ b/src/jalview/schemes/ColourSchemes.java @@ -6,6 +6,7 @@ import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceI; import java.awt.Color; +import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.LinkedHashMap; @@ -182,14 +183,15 @@ public class ColourSchemes * definition of residue colours in XML format as defined in * JalviewUserColours.xsd. * - * @param file + * @param filePath * * @return */ - public static UserColourScheme loadColourScheme(String file) + public static UserColourScheme loadColourScheme(String filePath) { UserColourScheme ucs = null; Color[] newColours = null; + File file = new File(filePath); try { InputStreamReader in = new InputStreamReader( diff --git a/src/jalview/schemes/ResidueColourScheme.java b/src/jalview/schemes/ResidueColourScheme.java index 57f7d57..7dbcced 100755 --- a/src/jalview/schemes/ResidueColourScheme.java +++ b/src/jalview/schemes/ResidueColourScheme.java @@ -134,24 +134,46 @@ public abstract class ResidueColourScheme implements ColourSchemeI @Override public Color findColour(char c, int j, SequenceI seq) { - Color currentColour; + Color colour = Color.white; - if (colors != null && symbolIndex != null && (threshold == 0) - || aboveThreshold(c, j)) + if (colors != null && symbolIndex != null) { - currentColour = colors[symbolIndex[c]]; + colour = colors[symbolIndex[c]]; } - else + colour = adjustColour(c, j, colour); + + return colour; + } + + /** + * Adjusts colour by applying thresholding or conservation shading, if in + * force. That is + * + * + * @param symbol + * @param column + * @param colour + * @return + */ + protected Color adjustColour(char symbol, int column, Color colour) + { + if (!aboveThreshold(symbol, column)) { - currentColour = Color.white; + colour = Color.white; } if (conservationColouring) { - currentColour = applyConservation(currentColour, j); + colour = applyConservation(colour, column); } - - return currentColour; + return colour; } /** @@ -195,6 +217,10 @@ public abstract class ResidueColourScheme implements ColourSchemeI */ public boolean aboveThreshold(char residue, int column) { + if (threshold == 0) + { + return true; + } if ('a' <= residue && residue <= 'z') { // TO UPPERCASE !!! diff --git a/src/jalview/schemes/UserColourScheme.java b/src/jalview/schemes/UserColourScheme.java index 09cef92..969e6b3 100755 --- a/src/jalview/schemes/UserColourScheme.java +++ b/src/jalview/schemes/UserColourScheme.java @@ -59,15 +59,24 @@ public class UserColourScheme extends ResidueColourScheme public ColourSchemeI getInstance(AnnotatedCollectionI sg, Map hiddenRepSequences) { - UserColourScheme usc = new UserColourScheme(colors); - if (lowerCaseColours != null) + return new UserColourScheme(this); + } + + /** + * Copy constructor + * + * @return + */ + protected UserColourScheme(UserColourScheme from) + { + this(from.colors); + schemeName = from.schemeName; + if (from.lowerCaseColours != null) { - usc.schemeName = schemeName; - usc.lowerCaseColours = new Color[lowerCaseColours.length]; - System.arraycopy(lowerCaseColours, 0, usc.lowerCaseColours, 0, - lowerCaseColours.length); + lowerCaseColours = new Color[lowerCaseColours.length]; + System.arraycopy(from.lowerCaseColours, 0, lowerCaseColours, 0, + from.lowerCaseColours.length); } - return usc; } /** diff --git a/test/jalview/schemes/ClustalxColourSchemeTest.java b/test/jalview/schemes/ClustalxColourSchemeTest.java index fda7d27..acd2e8d 100644 --- a/test/jalview/schemes/ClustalxColourSchemeTest.java +++ b/test/jalview/schemes/ClustalxColourSchemeTest.java @@ -85,4 +85,50 @@ public class ClustalxColourSchemeTest // TODO more test cases; check if help documentation matches implementation } + + // @formatter:on + + /** + * Test for colour calculation when the consensus percentage ignores gapped + * sequences + */ + @Test(groups = "Functional") + public void testFindColour_ignoreGaps() + { + /* + * CCC + * CCC + * -CC + * first column is 66% C (blue) including gaps + * or 100% C ignoring gaps + */ + String fasta = ">seq1\nCCC\n>seq2\nccc\n>seq3\n-CC\n"; + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(fasta, + DataSourceType.PASTE); + AlignmentI al = af.getViewport().getAlignment(); + ClustalxColourScheme cs = new ClustalxColourScheme(al, null); + + /* + * column 1 is 66% C which is above Clustalx threshold of 60% + */ + Color clustalBlue = ClustalxColourScheme.ClustalColour.BLUE.colour; + assertEquals(cs.findColour('C', 0, al.getSequenceAt(0)), clustalBlue); + + /* + * set directly to ignore gaps + */ + cs.setIncludeGaps(false); + Color clustalPink = ClustalxColourScheme.ClustalColour.PINK.colour; + assertEquals(cs.findColour('C', 0, al.getSequenceAt(0)), clustalPink); + + /* + * set ignore gaps on the viewport... + */ + cs.setIncludeGaps(true); + assertEquals(cs.findColour('C', 0, al.getSequenceAt(0)), clustalBlue); + af.getViewport().setIgnoreGapsConsensus(true, af.alignPanel); + // next test fails: colour scheme does not read ignore gaps flag from + // viewport + // assertEquals(cs.findColour('C', 0, al.getSequenceAt(0)), clustalPink); + } } diff --git a/test/jalview/schemes/ResidueColourSchemeTest.java b/test/jalview/schemes/ResidueColourSchemeTest.java index 077abd4..c3ea385 100644 --- a/test/jalview/schemes/ResidueColourSchemeTest.java +++ b/test/jalview/schemes/ResidueColourSchemeTest.java @@ -81,11 +81,11 @@ public class ResidueColourSchemeTest rcs.setThreshold(0, true); assertTrue(rcs.aboveThreshold('a', 0)); assertTrue(rcs.aboveThreshold('S', 0)); - assertFalse(rcs.aboveThreshold('W', 0)); + assertTrue(rcs.aboveThreshold('W', 0)); assertTrue(rcs.aboveThreshold('R', 1)); - assertFalse(rcs.aboveThreshold('W', 2)); + assertTrue(rcs.aboveThreshold('W', 2)); assertTrue(rcs.aboveThreshold('t', 3)); - assertFalse(rcs.aboveThreshold('Q', 3)); + assertTrue(rcs.aboveThreshold('Q', 3)); /* * with threshold, include gaps -- 1.7.10.2