JAL-4366 hacked in a 1:1 mapping
authorJames Procter <j.procter@dundee.ac.uk>
Fri, 15 Dec 2023 14:17:24 +0000 (14:17 +0000)
committerJames Procter <j.procter@dundee.ac.uk>
Fri, 15 Dec 2023 14:17:24 +0000 (14:17 +0000)
src/jalview/structure/StructureSelectionManager.java

index ec3e0a0..ad5da67 100644 (file)
@@ -54,6 +54,7 @@ import jalview.io.AppletFormatAdapter;
 import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
 import jalview.structure.StructureImportSettings.TFType;
+import jalview.util.MapList;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
@@ -558,6 +559,7 @@ public class StructureSelectionManager
       String maxChainId = " ";
       PDBChain maxChain = null;
       boolean first = true;
+      PDBChain idLengthChain = null;
       for (PDBChain chain : pdb.getChains())
       {
         if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
@@ -570,10 +572,11 @@ public class StructureSelectionManager
         final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
         AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
                 type);
-        // equivalent to:
-        // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
-        // as.calcScoreMatrix();
-        // as.traceAlignment();
+        // TODO: JAL-4366 determinine of a crummy alignment but exact match should make this chain the one to be mapped to a 3di sequence
+        if (as.s1str.length() == 4 + as.s2str.length())
+        {
+          idLengthChain = chain;
+        }
 
         if (first || as.maxscore > max
                 || (as.maxscore == max && chain.id.equals(targetChainId)))
@@ -589,13 +592,12 @@ public class StructureSelectionManager
       {
         continue;
       }
-
       if (sourceType == DataSourceType.PASTE)
       {
         pdbFile = "INLINE" + pdb.getId();
       }
-
       List<StructureMapping> seqToStrucMapping = new ArrayList<>();
+
       if (isMapUsingSIFTs && seq.isProtein())
       {
         if (progress != null)
@@ -670,6 +672,9 @@ public class StructureSelectionManager
           }
           else
           {
+            /*
+             * fallback to NeedlemanWunch.
+             */
             StructureMapping nwMapping = getNWMappings(seq, pdbFile,
                     maxChainId, maxChain, pdb, maxAlignseq);
             seqToStrucMapping.add(nwMapping);
@@ -683,17 +688,33 @@ public class StructureSelectionManager
       }
       else
       {
-        if (progress != null)
+        // Not doing SIFTS
+        // first check if we should use an identity mapping
+        if (idLengthChain != null && maxAlignseq.getS2Coverage() < 0.5)
         {
-          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
+        {
+          // 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));
       }
       if (forStructureView)
       {
@@ -795,6 +816,17 @@ public class StructureSelectionManager
     }
     return curChainMapping;
   }
+  
+  /**
+   * construct a mapping based on a pairwise alignment of the sequence and chain
+   * @param seq
+   * @param pdbFile
+   * @param maxChainId
+   * @param maxChain
+   * @param pdb
+   * @param maxAlignseq
+   * @return
+   */
 
   private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
           String maxChainId, PDBChain maxChain, StructureFile pdb,
@@ -876,6 +908,82 @@ public class StructureSelectionManager
     maxChain.transferResidueAnnotation(nwMapping, sqmpping);
     return nwMapping;
   }
+  
+  /**
+   * construct a 1:1 mapping using given residue and sequence numbering 
+   * @param seq
+   * @param pdbFile
+   * @param identityChainId
+   * @param identityChain
+   * @param pdb
+   * @return
+   */
+
+  private StructureMapping getIdMappings(SequenceI seq, String pdbFile,
+          String identityChainId, PDBChain identityChain, StructureFile pdb)
+  {
+    final StringBuilder mappingDetails = new StringBuilder(128);
+    mappingDetails.append(NEWLINE)
+            .append("Sequence \u27f7 Structure mapping details");
+    mappingDetails.append(NEWLINE);
+    mappingDetails.append("Method: Matching length 1:1");
+    mappingDetails.append(NEWLINE).append("PDB Sequence is :")
+            .append(NEWLINE).append("Sequence = ")
+            .append(identityChain.sequence.getSequenceAsString());
+    mappingDetails.append(NEWLINE).append("No of residues = ")
+            .append(identityChain.residues.size()).append(NEWLINE)
+            .append(NEWLINE);
+
+    mappingDetails.append(NEWLINE)
+            .append("Aligned Sequence is: " + seq.getDisplayId(true));
+    mappingDetails.append(NEWLINE)
+            .append("Sequence = " + seq.getSequenceAsString());
+
+    int from = Math.max(seq.getStart(),identityChain.sequence.getStart());
+    int to = Math.min(seq.getEnd(), identityChain.sequence.getEnd());
+    jalview.datamodel.Mapping sqmpping = new jalview.datamodel.Mapping(seq,
+            new MapList(new int[]
+            { from,to },
+                    new int[]
+                    { from,to },
+                    1, 1));
+    identityChain.mapChainWith(sqmpping, seq);
+
+    identityChain.transferRESNUMFeatures(seq, null,
+            pdb.getId().toLowerCase(Locale.ROOT));
+
+    // Construct mapping
+    // TODO REFACTOR TO PDBChain as a builder
+    HashMap<Integer, int[]> mapping = new HashMap<>();
+    int resNum = -10000;
+    int index = 0;
+    char insCode = ' ';
+
+    do
+    {
+      Atom tmp = identityChain.atoms.elementAt(index);
+      if ((resNum != tmp.resNumber || insCode != tmp.insCode)
+              && tmp.alignmentMapping != -1)
+      {
+        resNum = tmp.resNumber;
+        insCode = tmp.insCode;
+        if (tmp.alignmentMapping >= -1)
+        {
+          mapping.put(tmp.alignmentMapping + 1,
+                  new int[]
+                  { tmp.resNumber, tmp.atomIndex });
+        }
+      }
+
+      index++;
+    } while (index < identityChain.atoms.size());
+
+    StructureMapping idMapping = new StructureMapping(seq, pdbFile,
+            pdb.getId(), identityChainId, mapping,
+            mappingDetails.toString());
+    identityChain.transferResidueAnnotation(idMapping, sqmpping);
+    return idMapping;
+  }
 
   public void removeStructureViewerListener(Object svl, String[] pdbfiles)
   {