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