JAL-4019 small but important corrections to array order, and X colour
[jalview.git] / src / jalview / schemes / ResidueProperties.java
index bf30ed6..42d03ec 100755 (executable)
@@ -20,8 +20,6 @@
  */
 package jalview.schemes;
 
-import jalview.analysis.GeneticCodes;
-
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -29,9 +27,12 @@ import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Vector;
 
+import jalview.analysis.GeneticCodes;
+
 public class ResidueProperties
 {
   // Stores residue codes/names and colours and other things
@@ -47,6 +48,8 @@ public class ResidueProperties
 
   public static final Map<String, String> nucleotideName = new HashMap<>();
 
+  public static final Map<String, String> nucleotideAmbiguityName = new HashMap<>();
+
   // lookup from modified amino acid (e.g. MSE) to canonical form (e.g. MET)
   public static final Map<String, String> modifications = new HashMap<>();
 
@@ -117,73 +120,48 @@ public class ResidueProperties
   /**
    * maximum (gap) index for matrices involving nucleotide alphabet
    */
-  public final static int maxNucleotideIndex = 10;
+  // public final static int maxNucleotideIndex = 10;
+  public final static int maxNucleotideIndex;
 
   static
   {
+    String[][] namesArray = { { "a", "Adenine" }, { "c", "Cytosine" },
+        { "g", "Guanine" },
+        { "t", "Thymine" },
+        { "u", "Uracil" },
+        { "i", "Inosine" },
+        { "x", "Xanthine" },
+        { "r", "Unknown Purine" },
+        { "y", "Unknown Pyrimidine" },
+        { "w", "Weak nucleotide (A or T)" },
+        { "s", "Strong nucleotide (G or C)" },
+        { "m", "Amino (A or C)" },
+        { "k", "Keto (G or T)" },
+        { "b", "Not A (G or C or T)" },
+        { "h", "Not G (A or C or T)" },
+        { "d", "Not C (A or G or T)" },
+        { "v", "Not T (A or G or C)" },
+        { "n", "Unknown" } };
+    // "gap" index
+    maxNucleotideIndex = namesArray.length;
+
     nucleotideIndex = new int[255];
     for (int i = 0; i < 255; i++)
     {
-      nucleotideIndex[i] = 10; // non-nucleotide symbols are all non-gap gaps.
+      nucleotideIndex[i] = maxNucleotideIndex; // non-nucleotide symbols are all
+                                               // non-gap gaps.
     }
 
-    nucleotideIndex['A'] = 0;
-    nucleotideIndex['a'] = 0;
-    nucleotideIndex['C'] = 1;
-    nucleotideIndex['c'] = 1;
-    nucleotideIndex['G'] = 2;
-    nucleotideIndex['g'] = 2;
-    nucleotideIndex['T'] = 3;
-    nucleotideIndex['t'] = 3;
-    nucleotideIndex['U'] = 4;
-    nucleotideIndex['u'] = 4;
-    nucleotideIndex['I'] = 5;
-    nucleotideIndex['i'] = 5;
-    nucleotideIndex['X'] = 6;
-    nucleotideIndex['x'] = 6;
-    nucleotideIndex['R'] = 7;
-    nucleotideIndex['r'] = 7;
-    nucleotideIndex['Y'] = 8;
-    nucleotideIndex['y'] = 8;
-    nucleotideIndex['N'] = 9;
-    nucleotideIndex['n'] = 9;
-
-    nucleotideName.put("A", "Adenine");
-    nucleotideName.put("a", "Adenine");
-    nucleotideName.put("G", "Guanine");
-    nucleotideName.put("g", "Guanine");
-    nucleotideName.put("C", "Cytosine");
-    nucleotideName.put("c", "Cytosine");
-    nucleotideName.put("T", "Thymine");
-    nucleotideName.put("t", "Thymine");
-    nucleotideName.put("U", "Uracil");
-    nucleotideName.put("u", "Uracil");
-    nucleotideName.put("I", "Inosine");
-    nucleotideName.put("i", "Inosine");
-    nucleotideName.put("X", "Xanthine");
-    nucleotideName.put("x", "Xanthine");
-    nucleotideName.put("R", "Unknown Purine");
-    nucleotideName.put("r", "Unknown Purine");
-    nucleotideName.put("Y", "Unknown Pyrimidine");
-    nucleotideName.put("y", "Unknown Pyrimidine");
-    nucleotideName.put("N", "Unknown");
-    nucleotideName.put("n", "Unknown");
-    nucleotideName.put("W", "Weak nucleotide (A or T)");
-    nucleotideName.put("w", "Weak nucleotide (A or T)");
-    nucleotideName.put("S", "Strong nucleotide (G or C)");
-    nucleotideName.put("s", "Strong nucleotide (G or C)");
-    nucleotideName.put("M", "Amino (A or C)");
-    nucleotideName.put("m", "Amino (A or C)");
-    nucleotideName.put("K", "Keto (G or T)");
-    nucleotideName.put("k", "Keto (G or T)");
-    nucleotideName.put("B", "Not A (G or C or T)");
-    nucleotideName.put("b", "Not A (G or C or T)");
-    nucleotideName.put("H", "Not G (A or C or T)");
-    nucleotideName.put("h", "Not G (A or C or T)");
-    nucleotideName.put("D", "Not C (A or G or T)");
-    nucleotideName.put("d", "Not C (A or G or T)");
-    nucleotideName.put("V", "Not T (A or G or C");
-    nucleotideName.put("v", "Not T (A or G or C");
+    for (int i = 0; i < namesArray.length; i++)
+    {
+      char c = namesArray[i][0].charAt(0);
+      nucleotideIndex[c] = i;
+      // Character.toUpperCase is Locale insensitive
+      nucleotideIndex[Character.toUpperCase(c)] = i;
+      nucleotideName.put(namesArray[i][0], namesArray[i][1]);
+      nucleotideName.put(namesArray[i][0].toUpperCase(Locale.ROOT),
+              namesArray[i][1]);
+    }
 
   }
 
@@ -361,6 +339,37 @@ public class ResidueProperties
       Color.white, // R
       Color.white, // Y
       Color.white, // N
+      Color.white, // w
+      Color.white, // s
+      Color.white, // m
+      Color.white, // k
+      Color.white, // b
+      Color.white, // h
+      Color.white, // d
+      Color.white, // v
+      Color.white, // Gap
+  };
+
+  // this colour scheme devised by sduce
+  public static final Color[] nucleotideAmbiguity = {
+      Color.decode("#f0fff0"), // a
+      Color.decode("#f0fff0"), // c
+      Color.decode("#f0fff0"), // g
+      Color.decode("#f0fff0"), // t
+      Color.decode("#f0fff0"), // u
+      Color.decode("#ffffff"), // i
+      Color.decode("#4f6f6f"), // x
+      Color.decode("#CD5C5C"), // r
+      Color.decode("#008000"), // y
+      Color.decode("#4682B4"), // w
+      Color.decode("#FF8C00"), // s
+      Color.decode("#9ACD32"), // m
+      Color.decode("#9932CC"), // k
+      Color.decode("#8b4513"), // b
+      Color.decode("#808080"), // h
+      Color.decode("#483D8B"), // d
+      Color.decode("#b8860b"), // v
+      Color.decode("#2f4f4f"), // n
       Color.white, // Gap
   };
 
@@ -405,6 +414,125 @@ public class ResidueProperties
       Color.white // ' '
   };
 
+  /*
+   * flower, blossom, sunset, ocean colour schemes from geocos.
+   * See https://gecos.biotite-python.org/
+   * https://raw.githubusercontent.com/biotite-dev/biotite/master/src/biotite/sequence/graphics/color_schemes/flower.json
+   * and https://bmcbioinformatics.biomedcentral.com/articles/10.1186/s12859-020-3526-6
+   * (https://doi.org/10.1186/s12859-020-3526-6)
+   */
+  public static final Color[] flower = { new Color(177, 138, 81), // A
+      new Color(131, 191, 241), // R
+      new Color(11, 206, 198), // N
+      new Color(1, 165, 120), // D
+      new Color(255, 87, 1), // C
+      new Color(114, 149, 174), // Q
+      new Color(45, 160, 161), // E
+      new Color(177, 194, 60), // G
+      new Color(1, 148, 249), // H
+      new Color(242, 118, 99), // I
+      new Color(223, 110, 117), // L
+      new Color(127, 195, 215), // K
+      new Color(254, 157, 175), // M
+      new Color(250, 85, 157), // F
+      new Color(79, 163, 42), // P
+      new Color(180, 189, 155), // S
+      new Color(210, 181, 118), // T
+      new Color(255, 45, 237), // W
+      new Color(201, 110, 207), // Y
+      new Color(253, 153, 123), // V
+      Color.white, // B
+      Color.white, // Z
+      Color.white, // X
+      Color.white, // -
+      Color.white, // *
+      Color.white // .
+  };
+
+  public static final Color[] blossom = { new Color(139, 196, 180), // A
+      new Color(252, 149, 2), // R
+      new Color(181, 194, 6), // N
+      new Color(95, 165, 5), // D
+      new Color(8, 147, 254), // C
+      new Color(191, 133, 39), // Q
+      new Color(219, 181, 1), // E
+      new Color(0, 211, 130), // G
+      new Color(255, 87, 1), // H
+      new Color(154, 186, 243), // I
+      new Color(205, 165, 220), // L
+      new Color(254, 165, 39), // K
+      new Color(245, 161, 184), // M
+      new Color(247, 79, 168), // F
+      new Color(16, 214, 49), // P
+      new Color(126, 157, 89), // S
+      new Color(0, 162, 156), // T
+      new Color(254, 8, 251), // W
+      new Color(255, 78, 122), // Y
+      new Color(135, 192, 228), // V
+      Color.white, // B
+      Color.white, // Z
+      Color.white, // X
+      Color.white, // -
+      Color.white, // *
+      Color.white // .
+  };
+
+  public static final Color[] sunset = { new Color(254, 160, 253), // A
+      new Color(133, 116, 106), // R
+      new Color(171, 200, 245), // N
+      new Color(46, 123, 190), // D
+      new Color(252, 12, 254), // C
+      new Color(140, 110, 129), // Q
+      new Color(103, 120, 146), // E
+      new Color(39, 153, 255), // G
+      new Color(219, 197, 142), // H
+      new Color(250, 33, 161), // I
+      new Color(224, 30, 130), // L
+      new Color(222, 190, 204), // K
+      new Color(209, 62, 123), // M
+      new Color(255, 56, 93), // F
+      new Color(87, 102, 249), // P
+      new Color(231, 180, 253), // S
+      new Color(166, 88, 183), // T
+      new Color(255, 55, 1), // W
+      new Color(203, 83, 57), // Y
+      new Color(254, 81, 184), // V
+      Color.white, // B
+      Color.white, // Z
+      Color.white, // X
+      Color.white, // -
+      Color.white, // *
+      Color.white // .
+  };
+
+  public static final Color[] ocean = { new Color(198, 202, 155), // A
+      new Color(12, 160, 168), // R
+      new Color(10, 223, 195), // N
+      new Color(76, 223, 161), // D
+      new Color(198, 129, 54), // C
+      new Color(139, 211, 209), // Q
+      new Color(96, 218, 201), // E
+      new Color(51, 165, 81), // G
+      new Color(0, 207, 254), // H
+      new Color(242, 186, 170), // I
+      new Color(187, 138, 131), // L
+      new Color(64, 160, 144), // K
+      new Color(164, 139, 136), // M
+      new Color(171, 136, 174), // F
+      new Color(175, 211, 101), // P
+      new Color(109, 155, 116), // S
+      new Color(141, 149, 102), // T
+      new Color(117, 138, 238), // W
+      new Color(186, 195, 252), // Y
+      new Color(233, 190, 164), // V
+      Color.white, // B
+      Color.white, // Z
+      Color.white, // X
+      Color.white, // -
+      Color.white, // *
+      Color.white // .
+  };
+
   // Dunno where I got these numbers from
   public static final double[] hyd2 = { 0.62, // A
       0.29, // R
@@ -500,7 +628,8 @@ public class ResidueProperties
 
   public static String STOP = "STOP";
 
-  public static List<String> STOP_CODONS = Arrays.asList("TGA", "TAA", "TAG");
+  public static List<String> STOP_CODONS = Arrays.asList("TGA", "TAA",
+          "TAG");
 
   public static String START = "ATG";
 
@@ -905,47 +1034,51 @@ public class ResidueProperties
     return peptide;
   }
 
-  public static Hashtable<String, String> toDssp3State;
+  /*
+   * lookup of (A-Z) alternative secondary structure symbols'
+   * equivalents in DSSP3 notation
+   */
+  private static char[] toDssp3State;
   static
   {
-    toDssp3State = new Hashtable<>();
-    toDssp3State.put("H", "H");
-    toDssp3State.put("E", "E");
-    toDssp3State.put("C", " ");
-    toDssp3State.put(" ", " ");
-    toDssp3State.put("T", " ");
-    toDssp3State.put("B", "E");
-    toDssp3State.put("G", "H");
-    toDssp3State.put("I", "H");
-    toDssp3State.put("X", " ");
+    toDssp3State = new char[9]; // for 'A'-'I'; extend if needed
+    Arrays.fill(toDssp3State, ' ');
+    toDssp3State['B' - 'A'] = 'E';
+    toDssp3State['E' - 'A'] = 'E';
+    toDssp3State['G' - 'A'] = 'H';
+    toDssp3State['H' - 'A'] = 'H';
+    toDssp3State['I' - 'A'] = 'H';
   }
 
   /**
    * translate from other dssp secondary structure alphabets to 3-state
    * 
-   * @param ssstring
-   * @return ssstring as a three-state secondary structure assignment.
+   * @param ssString
+   * @return ssstring
    */
-  public static String getDssp3state(String ssstring)
+  public static String getDssp3state(String ssString)
   {
-    if (ssstring == null)
+    if (ssString == null)
     {
       return null;
     }
-    StringBuffer ss = new StringBuffer();
-    for (int i = 0; i < ssstring.length(); i++)
+    int lookupSize = toDssp3State.length;
+    int len = ssString.length();
+    char[] trans = new char[len];
+    for (int i = 0; i < len; i++)
     {
-      String ssc = ssstring.substring(i, i + 1);
-      if (toDssp3State.containsKey(ssc))
+      char c = ssString.charAt(i);
+      int index = c - 'A';
+      if (index < 0 || index >= lookupSize)
       {
-        ss.append(toDssp3State.get(ssc));
+        trans[i] = ' ';
       }
       else
       {
-        ss.append(" ");
+        trans[i] = toDssp3State[index];
       }
     }
-    return ss.toString();
+    return new String(trans);
   }
 
   static
@@ -2274,6 +2407,10 @@ public class ResidueProperties
 
   // main method generates perl representation of residue property hash
   // / cut here
+  /**
+   * @j2sIgnore
+   * @param args
+   */
   public static void main(String[] args)
   {
     Hashtable<String, Vector<String>> aaProps = new Hashtable<>();
@@ -2339,7 +2476,7 @@ public class ResidueProperties
         {
           continue;
         }
-        nuc = nuc.toUpperCase();
+        nuc = nuc.toUpperCase(Locale.ROOT);
         if (!result.contains(nuc))
         {
           result.add(nuc);
@@ -2358,7 +2495,7 @@ public class ResidueProperties
         {
           continue;
         }
-        res = res.toUpperCase();
+        res = res.toUpperCase(Locale.ROOT);
         if (!result.contains(res))
         {
           result.add(res);
@@ -2383,7 +2520,7 @@ public class ResidueProperties
       return '0';
     }
     Integer index = ResidueProperties.aa3Hash
-            .get(threeLetterCode.toUpperCase());
+            .get(threeLetterCode.toUpperCase(Locale.ROOT));
     return index == null ? '0' : aa[index].charAt(0);
   }
 }