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.multiple.util; 022 023import java.io.IOException; 024import java.io.PrintWriter; 025import java.io.StringWriter; 026import java.util.ArrayList; 027import java.util.List; 028import java.util.Locale; 029 030import javax.vecmath.Matrix4d; 031 032import org.biojava.nbio.core.util.PrettyXMLWriter; 033import org.biojava.nbio.structure.Atom; 034import org.biojava.nbio.structure.PdbId; 035import org.biojava.nbio.structure.ResidueRange; 036import org.biojava.nbio.structure.StructureException; 037import org.biojava.nbio.structure.StructureIdentifier; 038import org.biojava.nbio.structure.SubstructureIdentifier; 039import org.biojava.nbio.structure.align.multiple.Block; 040import org.biojava.nbio.structure.align.multiple.MultipleAlignment; 041import org.biojava.nbio.structure.align.multiple.MultipleAlignmentEnsemble; 042import org.biojava.nbio.structure.align.xml.MultipleAlignmentXMLConverter; 043 044/** 045 * This class contains functions for the conversion of {@link MultipleAlignment} 046 * to various String outputs. 047 * <p> 048 * Supported formats: FASTA, FatCat, Aligned Residues, Transformation Matrices, 049 * XML, 3D format. 050 * 051 * @author Aleix Lafita 052 * @since 4.1.0 053 * 054 */ 055public class MultipleAlignmentWriter { 056 057 /** 058 * Converts the {@link MultipleAlignment} into a multiple sequence alignment 059 * String in FASTA format. 060 * 061 * @param alignment 062 * MultipleAlignment 063 * @return String multiple sequence alignment in FASTA format 064 * @see MultipleAlignmentTools#getSequenceAlignment(MultipleAlignment) 065 */ 066 public static String toFASTA(MultipleAlignment alignment) { 067 068 // Get the alignment sequences 069 List<String> alnSequences = MultipleAlignmentTools 070 .getSequenceAlignment(alignment); 071 072 String fasta = ""; 073 for (int st = 0; st < alignment.size(); st++) { 074 // Add the structure identifier as the head of the FASTA 075 fasta += ">" + alignment.getEnsemble().getStructureIdentifiers().get(st).getIdentifier() 076 + "\n" + alnSequences.get(st) + "\n"; 077 } 078 return fasta; 079 } 080 081 /** 082 * Converts the {@link MultipleAlignment} into a FatCat String format. 083 * Includes summary information about the alignment in the top and a 084 * multiple sequence alignment at the bottom. 085 * 086 * @param alignment 087 * MultipleAlignment 088 * @return String multiple sequence alignment in FASTA format 089 * @see MultipleAlignmentTools#getSequenceAlignment(MultipleAlignment) 090 */ 091 public static String toFatCat(MultipleAlignment alignment) { 092 093 // Initialize the String and put the summary information 094 StringWriter fatcat = new StringWriter(); 095 fatcat.append(alignment.toString() + "\n\n"); 096 097 // Get the alignment sequences and the mapping 098 List<Integer> mapSeqToStruct = new ArrayList<Integer>(); 099 List<String> alnSequences = MultipleAlignmentTools 100 .getSequenceAlignment(alignment, mapSeqToStruct); 101 102 // Get the String of the Block Numbers for Position 103 String blockNumbers = ""; 104 for (int pos = 0; pos < alnSequences.get(0).length(); pos++) { 105 int blockNr = MultipleAlignmentTools.getBlockForSequencePosition( 106 alignment, mapSeqToStruct, pos); 107 if (blockNr != -1) { 108 blockNumbers = blockNumbers.concat(String.valueOf(blockNr + 1)); 109 } else 110 blockNumbers = blockNumbers.concat(" "); 111 } 112 113 // Write the Sequence Alignment 114 for (int str = 0; str < alignment.size(); str++) { 115 if (str < 9) { 116 fatcat.append("Chain 0" + (str + 1) + ": " 117 + alnSequences.get(str) + "\n"); 118 } else { 119 fatcat.append("Chain " + (str + 1) + ": " 120 + alnSequences.get(str) + "\n"); 121 } 122 if (str != alignment.size() - 1) { 123 fatcat.append(" " + blockNumbers + "\n"); 124 } 125 } 126 return fatcat.toString(); 127 } 128 129 /** 130 * Converts the alignment to its simplest form: a list of groups of aligned 131 * residues. Format is one line per residue group, tab delimited: 132 * <ul> 133 * <li>PDB number (includes insertion code) 134 * <li>Chain 135 * <li>Amino Acid (three letter code)</li> 136 * </ul> 137 * Example: <code>52 A ALA 102 A VAL 154 A THR</code> 138 * <p> 139 * Note that this format loses information about blocks. 140 * 141 * @param multAln 142 * MultipleAlignment object 143 * @return a String representation of the aligned residues. 144 */ 145 public static String toAlignedResidues(MultipleAlignment multAln) { 146 StringWriter residueGroup = new StringWriter(); 147 148 // Write structure names & PDB codes 149 for (int str = 0; str < multAln.size(); str++) { 150 residueGroup.append("#Struct" + (str + 1) + ":\t"); 151 residueGroup.append(multAln.getEnsemble().getStructureIdentifiers() 152 .get(str).getIdentifier()); 153 residueGroup.append("\n"); 154 } 155 // Whrite header for columns 156 for (int str = 0; str < multAln.size(); str++) 157 residueGroup.append("#Num" + (str + 1) + "\tChain" + (str + 1) 158 + "\tAA" + (str + 1) + "\t"); 159 residueGroup.append("\n"); 160 161 // Write optimally aligned pairs 162 for (Block b : multAln.getBlocks()) { 163 for (int res = 0; res < b.length(); res++) { 164 for (int str = 0; str < multAln.size(); str++) { 165 Integer residue = b.getAlignRes().get(str).get(res); 166 if (residue == null) { 167 residueGroup.append("-"); 168 residueGroup.append('\t'); 169 residueGroup.append("-"); 170 residueGroup.append('\t'); 171 residueGroup.append("-"); 172 residueGroup.append('\t'); 173 } else { 174 Atom atom = multAln.getAtomArrays().get(str)[residue]; 175 176 residueGroup.append(atom.getGroup().getResidueNumber() 177 .toString()); 178 residueGroup.append('\t'); 179 residueGroup.append(atom.getGroup().getChain() 180 // ABradley - I'm assuming Auth Id's here 04/05/16 181 .getName()); 182 residueGroup.append('\t'); 183 residueGroup.append(atom.getGroup().getPDBName()); 184 residueGroup.append('\t'); 185 } 186 } 187 residueGroup.append('\n'); 188 } 189 } 190 return residueGroup.toString(); 191 } 192 193 /** 194 * Converts the transformation Matrices of the alignment into a String 195 * output. 196 * 197 * @param afpChain 198 * @return String transformation Matrices 199 */ 200 public static String toTransformMatrices(MultipleAlignment alignment) { 201 202 StringBuffer txt = new StringBuffer(); 203 204 for (int bs = 0; bs < alignment.getBlockSets().size(); bs++) { 205 206 List<Matrix4d> btransforms = alignment.getBlockSet(bs) 207 .getTransformations(); 208 if (btransforms == null || btransforms.size() < 1) 209 continue; 210 211 if (alignment.getBlockSets().size() > 1) { 212 txt.append("Operations for block "); 213 txt.append(bs + 1); 214 txt.append("\n"); 215 } 216 217 for (int str = 0; str < alignment.size(); str++) { 218 String origString = "ref"; 219 220 txt.append(String.format(Locale.US, " X"+(str+1)+ " = (%9.6f)*X"+ 221 origString +" + (%9.6f)*Y"+ 222 origString +" + (%9.6f)*Z"+ 223 origString +" + (%12.6f)", 224 btransforms.get(str).getElement(0,0), 225 btransforms.get(str).getElement(0,1), 226 btransforms.get(str).getElement(0,2), 227 btransforms.get(str).getElement(0,3))); 228 txt.append( "\n"); 229 txt.append(String.format(Locale.US, " Y"+(str+1)+" = (%9.6f)*X"+ 230 origString +" + (%9.6f)*Y"+ 231 origString +" + (%9.6f)*Z"+ 232 origString +" + (%12.6f)", 233 btransforms.get(str).getElement(1,0), 234 btransforms.get(str).getElement(1,1), 235 btransforms.get(str).getElement(1,2), 236 btransforms.get(str).getElement(1,3))); 237 txt.append( "\n"); 238 txt.append(String.format(Locale.US, " Z"+(str+1)+" = (%9.6f)*X"+ 239 origString +" + (%9.6f)*Y"+ 240 origString +" + (%9.6f)*Z"+ 241 origString +" + (%12.6f)", 242 btransforms.get(str).getElement(2,0), 243 btransforms.get(str).getElement(2,1), 244 btransforms.get(str).getElement(2,2), 245 btransforms.get(str).getElement(2,3))); 246 txt.append("\n\n"); 247 } 248 } 249 return txt.toString(); 250 } 251 252 /** 253 * Converts all the information of a multiple alignment ensemble into an XML 254 * String format. Cached variables, like transformation matrices and scores, 255 * are also converted. 256 * 257 * @param ensemble 258 * the MultipleAlignmentEnsemble to convert. 259 * @return String XML representation of the ensemble 260 * @throws IOException 261 * @see MultipleAlignmentXMLConverter Helper methods for XML conversion 262 */ 263 public static String toXML(MultipleAlignmentEnsemble ensemble) 264 throws IOException { 265 266 StringWriter result = new StringWriter(); 267 PrintWriter writer = new PrintWriter(result); 268 PrettyXMLWriter xml = new PrettyXMLWriter(writer); 269 270 MultipleAlignmentXMLConverter.printXMLensemble(xml, ensemble); 271 272 writer.close(); 273 274 return result.toString(); 275 } 276 277 /** 278 * Outputs a pairwise alignment in I-TASSER's 3D Format for target-template 279 * alignment. http://zhanglab.ccmb.med.umich.edu/I-TASSER/option4.html 280 * 281 * <p> 282 * The format is closely related to a standard PDB file, but contains only 283 * CA atoms and adds two columns for specifying the alignment: 284 * 285 * <pre> 286 * ATOM 2001 CA MET 1 41.116 -30.727 6.866 129 THR 287 * ATOM 2002 CA ALA 2 39.261 -27.408 6.496 130 ARG 288 * ATOM 2003 CA ALA 3 35.665 -27.370 7.726 131 THR 289 * ATOM 2004 CA ARG 4 32.662 -25.111 7.172 132 ARG 290 * ATOM 2005 CA GLY 5 29.121 -25.194 8.602 133 ARG 291 * 292 * Column 1 -30: Atom & Residue records of query sequence. 293 * Column 31-54: Coordinates of atoms in query copied from corresponding atoms in template. 294 * Column 55-59: Corresponding residue number in template based on alignment 295 * Column 60-64: Corresponding residue name in template 296 * </pre> 297 * 298 * <p> 299 * Note that the output is a pairwise alignment. Only the first and second 300 * rows in the MultipleAlignment will be used, others ignored. 301 * 302 * <p> 303 * This method supports topology-independent alignments. The output will 304 * have sequence order matching the query, but include atoms from the 305 * template. 306 * 307 * @param alignment 308 * A <em>full</em> multiple alignment between proteins 309 * @param queryIndex 310 * index of the query within the multiple alignment 311 * @param templateIndex 312 * index of the template within the multiple alignment 313 * @return The file contents as a string 314 * @throws StructureException If an error occurs parsing the alignment's structure names 315 */ 316 public static String to3DFormat(MultipleAlignment alignment, 317 int queryIndex, int templateIndex) throws StructureException { 318 List<Atom[]> atomArrays = alignment.getEnsemble().getAtomArrays(); 319 Atom[] queryAtoms = atomArrays.get(queryIndex); 320 Atom[] templateAtoms = atomArrays.get(templateIndex); 321 322 List<Block> blocks = alignment.getBlocks(); 323 MultipleAlignmentTools.sortBlocks(blocks, queryIndex); 324 325 StringBuilder str = new StringBuilder(); 326 327 // Gather info about the template structure 328 StructureIdentifier tName = alignment.getEnsemble().getStructureIdentifiers() 329 .get(templateIndex); 330 SubstructureIdentifier canon = tName.toCanonical(); 331 PdbId tPdbId = canon.getPdbId(); 332 String tChain = null; 333 for(ResidueRange range : canon.getResidueRanges()) { 334 tChain = range.getChainName(); 335 break; 336 } 337 338 if (tChain == null) { 339 // Use the chain of the first template block 340 for (Integer i : blocks.get(0).getAlignRes().get(templateIndex)) { 341 if (i != null) { 342 tChain = templateAtoms[i].getGroup().getChainId(); 343 break; 344 } 345 } 346 } 347 str.append(String 348 .format("REMARK Template name:%s:%s\n", tPdbId, tChain)); 349 for (Block block : blocks) { 350 List<Integer> qAlign = block.getAlignRes().get(queryIndex); 351 List<Integer> tAlign = block.getAlignRes().get(templateIndex); 352 for (int i = 0; i < block.length(); i++) { 353 Integer qRes = qAlign.get(i); 354 Integer tRes = tAlign.get(i); 355 356 // skip gaps 357 if (qRes == null || tRes == null) 358 continue; 359 360 // Get PDB-format ATOM records 361 String qPDB = queryAtoms[qRes].toPDB(); 362 String tPDB = templateAtoms[tRes].toPDB(); 363 364 // merge the two records into 3D format 365 str.append(qPDB.substring(0, 30)); // up through coordinates 366 str.append(tPDB.substring(30, 54)); // coordinates 367 str.append(tPDB.substring(22, 27)); // residue number 368 str.append(' '); 369 str.append(tPDB.substring(17, 20)); 370 str.append('\n'); 371 } 372 } 373 return str.toString(); 374 } 375 376}