JAL-674 methods and test for PDB derived annotation stored and recovered in project
[jalview.git] / src / MCview / PDBChain.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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 MCview;
22
23 import jalview.analysis.AlignSeq;
24 import jalview.datamodel.AlignmentAnnotation;
25 import jalview.datamodel.Annotation;
26 import jalview.datamodel.Sequence;
27 import jalview.datamodel.SequenceFeature;
28 import jalview.datamodel.SequenceI;
29 import jalview.schemes.ResidueProperties;
30 import jalview.structure.StructureMapping;
31
32 import java.awt.Color;
33 import java.util.Enumeration;
34 import java.util.List;
35 import java.util.Vector;
36
37 public class PDBChain
38 {
39   /**
40    * SequenceFeature group for PDB File features added to sequences
41    */
42   private static final String PDBFILEFEATURE = "PDBFile";
43
44   private static final String IEASTATUS = "IEA:jalview";
45
46   public String id;
47
48   public Vector bonds = new Vector();
49
50   public Vector atoms = new Vector();
51
52   public Vector residues = new Vector();
53
54   public int offset;
55
56   public Sequence sequence;
57
58   public boolean isNa = false;
59
60   public boolean isVisible = true;
61
62   public int pdbstart = 0;
63
64   public int pdbend = 0;
65
66   public int seqstart = 0;
67
68   public int seqend = 0;
69
70   public String pdbid = "";
71
72   public PDBChain(String pdbid, String id)
73   {
74     this.pdbid = pdbid.toLowerCase();
75     this.id = id;
76   }
77
78   /**
79    * character used to write newlines
80    */
81   protected String newline = System.getProperty("line.separator");
82
83   public void setNewlineString(String nl)
84   {
85     newline = nl;
86   }
87
88   public String getNewlineString()
89   {
90     return newline;
91   }
92
93   public String print()
94   {
95     String tmp = "";
96
97     for (int i = 0; i < bonds.size(); i++)
98     {
99       tmp = tmp + ((Bond) bonds.elementAt(i)).at1.resName + " "
100               + ((Bond) bonds.elementAt(i)).at1.resNumber + " " + offset
101               + newline;
102     }
103
104     return tmp;
105   }
106
107   /**
108    * Annotate the residues with their corresponding positions in s1 using the
109    * alignment in as NOTE: This clears all atom.alignmentMapping values on the
110    * structure.
111    * 
112    * @param as
113    * @param s1
114    */
115   public void makeExactMapping(AlignSeq as, SequenceI s1)
116   {
117     int pdbpos = as.getSeq2Start() - 2;
118     int alignpos = s1.getStart() + as.getSeq1Start() - 3;
119     // first clear out any old alignmentMapping values:
120     for (Atom atom : (Vector<Atom>) atoms)
121     {
122       atom.alignmentMapping = -1;
123     }
124     // and now trace the alignment onto the atom set.
125     for (int i = 0; i < as.astr1.length(); i++)
126     {
127       if (as.astr1.charAt(i) != '-')
128       {
129         alignpos++;
130       }
131
132       if (as.astr2.charAt(i) != '-')
133       {
134         pdbpos++;
135       }
136
137       if (as.astr1.charAt(i) == as.astr2.charAt(i))
138       {
139         Residue res = (Residue) residues.elementAt(pdbpos);
140         Enumeration en = res.atoms.elements();
141         while (en.hasMoreElements())
142         {
143           Atom atom = (Atom) en.nextElement();
144           atom.alignmentMapping = alignpos;
145         }
146       }
147     }
148   }
149
150   /**
151    * copy over the RESNUM seqfeatures from the internal chain sequence to the
152    * mapped sequence
153    * 
154    * @param seq
155    * @param status
156    *          The Status of the transferred annotation
157    * @return the features added to sq (or its dataset)
158    */
159   public SequenceFeature[] transferRESNUMFeatures(SequenceI seq,
160           String status)
161   {
162     SequenceI sq = seq;
163     while (sq != null && sq.getDatasetSequence() != null)
164     {
165       sq = sq.getDatasetSequence();
166       if (sq == sequence)
167       {
168         return null;
169       }
170     }
171     /**
172      * Remove any existing features for this chain if they exist ?
173      * SequenceFeature[] seqsfeatures=seq.getSequenceFeatures(); int
174      * totfeat=seqsfeatures.length; // Remove any features for this exact chain
175      * ? for (int i=0; i<seqsfeatures.length; i++) { }
176      */
177     if (status == null)
178     {
179       status = PDBChain.IEASTATUS;
180     }
181     SequenceFeature[] features = sequence.getSequenceFeatures();
182     for (int i = 0; i < features.length; i++)
183     {
184       if (features[i].getFeatureGroup().equals(pdbid))
185       {
186         SequenceFeature tx = new SequenceFeature(features[i]);
187         tx.setBegin(1 + ((Atom) ((Residue) residues.elementAt(tx.getBegin()
188                 - offset)).atoms.elementAt(0)).alignmentMapping);
189         tx.setEnd(1 + ((Atom) ((Residue) residues.elementAt(tx.getEnd()
190                 - offset)).atoms.elementAt(0)).alignmentMapping);
191         tx.setStatus(status
192                 + ((tx.getStatus() == null || tx.getStatus().length() == 0) ? ""
193                         : ":" + tx.getStatus()));
194         if (tx.begin != 0 && tx.end != 0)
195         {
196           sq.addSequenceFeature(tx);
197         }
198       }
199     }
200     return features;
201   }
202
203   public void makeCaBondList()
204   {
205     boolean na = false;
206     int numNa = 0;
207     for (int i = 0; i < (residues.size() - 1); i++)
208     {
209       Residue tmpres = (Residue) residues.elementAt(i);
210       Residue tmpres2 = (Residue) residues.elementAt(i + 1);
211       Atom at1 = tmpres.findAtom("CA");
212       Atom at2 = tmpres2.findAtom("CA");
213       na = false;
214       if ((at1 == null) && (at2 == null))
215       {
216         na = true;
217         at1 = tmpres.findAtom("P");
218         at2 = tmpres2.findAtom("P");
219       }
220       if ((at1 != null) && (at2 != null))
221       {
222         if (at1.chain.equals(at2.chain))
223         {
224           if (na)
225           {
226             numNa++;
227           }
228           makeBond(at1, at2);
229         }
230       }
231       else
232       {
233         System.out.println("not found " + i);
234       }
235     }
236     if (numNa > 0 && ((numNa / residues.size()) > 0.99))
237     {
238       isNa = true;
239     }
240   }
241
242   public void makeBond(Atom at1, Atom at2)
243   {
244     float[] start = new float[3];
245     float[] end = new float[3];
246
247     start[0] = at1.x;
248     start[1] = at1.y;
249     start[2] = at1.z;
250
251     end[0] = at2.x;
252     end[1] = at2.y;
253     end[2] = at2.z;
254
255     bonds.addElement(new Bond(start, end, at1, at2));
256   }
257
258   public void makeResidueList()
259   {
260     int count = 0;
261     Object symbol;
262     boolean deoxyn = false;
263     boolean nucleotide = false;
264     StringBuffer seq = new StringBuffer();
265     Vector resFeatures = new Vector();
266     Vector resAnnotation = new Vector();
267     int i, iSize = atoms.size() - 1;
268     int resNumber = -1;
269     for (i = 0; i <= iSize; i++)
270     {
271       Atom tmp = (Atom) atoms.elementAt(i);
272       resNumber = tmp.resNumber;
273       int res = resNumber;
274
275       if (i == 0)
276       {
277         offset = resNumber;
278       }
279
280       Vector resAtoms = new Vector();
281       // Add atoms to a vector while the residue number
282       // remains the same as the first atom's resNumber (res)
283       while ((resNumber == res) && (i < atoms.size()))
284       {
285         resAtoms.addElement(atoms.elementAt(i));
286         i++;
287
288         if (i < atoms.size())
289         {
290           resNumber = ((Atom) atoms.elementAt(i)).resNumber;
291         }
292         else
293         {
294           resNumber++;
295         }
296       }
297
298       // We need this to keep in step with the outer for i = loop
299       i--;
300
301       // Make a new Residue object with the new atoms vector
302       residues.addElement(new Residue(resAtoms, resNumber - 1, count));
303
304       Residue tmpres = (Residue) residues.lastElement();
305       Atom tmpat = (Atom) tmpres.atoms.elementAt(0);
306       // Make A new SequenceFeature for the current residue numbering
307       SequenceFeature sf = new SequenceFeature("RESNUM", tmpat.resName
308               + ":" + tmpat.resNumIns + " " + pdbid + id, "", offset
309               + count, offset + count, pdbid);
310       // MCview.PDBChain.PDBFILEFEATURE);
311       resFeatures.addElement(sf);
312       resAnnotation.addElement(new Annotation(tmpat.tfactor));
313       // Keep totting up the sequence
314       if ((symbol = ResidueProperties.getAA3Hash().get(tmpat.resName)) == null)
315       {
316         String nucname = tmpat.resName.trim();
317         // use the aaIndex rather than call 'toLower' - which would take a bit
318         // more time.
319         deoxyn = nucname.length() == 2
320                 && ResidueProperties.aaIndex[nucname.charAt(0)] == ResidueProperties.aaIndex['D'];
321         if (tmpat.name.equalsIgnoreCase("CA")
322                 || ResidueProperties.nucleotideIndex[nucname
323                         .charAt((deoxyn ? 1 : 0))] == -1)
324         {
325           seq.append("X");
326           // System.err.println("PDBReader:Null aa3Hash for " +
327           // tmpat.resName);
328         }
329         else
330         {
331           // nucleotide flag
332           nucleotide = true;
333           seq.append(nucname.charAt((deoxyn ? 1 : 0)));
334         }
335       }
336       else
337       {
338         if (nucleotide)
339         {
340           System.err
341                   .println("Warning: mixed nucleotide and amino acid chain.. its gonna do bad things to you!");
342         }
343         seq.append(ResidueProperties.aa[((Integer) symbol).intValue()]);
344       }
345       count++;
346     }
347
348     if (id.length() < 1)
349     {
350       id = " ";
351     }
352     isNa = nucleotide;
353     sequence = new Sequence(id, seq.toString(), offset, resNumber - 1); // Note:
354     // resNumber-offset
355     // ~=
356     // seq.size()
357     // Add normalised feature scores to RESNUM indicating start/end of sequence
358     // sf.setScore(offset+count);
359
360     // System.out.println("PDB Sequence is :\nSequence = " + seq);
361     // System.out.println("No of residues = " + residues.size());
362     for (i = 0, iSize = resFeatures.size(); i < iSize; i++)
363     {
364       sequence.addSequenceFeature((SequenceFeature) resFeatures
365               .elementAt(i));
366       resFeatures.setElementAt(null, i);
367     }
368     Annotation[] annots = new Annotation[resAnnotation.size()];
369     float max = 0;
370     for (i = 0, iSize = annots.length; i < iSize; i++)
371     {
372       annots[i] = (Annotation) resAnnotation.elementAt(i);
373       if (annots[i].value > max)
374       {
375         max = annots[i].value;
376       }
377       resAnnotation.setElementAt(null, i);
378     }
379     AlignmentAnnotation tfactorann = new AlignmentAnnotation(
380             "PDB.TempFactor", "Temperature Factor for "
381                     + sequence.getName(), annots, 0, max,
382             AlignmentAnnotation.LINE_GRAPH);
383     tfactorann.setSequenceRef(sequence);
384     sequence.addAlignmentAnnotation(tfactorann);
385   }
386
387   public void setChargeColours()
388   {
389     for (int i = 0; i < bonds.size(); i++)
390     {
391       try
392       {
393         Bond b = (Bond) bonds.elementAt(i);
394
395         if (b.at1.resName.equalsIgnoreCase("ASP")
396                 || b.at1.resName.equalsIgnoreCase("GLU"))
397         {
398           b.startCol = Color.red;
399         }
400         else if (b.at1.resName.equalsIgnoreCase("LYS")
401                 || b.at1.resName.equalsIgnoreCase("ARG"))
402         {
403           b.startCol = Color.blue;
404         }
405         else if (b.at1.resName.equalsIgnoreCase("CYS"))
406         {
407           b.startCol = Color.yellow;
408         }
409         else
410         {
411           b.startCol = Color.lightGray;
412         }
413
414         if (b.at2.resName.equalsIgnoreCase("ASP")
415                 || b.at2.resName.equalsIgnoreCase("GLU"))
416         {
417           b.endCol = Color.red;
418         }
419         else if (b.at2.resName.equalsIgnoreCase("LYS")
420                 || b.at2.resName.equalsIgnoreCase("ARG"))
421         {
422           b.endCol = Color.blue;
423         }
424         else if (b.at2.resName.equalsIgnoreCase("CYS"))
425         {
426           b.endCol = Color.yellow;
427         }
428         else
429         {
430           b.endCol = Color.lightGray;
431         }
432       } catch (Exception e)
433       {
434         Bond b = (Bond) bonds.elementAt(i);
435         b.startCol = Color.gray;
436         b.endCol = Color.gray;
437       }
438     }
439   }
440
441   public void setChainColours(jalview.schemes.ColourSchemeI cs)
442   {
443     Bond b;
444     int index;
445     for (int i = 0; i < bonds.size(); i++)
446     {
447       try
448       {
449         b = (Bond) bonds.elementAt(i);
450
451         index = ((Integer) ResidueProperties.aa3Hash.get(b.at1.resName))
452                 .intValue();
453         b.startCol = cs.findColour(ResidueProperties.aa[index].charAt(0));
454
455         index = ((Integer) ResidueProperties.aa3Hash.get(b.at2.resName))
456                 .intValue();
457         b.endCol = cs.findColour(ResidueProperties.aa[index].charAt(0));
458
459       } catch (Exception e)
460       {
461         b = (Bond) bonds.elementAt(i);
462         b.startCol = Color.gray;
463         b.endCol = Color.gray;
464       }
465     }
466   }
467
468   public void setChainColours(Color col)
469   {
470     for (int i = 0; i < bonds.size(); i++)
471     {
472       Bond tmp = (Bond) bonds.elementAt(i);
473       tmp.startCol = col;
474       tmp.endCol = col;
475     }
476   }
477
478   public AlignmentAnnotation[] transferResidueAnnotation(SequenceI seq,
479           String status)
480   {
481     AlignmentAnnotation[] transferred = null;
482
483     return transferred;
484
485   }
486
487   /**
488    * copy any sequence annotation onto the sequence mapped using the provided
489    * StructureMapping
490    * 
491    * @param mapping
492    */
493   public void transferResidueAnnotation(StructureMapping mapping)
494   {
495     SequenceI sq = mapping.getSequence();
496     if (sq != null)
497     {
498       if (sequence != null && sequence.getAnnotation() != null)
499       {
500
501       }
502       float min = -1, max = 0;
503       Annotation[] an = new Annotation[sq.getEnd() - sq.getStart() + 1];
504       for (int i = sq.getStart(), j = sq.getEnd(), k = 0; i <= j; i++, k++)
505       {
506         int prn = mapping.getPDBResNum(k + 1);
507
508         an[k] = new Annotation(prn);
509         if (min == -1)
510         {
511           min = k;
512           max = k;
513         }
514         else
515         {
516           if (min > k)
517           {
518             min = k;
519           }
520           else if (max < k)
521           {
522             max = k;
523           }
524         }
525       }
526       sq.addAlignmentAnnotation(new AlignmentAnnotation("PDB.RESNUM",
527               "PDB Residue Numbering for " + this.pdbid + ":" + this.id,
528               an, min, max, AlignmentAnnotation.LINE_GRAPH));
529
530     }
531   }
532 }