d6eb1ebbbcbf0c3709a71c43a1cce75e62f35852
[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.SequenceI;
25
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29
30 public class StructureMapping
31 {
32   /**
33    * Space character constant, recommended for consistent representation when no
34    * chain specified
35    */
36   public static String NO_CHAIN = " ";
37
38   String mappingDetails;
39
40   SequenceI sequence;
41
42   String pdbfile;
43
44   String pdbid;
45
46   String pdbchain;
47
48   public static final int UNASSIGNED_VALUE = -1;
49
50   private static final int PDB_RES_NUM_INDEX = 0;
51
52   private static final int PDB_ATOM_NUM_INDEX = 1;
53
54   // Mapping key is residue index while value is an array containing PDB resNum,
55   // and atomNo
56   HashMap<Integer, int[]> mapping;
57
58   /**
59    * Constructor
60    * 
61    * @param seq
62    * @param pdbfile
63    * @param pdbid
64    * @param chain
65    * @param mapping
66    *          a map from sequence to two values, { resNo, atomNo } in the
67    *          structure
68    * @param mappingDetails
69    */
70   public StructureMapping(SequenceI seq, String pdbfile, String pdbid,
71           String chain, HashMap<Integer, int[]> mapping,
72           String mappingDetails)
73   {
74     sequence = seq;
75     this.pdbfile = pdbfile;
76     this.pdbid = pdbid;
77     this.pdbchain = chain;
78     this.mapping = mapping;
79     this.mappingDetails = mappingDetails;
80   }
81
82   public SequenceI getSequence()
83   {
84     return sequence;
85   }
86
87   public String getChain()
88   {
89     return pdbchain;
90   }
91
92   public String getPdbId()
93   {
94     return pdbid;
95   }
96
97   /**
98    * 
99    * @param seqpos
100    * @return 0 or corresponding atom number for the sequence position
101    */
102   public int getAtomNum(int seqpos)
103   {
104     int[] resNumAtomMap = mapping.get(seqpos);
105     if (resNumAtomMap != null)
106     {
107       return resNumAtomMap[PDB_ATOM_NUM_INDEX];
108     }
109     else
110     {
111       return UNASSIGNED_VALUE;
112     }
113   }
114
115   /**
116    * 
117    * @param seqpos
118    * @return 0 or the corresponding residue number for the sequence position
119    */
120   public int getPDBResNum(int seqpos)
121   {
122     int[] resNumAtomMap = mapping.get(seqpos);
123     if (resNumAtomMap != null)
124     {
125       return resNumAtomMap[PDB_RES_NUM_INDEX];
126     }
127     else
128     {
129       return UNASSIGNED_VALUE;
130     }
131   }
132
133   /**
134    * Returns a (possibly empty) list of [start, end] residue positions in the
135    * mapped structure, corresponding to the given range of sequence positions
136    * 
137    * @param fromSeqPos
138    * @param toSeqPos
139    * @return
140    */
141   public List<int[]> getPDBResNumRanges(int fromSeqPos, int toSeqPos)
142   {
143     List<int[]> result = new ArrayList<int[]>();
144     int startRes = -1;
145     int endRes = -1;
146
147     for (int i = fromSeqPos; i <= toSeqPos; i++)
148     {
149       int resNo = getPDBResNum(i);
150       if (resNo == UNASSIGNED_VALUE)
151       {
152         continue; // no mapping from this sequence position
153       }
154       if (startRes == -1)
155       {
156         startRes = resNo;
157         endRes = resNo;
158       }
159       if (resNo >= startRes && resNo <= endRes)
160       {
161         // within the current range - no change
162         continue;
163       }
164       if (resNo == startRes - 1)
165       {
166         // extend beginning of current range
167         startRes--;
168         continue;
169       }
170       if (resNo == endRes + 1)
171       {
172         // extend end of current range
173         endRes++;
174         continue;
175       }
176
177       /*
178        * resNo is not within or contiguous with last range,
179        * so write out the last range
180        */
181       result.add(new int[] { startRes, endRes });
182       startRes = resNo;
183       endRes = resNo;
184     }
185
186     /*
187      * and add the last range
188      */
189     if (startRes != -1)
190     {
191       result.add(new int[] { startRes, endRes });
192     }
193
194     return result;
195   }
196
197   /**
198    * 
199    * @param pdbResNum
200    * @return -1 or the corresponding sequence position for a pdb residue number
201    */
202   public int getSeqPos(int pdbResNum)
203   {
204     for (Integer seqPos : mapping.keySet())
205     {
206       if (pdbResNum == getPDBResNum(seqPos))
207       {
208         return seqPos;
209       }
210     }
211     return UNASSIGNED_VALUE;
212   }
213
214   /**
215    * transfer a copy of an alignment annotation row in the PDB chain coordinate
216    * system onto the mapped sequence
217    * 
218    * @param ana
219    * @return the copy that was remapped to the mapped sequence
220    * @note this method will create a copy and add it to the dataset sequence for
221    *       the mapped sequence as well as the mapped sequence (if it is not a
222    *       dataset sequence).
223    */
224   public AlignmentAnnotation transfer(AlignmentAnnotation ana)
225   {
226     AlignmentAnnotation ala_copy = new AlignmentAnnotation(ana);
227     SequenceI ds = sequence;
228     while (ds.getDatasetSequence() != null)
229     {
230       ds = ds.getDatasetSequence();
231     }
232     // need to relocate annotation from pdb coordinates to local sequence
233     // -1,-1 doesn't look at pdbresnum but fails to remap sequence positions...
234
235     ala_copy.remap(ds, mapping, -1, -1, 0);
236     ds.addAlignmentAnnotation(ala_copy);
237     if (ds != sequence)
238     {
239       // mapping wasn't to an original dataset sequence, so we make a copy on
240       // the mapped sequence too
241       ala_copy = new AlignmentAnnotation(ala_copy);
242       sequence.addAlignmentAnnotation(ala_copy);
243     }
244     return ala_copy;
245   }
246
247   public String getMappingDetailsOutput()
248   {
249     return mappingDetails;
250   }
251
252 }