Merge branch 'develop' into trialMerge
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 6 Jun 2019 13:33:40 +0000 (14:33 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 6 Jun 2019 13:33:40 +0000 (14:33 +0100)
Conflicts:
src/jalview/datamodel/features/SequenceFeatures.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/SeqPanel.java
src/jalview/project/Jalview2XML.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
test/jalview/io/Jalview2xmlTests.java

18 files changed:
1  2 
src/jalview/analysis/scoremodels/FeatureDistanceModel.java
src/jalview/api/FeatureRenderer.java
src/jalview/appletgui/SeqPanel.java
src/jalview/datamodel/features/SequenceFeatures.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/FeatureTypeSettings.java
src/jalview/gui/SeqPanel.java
src/jalview/io/vcf/VCFLoader.java
src/jalview/project/Jalview2XML.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
test/jalview/gui/FeatureSettingsTest.java
test/jalview/io/FeaturesFileTest.java
test/jalview/project/Jalview2xmlTests.java
test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java

Simple merge
Simple merge
@@@ -26,8 -25,7 +25,6 @@@ import jalview.io.gff.SequenceOntologyF
  import jalview.io.gff.SequenceOntologyI;
  
  import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Comparator;
 -import java.util.Arrays;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
   */
  package jalview.gui;
  
 +import jalview.api.AlignViewportI;
  import jalview.api.FeatureColourI;
  import jalview.api.FeatureSettingsControllerI;
 +import jalview.api.ViewStyleI;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.SequenceI;
+ import jalview.datamodel.features.FeatureMatcher;
  import jalview.datamodel.features.FeatureMatcherI;
  import jalview.datamodel.features.FeatureMatcherSet;
  import jalview.datamodel.features.FeatureMatcherSetI;
@@@ -389,8 -398,8 +409,6 @@@ public class FeatureSettings extends JP
            final Object typeCol, final Map<String, float[][]> minmax, int x,
            int y)
    {
--    final FeatureColourI featureColour = (FeatureColourI) typeCol;
--
      JPopupMenu men = new JPopupMenu(MessageManager
              .formatMessage("label.settings_for_param", new String[]
              { type }));
    }
  
    /**
 +   * Repaints alignment, structure and overview (if shown). If there is a
 +   * complementary view which is showing this view's features, then also
 +   * repaints that.
 +   */
 +  void refreshDisplay()
 +  {
 +    af.alignPanel.paintAlignment(true, true);
 +    AlignViewportI complement = af.getViewport().getCodingComplement();
 +    if (complement != null && complement.isShowComplementFeatures())
 +    {
 +      AlignFrame af2 = Desktop.getAlignFrameFor(complement);
 +      af2.alignPanel.paintAlignment(true, true);
 +    }
 +  }
 +
++  /**
+    * Answers a suitable tooltip to show on the colour cell of the table
+    * 
+    * @param fcol
+    * @param withHint
 -   *          if true include 'click to edit' and similar text
++   *                   if true include 'click to edit' and similar text
+    * @return
+    */
+   public static String getColorTooltip(FeatureColourI fcol,
+           boolean withHint)
+   {
+     if (fcol == null)
+     {
+       return null;
+     }
+     if (fcol.isSimpleColour())
+     {
+       return withHint ? BASE_TOOLTIP : null;
+     }
+     String description = fcol.getDescription();
+     description = description.replaceAll("<", "&lt;");
+     description = description.replaceAll(">", "&gt;");
+     StringBuilder tt = new StringBuilder(description);
+     if (withHint)
+     {
+       tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
+     }
+     return JvSwingUtils.wrapTooltip(true, tt.toString());
+   }
+   public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
+           int w, int h)
+   {
+     boolean thr = false;
+     StringBuilder tx = new StringBuilder();
+   
+     if (gcol.isColourByAttribute())
+     {
+       tx.append(FeatureMatcher
+               .toAttributeDisplayName(gcol.getAttributeName()));
+     }
+     else if (!gcol.isColourByLabel())
+     {
+       tx.append(MessageManager.getString("label.score"));
+     }
+     tx.append(" ");
+     if (gcol.isAboveThreshold())
+     {
+       thr = true;
+       tx.append(">");
+     }
+     if (gcol.isBelowThreshold())
+     {
+       thr = true;
+       tx.append("<");
+     }
+     if (gcol.isColourByLabel())
+     {
+       if (thr)
+       {
+         tx.append(" ");
+       }
+       if (!gcol.isColourByAttribute())
+       {
+         tx.append("Label");
+       }
+       comp.setIcon(null);
+     }
+     else
+     {
+       Color newColor = gcol.getMaxColour();
+       comp.setBackground(newColor);
+       // System.err.println("Width is " + w / 2);
+       Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
+       comp.setIcon(ficon);
+       // tt+="RGB value: Max (" + newColor.getRed() + ", "
+       // + newColor.getGreen() + ", " + newColor.getBlue()
+       // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
+       // + ", " + minCol.getBlue() + ")");
+     }
+     comp.setHorizontalAlignment(SwingConstants.CENTER);
+     comp.setText(tx.toString());
+   }
    // ///////////////////////////////////////////////////////////////////////
    // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
    // ///////////////////////////////////////////////////////////////////////
   */
  package jalview.gui;
  
 +import jalview.api.AlignViewportI;
  import jalview.api.AlignmentViewPanel;
  import jalview.api.FeatureColourI;
+ import jalview.bin.Cache;
  import jalview.datamodel.GraphLine;
  import jalview.datamodel.features.FeatureAttributes;
  import jalview.datamodel.features.FeatureAttributes.Datatype;
@@@ -49,7 -49,7 +50,8 @@@ import jalview.util.MappingUtils
  import jalview.util.MessageManager;
  import jalview.util.Platform;
  import jalview.viewmodel.AlignmentViewport;
+ import jalview.viewmodel.ViewportRanges;
 +import jalview.viewmodel.seqfeatures.FeatureRendererModel;
  
  import java.awt.BorderLayout;
  import java.awt.Color;
Simple merge
@@@ -58,7 -58,7 +58,6 @@@ import jalview.gui.AlignmentPanel
  import jalview.gui.AppVarna;
  import jalview.gui.ChimeraViewFrame;
  import jalview.gui.Desktop;
- import jalview.gui.Jalview2XML_V1;
 -import jalview.gui.FeatureRenderer;
  import jalview.gui.JvOptionPane;
  import jalview.gui.OOMWarning;
  import jalview.gui.PCAPanel;
@@@ -22,14 -22,10 +22,14 @@@ package jalview.renderer.seqfeatures
  
  import jalview.api.AlignViewportI;
  import jalview.api.FeatureColourI;
+ import jalview.datamodel.ContiguousI;
 +import jalview.datamodel.MappedFeatures;
- import jalview.datamodel.Range;
  import jalview.datamodel.SequenceFeature;
  import jalview.datamodel.SequenceI;
 +import jalview.gui.AlignFrame;
 +import jalview.gui.Desktop;
  import jalview.util.Comparison;
 +import jalview.util.ReverseListIterator;
  import jalview.viewmodel.seqfeatures.FeatureRendererModel;
  
  import java.awt.AlphaComposite;
@@@ -276,9 -272,8 +276,9 @@@ public class FeatureRenderer extends Fe
      /*
       * if columns are all gapped, or sequence has no features, nothing to do
       */
-     Range visiblePositions = seq.findPositions(start+1, end+1);
-     if (visiblePositions == null || (!seq.getFeatures().hasFeatures()
-             && !av.isShowComplementFeatures()))
+     ContiguousI visiblePositions = seq.findPositions(start + 1, end + 1);
 -    if (visiblePositions == null || !seq.getFeatures().hasFeatures())
++    if (visiblePositions == null || !seq.getFeatures().hasFeatures()
++            && !av.isShowComplementFeatures())
      {
        return null;
      }
    }
  
    /**
 +   * Find any features on the CDS/protein complement of the sequence region and
 +   * draw them, with visibility and colouring as configured in the complementary
 +   * viewport
 +   * 
 +   * @param g
 +   * @param seq
 +   * @param start
 +   * @param end
 +   * @param y1
 +   * @param colourOnly
 +   * @param visiblePositions
 +   * @param drawnColour
 +   * @return
 +   */
 +  Color drawComplementFeatures(final Graphics g, final SequenceI seq,
 +          int start, int end, int y1, boolean colourOnly,
-           Range visiblePositions, Color drawnColour)
++          ContiguousI visiblePositions, Color drawnColour)
 +  {
 +    AlignViewportI comp = av.getCodingComplement();
 +    FeatureRenderer fr2 = Desktop.getAlignFrameFor(comp)
 +            .getFeatureRenderer();
-     for (int pos = visiblePositions.start; pos <= visiblePositions.end; pos++)
++
++    final int visibleStart = visiblePositions.getBegin();
++    final int visibleEnd = visiblePositions.getEnd();
++
++    for (int pos = visibleStart; pos <= visibleEnd; pos++)
 +    {
 +      int column = seq.findIndex(pos);
 +      MappedFeatures mf = fr2
 +              .findComplementFeaturesAtResidue(seq, pos);
 +      if (mf != null)
 +      {
 +        for (SequenceFeature sf : mf.features)
 +        {
 +          FeatureColourI fc = fr2.getFeatureStyle(sf.getType());
 +          Color featureColour = fr2.getColor(sf, fc);
 +          renderFeature(g, seq, column - 1, column - 1, featureColour,
 +                  start, end, y1, colourOnly);
 +          drawnColour = featureColour;
 +        }
 +      }
 +    }
 +    return drawnColour;
 +  }
 +
 +  /**
     * Called when alignment in associated view has new/modified features to
     * discover and display.
     * 
@@@ -1155,98 -1156,32 +1161,126 @@@ public abstract class FeatureRendererMo
      return filter == null ? true : filter.matches(sf);
    }
  
 +  /**
 +   * Answers a bean containing a mapping, and a list of features in this
 +   * alignment at a position (or range) which is mappable from the given
 +   * sequence residue position in a mapped alignment. Features are returned in
 +   * render order of feature type (on top last), with order within feature type
 +   * undefined. If no features or mapping are found, answers null.
 +   * 
 +   * @param sequence
 +   * @param pos
 +   * @return
 +   */
 +  public MappedFeatures findComplementFeaturesAtResidue(SequenceI sequence,
 +          int pos)
 +  {
 +    SequenceI ds = sequence.getDatasetSequence();
 +    if (ds == null)
 +    {
 +      ds = sequence;
 +    }
 +    final char residue = ds.getCharAt(pos - ds.getStart());
 +
 +    List<SequenceFeature> found = new ArrayList<>();
 +    List<AlignedCodonFrame> mappings = this.av.getAlignment()
 +            .getCodonFrame(sequence);
 +
 +    /*
 +     * todo: direct lookup of CDS for peptide and vice-versa; for now,
 +     * have to search through an unordered list of mappings for a candidate
 +     */
 +    Mapping mapping = null;
 +    SequenceI mapFrom = null;
 +
 +    for (AlignedCodonFrame acf : mappings)
 +    {
 +      mapping = acf.getMappingForSequence(sequence, true);
 +      if (mapping == null || mapping.getMap().getFromRatio() == mapping
 +              .getMap().getToRatio())
 +      {
 +        continue; // we are only looking for 3:1 or 1:3 mappings
 +      }
 +      SearchResultsI sr = new SearchResults();
 +      acf.markMappedRegion(ds, pos, sr);
 +      for (SearchResultMatchI match : sr.getResults())
 +      {
 +        int fromRes = match.getStart();
 +        int toRes = match.getEnd();
 +        mapFrom = match.getSequence();
 +        List<SequenceFeature> fs = findFeaturesAtResidue(
 +                match.getSequence(), fromRes, toRes);
 +        for (SequenceFeature sf : fs)
 +        {
 +          if (!found.contains(sf))
 +          {
 +            found.add(sf);
 +          }
 +        }
 +      }
 +
 +      /*
 +       * just take the first mapped features we find
 +       */
 +      if (!found.isEmpty())
 +      {
 +        break;
 +      }
 +    }
 +    if (found.isEmpty())
 +    {
 +      return null;
 +    }
 +
 +    /*
 +     * sort by renderorder, inefficiently
 +     */
 +    List<SequenceFeature> result = new ArrayList<>();
 +    for (String type : renderOrder)
 +    {
 +      for (SequenceFeature sf : found)
 +      {
 +        if (type.equals(sf.getType()))
 +        {
 +          result.add(sf);
 +          if (result.size() == found.size())
 +          {
 +            return new MappedFeatures(mapping, mapFrom, pos, residue,
 +                    result);
 +          }
 +        }
 +      }
 +    }
 +    
 +    return new MappedFeatures(mapping, mapFrom, pos, residue, result);
 +  }
 +
+   @Override
+   public boolean isVisible(SequenceFeature feature)
+   {
+     if (feature == null)
+     {
+       return false;
+     }
+     if (getFeaturesDisplayed() == null
+             || !getFeaturesDisplayed().isVisible(feature.getType()))
+     {
+       return false;
+     }
+     if (featureGroupNotShown(feature))
+     {
+       return false;
+     }
+     FeatureColourI fc = featureColours.get(feature.getType());
+     if (fc != null && fc.isOutwithThreshold(feature))
+     {
+       return false;
+     }
+     if (!featureMatchesFilters(feature))
+     {
+       return false;
+     }
+     return true;
+   }
  }
@@@ -13,8 -13,8 +13,9 @@@ import jalview.datamodel.features.Featu
  import jalview.io.DataSourceType;
  import jalview.io.FileLoader;
  import jalview.schemes.FeatureColour;
+ import jalview.schemes.FeatureColourTest;
  import jalview.util.matcher.Condition;
 +import jalview.viewmodel.seqfeatures.FeatureRendererModel;
  
  import java.awt.Color;
  import java.io.File;
@@@ -525,13 -521,40 +521,40 @@@ public class FeaturesFileTes
              + "Pfam\tff0000\n"
              + "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
              + "\nSTARTGROUP\tuniprot\n"
 -            + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
              + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\t0.0\n"
              + "<html>Pfam domain<a href=\"http://pfam.xfam.org/family/PF00111\">Pfam_3_4</a></html>\tFER_CAPAA\t-1\t20\t20\tPfam\t0.0\n"
 +            + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
              + "ENDGROUP\tuniprot\n"
-             // null / empty group features output after features in named
-             // groups:
+             // null / empty group features are output after named groups
+             + "\ndesc2\tFER_CAPAN\t-1\t4\t9\tPfam\n"
+             + "\ndesc4\tFER1_SOLLC\t-1\t5\t8\tPfam\t-2.6\n";
+     assertEquals(expected, exported);
+     /*
+      * hide uniprot group
+      */
+     fr.setGroupVisibility("uniprot", false);
+     expected = "METAL\tcc9900\n" + "Pfam\tff0000\n"
+             + "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
+             + "\ndesc2\tFER_CAPAN\t-1\t4\t9\tPfam\n"
+             + "\ndesc4\tFER1_SOLLC\t-1\t5\t8\tPfam\t-2.6\n";
+     exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
+             false);
+     assertEquals(expected, exported);
+     /*
+      * include non-positional (overrides group not shown)
+      */
+     exported = featuresFile.printJalviewFormat(al.getSequencesArray(), fr,
+             true);
+     expected = "METAL\tcc9900\n" + "Pfam\tff0000\n"
+             + "GAMMA-TURN\tscore|ff0000|00ffff|noValueMin|20.0|95.0|below|66.0\n"
+             + "\nSTARTGROUP\tuniprot\n"
+             + "Cath\tFER_CAPAA\t-1\t0\t0\tDomain\t0.0\n"
+             + "ENDGROUP\tuniprot\n"
+             + "\ndesc1\tFER_CAPAN\t-1\t0\t0\tPfam\t1.3\n"
              + "desc2\tFER_CAPAN\t-1\t4\t9\tPfam\n"
+             + "\ndesc3\tFER1_SOLLC\t-1\t0\t0\tPfam\n"
              + "desc4\tFER1_SOLLC\t-1\t5\t8\tPfam\t-2.6\n";
      assertEquals(expected, exported);
    }