001/* 002 * BioJava 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 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.nbio.structure.align.gui.aligpanel; 022 023import java.awt.Color; 024import java.awt.Font; 025import java.awt.Graphics; 026import java.awt.Graphics2D; 027import java.awt.Point; 028import java.awt.Rectangle; 029import java.awt.RenderingHints; 030import java.awt.event.ActionEvent; 031import java.awt.event.WindowEvent; 032import java.awt.event.WindowListener; 033import java.util.ArrayList; 034import java.util.BitSet; 035import java.util.List; 036 037import org.biojava.nbio.structure.Atom; 038import org.biojava.nbio.structure.StructureException; 039import org.biojava.nbio.structure.align.gui.JPrintPanel; 040import org.biojava.nbio.structure.align.gui.MenuCreator; 041import org.biojava.nbio.structure.align.gui.MultipleAlignmentJmolDisplay; 042import org.biojava.nbio.structure.align.gui.jmol.AbstractAlignmentJmol; 043import org.biojava.nbio.structure.align.gui.jmol.JmolTools; 044import org.biojava.nbio.structure.align.model.AFPChain; 045import org.biojava.nbio.structure.align.multiple.MultipleAlignment; 046import org.biojava.nbio.structure.align.multiple.MultipleAlignmentEnsembleImpl; 047import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentTools; 048import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentWriter; 049import org.biojava.nbio.structure.align.util.AFPAlignmentDisplay; 050import org.biojava.nbio.structure.gui.events.AlignmentPositionListener; 051import org.biojava.nbio.structure.gui.util.AlignedPosition; 052 053/** 054 * A JPanel that can display the sequence alignment of a 055 * {@link MultipleAlignment} in a nice way and interact with Jmol by 056 * selecting the aligned atoms of the sequence selection. 057 * <p> 058 * Coloring options include: sequence similarity, by Block or by Structure. 059 * Colors are connected with the JmolPanel using the same pattelete. 060 * <p> 061 * The positions can be selected individually or in ranges and they will be 062 * translated to jmol commands. 063 * 064 * @author Aleix Lafita 065 * @since 4.1.0 066 * 067 */ 068public class MultipleAligPanel extends JPrintPanel 069implements AlignmentPositionListener, WindowListener { 070 071 private static final long serialVersionUID = -6892229111166263764L; 072 073 private MultipleAlignment multAln; 074 private List<String> alnSeq; //sequence alignment 075 private List<Integer> mapSeqToStruct; //mapping from sequence to structure 076 077 int size; //number of structures 078 int length; //number of aligned positions in sequence alignment 079 080 private Font seqFont; 081 private Font eqFont; 082 083 private AbstractAlignmentJmol jmol; 084 private MultipleAligPanelMouseMotionListener mouseMoLi; 085 private MultipleAlignmentCoordManager coordManager; 086 087 private BitSet selection; 088 private boolean selectionLocked; 089 090 private boolean colorBySimilarity=false; 091 private boolean colorByAlignmentBlock=false; 092 093 private static final Color COLOR_EQUAL = Color.decode("#6A93D4"); 094 private static final Color COLOR_SIMILAR = Color.decode("#D460CF"); 095 096 /** 097 * Default constructor. Empty MultipleAligPanel instance. 098 */ 099 public MultipleAligPanel(){ 100 super(); 101 this.setBackground(Color.white); 102 seqFont = new Font("SansSerif",Font.PLAIN,12); 103 eqFont = new Font("SansSerif",Font.BOLD,12); 104 105 mouseMoLi = new MultipleAligPanelMouseMotionListener(this); 106 this.addMouseMotionListener(mouseMoLi); 107 this.addMouseListener(mouseMoLi); 108 mouseMoLi.addAligPosListener(this); 109 selection = new BitSet(); 110 111 multAln = null; 112 alnSeq = null; 113 mapSeqToStruct = null; 114 } 115 116 /** 117 * Constructor using an afpChain and the atom arrays for pairwise 118 * alignments. The AFPChain is converted into a MultipleAlignment. 119 * 120 * @param afpChain 121 * @param ca1 122 * @param ca2 123 * @throws StructureException 124 */ 125 public MultipleAligPanel(AFPChain afpChain, Atom[] ca1, Atom[] ca2, 126 AbstractAlignmentJmol jmol) throws StructureException { 127 128 this(); 129 130 String algorithm = afpChain.getAlgorithmName(); 131 boolean flex = false; 132 if (algorithm != null){ 133 if (algorithm.contains("flexible")) flex = true; 134 } 135 136 //Convert the apfChain into a MultipleAlignment object 137 MultipleAlignmentEnsembleImpl ensemble = 138 new MultipleAlignmentEnsembleImpl(afpChain, ca1, ca2, flex); 139 this.multAln = ensemble.getMultipleAlignment(0); 140 141 //Create the sequence alignment and the structure-sequence mapping. 142 this.mapSeqToStruct = new ArrayList<Integer>(); 143 this.alnSeq = MultipleAlignmentTools.getSequenceAlignment( 144 this.multAln, this.mapSeqToStruct); 145 146 //Initialize other memeber variables of the panel 147 this.size = multAln.size(); 148 this.length = alnSeq.get(0).length(); 149 150 coordManager = new MultipleAlignmentCoordManager(size, length); 151 this.jmol = jmol; 152 } 153 154 /** 155 * Constructor using a MultipleAlignment. 156 * 157 * @param multAln 158 * @param colors 159 */ 160 public MultipleAligPanel(MultipleAlignment msa, AbstractAlignmentJmol jm) { 161 this(); 162 this.multAln = msa; 163 164 //Create the sequence alignment and the structure-sequence mapping. 165 this.mapSeqToStruct = new ArrayList<Integer>(); 166 this.alnSeq = MultipleAlignmentTools.getSequenceAlignment( 167 this.multAln, this.mapSeqToStruct); 168 169 this.size = multAln.size(); 170 this.length = this.alnSeq.get(0).length(); 171 172 coordManager = new MultipleAlignmentCoordManager(size, length); 173 this.jmol = jm; 174 } 175 176 public MultipleAlignmentCoordManager getCoordManager() { 177 return coordManager; 178 } 179 180 public void addAlignmentPositionListener(AlignmentPositionListener li){ 181 mouseMoLi.addAligPosListener(li); 182 } 183 184 public void destroy(){ 185 186 multAln = null; 187 alnSeq = null; 188 mouseMoLi.destroy(); 189 jmol = null; 190 selection = null; 191 } 192 193 @Override 194 public void paintComponent(Graphics g){ 195 196 super.paintComponent(g); 197 198 Graphics2D g2D = (Graphics2D) g; 199 200 g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 201 RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 202 203 g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 204 RenderingHints.VALUE_ANTIALIAS_ON); 205 206 int startpos = 0; 207 int endpos = length; 208 209 String summary = multAln.toString(); 210 g2D.drawString(summary, 20, coordManager.getSummaryPos()); 211 212 Color significantCol = Color.black; 213 //if (multAln.isSignificantResult()) significantCol = Color.green; 214 215 g2D.setPaint(significantCol); 216 Rectangle sig = new Rectangle(10,10,10,10); 217 g2D.fill(sig); 218 219 for (int i = startpos; i < endpos; i++){ 220 221 boolean isGapped = false; 222 g2D.setFont(seqFont); 223 224 if (mapSeqToStruct.get(i)!=-1) g2D.setFont(eqFont); 225 else isGapped = true; 226 227 //Loop through every structure to get all the points 228 List<Point> points = new ArrayList<Point>(); 229 for (int str=0; str<size; str++) points.add( 230 coordManager.getPanelPos(str,i)); 231 Point p1 = points.get(0); 232 Point p2 = points.get(points.size()-1); 233 234 for (int str=0; str<size; str++){ 235 236 char c = alnSeq.get(str).charAt(i); 237 Color bg = jmol.getColorPalette().getColorPalette(size)[str]; 238 239 //Color only if the position is aligned 240 if (!isGapped){ 241 //Color by sequence similarity (equal or similar) 242 if (colorBySimilarity){ 243 boolean equal = true; 244 boolean similar = true; 245 char c1 = '-'; 246 for (int st=0; st<size-1; st++){ 247 if (alnSeq.get(st).charAt(i) != '-') { 248 c1 = alnSeq.get(st).charAt(i); 249 } 250 char c2 = alnSeq.get(st+1).charAt(i); 251 //If any position is a gap continue 252 if (c1=='-' || c2=='-' || 253 Character.isLowerCase(c1) || 254 Character.isLowerCase(c2)) { 255 continue; 256 } 257 if (equal && c1 == c2) 258 continue; 259 else equal = false; 260 if (AFPAlignmentDisplay.aaScore(c1, c2) > 0) 261 continue; 262 else similar = false; break; 263 } 264 if (equal) bg = COLOR_EQUAL; 265 else if (similar) bg = COLOR_SIMILAR; 266 else bg = Color.LIGHT_GRAY; 267 } 268 //Color by alignment block the same way as in the Jmol 269 else if (colorByAlignmentBlock){ 270 int blockNr = MultipleAlignmentTools. 271 getBlockForSequencePosition( 272 multAln,mapSeqToStruct,i); 273 bg = jmol.getColorPalette().getColorPalette( 274 multAln.getBlocks().size())[blockNr]; 275 } 276 if (isSelected(i)) bg = Color.YELLOW; 277 278 if (Character.isUpperCase(c) && c!='-'){ 279 g2D.setPaint(bg); 280 Rectangle rec = new Rectangle(points.get(str).x-1, 281 points.get(str).y-11, (p2.x-p1.x)+12, 282 (p2.y-p1.y)/size); 283 g2D.fill(rec); 284 } 285 } 286 287 // draw the AA sequence 288 g2D.setColor(Color.black); 289 g2D.drawString(String.valueOf(c), points.get(str).x, points.get(str).y); 290 } 291 } 292 293 int nrLines = (length-1) / 294 (MultipleAlignmentCoordManager.DEFAULT_LINE_LENGTH); 295 296 for (int i = 0 ; i < nrLines+1 ; i++){ 297 298 // draw legend at i 299 for (int str=0; str<size; str++){ 300 301 Point p1 = coordManager.getLegendPosition(i,str); 302 303 int aligPos = i * 304 MultipleAlignmentCoordManager.DEFAULT_LINE_LENGTH; 305 Atom a1 = null; 306 while (a1==null && 307 aligPos < Math.min((i+1)*MultipleAlignmentCoordManager. 308 DEFAULT_LINE_LENGTH-1,length)){ 309 a1 = MultipleAlignmentTools.getAtomForSequencePosition( 310 multAln, mapSeqToStruct, str, aligPos); 311 aligPos++; 312 } 313 String label1 = JmolTools.getPdbInfo(a1,false); 314 g2D.drawString(label1, p1.x,p1.y); 315 316 Point p3 = coordManager.getEndLegendPosition(i,str); 317 318 aligPos = (i*MultipleAlignmentCoordManager.DEFAULT_LINE_LENGTH+ 319 MultipleAlignmentCoordManager.DEFAULT_LINE_LENGTH - 1); 320 if (aligPos > length) aligPos = length-1; 321 Atom a3 = null; 322 while (a3==null && aligPos > Math.max(i* 323 MultipleAlignmentCoordManager.DEFAULT_LINE_LENGTH,0)){ 324 a3 = MultipleAlignmentTools.getAtomForSequencePosition( 325 multAln, mapSeqToStruct, str, aligPos); 326 aligPos--; 327 } 328 329 String label3 = JmolTools.getPdbInfo(a3,false); 330 331 g2D.drawString(label3, p3.x,p3.y); 332 } 333 } 334 } 335 336 private boolean isSelected(int alignmentPosition) { 337 return selection.get(alignmentPosition); 338 } 339 340 @Override 341 public void mouseOverPosition(AlignedPosition p) { 342 343 if (!selectionLocked) selection.clear(); 344 345 selection.set(p.getPos1()); 346 updateJmolDisplay(); 347 this.repaint(); 348 } 349 350 private void updateJmolDisplay() { 351 352 if (jmol == null) return; 353 354 StringBuffer cmd = new StringBuffer("select "); 355 int nrSelected = 0; 356 for (int i=0; i<length; i++){ 357 if (selection.get(i)){ 358 for (int str=0; str<size; str++){ 359 Atom a = MultipleAlignmentTools.getAtomForSequencePosition( 360 multAln, mapSeqToStruct,str,i); 361 if (a != null) { 362 cmd.append(JmolTools.getPdbInfo(a)); 363 cmd.append("/"+(str+1)+", "); 364 } 365 } 366 nrSelected++; 367 } 368 } 369 if (nrSelected == 0) cmd.append(" none;"); 370 else cmd.append(" none; set display selected;"); 371 //System.out.println(cmd.toString()); 372 jmol.evalString(cmd.toString()); 373 } 374 375 376 @Override 377 public void positionSelected(AlignedPosition p) { 378 mouseOverPosition(p); 379 } 380 381 @Override 382 public void rangeSelected(AlignedPosition start, AlignedPosition end) { 383 384 if (!selectionLocked) selection.clear(); 385 selection.set(start.getPos1(), end.getPos1()+1); 386 updateJmolDisplay(); 387 this.repaint(); 388 } 389 390 @Override 391 public void selectionLocked() { 392 selectionLocked = true; 393 } 394 395 @Override 396 public void selectionUnlocked() { 397 selectionLocked = false; 398 selection.clear(); 399 this.repaint(); 400 } 401 402 @Override 403 public void toggleSelection(AlignedPosition p) { 404 selection.flip(p.getPos1()); 405 updateJmolDisplay(); 406 this.repaint(); 407 } 408 409 public void setStructureAlignmentJmol(AbstractAlignmentJmol jmol) { 410 this.jmol = jmol; 411 412 } 413 414 @Override 415 public void windowActivated(WindowEvent e) {} 416 417 @Override 418 public void windowClosed(WindowEvent e) {} 419 420 @Override 421 public void windowClosing(WindowEvent e) { 422 destroy(); 423 } 424 425 @Override 426 public void windowDeactivated(WindowEvent e) {} 427 428 @Override 429 public void windowDeiconified(WindowEvent e) {} 430 431 @Override 432 public void windowIconified(WindowEvent e) {} 433 434 @Override 435 public void windowOpened(WindowEvent e) {} 436 437 @Override 438 public void actionPerformed(ActionEvent e) { 439 String cmd = e.getActionCommand(); 440 if ( cmd.equals(MenuCreator.PRINT)) { 441 super.actionPerformed(e); 442 } else if (cmd.equals(MenuCreator.FASTA_FORMAT)){ 443 String result = MultipleAlignmentWriter.toFASTA(multAln); 444 MultipleAlignmentJmolDisplay.showAlignmentImage(multAln, result); 445 } else if ( cmd.equals(MenuCreator.PAIRS_ONLY)) { 446 String result = MultipleAlignmentWriter.toAlignedResidues(multAln); 447 MultipleAlignmentJmolDisplay.showAlignmentImage(multAln, result); 448 } else if (cmd.equals(MenuCreator.FATCAT_TEXT)){ 449 String result = MultipleAlignmentWriter.toFatCat(multAln); 450 MultipleAlignmentJmolDisplay.showAlignmentImage(multAln, result); 451 } else if (cmd.equals(MenuCreator.SELECT_EQR)){ 452 selectEQR(); 453 } else if ( cmd.equals(MenuCreator.SIMILARITY_COLOR)){ 454 colorBySimilarity(true); 455 } else if (cmd.equals(MenuCreator.EQR_COLOR)){ 456 colorBySimilarity(false); 457 } else if ( cmd.equals(MenuCreator.FATCAT_BLOCK)){ 458 colorByAlignmentBlock(); 459 } else { 460 System.err.println("Unknown command:" + cmd); 461 } 462 } 463 464 private void selectEQR() { 465 466 selection.clear(); 467 468 for (int pos=0; pos<length; pos++){ 469 if (mapSeqToStruct.get(pos)!=-1) selection.flip(pos); 470 } 471 mouseMoLi.triggerSelectionLocked(true); 472 updateJmolDisplay(); 473 this.repaint(); 474 } 475 476 private void colorByAlignmentBlock() { 477 colorByAlignmentBlock = true; 478 colorBySimilarity = false; 479 this.repaint(); 480 } 481 482 private void colorBySimilarity(boolean flag) { 483 this.colorBySimilarity = flag; 484 colorByAlignmentBlock = false; 485 this.repaint(); 486 } 487 488 public List<Atom[]> getAtomArrays() { 489 return multAln.getAtomArrays(); 490 } 491 public MultipleAlignment getMultipleAlignment(){ 492 return multAln; 493 } 494 public List<String> getAlnSequences(){ 495 return alnSeq; 496 } 497 public List<Integer> getMapSeqToStruct(){ 498 return mapSeqToStruct; 499 } 500}