4174f5b8316eb82d900217207a4567d31ea1712d
[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   public static final int UNASSIGNED_VALUE = Integer.MIN_VALUE;
34
35   private static final int PDB_RES_NUM_INDEX = 0;
36
37   private static final int PDB_ATOM_NUM_INDEX = 1;
38
39   String mappingDetails;
40
41   SequenceI sequence;
42
43   String pdbfile;
44
45   String pdbid;
46
47   String pdbchain;
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   /**
56    * Constructor
57    * 
58    * @param seq
59    * @param pdbfile
60    * @param pdbid
61    * @param chain
62    * @param mapping
63    *          a map from sequence to two values, { resNo, atomNo } in the
64    *          structure
65    * @param mappingDetails
66    */
67   public StructureMapping(SequenceI seq, String pdbfile, String pdbid,
68           String chain, HashMap<Integer, int[]> mapping,
69           String mappingDetails)
70   {
71     sequence = seq;
72     this.pdbfile = pdbfile;
73     this.pdbid = pdbid;
74     this.pdbchain = chain;
75     this.mapping = mapping;
76     this.mappingDetails = mappingDetails;
77   }
78
79   public StructureMapping(SequenceI seq, String pdbFile2, String pdbId2,
80           String chain, HashMap<Integer, int[]> mapping2,
81           String mappingOutput, Mapping seqToPdbMapping)
82   {
83     this(seq, pdbFile2, pdbId2, chain, mapping2, mappingOutput);
84     this.seqToPdbMapping = seqToPdbMapping;
85   }
86
87   public SequenceI getSequence()
88   {
89     return sequence;
90   }
91
92   public String getChain()
93   {
94     return pdbchain;
95   }
96
97   public String getPdbId()
98   {
99     return pdbid;
100   }
101
102   /**
103    * 
104    * @param seqpos
105    * @return 0 or corresponding atom number for the sequence position
106    */
107   public int getAtomNum(int seqpos)
108   {
109     int[] resNumAtomMap = mapping.get(seqpos);
110     if (resNumAtomMap != null)
111     {
112       return resNumAtomMap[PDB_ATOM_NUM_INDEX];
113     }
114     else
115     {
116       return UNASSIGNED_VALUE;
117     }
118   }
119
120   /**
121    * 
122    * @param seqpos
123    * @return UNASSIGNED_VALUE or the corresponding residue number for the
124    *         sequence position
125    */
126   public int getPDBResNum(int seqpos)
127   {
128     int[] resNumAtomMap = mapping.get(seqpos);
129     if (resNumAtomMap != null)
130     {
131       return resNumAtomMap[PDB_RES_NUM_INDEX];
132     }
133     else
134     {
135       return UNASSIGNED_VALUE;
136     }
137   }
138
139   /**
140    * Returns a (possibly empty) list of [start, end] residue positions in the
141    * mapped structure, corresponding to the given range of sequence positions
142    * 
143    * @param fromSeqPos
144    * @param toSeqPos
145    * @return
146    */
147   public List<int[]> getPDBResNumRanges(int fromSeqPos, int toSeqPos)
148   {
149     List<int[]> result = new ArrayList<>();
150     int startRes = -1;
151     int endRes = -1;
152
153     for (int i = fromSeqPos; i <= toSeqPos; i++)
154     {
155       int resNo = getPDBResNum(i);
156       if (resNo == UNASSIGNED_VALUE)
157       {
158         continue; // no mapping from this sequence position
159       }
160       if (startRes == -1)
161       {
162         startRes = resNo;
163         endRes = resNo;
164       }
165       if (resNo >= startRes && resNo <= endRes)
166       {
167         // within the current range - no change
168         continue;
169       }
170       if (resNo == startRes - 1)
171       {
172         // extend beginning of current range
173         startRes--;
174         continue;
175       }
176       if (resNo == endRes + 1)
177       {
178         // extend end of current range
179         endRes++;
180         continue;
181       }
182
183       /*
184        * resNo is not within or contiguous with last range,
185        * so write out the last range
186        */
187       result.add(new int[] { startRes, endRes });
188       startRes = resNo;
189       endRes = resNo;
190     }
191
192     /*
193      * and add the last range
194      */
195     if (startRes != -1)
196     {
197       result.add(new int[] { startRes, endRes });
198     }
199
200     return result;
201   }
202
203   /**
204    * 
205    * @param pdbResNum
206    * @return -1 or the corresponding sequence position for a pdb residue number
207    */
208   public int getSeqPos(int pdbResNum)
209   {
210     for (Integer seqPos : mapping.keySet())
211     {
212       if (pdbResNum == getPDBResNum(seqPos))
213       {
214         return seqPos;
215       }
216     }
217     return UNASSIGNED_VALUE;
218   }
219
220   /**
221    * transfer a copy of an alignment annotation row in the PDB chain coordinate
222    * system onto the mapped sequence
223    * 
224    * @param ana
225    * @return the copy that was remapped to the mapped sequence
226    * @note this method will create a copy and add it to the dataset sequence for
227    *       the mapped sequence as well as the mapped sequence (if it is not a
228    *       dataset sequence).
229    */
230   public AlignmentAnnotation transfer(AlignmentAnnotation ana)
231   {
232     AlignmentAnnotation ala_copy = new AlignmentAnnotation(ana);
233     SequenceI ds = sequence;
234     while (ds.getDatasetSequence() != null)
235     {
236       ds = ds.getDatasetSequence();
237     }
238     // need to relocate annotation from pdb coordinates to local sequence
239     // -1,-1 doesn't look at pdbresnum but fails to remap sequence positions...
240
241     ala_copy.remap(ds, mapping, -1, -1, 0);
242     ds.addAlignmentAnnotation(ala_copy);
243     if (ds != sequence)
244     {
245       // mapping wasn't to an original dataset sequence, so we make a copy on
246       // the mapped sequence too
247       ala_copy = new AlignmentAnnotation(ala_copy);
248       sequence.addAlignmentAnnotation(ala_copy);
249     }
250     return ala_copy;
251   }
252
253   public String getMappingDetailsOutput()
254   {
255     return mappingDetails;
256   }
257
258   public HashMap<Integer, int[]> getMapping()
259   {
260     return mapping;
261   }
262
263   public Mapping getSeqToPdbMapping()
264   {
265     return seqToPdbMapping;
266   }
267
268   /**
269    * A hash function that satisfies the contract that if two mappings are
270    * equal(), they have the same hashCode
271    */
272   @Override
273   public int hashCode()
274   {
275     final int prime = 31;
276     int result = 1;
277     result = prime * result
278             + ((mappingDetails == null) ? 0 : mappingDetails.hashCode());
279     result = prime * result
280             + ((pdbchain == null) ? 0 : pdbchain.hashCode());
281     result = prime * result + ((pdbfile == null) ? 0 : pdbfile.hashCode());
282     result = prime * result + ((pdbid == null) ? 0 : pdbid.hashCode());
283     result = prime * result
284             + ((seqToPdbMapping == null) ? 0 : seqToPdbMapping.hashCode());
285     result = prime * result
286             + ((sequence == null) ? 0 : sequence.hashCode());
287     return result;
288   }
289
290   @Override
291   public boolean equals(Object obj)
292   {
293     if (this == obj)
294     {
295       return true;
296     }
297     if (obj == null)
298     {
299       return false;
300     }
301     if (getClass() != obj.getClass())
302     {
303       return false;
304     }
305     StructureMapping other = (StructureMapping) obj;
306     if (mappingDetails == null)
307     {
308       if (other.mappingDetails != null)
309       {
310         return false;
311       }
312     }
313     else if (!mappingDetails.equals(other.mappingDetails))
314     {
315       return false;
316     }
317     if (pdbchain == null)
318     {
319       if (other.pdbchain != null)
320       {
321         return false;
322       }
323     }
324     else if (!pdbchain.equals(other.pdbchain))
325     {
326       return false;
327     }
328     if (pdbfile == null)
329     {
330       if (other.pdbfile != null)
331       {
332         return false;
333       }
334     }
335     else if (!pdbfile.equals(other.pdbfile))
336     {
337       return false;
338     }
339     if (pdbid == null)
340     {
341       if (other.pdbid != null)
342       {
343         return false;
344       }
345     }
346     else if (!pdbid.equals(other.pdbid))
347     {
348       return false;
349     }
350     if (seqToPdbMapping == null)
351     {
352       if (other.seqToPdbMapping != null)
353       {
354         return false;
355       }
356     }
357     else if (!seqToPdbMapping.equals(other.seqToPdbMapping))
358     {
359       return false;
360     }
361     if (sequence != other.sequence)
362     {
363       return false;
364     }
365
366     return true;
367   }
368 }