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