Merge branch 'develop' into feature/JAL-244_Automatically_adjust_left_margin_to_avoid...
authorBen Soares <b.soares@dundee.ac.uk>
Thu, 27 Jul 2023 19:18:50 +0000 (20:18 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Thu, 27 Jul 2023 19:18:50 +0000 (20:18 +0100)
19 files changed:
src/jalview/analysis/AverageDistanceEngine.java
src/jalview/datamodel/ContactListImpl.java
src/jalview/datamodel/ContactMatrixI.java
src/jalview/datamodel/GroupSet.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/TreeCanvas.java
src/jalview/gui/TreePanel.java
src/jalview/renderer/ContactMapRenderer.java
src/jalview/util/ColorUtils.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/ws/datamodel/alphafold/PAEContactMatrix.java
test/jalview/analysis/AverageDistanceEngineTest.java
test/jalview/datamodel/PAEContactMatrixTest.java
test/jalview/io/cache/JvCacheableInputBoxTest.java
test/jalview/project/Jalview2xmlTests.java
utils/biotools/Jalview.json [new file with mode: 0644]
utils/biotools/README.md [new file with mode: 0644]

index f4d69d5..d81dd44 100644 (file)
@@ -44,29 +44,31 @@ public class AverageDistanceEngine extends TreeEngine
 
   AlignmentAnnotation aa;
 
+  // 0 - normalised dot product
+  // 1 - L1 - ie (abs(v_1-v_2)/dim(v))
+  // L1 is more rational - since can reason about value of difference,
+  // normalised dot product might give cleaner clusters, but more difficult to
+  // understand.
+
+  int mode = 1;
+
   /**
    * compute cosine distance matrix for a given contact matrix and create a
    * UPGMA tree
-   * 
    * @param cm
+   * @param cosineOrDifference false - dot product : true - L1
    */
   public AverageDistanceEngine(AlignmentViewport av, AlignmentAnnotation aa,
-          ContactMatrixI cm)
+          ContactMatrixI cm, boolean cosineOrDifference)
   {
     this.av = av;
     this.aa = aa;
     this.cm = cm;
+    mode = (cosineOrDifference) ? 1 :0; 
     calculate(cm);
 
   }
 
-  // 0 - normalised dot product
-  // 1 - L1 - ie (abs(v_1-v_2)/dim(v))
-  // L1 is more rational - since can reason about value of difference,
-  // normalised dot product might give cleaner clusters, but more difficult to
-  // understand.
-
-  int mode = 1;
 
   public void calculate(ContactMatrixI cm)
   {
index 69dcf71..bb31c5d 100644 (file)
@@ -55,9 +55,9 @@ public class ContactListImpl implements ContactListI
     {
       from_column = 0;
     }
-    if (to_column > getContactHeight())
+    if (to_column >= getContactHeight())
     {
-      to_column = getContactHeight();
+      to_column = getContactHeight()-1;
     }
     ContactRange cr = new ContactRange();
     cr.setFrom_column(from_column);
index 1d20987..925025f 100644 (file)
@@ -5,6 +5,9 @@ import java.util.Arrays;
 import java.util.BitSet;
 import java.util.List;
 
+import jalview.util.ColorUtils;
+import jalview.ws.datamodel.MappableContactMatrixI;
+
 public interface ContactMatrixI
 {
 
@@ -122,4 +125,79 @@ public interface ContactMatrixI
   }
 
   void setGroupSet(GroupSet makeGroups);
+
+  default void randomlyReColourGroups() {
+    if (hasGroupSet())
+    {
+      GroupSetI groups = getGroupSet();
+      for (BitSet group:groups.getGroups())
+      {
+        groups.setColorForGroup(group, ColorUtils.getARandomColor());
+      }
+    }
+  }
+
+  default void transferGroupColorsTo(AlignmentAnnotation aa)
+  {
+    if (hasGroupSet())
+    {
+      GroupSetI groups = getGroupSet();
+      // stash colors in linked annotation row.
+      // doesn't work yet. TESTS!
+      int sstart = aa.sequenceRef != null ? aa.sequenceRef.getStart() - 1
+              : 0;
+      Annotation ae;
+      Color gpcol = null;
+      int[] seqpos = null;
+      for (BitSet gp : groups.getGroups())
+      {
+        gpcol = groups.getColourForGroup(gp);
+        for (int p = gp.nextSetBit(0); p >= 0
+                && p < Integer.MAX_VALUE; p = gp.nextSetBit(p + 1))
+        {
+          if (this instanceof MappableContactMatrixI)
+          {
+            MappableContactMatrixI mcm = (MappableContactMatrixI) this;
+            seqpos = mcm.getMappedPositionsFor(aa.sequenceRef, p);
+            if (seqpos == null)
+            {
+              // no mapping for this column.
+              continue;
+            }
+            // TODO: handle ranges...
+            ae = aa.getAnnotationForPosition(seqpos[0]);
+          }
+          else
+          {
+            ae = aa.getAnnotationForPosition(p + sstart);
+          }
+          if (ae != null)
+          {
+            ae.colour = gpcol.brighter().darker();
+          }
+        }
+      }
+    }
+  }
+  
+  /**
+   * look up the colour for a column in the associated contact matrix 
+   * @return Color.white or assigned colour
+   */
+  default Color getGroupColorForPosition(int column)
+  {
+    if (hasGroupSet())
+    {
+      GroupSetI groups = getGroupSet();
+      for (BitSet gp:groups.getGroups())
+      {
+        if (gp.get(column))
+        {
+          return groups.getColourForGroup(gp);
+        }
+      }
+    }
+    return Color.white;
+  }
+  
 }
index c7a73b7..db38e7b 100644 (file)
@@ -145,30 +145,59 @@ public class GroupSet implements GroupSetI
     return treeType;
   }
 
-  public static GroupSet makeGroups(ContactMatrixI matrix, float thresh,
+  public static GroupSet makeGroups(ContactMatrixI matrix, boolean autoCut)
+  {
+    return makeGroups(matrix, autoCut, 0, autoCut);
+  }
+  public static GroupSet makeGroups(ContactMatrixI matrix, boolean auto, float thresh,
           boolean abs)
   {
     AverageDistanceEngine clusterer = new AverageDistanceEngine(null, null,
-            matrix);
+            matrix, true);
     double height = clusterer.findHeight(clusterer.getTopNode());
+    Console.debug("Column tree height: " + height);
     String newick = new jalview.io.NewickFile(clusterer.getTopNode(), false,
             true).print();
     String treeType = "UPGMA";
     Console.trace("Newick string\n" + newick);
 
     List<BinaryNode> nodegroups;
-    if (abs ? height > thresh : 0 < thresh && thresh < 1)
+    float cut = -1f;
+    if (auto)
     {
-      float cut = abs ? (float) (thresh / height) : thresh;
-      Console.debug("Threshold " + cut + " for height=" + height);
-
-      nodegroups = clusterer.groupNodes(cut);
+      double rootw = 0;
+      int p = 2;
+      BinaryNode bn = clusterer.getTopNode();
+      while (p-- > 0 & bn.left() != null)
+      {
+        if (bn.left() != null)
+        {
+          bn = bn.left();
+        }
+        if (bn.left() != null)
+        {
+          rootw = bn.height;
+        }
+      }
+      thresh = Math.max((float) (rootw / height) - 0.01f, 0);
+      cut = thresh;
+      nodegroups = clusterer.groupNodes(thresh);
     }
     else
     {
-      nodegroups = new ArrayList<BinaryNode>();
-      nodegroups.add(clusterer.getTopNode());
+      if (abs ? (height > thresh) : (0 < thresh && thresh < 1))
+      {
+        cut = abs ? thresh : (float) (thresh * height);
+        Console.debug("Threshold " + cut + " for height=" + height);
+        nodegroups = clusterer.groupNodes(cut);
+      }
+      else
+      {
+        nodegroups = new ArrayList<BinaryNode>();
+        nodegroups.add(clusterer.getTopNode());
+      }
     }
+    
     List<BitSet> groups = new ArrayList<>();
     for (BinaryNode root : nodegroups)
     {
@@ -179,7 +208,8 @@ public class GroupSet implements GroupSetI
       }
       groups.add(gpset);
     }
-    GroupSet grps = new GroupSet(abs, thresh, groups, treeType, newick);
+    GroupSet grps = new GroupSet(abs, (cut == -1f) ? thresh : cut, groups,
+            treeType, newick);
     return grps;
   }
 
index 7f9bfff..3e24e67 100644 (file)
@@ -4270,10 +4270,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       NewickFile fin = new NewickFile(
               new FileParse(cm.getNewick(), DataSourceType.PASTE));
-      String title = cm.getAnnotLabel() + " " + cm.getTreeMethod() + " tree"
-              + aa.sequenceRef != null
+      String title = aa.label + " "
+              + cm.getTreeMethod() + " tree" + (aa.sequenceRef != null
                       ? (" for " + aa.sequenceRef.getDisplayId(false))
-                      : "";
+                      : "");
 
       showColumnWiseTree(fin, aa, title, w, h, x, y);
     } catch (Throwable xx)
@@ -4293,7 +4293,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         return null;
       }
-      TreePanel tp = new TreePanel(alignPanel, nf, aa, title);
+      TreePanel tp = new TreePanel(alignPanel, nf, aa, treeTitle);
 
       tp.setSize(w, h);
 
@@ -4302,7 +4302,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         tp.setLocation(x, y);
       }
 
-      Desktop.addInternalFrame(tp, title, w, h);
+      Desktop.addInternalFrame(tp, treeTitle, w, h);
       return tp;
     } catch (Throwable xx)
     {
index 241e0a3..c4a40d8 100755 (executable)
@@ -515,8 +515,10 @@ public class AnnotationLabels extends JPanel
           public void actionPerformed(ActionEvent e)
           {
             sel_row.setShowGroupsForContactMatrix(chitem.getState());
-            ap.getAnnotationPanel()
-                    .paint(ap.getAnnotationPanel().getGraphics());
+            // so any annotation colour changes are propagated - though they
+            // probably won't be unless the annotation row colours are removed
+            // too!
+            ap.alignmentChanged();
           }
         });
         pop.add(chitem);
@@ -562,7 +564,10 @@ public class AnnotationLabels extends JPanel
                                 "action.clustering_matrix_for",
                                 cm.getAnnotDescr(), 5f),
                         progBar = System.currentTimeMillis());
-                cm.setGroupSet(GroupSet.makeGroups(cm, 5f, true));
+                cm.setGroupSet(GroupSet.makeGroups(cm, true));
+                cm.randomlyReColourGroups();
+                cm.transferGroupColorsTo(alignmentAnnotation);
+                ap.alignmentChanged();
                 ap.alignFrame.showContactMapTree(alignmentAnnotation, cm);
                 ap.alignFrame.setProgressBar(null, progBar);
               }
index 8e04048..8321741 100755 (executable)
@@ -737,7 +737,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         if (evt.isControlDown()
                 && PAEContactMatrix.PAEMATRIX.equals(clicked.getCalcId()))
         {
-          int c = fr - 1;
+          int c = fr;
           ContactRange cr = forCurrentX.getRangeFor(fr, to);
           double cval;
           // TODO: could use GraphLine instead of arbitrary picking
@@ -747,7 +747,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
           // controls feathering - what other elements in row/column
           // should we select
           double thresh = cr.getMean() + (cr.getMax() - cr.getMean()) * .15;
-          while (c > 0)
+          while (c >= 0)
           {
             cval = forCurrentX.getContactAt(c);
             if (// cr.getMin() <= cval &&
@@ -1078,12 +1078,12 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       ContactGeometry lastXcgeom = new ContactGeometry(forFromX,
               cma.graphHeight);
       ContactGeometry.contactInterval lastXci = lastXcgeom
-              .mapFor(rowIndex[1], rowIndex[1] - deltaY);
+              .mapFor(rowIndex[1], rowIndex[1] + deltaY);
 
       ContactGeometry cXcgeom = new ContactGeometry(forToX,
               cma.graphHeight);
       ContactGeometry.contactInterval cXci = cXcgeom.mapFor(rowIndex[1],
-              rowIndex[1] - deltaY);
+              rowIndex[1] + deltaY);
 
       // mark rectangular region formed by drag
       jalview.bin.Console.trace("Matrix Selection from last(" + fromXc
@@ -1247,7 +1247,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       {
         row = i;
         res[0] = row;
-        res[1] = height - yPos;
+        res[1] = yPos-lheight;
         break;
       }
     }
@@ -1301,6 +1301,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     // TODO abstract tooltip generator so different implementations can be built
     if (ann.graph == AlignmentAnnotation.CONTACT_MAP)
     {
+      if (rowAndOffset>=ann.graphHeight)
+      {
+        return null;
+      }
       ContactListI clist = av.getContactList(ann, column);
       if (clist != null)
       {
index 55ce44a..6fbd422 100755 (executable)
@@ -53,6 +53,7 @@ import javax.swing.ToolTipManager;
 import jalview.analysis.Conservation;
 import jalview.analysis.TreeModel;
 import jalview.api.AlignViewportI;
+import jalview.bin.Console;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.BinaryNode;
@@ -66,6 +67,7 @@ import jalview.datamodel.SequenceNode;
 import jalview.gui.JalviewColourChooser.ColourChooserListener;
 import jalview.schemes.ColourSchemeI;
 import jalview.structure.SelectionSource;
+import jalview.util.ColorUtils;
 import jalview.util.Format;
 import jalview.util.MessageManager;
 import jalview.ws.datamodel.MappableContactMatrixI;
@@ -221,6 +223,13 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
     boolean has_placeholders = false;
     longestName = "";
 
+    AlignmentAnnotation aa = tp.getAssocAnnotation();
+    ContactMatrixI cm = (aa!=null) ? av.getContactMatrix(aa) : null;
+    if (cm!=null && cm.hasCutHeight())
+    {
+      threshold=(float) cm.getCutHeight();
+    }
+    
     for (int i = 0; i < leaves.size(); i++)
     {
       BinaryNode lf = leaves.elementAt(i);
@@ -236,6 +245,14 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
         longestName = TreeCanvas.PLACEHOLDER
                 + ((Sequence) lf.element()).getName();
       }
+      if (tp.isColumnWise() && cm!=null)
+      {
+        // get color from group colours, if they are set for the matrix
+        try {
+          Color col = cm.getGroupColorForPosition(parseColumnNode(lf));
+          setColor(lf,col.brighter());
+        } catch (NumberFormatException ex) {};
+      }
     }
 
     setMarkPlaceholders(has_placeholders);
@@ -259,7 +276,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
    * @param offy
    *          DOCUMENT ME!
    */
-  public void drawNode(Graphics g, BinaryNode node, float chunk,
+  public void drawNode(Graphics g, BinaryNode node, double chunk,
           double wscale, int width, int offx, int offy)
   {
     if (node == null)
@@ -778,7 +795,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
               + ((BinaryNode) top.right()).count;
     }
 
-    float chunk = (float) (height - (offy)) / top.count;
+    double chunk = (double) (height - (offy)) / (double)top.count;
 
     drawNode(g2, tree.getTopNode(), chunk, wscale, width, offx, offy);
 
@@ -1025,7 +1042,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
           threshold = 0f;
         }
       }
-
+      Console.log.debug("Tree cut threshold set at:" + threshold);
       PaintRefresher.Refresh(tp,
               getAssociatedPanel().av.getSequenceSetId());
       repaint();
@@ -1040,8 +1057,8 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
     Map<BitSet, Color> colors = new HashMap();
     for (int i = 0; i < groups.size(); i++)
     {
-      Color col = new Color((int) (Math.random() * 255),
-              (int) (Math.random() * 255), (int) (Math.random() * 255));
+      Color col = ColorUtils.getARandomColor();
+      
       setColor(groups.get(i), col.brighter());
 
       Vector<BinaryNode> l = tree.findLeaves(groups.get(i));
@@ -1071,41 +1088,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
             cm.setColorForGroup(gp, colors.get(gp));
           }
         }
-        // stash colors in linked annotation row.
-        // doesn't work yet. TESTS!
-        int sstart = aa.sequenceRef != null ? aa.sequenceRef.getStart() - 1
-                : 0;
-        Annotation ae;
-        Color gpcol = null;
-        int[] seqpos = null;
-        for (BitSet gp : colors.keySet())
-        {
-          gpcol = colors.get(gp);
-          for (int p = gp.nextSetBit(0); p >= 0
-                  && p < Integer.MAX_VALUE; p = gp.nextSetBit(p + 1))
-          {
-            if (cm instanceof MappableContactMatrixI)
-            {
-              MappableContactMatrixI mcm = (MappableContactMatrixI) cm;
-              seqpos = mcm.getMappedPositionsFor(aa.sequenceRef, p);
-              if (seqpos == null)
-              {
-                // no mapping for this column.
-                continue;
-              }
-              // TODO: handle ranges...
-              ae = aa.getAnnotationForPosition(seqpos[0]);
-            }
-            else
-            {
-              ae = aa.getAnnotationForPosition(p + sstart);
-            }
-            if (ae != null)
-            {
-              ae.colour = gpcol.brighter().darker();
-            }
-          }
-        }
+        cm.transferGroupColorsTo(aa);
       }
     }
 
@@ -1123,15 +1106,18 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       }
     }
   }
-
+  private int parseColumnNode(BinaryNode bn) throws NumberFormatException
+  {
+    return Integer.parseInt(
+            bn.getName().substring(bn.getName().indexOf("c") + 1));
+  }
   private boolean isColumnForNodeSelected(BinaryNode bn)
   {
     SequenceI rseq = tp.assocAnnotation.sequenceRef;
     int colm = -1;
     try
     {
-      colm = Integer.parseInt(
-              bn.getName().substring(bn.getName().indexOf("c") + 1));
+      colm = parseColumnNode(bn);
     } catch (Exception e)
     {
       return false;
@@ -1198,8 +1184,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
         // parse out from nodename
         try
         {
-          colm = Integer.parseInt(
-                  bn.getName().substring(bn.getName().indexOf("c") + 1));
+          colm = parseColumnNode(bn);
         } catch (Exception e)
         {
           continue;
@@ -1244,14 +1229,14 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       if (mcm!=null)
       {
         int[] seqpos = mcm.getMappedPositionsFor(
-                tp.assocAnnotation.sequenceRef, colm);
+                rseq, colm);
         if (seqpos == null)
         {
           // no mapping for this column.
           continue;
         }
         // TODO: handle ranges...
-        offp = seqpos[0]-1;
+        offp = rseq.findIndex(seqpos[0])-1;
       }
       else
       {
index 5ccd68d..2eff542 100755 (executable)
@@ -179,17 +179,18 @@ public class TreePanel extends GTreePanel
     this.treeType = type;
     this.scoreModelName = modelName;
 
+    treeCanvas = new TreeCanvas(this, ap, scrollPane);
+    scrollPane.setViewportView(treeCanvas);
+    
     if (columnWise)
     {
       bootstrapMenu.setVisible(false);
-      placeholdersMenu.setSelected(false);
+      placeholdersMenu.setState(false);
       placeholdersMenu.setVisible(false);
-      fitToWindow.setSelected(false);
+      fitToWindow.setState(false);
       sortAssocViews.setVisible(false);
     }
 
-    treeCanvas = new TreeCanvas(this, ap, scrollPane);
-    scrollPane.setViewportView(treeCanvas);
 
     addKeyListener(new KeyAdapter()
     {
@@ -391,9 +392,9 @@ public class TreePanel extends GTreePanel
                 ? new NJTree(av, sm, similarityParams)
                 : new AverageDistanceTree(av, sm, similarityParams);
         tree = new TreeModel(njtree);
-        showDistances(true);
+        // don't display distances for columnwise trees        
       }
-
+      showDistances(!columnWise);
       tree.reCount(tree.getTopNode());
       tree.findHeight(tree.getTopNode());
       treeCanvas.setTree(tree);
index 8f38d02..0471145 100644 (file)
@@ -109,11 +109,12 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI
     }
     eRes = Math.min(eRes, aa_annotations.length);
 
-    int x = 0, y2 = y;
+    int x = 0, topY = y;
 
-    g.setColor(shade.no_data);
-
-    g.drawLine(x, y2, (eRes - sRes) * charWidth, y2);
+    // uncomment below to render whole area of matrix as pink
+    // g.setColor(shade.no_data);
+    // g.fillRect(x, topY-_aa.height, (eRes - sRes) * charWidth, _aa.graphHeight);
+    
     boolean showGroups = _aa.isShowGroupsForContactMatrix();
     int column;
     int aaMax = aa_annotations.length - 1;
@@ -165,11 +166,11 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI
       final ContactGeometry cgeom = new ContactGeometry(contacts,
               _aa.graphHeight);
 
-      for (int ht = y2, eht = y2
-              - _aa.graphHeight; ht >= eht; ht -= cgeom.pixels_step)
+      for (int ht = 0, botY = topY
+              - _aa.height; ht < _aa.graphHeight; ht += cgeom.pixels_step)
       {
-        ContactGeometry.contactInterval ci = cgeom.mapFor(y2 - ht,
-                y2 - ht + cgeom.pixels_step);
+        ContactGeometry.contactInterval ci = cgeom.mapFor(ht,
+                ht + cgeom.pixels_step);
         // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
         // cend = (int) Math.min(contact_height,
         // Math.ceil(cstart + contacts_per_pixel * pixels_step));
@@ -216,11 +217,11 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI
         g.setColor(col);
         if (cgeom.pixels_step > 1)
         {
-          g.fillRect(x * charWidth, ht, charWidth, 1 + cgeom.pixels_step);
+          g.fillRect(x * charWidth, botY+ht, charWidth, 1 + cgeom.pixels_step);
         }
         else
         {
-          g.drawLine(x * charWidth, ht, (x + 1) * charWidth, ht);
+          g.drawLine(x * charWidth, botY+ht, (x + 1) * charWidth, botY+ht);
         }
       }
       x++;
index 6734735..b728c9d 100644 (file)
@@ -68,7 +68,18 @@ public class ColorUtils
     return color;
 
   }
+  
+  /**
+   * 
+   * @return random color
+   */
+  public static final Color getARandomColor()
+  {
 
+    Color col = new Color((int) (Math.random() * 255),
+            (int) (Math.random() * 255), (int) (Math.random() * 255));
+    return col;
+  }
   /**
    * Convert to Tk colour code format
    * 
index a8c93cf..33cb9dc 100644 (file)
@@ -2132,7 +2132,7 @@ public abstract class AlignmentViewport
 
         if (aa.graph > 0)
         {
-          aa.height += aa.graphHeight;
+          aa.height += aa.graphHeight+20;
         }
 
         if (aa.height == 0)
index c822ef4..22884f1 100644 (file)
@@ -21,6 +21,21 @@ import jalview.util.MapList;
 import jalview.util.MapUtils;
 import jalview.ws.dbsources.EBIAlfaFold;
 
+/**
+ * routines and class for holding predicted alignment error matrices as produced
+ * by alphafold et al.
+ * 
+ * getContactList(column) returns the vector of predicted alignment errors for
+ * reference position given by column getElementAt(column, i) returns the
+ * predicted superposition error for the ith position when column is used as
+ * reference
+ * 
+ * Many thanks to Ora Schueler Furman for noticing that earlier development
+ * versions did not show the PAE oriented correctly
+ *
+ * @author jprocter
+ *
+ */
 public class PAEContactMatrix extends
         MappableContactMatrix<PAEContactMatrix> implements ContactMatrixI
 {
@@ -129,17 +144,18 @@ public class PAEContactMatrix extends
         Object d = scores.next();
         if (d instanceof Double)
         {
-          elements[row][col++] = ((Double) d).longValue();
+          elements[col][row] = ((Double) d).longValue();
         }
         else
         {
-          elements[row][col++] = (float) ((Long) d).longValue();
+          elements[col][row] = (float) ((Long) d).longValue();
         }
 
-        if (maxscore < elements[row][col - 1])
+        if (maxscore < elements[col][row])
         {
-          maxscore = elements[row][col - 1];
+          maxscore = elements[col][row];
         }
+        col++;
       }
       row++;
       col = 0;
@@ -180,7 +196,7 @@ public class PAEContactMatrix extends
     cols = ((List<Long>) pae_obj.get("residue2")).iterator();
     Iterator<Double> scores = ((List<Double>) pae_obj.get("distance"))
             .iterator();
-    elements = new float[maxrow][maxcol];
+    elements = new float[maxcol][maxrow];
     while (scores.hasNext())
     {
       float escore = scores.next().floatValue();
@@ -194,13 +210,17 @@ public class PAEContactMatrix extends
       {
         maxcol = col;
       }
-      elements[row - 1][col - 1] = escore;
+      elements[col - 1][row-1] = escore;
     }
 
     maxscore = ((Double) MapUtils.getFirst(pae_obj,
             "max_predicted_aligned_error", "max_pae")).floatValue();
   }
 
+  /**
+   * getContactList(column) @returns the vector of predicted alignment errors
+   * for reference position given by column
+   */
   @Override
   public ContactListI getContactList(final int column)
   {
@@ -235,6 +255,10 @@ public class PAEContactMatrix extends
     });
   }
 
+  /**
+   * getElementAt(column, i) @returns the predicted superposition error for the
+   * ith position when column is used as reference
+   */
   @Override
   protected double getElementAt(int _column, int i)
   {
index 5a8361d..760e0ba 100644 (file)
@@ -67,7 +67,7 @@ public class AverageDistanceEngineTest
             + matrix.getMin());
     long start = System.currentTimeMillis();
     AverageDistanceEngine clusterer = new AverageDistanceEngine(
-            af.getViewport(), null, matrix);
+            af.getViewport(), null, matrix, false);
     System.out.println("built a tree in "
             + (System.currentTimeMillis() - start) * 0.001 + " seconds.");
     StringBuffer sb = new StringBuffer();
index 67edb37..bdfa5a3 100644 (file)
@@ -73,7 +73,7 @@ public class PAEContactMatrixTest
     verifyPAEmatrix(seq, aa, 0, 0, 4);
 
     // test clustering
-    paematrix.setGroupSet(GroupSet.makeGroups(paematrix, 0.1f, false));
+    paematrix.setGroupSet(GroupSet.makeGroups(paematrix, false,0.1f, false));
 
     // remap - test the MappableContactMatrix.liftOver method
     SequenceI newseq = new Sequence("Seq", "ASDQEASDQEASDQE");
index 3e0aa36..288444e 100644 (file)
@@ -57,6 +57,7 @@ public class JvCacheableInputBoxTest
 
     try
     {
+      // fix for JAL-4153
       // This delay is essential to prevent the assertion below from executing
       // before swing thread finishes updating the combo-box
       SwingUtilities.invokeAndWait(() -> {
@@ -86,6 +87,7 @@ public class JvCacheableInputBoxTest
 
     try
     {
+      // fix for JAL-4153
       // This delay is to let cacheBox.updateCache() finish updating the cache
       SwingUtilities.invokeAndWait(() -> {
         try
index 37f946b..c9532cc 100644 (file)
@@ -1563,7 +1563,7 @@ public class Jalview2xmlTests extends Jalview2xmlBase
       {
         paevals[i][j] = ((i - j < 2)
                 || ((i > 1 && i < 5) && (j > 1 && i < 5))) ? 1 : 0f;
-        paevals[j][i] = paevals[i][j];
+        paevals[j][i] = -paevals[i][j];
       }
     }
     PAEContactMatrix dummyMat = new PAEContactMatrix(sq, paevals);
@@ -1573,7 +1573,8 @@ public class Jalview2xmlTests extends Jalview2xmlBase
     float[][] vals = ContactMatrix.fromFloatStringToContacts(content,
             sq.getLength(), sq.getLength());
     assertEquals(vals[3][4], paevals[3][4]);
-    dummyMat.setGroupSet(GroupSet.makeGroups(dummyMat, 0.5f, false));
+    assertEquals(vals[4][3], paevals[4][3]);
+    dummyMat.setGroupSet(GroupSet.makeGroups(dummyMat, false,0.5f, false));
     Assert.assertNotSame(dummyMat.getNewick(), "");
     AlignmentAnnotation paeCm = sq.addContactList(dummyMat);
     al.addAnnotation(paeCm);
diff --git a/utils/biotools/Jalview.json b/utils/biotools/Jalview.json
new file mode 100644 (file)
index 0000000..69aa95c
--- /dev/null
@@ -0,0 +1,417 @@
+{
+  "name": "Jalview",
+  "description": "Jalview is a free program for multiple sequence alignment editing, visualisation and analysis. Use it to view and edit sequence alignments, analyse them with phylogenetic trees and principal components analysis (PCA) plots and explore molecular structures and annotation.",
+  "homepage": "https://www.jalview.org/",
+  "biotoolsID": "Jalview",
+  "biotoolsCURIE": "biotools:Jalview",
+  "version": [
+    "2.11.2.7"
+  ],
+  "relation": [
+    {
+      "biotoolsID": "jabaws",
+      "type": "uses"
+    },
+    {
+      "biotoolsID": "chimera",
+      "type": "uses"
+    },
+    {
+      "biotoolsID": "chimerax",
+      "type": "uses"
+    },
+    {
+      "biotoolsID": "pymol",
+      "type": "uses"
+    },
+    {
+      "biotoolsID": "bioconda",
+      "type": "includedIn"
+    },
+    {
+      "biotoolsID": "3d-beacons",
+      "type": "uses"
+    },
+    {
+      "biotoolsID": "uniprot",
+      "type": "uses"
+    },
+    {
+      "biotoolsID": "pfam",
+      "type": "uses"
+    },
+    {
+      "biotoolsID": "ensembl",
+      "type": "uses"
+    },
+    {
+      "biotoolsID": "pdb",
+      "type": "uses"
+    },
+    {
+      "biotoolsID": "rfam",
+      "type": "uses"
+    }
+  ],
+  "function": [
+    {
+      "operation": [
+        {
+          "uri": "http://edamontology.org/operation_0564",
+          "term": "Sequence visualisation"
+        },
+        {
+          "uri": "http://edamontology.org/operation_0324",
+          "term": "Phylogenetic tree analysis"
+        },
+        {
+          "uri": "http://edamontology.org/operation_3081",
+          "term": "Sequence alignment editing"
+        }
+      ],
+      "input": [
+        {
+          "data": {
+            "uri": "http://edamontology.org/data_0863",
+            "term": "Sequence alignment"
+          },
+          "format": [
+            {
+              "uri": "http://edamontology.org/format_1939",
+              "term": "GFF3-seq"
+            },
+            {
+              "uri": "http://edamontology.org/format_1982",
+              "term": "ClustalW format"
+            },
+            {
+              "uri": "http://edamontology.org/format_1961",
+              "term": "Stockholm format"
+            },
+            {
+              "uri": "http://edamontology.org/format_1984",
+              "term": "FASTA-aln"
+            },
+            {
+              "uri": "http://edamontology.org/format_1938",
+              "term": "GFF2-seq"
+            },
+            {
+              "uri": "http://edamontology.org/format_1929",
+              "term": "FASTA"
+            },
+            {
+              "uri": "http://edamontology.org/format_1948",
+              "term": "nbrf/pir"
+            },
+            {
+              "uri": "http://edamontology.org/format_3774",
+              "term": "BioJSON (Jalview)"
+            },
+            {
+              "uri": "http://edamontology.org/format_1997",
+              "term": "PHYLIP format"
+            },
+            {
+              "uri": "http://edamontology.org/format_3313",
+              "term": "BLC"
+            },
+            {
+              "uri": "http://edamontology.org/format_3311",
+              "term": "RNAML"
+            },
+            {
+              "uri": "http://edamontology.org/format_1947",
+              "term": "GCG MSF"
+            },
+            {
+              "uri": "http://edamontology.org/format_3015",
+              "term": "Pileup"
+            },
+            {
+              "uri": "http://edamontology.org/format_1477",
+              "term": "mmCIF"
+            },
+            {
+              "uri": "http://edamontology.org/format_3016",
+              "term": "VCF"
+            },
+            {
+              "uri": "http://edamontology.org/format_1915",
+              "term": "Format"
+            }
+          ]
+        },
+        {
+          "data": {
+            "uri": "http://edamontology.org/data_0886",
+            "term": "Structure alignment"
+          },
+          "format": [
+            {
+              "uri": "http://edamontology.org/format_1476",
+              "term": "PDB"
+            }
+          ]
+        }
+      ],
+      "output": [
+        {
+          "data": {
+            "uri": "http://edamontology.org/data_0863",
+            "term": "Sequence alignment"
+          },
+          "format": [
+            {
+              "uri": "http://edamontology.org/format_1948",
+              "term": "nbrf/pir"
+            },
+            {
+              "uri": "http://edamontology.org/format_3464",
+              "term": "JSON"
+            },
+            {
+              "uri": "http://edamontology.org/format_1961",
+              "term": "Stockholm format"
+            },
+            {
+              "uri": "http://edamontology.org/format_1929",
+              "term": "FASTA"
+            },
+            {
+              "uri": "http://edamontology.org/format_1997",
+              "term": "PHYLIP format"
+            },
+            {
+              "uri": "http://edamontology.org/format_3313",
+              "term": "BLC"
+            },
+            {
+              "uri": "http://edamontology.org/format_3774",
+              "term": "BioJSON (Jalview)"
+            },
+            {
+              "uri": "http://edamontology.org/format_1947",
+              "term": "GCG MSF"
+            },
+            {
+              "uri": "http://edamontology.org/format_3015",
+              "term": "Pileup"
+            },
+            {
+              "uri": "http://edamontology.org/format_1982",
+              "term": "ClustalW format"
+            }
+          ]
+        },
+        {
+          "data": {
+            "uri": "http://edamontology.org/data_2884",
+            "term": "Plot"
+          },
+          "format": [
+            {
+              "uri": "http://edamontology.org/format_3603",
+              "term": "PNG"
+            },
+            {
+              "uri": "http://edamontology.org/format_2331",
+              "term": "HTML"
+            },
+            {
+              "uri": "http://edamontology.org/format_3466",
+              "term": "EPS"
+            },
+            {
+              "uri": "http://edamontology.org/format_3604",
+              "term": "SVG"
+            },
+            {
+              "uri": "http://edamontology.org/format_1915",
+              "term": "Format"
+            }
+          ]
+        }
+      ],
+      "note": "Other Input formats:\nAMSA (.amsa);\nJnetFile (.concise, .jnet);\nPFAM (.pfam);\nSubstitution matrix (.matrix);\nJalview Project File (.jvp);\nJalview Feature File (.features, .jvfeatures);\nJalview Annotations File (.annotations, .jvannotations);\n\n...\nOther Output formats:\nPFAM (.pfam);\nBioJS (.biojs) (interactive HTML/Javascript);\nJalview Project File (.jvp);"
+    }
+  ],
+  "toolType": [
+    "Desktop application"
+  ],
+  "topic": [
+    {
+      "uri": "http://edamontology.org/topic_0080",
+      "term": "Sequence analysis"
+    },
+    {
+      "uri": "http://edamontology.org/topic_0092",
+      "term": "Data visualisation"
+    }
+  ],
+  "operatingSystem": [
+    "Linux",
+    "Windows",
+    "Mac"
+  ],
+  "license": "GPL-3.0",
+  "maturity": "Mature",
+  "cost": "Free of charge",
+  "accessibility": "Open access",
+  "elixirPlatform": [
+    "Tools"
+  ],
+  "elixirNode": [
+    "UK"
+  ],
+  "link": [
+    {
+      "url": "https://discourse.jalview.org/",
+      "type": [
+        "Discussion forum"
+      ]
+    },
+    {
+      "url": "https://issues.jalview.org/",
+      "type": [
+        "Issue tracker"
+      ]
+    },
+    {
+      "url": "https://www.jalview.org/development/jalview_develop/",
+      "type": [
+        "Other"
+      ],
+      "note": "Latest development version"
+    },
+    {
+      "url": "https://source.jalview.org/crucible/browse/jalview",
+      "type": [
+        "Repository"
+      ]
+    },
+    {
+      "url": "https://twitter.com/Jalview",
+      "type": [
+        "Social media"
+      ],
+      "note": "Twitter feed"
+    },
+    {
+      "url": "https://www.youtube.com/channel/UCIjpnvZB770yz7ftbrJ0tfw",
+      "type": [
+        "Social media"
+      ],
+      "note": "YouTube training videos"
+    }
+  ],
+  "download": [
+    {
+      "url": "https://www.jalview.org/download",
+      "type": "Downloads page"
+    },
+    {
+      "url": "https://www.jalview.org/download/source/",
+      "type": "Source code"
+    },
+    {
+      "url": "https://www.jalview.org/download/?os=all",
+      "type": "Binaries",
+      "note": "Binaries for all platforms"
+    },
+    {
+      "url": "https://www.jalview.org/favicon.svg",
+      "type": "Icon"
+    },
+    {
+      "url": "https://www.jalview.org/download/other/jar/",
+      "type": "Binaries",
+      "note": "Executable JAR file"
+    }
+  ],
+  "documentation": [
+    {
+      "url": "https://www.jalview.org/about/citation",
+      "type": [
+        "Citation instructions"
+      ]
+    },
+    {
+      "url": "https://www.jalview.org/training/",
+      "type": [
+        "Training material"
+      ],
+      "note": "Hands-on exercises, Training courses and Training videos"
+    },
+    {
+      "url": "https://www.jalview.org/help/faq",
+      "type": [
+        "FAQ"
+      ]
+    },
+    {
+      "url": "https://www.jalview.org/help/documentation/",
+      "type": [
+        "User manual"
+      ]
+    }
+  ],
+  "publication": [
+    {
+      "doi": "10.1093/bioinformatics/btp033",
+      "metadata": {
+        "title": "Jalview Version 2-A multiple sequence alignment editor and analysis workbench",
+        "abstract": "Summary: Jalview Version 2 is a system for interactive WYSIWYG editing, analysis and annotation of multiple sequence alignments. Core features include keyboard and mouse-based editing, multiple views and alignment overviews, and linked structure display with Jmol. Jalview 2 is available in two forms: a lightweight Java applet for use in web applications, and a powerful desktop application that employs web services for sequence alignment, secondary structure prediction and the retrieval of alignments, sequences, annotation and structures from public databases and any DAS 1.53 compliant sequence or annotation server. © 2009 The Author(s).",
+        "date": "2009-05-07T00:00:00Z",
+        "citationCount": 5999,
+        "authors": [
+          {
+            "name": "Waterhouse A.M."
+          },
+          {
+            "name": "Procter J.B."
+          },
+          {
+            "name": "Martin D.M.A."
+          },
+          {
+            "name": "Clamp M."
+          },
+          {
+            "name": "Barton G.J."
+          }
+        ],
+        "journal": "Bioinformatics"
+      }
+    }
+  ],
+  "credit": [
+    {
+      "name": "Jim Procter",
+      "url": "http://www.lifesci.dundee.ac.uk/people/jim-procter",
+      "orcidid": "https://orcid.org/0000-0002-7865-7382",
+      "typeEntity": "Person",
+      "typeRole": [
+        "Primary contact"
+      ]
+    },
+    {
+      "name": "Geoff Barton",
+      "url": "https://www.lifesci.dundee.ac.uk/people/geoff-barton",
+      "orcidid": "https://orcid.org/0000-0002-9014-5355"
+    }
+  ],
+  "owner": "ben_s",
+  "additionDate": "2019-02-13T17:01:40Z",
+  "lastUpdate": "2023-07-22T09:24:44.755337Z",
+  "editPermission": {
+    "type": "group",
+    "authors": [
+      "ben_s",
+      "jimprocter"
+    ]
+  },
+  "validated": 1,
+  "homepage_status": 0,
+  "elixir_badge": 0
+}
diff --git a/utils/biotools/README.md b/utils/biotools/README.md
new file mode 100644 (file)
index 0000000..948a751
--- /dev/null
@@ -0,0 +1,13 @@
+This is the JSON representation of the latest Jalview release's record on bio.tools
+
+To update:
+1. go to https://bio.tools/Jalview
+2. log in and scroll down to the 'Update Record' button to open the edit interface.
+3. Make any chances to the entry - press Validate to ensure all is good
+4. Select the JSON tab and copy paste into
+
+``
+cat > utils/biotools/Jalview.json
+``
+
+Thanks to Herve Menager for the tutorial on storing bio.tools records with the tool's software repository at [CoFest 2023](https://www.open-bio.org/events/bosc-2023/obf-bosc-collaborationfest-2023)
\ No newline at end of file