JAL-2946 catch unexpected SIFTS mapping exceptions and failover to Needleman and...
[jalview.git] / src / jalview / structure / StructureMapping.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.structure;
22
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.Mapping;
25 import jalview.datamodel.SequenceI;
26
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30
31 public class StructureMapping
32 {
33   String mappingDetails;
34
35   SequenceI sequence;
36
37   String pdbfile;
38
39   String pdbid;
40
41   String pdbchain;
42
43   public static final int UNASSIGNED_VALUE = Integer.MIN_VALUE;
44
45   private static final int PDB_RES_NUM_INDEX = 0;
46
47   private static final int PDB_ATOM_NUM_INDEX = 1;
48
49   // Mapping key is residue index while value is an array containing PDB resNum,
50   // and atomNo
51   HashMap<Integer, int[]> mapping;
52
53   jalview.datamodel.Mapping seqToPdbMapping = null;
54   /**
55    * Constructor
56    * 
57    * @param seq
58    * @param pdbfile
59    * @param pdbid
60    * @param chain
61    * @param mapping
62    *          a map from sequence to two values, { resNo, atomNo } in the
63    *          structure
64    * @param mappingDetails
65    */
66   public StructureMapping(SequenceI seq, String pdbfile, String pdbid,
67           String chain, HashMap<Integer, int[]> mapping,
68           String mappingDetails)
69   {
70     sequence = seq;
71     this.pdbfile = pdbfile;
72     this.pdbid = pdbid;
73     this.pdbchain = chain;
74     this.mapping = mapping;
75     this.mappingDetails = mappingDetails;
76   }
77
78   public StructureMapping(SequenceI seq, String pdbFile2, String pdbId2,
79           String chain, HashMap<Integer, int[]> mapping2,
80           String mappingOutput, Mapping seqToPdbMapping)
81   {
82     this(seq, pdbFile2, pdbId2, chain, mapping2, mappingOutput);
83     this.seqToPdbMapping = seqToPdbMapping;
84   }
85
86   public SequenceI getSequence()
87   {
88     return sequence;
89   }
90
91   public String getChain()
92   {
93     return pdbchain;
94   }
95
96   public String getPdbId()
97   {
98     return pdbid;
99   }
100
101   /**
102    * 
103    * @param seqpos
104    * @return 0 or corresponding atom number for the sequence position
105    */
106   public int getAtomNum(int seqpos)
107   {
108     int[] resNumAtomMap = mapping.get(seqpos);
109     if (resNumAtomMap != null)
110     {
111       return resNumAtomMap[PDB_ATOM_NUM_INDEX];
112     }
113     else
114     {
115       return UNASSIGNED_VALUE;
116     }
117   }
118
119   /**
120    * 
121    * @param seqpos
122    * @return UNASSIGNED_VALUE or the corresponding residue number for the
123    *         sequence position
124    */
125   public int getPDBResNum(int seqpos)
126   {
127     int[] resNumAtomMap = mapping.get(seqpos);
128     if (resNumAtomMap != null)
129     {
130       return resNumAtomMap[PDB_RES_NUM_INDEX];
131     }
132     else
133     {
134       return UNASSIGNED_VALUE;
135     }
136   }
137
138   /**
139    * Returns a (possibly empty) list of [start, end] residue positions in the
140    * mapped structure, corresponding to the given range of sequence positions
141    * 
142    * @param fromSeqPos
143    * @param toSeqPos
144    * @return
145    */
146   public List<int[]> getPDBResNumRanges(int fromSeqPos, int toSeqPos)
147   {
148     List<int[]> result = new ArrayList<int[]>();
149     int startRes = -1;
150     int endRes = -1;
151
152     for (int i = fromSeqPos; i <= toSeqPos; i++)
153     {
154       int resNo = getPDBResNum(i);
155       if (resNo == UNASSIGNED_VALUE)
156       {
157         continue; // no mapping from this sequence position
158       }
159       if (startRes == -1)
160       {
161         startRes = resNo;
162         endRes = resNo;
163       }
164       if (resNo >= startRes && resNo <= endRes)
165       {
166         // within the current range - no change
167         continue;
168       }
169       if (resNo == startRes - 1)
170       {
171         // extend beginning of current range
172         startRes--;
173         continue;
174       }
175       if (resNo == endRes + 1)
176       {
177         // extend end of current range
178         endRes++;
179         continue;
180       }
181
182       /*
183        * resNo is not within or contiguous with last range,
184        * so write out the last range
185        */
186       result.add(new int[] { startRes, endRes });
187       startRes = resNo;
188       endRes = resNo;
189     }
190
191     /*
192      * and add the last range
193      */
194     if (startRes != -1)
195     {
196       result.add(new int[] { startRes, endRes });
197     }
198
199     return result;
200   }
201
202   /**
203    * 
204    * @param pdbResNum
205    * @return -1 or the corresponding sequence position for a pdb residue number
206    */
207   public int getSeqPos(int pdbResNum)
208   {
209     for (Integer seqPos : mapping.keySet())
210     {
211       if (pdbResNum == getPDBResNum(seqPos))
212       {
213         return seqPos;
214       }
215     }
216     return UNASSIGNED_VALUE;
217   }
218
219   /**
220    * transfer a copy of an alignment annotation row in the PDB chain coordinate
221    * system onto the mapped sequence
222    * 
223    * @param ana
224    * @return the copy that was remapped to the mapped sequence
225    * @note this method will create a copy and add it to the dataset sequence for
226    *       the mapped sequence as well as the mapped sequence (if it is not a
227    *       dataset sequence).
228    */
229   public AlignmentAnnotation transfer(AlignmentAnnotation ana)
230   {
231     AlignmentAnnotation ala_copy = new AlignmentAnnotation(ana);
232     SequenceI ds = sequence;
233     while (ds.getDatasetSequence() != null)
234     {
235       ds = ds.getDatasetSequence();
236     }
237     // need to relocate annotation from pdb coordinates to local sequence
238     // -1,-1 doesn't look at pdbresnum but fails to remap sequence positions...
239
240     ala_copy.remap(ds, mapping, -1, -1, 0);
241     ds.addAlignmentAnnotation(ala_copy);
242     if (ds != sequence)
243     {
244       // mapping wasn't to an original dataset sequence, so we make a copy on
245       // the mapped sequence too
246       ala_copy = new AlignmentAnnotation(ala_copy);
247       sequence.addAlignmentAnnotation(ala_copy);
248     }
249     return ala_copy;
250   }
251
252   public String getMappingDetailsOutput()
253   {
254     return mappingDetails;
255   }
256
257   public HashMap<Integer, int[]> getMapping()
258   {
259     return mapping;
260   }
261
262   public Mapping getSeqToPdbMapping()
263   {
264     return seqToPdbMapping;
265   }
266 }