JAL-4134 display tree for columns - selecting an internal node selects column in...
authorJames Procter <j.procter@dundee.ac.uk>
Mon, 27 Feb 2023 17:27:44 +0000 (17:27 +0000)
committerJames Procter <j.procter@dundee.ac.uk>
Mon, 27 Feb 2023 17:27:44 +0000 (17:27 +0000)
src/jalview/gui/AlignFrame.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/TreeCanvas.java
src/jalview/gui/TreePanel.java

index 7b310b1..82ec1d2 100644 (file)
@@ -155,6 +155,7 @@ import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.ViewportRanges;
 import jalview.ws.DBRefFetcher;
 import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
+import jalview.ws.datamodel.alphafold.PAEContactMatrix;
 import jalview.ws.jws1.Discoverer;
 import jalview.ws.jws2.Jws2Discoverer;
 import jalview.ws.jws2.jabaws2.Jws2Instance;
@@ -4131,6 +4132,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return showNewickTree(nf, treeTitle, null, w, h, x, y);
   }
 
+
   /**
    * Add a treeviewer for the tree extracted from a Newick file object to the
    * current alignment view
@@ -4181,6 +4183,40 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     return tp;
   }
 
+
+  public void showContactMapTree(AlignmentAnnotation aa,
+          PAEContactMatrix cm)
+  {
+    int x = 4, y = 5;
+    int w = 400, h = 500;
+    try
+    {
+      NewickFile fin = new NewickFile(
+              new FileParse(cm.getNewickString(), DataSourceType.PASTE));
+      fin.parse();
+      if (fin.getTree() == null)
+      {
+        return;
+      }
+      String title = "PAE Matrix Tree for "
+              + cm.getReferenceSeq().getDisplayId(false);
+      TreePanel tp = new TreePanel(alignPanel, fin, aa, title);
+
+      tp.setSize(w, h);
+
+      if (x > 0 && y > 0)
+      {
+        tp.setLocation(x, y);
+      }
+
+      Desktop.addInternalFrame(tp, title, w, h);
+    } catch (Throwable xx)
+    {
+      Console.error("Unexpected exception showing tree for contact matrix",
+              xx);
+    }
+
+  }
   private boolean buildingMenu = false;
 
   /**
@@ -5930,6 +5966,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
     return lastFeatureSettingsBounds;
   }
+
 }
 
 class PrintThread extends Thread
index 804ab7b..c01a6f3 100755 (executable)
@@ -36,6 +36,7 @@ import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.awt.geom.AffineTransform;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
@@ -50,18 +51,23 @@ import javax.swing.ToolTipManager;
 
 import jalview.analysis.AlignSeq;
 import jalview.analysis.AlignmentUtils;
+import jalview.bin.Console;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.ContactMatrixI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
 import jalview.io.FileFormat;
 import jalview.io.FormatAdapter;
+import jalview.io.NewickFile;
 import jalview.util.Comparison;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.ws.datamodel.alphafold.PAEContactMatrix;
 
 /**
  * The panel that holds the labels for alignment annotations, providing
@@ -416,6 +422,31 @@ public class AnnotationLabels extends JPanel
         consclipbrd.addActionListener(this);
         pop.add(consclipbrd);
       }
+       if (aa[selectedRow].graph == AlignmentAnnotation.CONTACT_MAP
+              && PAEContactMatrix.PAEMATRIX
+                      .equals(aa[selectedRow].getCalcId()))
+      {
+        final PAEContactMatrix cm = (PAEContactMatrix) av
+                .getContactMatrix(aa[selectedRow]);
+        if (cm.getNewickString()!=null && cm.getNewickString().length()>0)
+        {
+          item = new JMenuItem("Show Matrix");
+          item.addActionListener(new ActionListener()
+          {
+
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+
+                ap.alignFrame.showContactMapTree(aa[selectedRow],cm);
+
+            }
+          });
+          pop.addSeparator();
+          pop.add(item);
+        }
+
+      }
     }
     pop.show(this, evt.getX(), evt.getY());
   }
index a1bcebd..b8bcc74 100755 (executable)
@@ -36,6 +36,7 @@ import java.awt.print.PageFormat;
 import java.awt.print.Printable;
 import java.awt.print.PrinterException;
 import java.awt.print.PrinterJob;
+import java.util.BitSet;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -51,6 +52,8 @@ import jalview.analysis.Conservation;
 import jalview.analysis.TreeModel;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.BinaryNode;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -806,12 +809,15 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
     else
     {
       Vector<BinaryNode> leaves = tree.findLeaves(highlightNode);
-
+      if (tp.getColumnWise()) {
+        markColumnsFor(getAssociatedPanels(), leaves, Color.red);
+      } else {
       for (int i = 0; i < leaves.size(); i++)
       {
         SequenceI seq = (SequenceI) leaves.elementAt(i).element();
         treeSelectionChanged(seq);
       }
+      }
       av.sendSelection();
     }
 
@@ -981,69 +987,109 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
       setColor(groups.get(i), col.brighter());
 
       Vector<BinaryNode> l = tree.findLeaves(groups.get(i));
+      if (!tp.getColumnWise()) {
+        createSeqGroupFor(aps, l, col);
+      } else {
+        markColumnsFor(aps,l,col);
+      }
+    }
 
-      Vector<SequenceI> sequences = new Vector<>();
-
-      for (int j = 0; j < l.size(); j++)
+    // notify the panel(s) to redo any group specific stuff
+    // also updates structure views if necessary
+    for (int a = 0; a < aps.length; a++)
+    {
+      aps[a].updateAnnotation();
+      final AlignViewportI codingComplement = aps[a].av
+              .getCodingComplement();
+      if (codingComplement != null)
       {
-        SequenceI s1 = (SequenceI) l.elementAt(j).element();
+        ((AlignViewport) codingComplement).getAlignPanel()
+                .updateAnnotation();
+      }
+    }
+  }
 
-        if (!sequences.contains(s1))
-        {
-          sequences.addElement(s1);
+  private void markColumnsFor(AlignmentPanel[] aps, Vector<BinaryNode> l,
+          Color col)
+  {
+    SequenceI rseq = tp.assocAnnotation.sequenceRef;
+    for (BinaryNode bn:l)
+    {
+      int colm=-1;
+      try {
+        colm = Integer.parseInt(bn.getName().substring(bn.getName().indexOf("c")+1));
+      } catch (Exception e)
+      {
+        continue;
+      }
+      ColumnSelection cs = av.getColumnSelection();
+      HiddenColumns hc = av.getAlignment().getHiddenColumns();
+      {
+        int offp = (rseq!=null) ? rseq.findIndex(rseq.getStart()-1+colm) : colm;
+        
+        if (!av.hasHiddenColumns() || hc.isVisible(offp))
+        { 
+          if (cs.contains(offp))
+          {
+            cs.removeElement(offp);
+          } else {
+            cs.addElement(offp);
+          }
         }
       }
+    } 
+  }
 
-      ColourSchemeI cs = null;
-      SequenceGroup _sg = new SequenceGroup(sequences, null, cs, true, true,
-              false, 0, av.getAlignment().getWidth() - 1);
+  public void createSeqGroupFor(AlignmentPanel[] aps, Vector<BinaryNode> l,
+          Color col)
+  {
 
-      _sg.setName("JTreeGroup:" + _sg.hashCode());
-      _sg.setIdColour(col);
+    Vector<SequenceI> sequences = new Vector<>();
 
-      for (int a = 0; a < aps.length; a++)
-      {
-        SequenceGroup sg = new SequenceGroup(_sg);
-        AlignViewport viewport = aps[a].av;
-
-        // Propagate group colours in each view
-        if (viewport.getGlobalColourScheme() != null)
-        {
-          cs = viewport.getGlobalColourScheme().getInstance(viewport, sg);
-          sg.setColourScheme(cs);
-          sg.getGroupColourScheme().setThreshold(
-                  viewport.getResidueShading().getThreshold(),
-                  viewport.isIgnoreGapsConsensus());
+    for (int j = 0; j < l.size(); j++)
+    {
+      SequenceI s1 = (SequenceI) l.elementAt(j).element();
 
-          if (viewport.getResidueShading().conservationApplied())
-          {
-            Conservation c = new Conservation("Group",
-                    sg.getSequences(null), sg.getStartRes(),
-                    sg.getEndRes());
-            c.calculate();
-            c.verdict(false, viewport.getConsPercGaps());
-            sg.cs.setConservation(c);
-          }
-        }
-        // indicate that associated structure views will need an update
-        viewport.setUpdateStructures(true);
-        // propagate structure view update and sequence group to complement view
-        viewport.addSequenceGroup(sg);
+      if (!sequences.contains(s1))
+      {
+        sequences.addElement(s1);
       }
     }
 
-    // notify the panel(s) to redo any group specific stuff
-    // also updates structure views if necessary
+    ColourSchemeI cs = null;
+    SequenceGroup _sg = new SequenceGroup(sequences, null, cs, true, true,
+            false, 0, av.getAlignment().getWidth() - 1);
+
+    _sg.setName("JTreeGroup:" + _sg.hashCode());
+    _sg.setIdColour(col);
+
     for (int a = 0; a < aps.length; a++)
     {
-      aps[a].updateAnnotation();
-      final AlignViewportI codingComplement = aps[a].av
-              .getCodingComplement();
-      if (codingComplement != null)
+      SequenceGroup sg = new SequenceGroup(_sg);
+      AlignViewport viewport = aps[a].av;
+
+      // Propagate group colours in each view
+      if (viewport.getGlobalColourScheme() != null)
       {
-        ((AlignViewport) codingComplement).getAlignPanel()
-                .updateAnnotation();
+        cs = viewport.getGlobalColourScheme().getInstance(viewport, sg);
+        sg.setColourScheme(cs);
+        sg.getGroupColourScheme().setThreshold(
+                viewport.getResidueShading().getThreshold(),
+                viewport.isIgnoreGapsConsensus());
+
+        if (viewport.getResidueShading().conservationApplied())
+        {
+          Conservation c = new Conservation("Group", sg.getSequences(null),
+                  sg.getStartRes(), sg.getEndRes());
+          c.calculate();
+          c.verdict(false, viewport.getConsPercGaps());
+          sg.cs.setConservation(c);
+        }
       }
+      // indicate that associated structure views will need an update
+      viewport.setUpdateStructures(true);
+      // propagate structure view update and sequence group to complement view
+      viewport.addSequenceGroup(sg);
     }
   }
 
index 9a4f2c4..6a07a4e 100755 (executable)
@@ -53,6 +53,7 @@ import jalview.bin.Console;
 import jalview.commands.CommandI;
 import jalview.commands.OrderCommand;
 import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.BinaryNode;
@@ -123,6 +124,30 @@ public class TreePanel extends GTreePanel
     initTreePanel(alignPanel, null, null, newtree, inputData);
   }
 
+  /**
+   * columnwise tree associated with positions in aa
+   * 
+   * @param alignPanel
+   * @param fin
+   * @param title
+   * @param aa
+   */
+  public TreePanel(AlignmentPanel alignPanel, NewickFile fin,
+          AlignmentAnnotation aa, String title)
+  {
+    this(alignPanel, fin, title, null);
+    columnWise=true;
+    assocAnnotation = aa;
+    
+
+  }
+  boolean columnWise=false;
+  AlignmentAnnotation assocAnnotation=null;
+  public boolean getColumnWise()
+  {
+    return columnWise;
+  }
+
   public AlignmentI getAlignment()
   {
     return getTreeCanvas().getViewport().getAlignment();