Merge branch 'spikes/JAL-4366_3di_msa_forfoldseeks' into spike/JAL-4366_JAL-4386_JAL... spike/JAL-4366_JAL-4386_JAL-4435_expandedmsafromstructure
authorJim Procter <jprocter@dundee.ac.uk>
Fri, 15 Nov 2024 17:08:29 +0000 (17:08 +0000)
committerJim Procter <jprocter@dundee.ac.uk>
Fri, 15 Nov 2024 17:08:29 +0000 (17:08 +0000)
 Conflicts:
resources/lang/Messages.properties
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/scoremodels/SecondaryStructureDistanceModel.java
src/jalview/api/AlignViewportI.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/gui/CalculationChooser.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/workers/SecondaryStructureConsensusThread.java

24 files changed:
1  2 
resources/lang/Messages.properties
src/jalview/analysis/AAFrequency.java
src/jalview/analysis/AlignmentAnnotationUtils.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/scoremodels/SecondaryStructureDistanceModel.java
src/jalview/api/AlignViewportI.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentAnnotation.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/CalculationChooser.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/project/Jalview2XML.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/workers/SecondaryStructureConsensusThread.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/renderer/ResidueShaderTest.java

Simple merge
@@@ -233,10 -233,8 +233,10 @@@ public class AAFrequenc
  
      int seqCount = sequences.length;
  
 +    int seqWithSSCount = 0;
 +
      ProfileI[] result = new ProfileI[width];
+     int maxSSannotcount=0;
      for (int column = start; column < end; column++)
      {
  
@@@ -3144,123 -3220,4 +3280,123 @@@ public class AlignmentUtil
  
    }
  
 +  public static Map<SequenceI, ArrayList<AlignmentAnnotation>> getSequenceAssociatedAlignmentAnnotations(
 +          AlignmentAnnotation[] alignAnnotList, String selectedSSSource)
 +  {
 +
 +    Map<SequenceI, ArrayList<AlignmentAnnotation>> ssAlignmentAnnotationForSequences = new HashMap<SequenceI, ArrayList<AlignmentAnnotation>>();
 +    if (alignAnnotList == null || alignAnnotList.length == 0)
 +    {
 +      return ssAlignmentAnnotationForSequences;
 +    }
 +
 +    for (AlignmentAnnotation aa : alignAnnotList)
 +    {
 +      if (aa.sequenceRef == null)
 +      {
 +        continue;
 +      }
 +
 +      if (isSecondaryStructureFrom(selectedSSSource, aa))
 +      {
 +        ssAlignmentAnnotationForSequences
 +                .computeIfAbsent(aa.sequenceRef.getDatasetSequence(),
 +                        k -> new ArrayList<>())
 +                .add(aa);
 +      }
 +    }
 +
 +    return ssAlignmentAnnotationForSequences;
 +
 +  }
 +
 +  /**
 +   * 
 +   * @param selectedSSSource
 +   * @param aa
 +   * @return true if aa is from a provider or all providers as specified by
 +   *         selectedSSSource
 +   */
 +  public static boolean isSecondaryStructureFrom(String selectedSSSource,
 +          AlignmentAnnotation aa)
 +  {
 +
 +    for (String label : Constants.SECONDARY_STRUCTURE_LABELS.keySet())
 +    {
 +
 +      if (label.equals(aa.label))
 +      {
 +
 +        if (selectedSSSource.equals(Constants.SS_ALL_PROVIDERS))
 +        {
 +          return true;
 +        }
 +        String ssSource = AlignmentUtils
 +                .extractSSSourceFromAnnotationDescription(aa);
 +        if (ssSource != null && ssSource.equals(selectedSSSource))
 +        {
 +          return true;
 +        }
 +      }
 +    }
 +    return false;
 +  }
 +  
 +  // Method to get the key for a given provider value
 +  public static String getSecondaryStructureProviderKey(String providerValue) {
 +      for (Map.Entry<String, String> entry : Constants.STRUCTURE_PROVIDERS.entrySet()) {
 +          if (entry.getValue().equals(providerValue)) {
 +              return entry.getKey();  // Return the key (abbreviation) for the matching provider value
 +          }
 +      }
 +      return null;  // Return null if no match is found
 +  }
 +  
 +  public static String reduceLabelLength(String label) {
 +    // Split the input by " | "
 +    String[] parts = label.split(" \\| ");
 +    
 +    // Map the full names to their abbreviations
 +    String reducedLabel = Arrays.stream(parts)
 +            .map(fullName -> Constants.STRUCTURE_PROVIDERS.entrySet().stream()
 +                    .filter(entry -> entry.getValue().equals(fullName))
 +                    .map(Map.Entry::getKey)
 +                    .findFirst()
 +                    .orElse(fullName)) // Use fullName if no abbreviation is found
 +            .collect(Collectors.joining(" | "));
 +    
 +    return reducedLabel; // Return the reduced label if abbreviations were applied
 +  }
 +  
 +  public static Color getSecondaryStructureProviderColor(String label) {
 +
 +    //return Constants.STRUCTURE_PROVIDERS_COLOR.getOrDefault(label, Color.BLACK);
 +    Color c =  Constants.STRUCTURE_PROVIDERS_COLOR.get(label.trim());
 +    if(c==null)
 +      c = Color.BLACK;
 +    return c;
 +  }
 +  
 +  
 +  public static void assignSecondaryStructureProviderColor(Map<String, Color> secondaryStructureProviderColorMap,
 +          List<String> labels) {
 +    
 +    // Use a Set to track unique labels
 +    Set<String> uniqueLabels = new HashSet<>(labels);
 +    
 +    Color[] palette = ColorBrewer.Paired.getColorPalette(uniqueLabels.size()); 
 +    
 +
 +    List<Color> colorList = new ArrayList<>();
 +    Collections.addAll(colorList, palette);
 +    Collections.shuffle(colorList);
 +    int i = 0;    
 +    
 +    // Loop through each unique label and add it to the map with a color.
 +    for (String label : uniqueLabels) {
 +        // Generate or retrieve a color for the label.
 +      secondaryStructureProviderColorMap.put(label.toUpperCase().trim(), colorList.get(i));
 +      i++;
 +    }
 +  }
- }
+ }
@@@ -382,4 -413,4 +382,4 @@@ public class SecondaryStructureDistance
      return "Score between sequences based on similarity between binary "
              + "vectors marking secondary structure displayed at each column";
    }
--}
++}
@@@ -584,11 -587,9 +588,15 @@@ public interface AlignViewportI extend
     * @return contact matrix or NULL
     */
    ContactMatrixI getContactMatrix(AlignmentAnnotation alignmentAnnotation);
 -  ProfilesI getSequenceSSConsensusHash();
  
 -  boolean is3di();
 +  Map<String, ProfilesI> getSequenceSSConsensusHash();
 +
 +  List<String> getSecondaryStructureSources();
  
 +  void setSecondaryStructureSources(List<String> secondaryStructureSources);
++  
++  boolean is3di();
++  
+   public AlignmentAnnotation getComparisonAnnotation();
 +
  }
Simple merge
@@@ -162,10 -158,10 +164,12 @@@ public class SequenceGroup implements A
  
    private boolean showConsensusHistogram;
  
+   private boolean showSSConsensusHistogram;
    private AnnotatedCollectionI context;
  
 +  public Map<String, ProfilesI> hSSConsensusProfileMap;
 +
    /**
     * Creates a new SequenceGroup object.
     */
        hidereps = seqsel.hidereps;
        showNonconserved = seqsel.showNonconserved;
        showSequenceLogo = seqsel.showSequenceLogo;
++      showSequenceSSLogo = seqsel.showSequenceSSLogo;
        normaliseSequenceLogo = seqsel.normaliseSequenceLogo;
        showConsensusHistogram = seqsel.showConsensusHistogram;
+       showSSConsensusHistogram = seqsel.showSSConsensusHistogram;
        idColour = seqsel.idColour;
        outlineColour = seqsel.outlineColour;
        seqrep = seqsel.seqrep;
@@@ -785,25 -794,9 +794,26 @@@ public abstract class JalviewJmolBindin
          }
          else
          {
 +          PDBEntry ppe = getPdbEntry(pe);
 +          if (ppe == null)
 +          {
 +            Console.warn(
 +                    "Please report under JAL-4440: Unexpected null entry for PDBEntry for a structure (for structure "
 +                            + fileName + ")");
 +
 +            continue;
 +          }
 +          if (ppe.getFile() == null)
 +          {
 +            Console.warn(
 +                    "Please report under JAL-4440: Unexpected null entry for file that we just tried to load into Jmol: pdbEntry: "
 +                            + ppe.toString() + "(for structure " + fileName
 +                            + ")");
 +            continue;
 +          }
-           File fl = new File(getPdbEntry(pe).getFile());
-           matches = fl.equals(new File(fileName));
+           String jvPdbFile = getPdbEntry(pe).getFile();
+           File fl = new File(jvPdbFile);
+           matches = fl.equals(new File(fileName)) || JmolCommands.filePathMatch(fileName, jvPdbFile);
            if (matches)
            {
              foundEntry = true;
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -927,8 -915,19 +929,19 @@@ public class GAlignFrame extends JInter
          showConsensusHistogram_actionPerformed(e);
        }
  
-     });    
+     });
+     showSSConsensusHistogram.setText(
+             MessageManager.getString("label.show_ssconsensus_histogram"));
+     showSSConsensusHistogram.addActionListener(new ActionListener()
+     {
+       @Override
+       public void actionPerformed(ActionEvent e)
+       {
 -        showConsensusHistogram_actionPerformed(e);
++        showSSConsensusHistogram_actionPerformed(e);
+       }
  
+     });
      showSequenceLogo
              .setText(MessageManager.getString("label.show_consensus_logo"));
      showSequenceLogo.addActionListener(new ActionListener()
Simple merge
@@@ -56,7 -58,7 +58,8 @@@ import jalview.io.AppletFormatAdapter
  import jalview.io.DataSourceType;
  import jalview.io.StructureFile;
  import jalview.structure.StructureImportSettings.TFType;
 +import jalview.util.Constants;
+ import jalview.util.MapList;
  import jalview.util.MappingUtils;
  import jalview.util.MessageManager;
  import jalview.util.Platform;
@@@ -597,9 -589,9 +590,10 @@@ public class StructureSelectionManage
        {
          pdbFile = "INLINE" + pdb.getId();
        }
        List<StructureMapping> seqToStrucMapping = new ArrayList<>();
+       List<StructureMapping> foundSiftsMappings = new ArrayList<>();
 +      String provider = null;
        if (isMapUsingSIFTs && seq.isProtein())
        {
          if (progress != null)
                jalview.bin.Console.errPrintln(e.getMessage());
              }
            }
+           // If sifts was successful, add mappings and return
            if (!foundSiftsMappings.isEmpty())
            {
-             seqToStrucMapping.addAll(foundSiftsMappings);
              ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
            }
-           else
-           {
-             StructureMapping nwMapping = getNWMappings(seq, pdbFile,
-                     maxChainId, maxChain, pdb, maxAlignseq);
-             seqToStrucMapping.add(nwMapping);
-             maxChain.transferRESNUMFeatures(seq, null,
-                     pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
-             // "IEA:Jalview" ?
-             maxChain.transferResidueAnnotation(nwMapping, sqmpping);
-             ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
-           }
+         }
+         
+         // If sifts was successful, add mappings and return
+         if (!foundSiftsMappings.isEmpty())
+         {
+           seqToStrucMapping.addAll(foundSiftsMappings);
          }
        }
-       else
+       if (foundSiftsMappings.isEmpty())
        {
-         if (progress != null)
+         // Not doing SIFTS, or SIFTS failed for some reason.
+         
+         // first check if we should use an identity mapping
+         if (idLengthChain != null && maxAlignseq.getS2Coverage() < 0.75)
          {
-           progress.setProgressBar(
-                   MessageManager.getString(
-                           "status.obtaining_mapping_with_nw_alignment"),
-                   progressSessionId);
+           Console.info(
+                   "Assuming 3Dsi identity mapping between structure and sequence");
+           StructureMapping matchMapping = getIdMappings(seq, pdbFile,
+                   idLengthChain.id, idLengthChain, pdb);
+           seqToStrucMapping.add(matchMapping);
+           ds.addPDBId(idLengthChain.sequence.getAllPDBEntries().get(0));
+           Console.info("Mapping added.");
+         }
+         else
+         {
+           if (maxAlignseq.getS1Coverage()<0.15 && maxAlignseq.getS2Coverage()<0.15)
+           {
+             // skip this - the NW alignment is spurious
+             continue;
+           }
+           // Construct a needleman wunsch mapping instead.
+           if (progress != null)
+           {
+             progress.setProgressBar(
+                     MessageManager.getString(
+                             "status.obtaining_mapping_with_nw_alignment"),
+                     progressSessionId);
+           }
+           StructureMapping nwMapping = getNWMappings(seq, pdbFile,
+                   maxChainId, maxChain, pdb, maxAlignseq);
+           seqToStrucMapping.add(nwMapping);
+           ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
          }
-         StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
-                 maxChain, pdb, maxAlignseq);
-         seqToStrucMapping.add(nwMapping);
-         ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
        }
++      ////// PROVIDER transfer metadata to annotation...
++      /////// TODO: USE PDBEntry directly if available !!! JAL-3914
 +      // JAL-4392 TODO: Unable to match PDBProvider with Annotation without
 +      // matching struct file path
 +      String ssAnnotDescriptionInPDB = null;
 +      String ssStructFilePathNameInPDB = pdb.getInFile(); // Structure file name
 +                                                          // in PDB data model
 +      // Secondary structure annotations in pdb data model
 +      AlignmentAnnotation[] ssAnnotationsInPDB = pdb.getSeqs().get(0)
 +              .getAnnotation(Constants.SS_ANNOTATION_LABEL);
 +      if (ssAnnotationsInPDB != null && ssAnnotationsInPDB.length > 0)
 +      {
 +        ssAnnotDescriptionInPDB = ssAnnotationsInPDB[0].description;
 +      }
 +
 +      // Match the PDB entry using file path in the pdb data model and get the
 +      // provider
 +      if (ssStructFilePathNameInPDB != null
 +              && seq.getDatasetSequence() != null)
 +      {
 +        Vector<PDBEntry> pdbEntries = seq.getDatasetSequence()
 +                .getAllPDBEntries();
 +        if (pdbEntries != null)
 +        {
 +          for (PDBEntry pdbEntry : pdbEntries)
 +          {
 +            if (pdbEntry.getFile() != null && ssStructFilePathNameInPDB
 +                    .startsWith(pdbEntry.getFile()))
 +            {
 +              provider = pdbEntry.getProvider();
 +              break;
 +            }
 +          }
 +          // Add provider value as property to the ss annotation
 +          if (provider != null)
 +          {
 +            // TODO - JAL-2880 JAL-4441 this should be applied to all structure
 +            // derived annotations, not just secondary structure!
 +            AlignmentAnnotation[] ssAnnotList = ds
 +                    .getAnnotation(Constants.SS_ANNOTATION_LABEL);
 +            if (ssAnnotList != null)
 +            {
 +              for (AlignmentAnnotation ssAnnot : ssAnnotList)
 +              {
 +                // Match the annotation description with the annotation in pdb
 +                // data object
 +                if (ssAnnot
 +                        .getProperty(Constants.SS_PROVIDER_PROPERTY) == null
 +                        && ssAnnot.description
 +                                .equals(ssAnnotDescriptionInPDB))
 +                {
 +                  ssAnnot.setProperty(Constants.SS_PROVIDER_PROPERTY,
 +                          provider);
 +                }
 +              }
 +            }
 +          }
 +        }
 +      }
 +
        if (forStructureView)
        {
          for (StructureMapping sm : seqToStrucMapping)
@@@ -1006,45 -946,6 +1020,46 @@@ public abstract class AlignmentViewpor
      {
        return;
      }
 +    List<String> ssSources = viewStyle.getSecondaryStructureSources();
 +    if (secondaryStructureConsensus.size() != ssSources.size())
 +    {
 +
 +      for (String source : ssSources)
 +      {
 +        boolean ssConsensusForSourcePresent = false;
 +        for (AlignmentAnnotation aa : secondaryStructureConsensus)
 +        {
 +          if (aa.description.startsWith(source))
 +          {
 +            ssConsensusForSourcePresent = true;
 +            break;
 +          }
 +        }
 +
 +        if (!ssConsensusForSourcePresent)
 +        {
++              // i18n'ed data - this will break when moving jalview projects between places
 +          AlignmentAnnotation ssConsensus = new AlignmentAnnotation(
 +                  MessageManager.getString("label.ssconsensus_label") + " "
 +                          + source,
 +                  source + " "
 +                          + MessageManager
 +                                  .getString("label.ssconsensus_descr"),
 +                  new Annotation[1], 0f, 100f,
 +                  AlignmentAnnotation.BAR_GRAPH);
 +
 +          ssConsensus.hasText = true;
 +          ssConsensus.autoCalculated = true;
 +          secondaryStructureConsensus.add(ssConsensus);
 +          if (showSSConsensus)
 +          {
 +            ssConsensus.visible = true;
 +            alignment.addAnnotation(ssConsensus);
 +
 +          }
 +        }
 +      }
 +    }
      if (calculator.getRegisteredWorkersOfClass(
              SecondaryStructureConsensusThread.class) == null)
      {
        consensus = new AlignmentAnnotation("Consensus",
                MessageManager.getString("label.consensus_descr"),
                new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
 +      setSecondaryStructureSources(alignment.getAlignmentAnnotation());
 +      List<String> secondaryStructureSources = getSecondaryStructureSources();
 +
++      // merge cruft - should we also check 
++      //     if (!alignment.isNucleotide() && showSSConsensus)
++
 +      if (secondaryStructureSources != null)
 +      {
 +
 +        secondaryStructureConsensus = new ArrayList<AlignmentAnnotation>();
 +        for (String ssSource : secondaryStructureSources)
 +        {
 +
 +          AlignmentAnnotation ssConsensus = new AlignmentAnnotation(
 +                  MessageManager.getString("label.ssconsensus_label") + " "
 +                          + ssSource,
 +                  ssSource + " "
 +                          + MessageManager
 +                                  .getString("label.ssconsensus_descr"),
 +                  new Annotation[1], 0f, 100f,
 +                  AlignmentAnnotation.BAR_GRAPH);
 +          secondaryStructureConsensus.add(ssConsensus);
 +        }
  
 +      }
  
        initConsensus(consensus);
 +      initSSConsensus(secondaryStructureConsensus);
        initGapCounts();
        initComplementConsensus();
      }
      }
    }
  
 -  private void initSSConsensus()
 +  private void initSSConsensus(
 +          List<AlignmentAnnotation> secondaryStructureConsensuses)
    {
 -    if (!alignment.isNucleotide() && showSSConsensus)
 +    if (secondaryStructureConsensuses == null)
      {
 -      if (secondaryStructureConsensus == null)
 -      {
 -        secondaryStructureConsensus = new AlignmentAnnotation(
 -                MessageManager.getString("label.ssconsensus_label"),
 -                MessageManager.getString("label.ssconsensus_descr"),
 -                new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
 +      return;
 +    }
++//    merge cruft - do we need ?
++    // if (!alignment.isNucleotide() && showSSConsensus)
++//    {
 -        secondaryStructureConsensus.hasText = true;
 -        secondaryStructureConsensus.autoCalculated = true;
 +    for (AlignmentAnnotation aa : secondaryStructureConsensuses)
 +    {
 +      aa.hasText = true;
 +      aa.autoCalculated = true;
 +
 +      if (showSSConsensus)
 +      {
 +        alignment.addAnnotation(aa);
        }
 -      alignment.addAnnotation(secondaryStructureConsensus);
 +
      }
    }
  
@@@ -240,36 -179,14 +240,38 @@@ public class SecondaryStructureConsensu
  
    public void updateResultAnnotation(boolean immediate)
    {
 -    AlignmentAnnotation ssConsensus = getSSConsensusAnnotation();
 -    ProfilesI hSSConsensus = (ProfilesI) getViewportSSConsensus();
 -    if (immediate || !calcMan.isWorking(this) && ssConsensus != null
 -            && hSSConsensus != null)
 +    List<AlignmentAnnotation> ssConsensuses = getSSConsensusAnnotation();
 +    Map<String, ProfilesI> ssConsensusProfileMap = getViewportSSConsensus();
 +    for (AlignmentAnnotation ssConsensus : ssConsensuses)
      {
 -      deriveSSConsensus(ssConsensus, hSSConsensus);
 -      
 -      ssConsensus.hasData=hSSConsensus.getCount()>0;
 +      ProfilesI ssConsensusProfile = null;
 +      for (String source : ssConsensusProfileMap.keySet())
 +      {
 +        if (ssConsensus.description.startsWith(source))
 +        {
 +          ssConsensusProfile = ssConsensusProfileMap.get(source);
 +          break;
 +        }
 +      }
 +      if (ssConsensusProfile == null)
 +      {
 +        continue;
 +      }
 +      if (immediate || !calcMan.isWorking(this) && ssConsensus != null
 +              && ssConsensusProfile != null)
 +      {
 +        if (ssConsensusProfile.get(0) != null)
 +          ssConsensus.setNoOfSequencesIncluded(
 +                  ssConsensusProfile.get(0).getSeqWithSSCount());
 +        deriveSSConsensus(ssConsensus, ssConsensusProfile);
++        ssConsensus.hasData=ssConsensusProfile.getCount()>0;
++        
 +        AlignmentAnnotation gap = getGapAnnotation();
 +        if (gap != null)
 +        {
 +          deriveGap(gap, ssConsensusProfile);
 +        }
 +      }
      }
    }