001/* 002 * PDB web development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * 015 * Created on Jun 13, 2009 016 * Created by Andreas Prlic 017 * 018 */ 019 020package org.biojava.nbio.structure.align.gui; 021 022import java.awt.Dimension; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.List; 026 027import javax.swing.Box; 028import javax.swing.JFrame; 029import javax.swing.JMenuBar; 030import javax.swing.JScrollPane; 031 032import org.biojava.nbio.structure.Atom; 033import org.biojava.nbio.structure.Group; 034import org.biojava.nbio.structure.Structure; 035import org.biojava.nbio.structure.StructureException; 036import org.biojava.nbio.structure.StructureTools; 037import org.biojava.nbio.structure.align.gui.aligpanel.AligPanel; 038import org.biojava.nbio.structure.align.gui.aligpanel.StatusDisplay; 039import org.biojava.nbio.structure.align.gui.jmol.AbstractAlignmentJmol; 040import org.biojava.nbio.structure.align.gui.jmol.JmolTools; 041import org.biojava.nbio.structure.align.gui.jmol.StructureAlignmentJmol; 042import org.biojava.nbio.structure.align.model.AFPChain; 043import org.biojava.nbio.structure.align.util.AFPAlignmentDisplay; 044import org.biojava.nbio.structure.align.util.AlignmentTools; 045import org.slf4j.Logger; 046import org.slf4j.LoggerFactory; 047 048/** A utility class for visualistion of structure alignments 049 * 050 * @author Andreas Prlic 051 * 052 */ 053public class DisplayAFP { 054 055 private static final Logger logger = LoggerFactory.getLogger(DisplayAFP.class); 056 057 //TODO: same as getEqrPos??? !!! 058 public static final List<Integer> getEQRAlignmentPos(AFPChain afpChain){ 059 List<Integer> lst = new ArrayList<Integer>(); 060 061 char[] s1 = afpChain.getAlnseq1(); 062 char[] s2 = afpChain.getAlnseq2(); 063 char[] symb = afpChain.getAlnsymb(); 064 boolean isFatCat = afpChain.getAlgorithmName().startsWith("jFatCat"); 065 066 for ( int i =0 ; i< s1.length; i++){ 067 char c1 = s1[i]; 068 char c2 = s2[i]; 069 070 if ( isAlignedPosition(i,c1,c2,isFatCat, symb)) { 071 lst.add(i); 072 } 073 074 } 075 return lst; 076 077 } 078 079 080 081 082 083 private static boolean isAlignedPosition(int i, char c1, char c2, boolean isFatCat,char[]symb) 084 { 085// if ( isFatCat){ 086 char s = symb[i]; 087 if ( c1 != '-' && c2 != '-' && s != ' '){ 088 return true; 089 } 090// } else { 091// 092// if ( c1 != '-' && c2 != '-') 093// return true; 094// } 095 096 return false; 097 098 099 } 100 101 /** 102 * Return a list of pdb Strings corresponding to the aligned positions of the molecule. 103 * Only supports a pairwise alignment with the AFPChain DS. 104 * 105 * @param aligPos 106 * @param afpChain 107 * @param ca 108 */ 109 public static final List<String> getPDBresnum(int aligPos, AFPChain afpChain, Atom[] ca){ 110 List<String> lst = new ArrayList<String>(); 111 if ( aligPos > 1) { 112 System.err.println("multiple alignments not supported yet!"); 113 return lst; 114 } 115 116 int blockNum = afpChain.getBlockNum(); 117 int[] optLen = afpChain.getOptLen(); 118 int[][][] optAln = afpChain.getOptAln(); 119 120 if ( optLen == null) 121 return lst; 122 123 for(int bk = 0; bk < blockNum; bk ++) { 124 125 for ( int i=0;i< optLen[bk];i++){ 126 127 int pos = optAln[bk][aligPos][i]; 128 if ( pos < ca.length) { 129 String pdbInfo = JmolTools.getPdbInfo(ca[pos]); 130 //lst.add(ca1[pos].getParent().getPDBCode()); 131 lst.add(pdbInfo); 132 } 133 } 134 135 } 136 return lst; 137 } 138 139 140 /** 141 * Return the atom at alignment position aligPos. at the present only works with block 0 142 * @param chainNr the number of the aligned pair. 0... first chain, 1... second chain. 143 * @param afpChain an afpChain object 144 * @param aligPos position on the alignment 145 * @param getPrevious gives the previous position if false, gives the next posible atom 146 * @return a CA atom that is at a particular position of the alignment 147 */ 148 public static final Atom getAtomForAligPos(AFPChain afpChain,int chainNr, int aligPos, Atom[] ca , boolean getPrevious ) throws StructureException{ 149 int[] optLen = afpChain.getOptLen(); 150 // int[][][] optAln = afpChain.getOptAln(); 151 152 if ( optLen == null) 153 return null; 154 155 if (chainNr < 0 || chainNr > 1){ 156 throw new StructureException("So far only pairwise alignments are supported, but you requested results for alinged chain nr " + chainNr); 157 } 158 159 //if ( afpChain.getAlgorithmName().startsWith("jFatCat")){ 160 161 /// for FatCat algorithms... 162 int capos = getUngappedFatCatPos(afpChain, chainNr, aligPos); 163 if ( capos < 0) { 164 165 capos = getNextFatCatPos(afpChain, chainNr, aligPos,getPrevious); 166 167 //System.out.println(" got next" + capos + " for " + chainNr + " alignedPos: " + aligPos); 168 } else { 169 //System.out.println("got aligned fatcat position: " + capos + " " + chainNr + " for alig pos: " + aligPos); 170 } 171 172 if ( capos < 0) { 173 System.err.println("could not match position " + aligPos + " in chain " + chainNr +". Returing null..."); 174 return null; 175 } 176 if ( capos > ca.length){ 177 System.err.println("Atom array "+ chainNr + " does not have " + capos +" atoms. Returning null."); 178 return null; 179 } 180 return ca[capos]; 181 //} 182 183 // 184 // 185 // int ungappedPos = getUngappedPos(afpChain, aligPos); 186 // System.out.println("getAtomForAligPOs " + aligPos + " " + ungappedPos ); 187 // return ca[ungappedPos]; 188 // 189 // if ( ungappedPos >= optAln[bk][chainNr].length) 190 // return null; 191 // int pos = optAln[bk][chainNr][ungappedPos]; 192 // if ( pos > ca.length) 193 // return null; 194 // return ca[pos]; 195 } 196 197 198 private static int getNextFatCatPos(AFPChain afpChain, int chainNr, 199 int aligPos, boolean getPrevious) { 200 201 char[] aseq; 202 if ( chainNr == 0 ) 203 aseq = afpChain.getAlnseq1(); 204 else 205 aseq = afpChain.getAlnseq2(); 206 207 if ( aligPos > aseq.length) 208 return -1; 209 if ( aligPos < 0) 210 return -1; 211 212 int blockNum = afpChain.getBlockNum(); 213 int[] optLen = afpChain.getOptLen(); 214 int[][][] optAln = afpChain.getOptAln(); 215 216 int p1, p2; 217 int p1b = 0; 218 int p2b = 0; 219 int len = 0; 220 221 222 223 boolean terminateNextMatch = false; 224 for(int i = 0; i < blockNum; i ++) { 225 for(int j = 0; j < optLen[i]; j ++) { 226 227 p1 = optAln[i][0][j]; 228 p2 = optAln[i][1][j]; 229 230 231 if(len > 0) { 232 233 int lmax = (p1 - p1b - 1)>(p2 - p2b - 1)?(p1 - p1b - 1):(p2 - p2b - 1); 234 235 // lmax gives the length of an alignment gap 236 237 //System.out.println(" pos "+ len+" p1-p2: " + p1 + " - " + p2 + " lmax: " + lmax + " p1b-p2b:"+p1b + " " + p2b + " terminate? "+ terminateNextMatch); 238 for(int k = 0; k < lmax; k ++) { 239 240 if(k >= (p1 - p1b - 1)) { 241 // a gap position in chain 0 242 if ( aligPos == len && chainNr == 0 ){ 243 if ( getPrevious) 244 return p1b; 245 else 246 terminateNextMatch = true; 247 } 248 } 249 else { 250 if ( aligPos == len && chainNr == 0) 251 return p1b+1+k; 252 253 254 } 255 if(k >= (p2 - p2b - 1)) { 256 // a gap position in chain 1 257 if ( aligPos == len && chainNr == 1){ 258 if ( getPrevious) 259 return p2b; 260 else 261 terminateNextMatch = true; 262 } 263 } 264 else { 265 if ( aligPos == len && chainNr == 1) { 266 return p2b+1+k; 267 } 268 269 270 } 271 len++; 272 273 } 274 } 275 276 if ( aligPos == len && chainNr == 0) 277 return p1; 278 if ( aligPos == len && chainNr == 1) 279 return p2; 280 281 282 283 if ( terminateNextMatch) 284 if ( chainNr == 0) 285 return p1; 286 else 287 return p2; 288 if ( len > aligPos) { 289 if ( getPrevious) { 290 if ( chainNr == 0) 291 return p1b; 292 else 293 return p2b; 294 } else { 295 terminateNextMatch = true; 296 } 297 } 298 299 len++; 300 p1b = p1; 301 p2b = p2; 302 303 304 305 306 } 307 } 308 309 310 // we did not find an aligned position 311 return -1; 312 313 } 314 315 private static final int getUngappedFatCatPos(AFPChain afpChain, int chainNr, int aligPos){ 316 char[] aseq; 317 if ( chainNr == 0 ) 318 aseq = afpChain.getAlnseq1(); 319 else 320 aseq = afpChain.getAlnseq2(); 321 322 if ( aligPos > aseq.length) 323 return -1; 324 if ( aligPos < 0) 325 return -1; 326 327 int blockNum = afpChain.getBlockNum(); 328 int[] optLen = afpChain.getOptLen(); 329 int[][][] optAln = afpChain.getOptAln(); 330 331 int p1, p2; 332 int p1b = 0; 333 int p2b = 0; 334 int len = 0; 335 336 337 for(int i = 0; i < blockNum; i ++) { 338 for(int j = 0; j < optLen[i]; j ++) { 339 340 p1 = optAln[i][0][j]; 341 p2 = optAln[i][1][j]; 342 343 344 if(len > 0) { 345 346 int lmax = (p1 - p1b - 1)>(p2 - p2b - 1)?(p1 - p1b - 1):(p2 - p2b - 1); 347 348 // lmax gives the length of an alignment gap 349 350 //System.out.println(" p1-p2: " + p1 + " - " + p2 + " lmax: " + lmax + " p1b-p2b:"+p1b + " " + p2b); 351 for(int k = 0; k < lmax; k ++) { 352 353 if(k >= (p1 - p1b - 1)) { 354 // a gap position in chain 0 355 if ( aligPos == len && chainNr == 0){ 356 return -1; 357 } 358 } 359 else { 360 if ( aligPos == len && chainNr == 0) 361 return p1b+1+k; 362 363 364 } 365 if(k >= (p2 - p2b - 1)) { 366 // a gap position in chain 1 367 if ( aligPos == len && chainNr == 1){ 368 return -1; 369 } 370 } 371 else { 372 if ( aligPos == len && chainNr == 1) { 373 return p2b+1+k; 374 } 375 376 377 } 378 len++; 379 380 } 381 } 382 383 if ( aligPos == len && chainNr == 0) 384 return p1; 385 if ( aligPos == len && chainNr == 1) 386 return p2; 387 388 len++; 389 p1b = p1; 390 p2b = p2; 391 392 393 } 394 } 395 396 397 // we did not find an aligned position 398 return -1; 399 } 400 401 402 /** 403 * Returns the first atom for each group 404 * @param ca 405 * @param hetatms 406 * @return 407 * @throws StructureException 408 */ 409 public static final Atom[] getAtomArray(Atom[] ca,List<Group> hetatms ) throws StructureException{ 410 List<Atom> atoms = new ArrayList<Atom>(); 411 Collections.addAll(atoms, ca); 412 413 logger.debug("got {} hetatoms", hetatms.size()); 414 415 // we only add atom nr 1, since the getAlignedStructure method actually adds the parent group, and not the atoms... 416 for (Group g : hetatms){ 417 if (g.size() < 1) 418 continue; 419 //if (debug) 420 // System.out.println("adding group " + g); 421 Atom a = g.getAtom(0); 422 //if (debug) 423 // System.out.println(a); 424 a.setGroup(g); 425 atoms.add(a); 426 } 427 428 Atom[] arr = atoms.toArray(new Atom[atoms.size()]); 429 430 return arr; 431 } 432 433 434 /** Note: ca2, hetatoms2 and nucleotides2 should not be rotated. This will be done here... 435 * */ 436 437 public static final StructureAlignmentJmol display(AFPChain afpChain,Group[] twistedGroups, Atom[] ca1, Atom[] ca2,List<Group> hetatms1, List<Group> hetatms2 ) throws StructureException { 438 439 List<Atom> twistedAs = new ArrayList<Atom>(); 440 441 for ( Group g: twistedGroups){ 442 if ( g == null ) 443 continue; 444 if ( g.size() < 1) 445 continue; 446 Atom a = g.getAtom(0); 447 twistedAs.add(a); 448 } 449 Atom[] twistedAtoms = twistedAs.toArray(new Atom[twistedAs.size()]); 450 twistedAtoms = StructureTools.cloneAtomArray(twistedAtoms); 451 452 Atom[] arr1 = getAtomArray(ca1, hetatms1); 453 Atom[] arr2 = getAtomArray(twistedAtoms, hetatms2); 454 455 // 456 457 //if ( hetatms2.size() > 0) 458 // System.out.println("atom after:" + hetatms2.get(0).getAtom(0)); 459 460 //if ( hetatms2.size() > 0) 461 // System.out.println("atom after:" + hetatms2.get(0).getAtom(0)); 462 463 String title = afpChain.getAlgorithmName() + " V." +afpChain.getVersion() + " : " + afpChain.getName1() + " vs. " + afpChain.getName2(); 464 465 //System.out.println(artificial.toPDB()); 466 467 468 469 StructureAlignmentJmol jmol = new StructureAlignmentJmol(afpChain,arr1,arr2); 470 //jmol.setStructure(artificial); 471 472 System.out.format("CA2[0]=(%.2f,%.2f,%.2f)%n", arr2[0].getX(), arr2[0].getY(), arr2[0].getZ()); 473 474 //jmol.setTitle("Structure Alignment: " + afpChain.getName1() + " vs. " + afpChain.getName2()); 475 jmol.setTitle(title); 476 return jmol; 477 } 478 479 public static void showAlignmentPanel(AFPChain afpChain, Atom[] ca1, Atom[] ca2, AbstractAlignmentJmol jmol) throws StructureException { 480 481 AligPanel me = new AligPanel(); 482 me.setAlignmentJmol(jmol); 483 me.setAFPChain(afpChain); 484 me.setCa1(ca1); 485 me.setCa2(ca2); 486 487 JFrame frame = new JFrame(); 488 489 frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 490 frame.setTitle(afpChain.getName1() + " vs. " + afpChain.getName2() + " | " + afpChain.getAlgorithmName() + " V. " + afpChain.getVersion()); 491 me.setPreferredSize(new Dimension(me.getCoordManager().getPreferredWidth() , me.getCoordManager().getPreferredHeight())); 492 493 JMenuBar menu = MenuCreator.getAlignmentPanelMenu(frame,me,afpChain,null); 494 frame.setJMenuBar(menu); 495 496 JScrollPane scroll = new JScrollPane(me); 497 scroll.setAutoscrolls(true); 498 499 StatusDisplay status = new StatusDisplay(); 500 status.setAfpChain(afpChain); 501 status.setCa1(ca1); 502 status.setCa2(ca2); 503 me.addAlignmentPositionListener(status); 504 505 Box vBox = Box.createVerticalBox(); 506 vBox.add(scroll); 507 vBox.add(status); 508 509 frame.getContentPane().add(vBox); 510 511 frame.pack(); 512 frame.setVisible(true); 513 // make sure they get cleaned up correctly: 514 frame.addWindowListener(me); 515 frame.addWindowListener(status); 516 } 517 518 public static void showAlignmentImage(AFPChain afpChain, String result) { 519 520 JFrame frame = new JFrame(); 521 522 String title = afpChain.getAlgorithmName() + " V."+afpChain.getVersion() + " : " + afpChain.getName1() + " vs. " + afpChain.getName2() ; 523 frame.setTitle(title); 524 frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 525 526 AlignmentTextPanel txtPanel = new AlignmentTextPanel(); 527 txtPanel.setText(result); 528 529 JMenuBar menu = MenuCreator.getAlignmentTextMenu(frame,txtPanel,afpChain,null); 530 531 frame.setJMenuBar(menu); 532 JScrollPane js = new JScrollPane(); 533 js.getViewport().add(txtPanel); 534 js.getViewport().setBorder(null); 535 //js.setViewportBorder(null); 536 //js.setBorder(null); 537 //js.setBackground(Color.white); 538 539 frame.getContentPane().add(js); 540 frame.pack(); 541 frame.setVisible(true); 542 543 } 544 545 /** Create a "fake" Structure objects that contains the two sets of atoms aligned on top of each other. 546 * 547 * @param afpChain the container of the alignment 548 * @param ca1 atoms for protein 1 549 * @param ca2 atoms for protein 2 550 * @return a protein structure with 2 models. 551 * @throws StructureException 552 */ 553 public static Structure createArtificalStructure(AFPChain afpChain, Atom[] ca1, 554 Atom[] ca2) throws StructureException{ 555 556 557 if ( afpChain.getNrEQR() < 1){ 558 return AlignmentTools.getAlignedStructure(ca1, ca2); 559 } 560 561 Group[] twistedGroups = AlignmentTools.prepareGroupsForDisplay(afpChain,ca1, ca2); 562 563 List<Atom> twistedAs = new ArrayList<Atom>(); 564 565 for ( Group g: twistedGroups){ 566 if ( g == null ) 567 continue; 568 if ( g.size() < 1) 569 continue; 570 Atom a = g.getAtom(0); 571 twistedAs.add(a); 572 } 573 Atom[] twistedAtoms = twistedAs.toArray(new Atom[twistedAs.size()]); 574 575 List<Group> hetatms = StructureTools.getUnalignedGroups(ca1); 576 List<Group> hetatms2 = StructureTools.getUnalignedGroups(ca2); 577 578 Atom[] arr1 = DisplayAFP.getAtomArray(ca1, hetatms); 579 Atom[] arr2 = DisplayAFP.getAtomArray(twistedAtoms, hetatms2); 580 581 Structure artificial = AlignmentTools.getAlignedStructure(arr1,arr2); 582 return artificial; 583 } 584}