]> git.donarmstrong.com Git - bamtools.git/blob - src/toolkit/bamtools_convert.cpp
Added Yaml for outputing BamAlignments.
[bamtools.git] / src / toolkit / bamtools_convert.cpp
1 // ***************************************************************************
2 // bamtools_convert.cpp (c) 2010 Derek Barnett, Erik Garrison
3 // Marth Lab, Department of Biology, Boston College
4 // All rights reserved.
5 // ---------------------------------------------------------------------------
6 // Last modified: 23 September 2010
7 // ---------------------------------------------------------------------------
8 // Converts between BAM and a number of other formats
9 // ***************************************************************************
10
11 #include <fstream>
12 #include <iostream>
13 #include <sstream>
14 #include <string>
15 #include <vector>
16 #include "bamtools_convert.h"
17 #include "bamtools_fasta.h"
18 #include "bamtools_options.h"
19 #include "bamtools_pileup_engine.h"
20 #include "bamtools_utilities.h"
21 #include "BGZF.h"
22 #include "BamMultiReader.h"
23 using namespace std;
24 using namespace BamTools;
25   
26 namespace BamTools { 
27   
28     // ---------------------------------------------
29     // ConvertTool constants
30   
31     // supported conversion format command-line names
32     static const string FORMAT_BED      = "bed";
33     static const string FORMAT_BEDGRAPH = "bedgraph";
34     static const string FORMAT_FASTA    = "fasta";
35     static const string FORMAT_FASTQ    = "fastq";
36     static const string FORMAT_JSON     = "json";
37     static const string FORMAT_YAML     = "yaml";
38     static const string FORMAT_SAM      = "sam";
39     static const string FORMAT_PILEUP   = "pileup";
40     static const string FORMAT_WIGGLE   = "wig";
41
42     // other constants
43     static const unsigned int FASTA_LINE_MAX = 50;
44     
45     // ---------------------------------------------
46     // ConvertPileupFormatVisitor declaration
47     
48     class ConvertPileupFormatVisitor : public PileupVisitor {
49       
50         // ctor & dtor
51         public:
52             ConvertPileupFormatVisitor(const RefVector& references, 
53                                        const string& fastaFilename,
54                                        const bool isPrintingMapQualities,
55                                        ostream* out);
56             ~ConvertPileupFormatVisitor(void);
57       
58         // PileupVisitor interface implementation
59         public:
60             void Visit(const PileupPosition& pileupData);
61             
62         // data members
63         private:
64             Fasta     m_fasta;
65             bool      m_hasFasta;
66             bool      m_isPrintingMapQualities;
67             ostream*  m_out;
68             RefVector m_references;
69     };
70     
71 } // namespace BamTools
72   
73 // ---------------------------------------------
74 // ConvertSettings implementation
75
76 struct ConvertTool::ConvertSettings {
77
78     // flags
79     bool HasInput;
80     bool HasOutput;
81     bool HasFormat;
82     bool HasRegion;
83
84     // pileup flags
85     bool HasFastaFilename;
86     bool IsOmittingSamHeader;
87     bool IsPrintingPileupMapQualities;
88     
89     // options
90     vector<string> InputFiles;
91     string OutputFilename;
92     string Format;
93     string Region;
94     
95     // pileup options
96     string FastaFilename;
97
98     // constructor
99     ConvertSettings(void)
100         : HasInput(false)
101         , HasOutput(false)
102         , HasFormat(false)
103         , HasRegion(false)
104         , HasFastaFilename(false)
105         , IsOmittingSamHeader(false)
106         , IsPrintingPileupMapQualities(false)
107         , OutputFilename(Options::StandardOut())
108         , FastaFilename("")
109     { } 
110 };    
111
112 // ---------------------------------------------
113 // ConvertToolPrivate implementation  
114   
115 struct ConvertTool::ConvertToolPrivate {
116   
117     // ctor & dtor
118     public:
119         ConvertToolPrivate(ConvertTool::ConvertSettings* settings);
120         ~ConvertToolPrivate(void);
121     
122     // interface
123     public:
124         bool Run(void);
125         
126     // internal methods
127     private:
128         void PrintBed(const BamAlignment& a);
129         void PrintBedGraph(const BamAlignment& a);
130         void PrintFasta(const BamAlignment& a);
131         void PrintFastq(const BamAlignment& a);
132         void PrintJson(const BamAlignment& a);
133         void PrintYaml(const BamAlignment& a);
134         void PrintSam(const BamAlignment& a);
135         void PrintWiggle(const BamAlignment& a);
136         
137         // special case - uses the PileupEngine
138         bool RunPileupConversion(BamMultiReader* reader);
139         
140     // data members
141     private: 
142         ConvertTool::ConvertSettings* m_settings;
143         RefVector m_references;
144         ostream m_out;
145 };
146
147 ConvertTool::ConvertToolPrivate::ConvertToolPrivate(ConvertTool::ConvertSettings* settings)
148     : m_settings(settings)
149     , m_out(cout.rdbuf()) // default output to cout
150 { }
151
152 ConvertTool::ConvertToolPrivate::~ConvertToolPrivate(void) { }
153
154 bool ConvertTool::ConvertToolPrivate::Run(void) {
155  
156     // ------------------------------------
157     // initialize conversion input/output
158         
159     // set to default input if none provided
160     if ( !m_settings->HasInput ) 
161         m_settings->InputFiles.push_back(Options::StandardIn());
162     
163     // open input files
164     BamMultiReader reader;
165     reader.Open(m_settings->InputFiles);
166     m_references = reader.GetReferenceData();
167
168     // set region if specified
169     BamRegion region;
170     if ( m_settings->HasRegion ) {
171         if ( Utilities::ParseRegionString(m_settings->Region, reader, region) ) {
172             if ( !reader.SetRegion(region) ) {
173                 cerr << "Could not set BamReader region to REGION: " << m_settings->Region << endl;
174                 return false;
175             }
176         } else {
177             cerr << "Could not parse REGION: " << m_settings->Region << endl;
178             return false;
179         }
180     }
181         
182     // if output file given
183     ofstream outFile;
184     if ( m_settings->HasOutput ) {
185       
186         // open output file stream
187         outFile.open(m_settings->OutputFilename.c_str());
188         if ( !outFile ) {
189             cerr << "Could not open " << m_settings->OutputFilename << " for output" << endl; 
190             return false; 
191         }
192         
193         // set m_out to file's streambuf
194         m_out.rdbuf(outFile.rdbuf()); 
195     }
196     
197     // -------------------------------------
198     // do conversion based on format
199     
200      bool convertedOk = true;
201     
202     // pileup is special case
203     // conversion not done per alignment, like the other formats
204     if ( m_settings->Format == FORMAT_PILEUP )
205         convertedOk = RunPileupConversion(&reader);
206     
207     // all other formats
208     else {
209     
210         bool formatError = false;
211         
212         // set function pointer to proper conversion method
213         void (BamTools::ConvertTool::ConvertToolPrivate::*pFunction)(const BamAlignment&) = 0;
214         if      ( m_settings->Format == FORMAT_BED )      pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintBed;
215         else if ( m_settings->Format == FORMAT_BEDGRAPH ) pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintBedGraph;
216         else if ( m_settings->Format == FORMAT_FASTA )    pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintFasta;
217         else if ( m_settings->Format == FORMAT_FASTQ )    pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintFastq;
218         else if ( m_settings->Format == FORMAT_JSON )     pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintJson;
219         else if ( m_settings->Format == FORMAT_YAML )     pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintYaml;
220         else if ( m_settings->Format == FORMAT_SAM )      pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintSam;
221         else if ( m_settings->Format == FORMAT_WIGGLE )   pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintWiggle;
222         else { 
223             cerr << "Unrecognized format: " << m_settings->Format << endl;
224             cerr << "Please see help|README (?) for details on supported formats " << endl;
225             formatError = true;
226             convertedOk = false;
227         }
228         
229         // if format selected ok
230         if ( !formatError ) {
231         
232             // if SAM format & not omitting header, print SAM header first
233             if ( (m_settings->Format == FORMAT_SAM) && !m_settings->IsOmittingSamHeader ) 
234                 m_out << reader.GetHeaderText();
235             
236             // iterate through file, doing conversion
237             BamAlignment a;
238             while ( reader.GetNextAlignment(a) ) 
239               (this->*pFunction)(a);
240             
241             // set flag for successful conversion
242             convertedOk = true;
243         }
244     }
245     
246     // ------------------------
247     // clean up & exit
248     
249     reader.Close();
250     if ( m_settings->HasOutput ) outFile.close();
251     return convertedOk;   
252 }
253
254 // ----------------------------------------------------------
255 // Conversion/output methods
256 // ----------------------------------------------------------
257
258 void ConvertTool::ConvertToolPrivate::PrintBed(const BamAlignment& a) { 
259   
260     // tab-delimited, 0-based half-open 
261     // (e.g. a 50-base read aligned to pos 10 could have BED coordinates (10, 60) instead of BAM coordinates (10, 59) )
262     // <chromName> <chromStart> <chromEnd> <readName> <score> <strand>
263
264     m_out << m_references.at(a.RefID).RefName << "\t"
265           << a.Position << "\t"
266           << a.GetEndPosition() + 1 << "\t"
267           << a.Name << "\t"
268           << a.MapQuality << "\t"
269           << (a.IsReverseStrand() ? "-" : "+") << endl;
270 }
271
272 void ConvertTool::ConvertToolPrivate::PrintBedGraph(const BamAlignment& a) { 
273     ; 
274 }
275
276 // print BamAlignment in FASTA format
277 // N.B. - uses QueryBases NOT AlignedBases
278 void ConvertTool::ConvertToolPrivate::PrintFasta(const BamAlignment& a) { 
279     
280     // >BamAlignment.Name
281     // BamAlignment.QueryBases (up to FASTA_LINE_MAX bases per line)
282     // ...
283     //
284     // N.B. - QueryBases are reverse-complemented if aligned to reverse strand
285   
286     // print header
287     m_out << "> " << a.Name << endl;
288     
289     // handle reverse strand alignment - bases 
290     string sequence = a.QueryBases;
291     if ( a.IsReverseStrand() )
292         Utilities::ReverseComplement(sequence);
293     
294     // if sequence fits on single line
295     if ( sequence.length() <= FASTA_LINE_MAX )
296         m_out << sequence << endl;
297     
298     // else split over multiple lines
299     else {
300       
301         size_t position = 0;
302         size_t seqLength = sequence.length(); // handle reverse strand alignment - bases & qualitiesth();
303         
304         // write subsequences to each line
305         while ( position < (seqLength - FASTA_LINE_MAX) ) {
306             m_out << sequence.substr(position, FASTA_LINE_MAX) << endl;
307             position += FASTA_LINE_MAX;
308         }
309         
310         // write final subsequence
311         m_out << sequence.substr(position) << endl;
312     }
313 }
314
315 // print BamAlignment in FASTQ format
316 // N.B. - uses QueryBases NOT AlignedBases
317 void ConvertTool::ConvertToolPrivate::PrintFastq(const BamAlignment& a) { 
318   
319     // @BamAlignment.Name
320     // BamAlignment.QueryBases
321     // +
322     // BamAlignment.Qualities
323     //
324     // N.B. - QueryBases are reverse-complemented (& Qualities reversed) if aligned to reverse strand .
325     //        Name is appended "/1" or "/2" if paired-end, to reflect which mate this entry is.
326   
327     // handle paired-end alignments
328     string name = a.Name;
329     if ( a.IsPaired() )
330         name.append( (a.IsFirstMate() ? "/1" : "/2") );
331   
332     // handle reverse strand alignment - bases & qualities
333     string qualities = a.Qualities;
334     string sequence  = a.QueryBases;
335     if ( a.IsReverseStrand() ) {
336         Utilities::Reverse(qualities);
337         Utilities::ReverseComplement(sequence);
338     }
339   
340     // write to output stream
341     m_out << "@" << name << endl
342           << sequence    << endl
343           << "+"         << endl
344           << qualities   << endl;
345 }
346
347 // print BamAlignment in JSON format
348 void ConvertTool::ConvertToolPrivate::PrintJson(const BamAlignment& a) {
349   
350     // write name & alignment flag
351     m_out << "{\"name\":\"" << a.Name << "\",\"alignmentFlag\":\"" << a.AlignmentFlag << "\",";
352     
353     // write reference name
354     if ( (a.RefID >= 0) && (a.RefID < (int)m_references.size()) ) 
355         m_out << "\"reference\":\"" << m_references[a.RefID].RefName << "\",";
356     
357     // write position & map quality
358     m_out << "\"position\":" << a.Position+1 << ",\"mapQuality\":" << a.MapQuality << ",";
359     
360     // write CIGAR
361     const vector<CigarOp>& cigarData = a.CigarData;
362     if ( !cigarData.empty() ) {
363         m_out << "\"cigar\":[";
364         vector<CigarOp>::const_iterator cigarBegin = cigarData.begin();
365         vector<CigarOp>::const_iterator cigarIter = cigarBegin;
366         vector<CigarOp>::const_iterator cigarEnd  = cigarData.end();
367         for ( ; cigarIter != cigarEnd; ++cigarIter ) {
368             const CigarOp& op = (*cigarIter);
369             if (cigarIter != cigarBegin) m_out << ",";
370             m_out << "\"" << op.Length << op.Type << "\"";
371         }
372         m_out << "],";
373     }
374     
375     // write mate reference name, mate position, & insert size
376     if ( a.IsPaired() && (a.MateRefID >= 0) && (a.MateRefID < (int)m_references.size()) ) {
377         m_out << "\"mate\":{"
378               << "\"reference\":\"" << m_references[a.MateRefID].RefName << "\","
379               << "\"position\":" << a.MatePosition+1
380               << ",\"insertSize\":" << a.InsertSize << "},";
381     }
382     
383     // write sequence
384     if ( !a.QueryBases.empty() ) 
385         m_out << "\"queryBases\":\"" << a.QueryBases << "\",";
386     
387     // write qualities
388     if ( !a.Qualities.empty() ) {
389         string::const_iterator s = a.Qualities.begin();
390         m_out << "\"qualities\":[" << static_cast<short>(*s) - 33;
391         ++s;
392         for (; s != a.Qualities.end(); ++s) {
393             m_out << "," << static_cast<short>(*s) - 33;
394         }
395         m_out << "],";
396     }
397     
398     // write tag data
399     const char* tagData = a.TagData.c_str();
400     const size_t tagDataLength = a.TagData.length();
401     size_t index = 0;
402     if (index < tagDataLength) {
403
404         m_out << "\"tags\":{";
405         
406         while ( index < tagDataLength ) {
407
408             if (index > 0)
409                 m_out << ",";
410             
411             // write tag name
412             m_out << "\"" << a.TagData.substr(index, 2) << "\":";
413             index += 2;
414             
415             // get data type
416             char type = a.TagData.at(index);
417             ++index;
418             
419             switch (type) {
420                 case('A') : 
421                     m_out << "\"" << tagData[index] << "\"";
422                     ++index; 
423                     break;
424                 
425                 case('C') : 
426                     m_out << (int)tagData[index]; 
427                     ++index; 
428                     break;
429                 
430                 case('c') : 
431                     m_out << (int)tagData[index];
432                     ++index; 
433                     break;
434                 
435                 case('S') : 
436                     m_out << BgzfData::UnpackUnsignedShort(&tagData[index]); 
437                     index += 2; 
438                     break;
439                     
440                 case('s') : 
441                     m_out << BgzfData::UnpackSignedShort(&tagData[index]);
442                     index += 2; 
443                     break;
444                 
445                 case('I') : 
446                     m_out << BgzfData::UnpackUnsignedInt(&tagData[index]);
447                     index += 4; 
448                     break;
449                 
450                 case('i') : 
451                     m_out << BgzfData::UnpackSignedInt(&tagData[index]);
452                     index += 4; 
453                     break;
454                 
455                 case('f') : 
456                     m_out << BgzfData::UnpackFloat(&tagData[index]);
457                     index += 4; 
458                     break;
459                 
460                 case('d') : 
461                     m_out << BgzfData::UnpackDouble(&tagData[index]);
462                     index += 8; 
463                     break;
464                 
465                 case('Z') :
466                 case('H') : 
467                     m_out << "\""; 
468                     while (tagData[index]) {
469                         m_out << tagData[index];
470                         ++index;
471                     }
472                     m_out << "\""; 
473                     ++index; 
474                     break;      
475             }
476             
477             if ( tagData[index] == '\0') 
478                 break;
479         }
480
481         m_out << "}";
482     }
483
484     m_out << "}" << endl;
485 }
486
487
488 // Print BamAlignment in YAML format
489 void ConvertTool::ConvertToolPrivate::PrintYaml(const BamAlignment& a) {
490
491
492         // write alignment name
493         m_out << "---" << endl;
494         m_out << a.Name << ":" << endl;
495
496         // write alignment data
497         m_out << "   " << "AlndBases: "  <<  a.AlignedBases << endl;
498         m_out << "   " << "Qualities: "     <<  a.Qualities << endl;
499         m_out << "   " << "Name: "          <<  a.Name << endl;
500         m_out << "   " << "Length: "        <<  a.Length << endl;
501         m_out << "   " << "TagData: "       <<  a.TagData << endl;
502         m_out << "   " << "RefID: "         <<  a.RefID << endl;
503         m_out << "   " << "RefName: "       <<  m_references[a.RefID].RefName << endl;
504         m_out << "   " << "Position: "      <<  a.Position << endl;
505         m_out << "   " << "Bin: "           <<  a.Bin << endl;
506         m_out << "   " << "MapQuality: "    <<  a.MapQuality << endl;
507         m_out << "   " << "AlignmentFlag: " <<  a.AlignmentFlag << endl;
508         m_out << "   " << "MateRefID: "     <<  a.MateRefID << endl;
509         m_out << "   " << "MatePosition: "  <<  a.MatePosition << endl;
510         m_out << "   " << "InsertSize: "    <<  a.InsertSize << endl;
511
512        // Write Cigar data
513        const vector<CigarOp>& cigarData = a.CigarData;
514        if ( !cigarData.empty() ) {
515           m_out << "   " <<  "Cigar: ";
516           vector<CigarOp>::const_iterator cigarBegin = cigarData.begin();
517           vector<CigarOp>::const_iterator cigarIter = cigarBegin;
518           vector<CigarOp>::const_iterator cigarEnd  = cigarData.end();
519           for ( ; cigarIter != cigarEnd; ++cigarIter ) {
520               const CigarOp& op = (*cigarIter);
521               m_out << op.Length << op.Type;
522           }
523        m_out << endl;
524        }
525
526 }
527
528
529 // print BamAlignment in SAM format
530 void ConvertTool::ConvertToolPrivate::PrintSam(const BamAlignment& a) {
531   
532     // tab-delimited
533     // <QNAME> <FLAG> <RNAME> <POS> <MAPQ> <CIGAR> <MRNM> <MPOS> <ISIZE> <SEQ> <QUAL> [ <TAG>:<VTYPE>:<VALUE> [...] ]
534   
535     // write name & alignment flag
536     m_out << a.Name << "\t" << a.AlignmentFlag << "\t";
537     
538     // write reference name
539     if ( (a.RefID >= 0) && (a.RefID < (int)m_references.size()) ) 
540         m_out << m_references[a.RefID].RefName << "\t";
541     else 
542         m_out << "*\t";
543     
544     // write position & map quality
545     m_out << a.Position+1 << "\t" << a.MapQuality << "\t";
546     
547     // write CIGAR
548     const vector<CigarOp>& cigarData = a.CigarData;
549     if ( cigarData.empty() ) m_out << "*\t";
550     else {
551         vector<CigarOp>::const_iterator cigarIter = cigarData.begin();
552         vector<CigarOp>::const_iterator cigarEnd  = cigarData.end();
553         for ( ; cigarIter != cigarEnd; ++cigarIter ) {
554             const CigarOp& op = (*cigarIter);
555             m_out << op.Length << op.Type;
556         }
557         m_out << "\t";
558     }
559     
560     // write mate reference name, mate position, & insert size
561     if ( a.IsPaired() && (a.MateRefID >= 0) && (a.MateRefID < (int)m_references.size()) ) {
562         if ( a.MateRefID == a.RefID ) m_out << "=\t";
563         else m_out << m_references[a.MateRefID].RefName << "\t";
564         m_out << a.MatePosition+1 << "\t" << a.InsertSize << "\t";
565     } 
566     else m_out << "*\t0\t0\t";
567     
568     // write sequence
569     if ( a.QueryBases.empty() ) m_out << "*\t";
570     else m_out << a.QueryBases << "\t";
571     
572     // write qualities
573     if ( a.Qualities.empty() ) m_out << "*";
574     else m_out << a.Qualities;
575     
576     // write tag data
577     const char* tagData = a.TagData.c_str();
578     const size_t tagDataLength = a.TagData.length();
579     
580     size_t index = 0;
581     while ( index < tagDataLength ) {
582
583         // write tag name   
584         string tagName = a.TagData.substr(index, 2);
585         m_out << "\t" << tagName << ":";
586         index += 2;
587         
588         // get data type
589         char type = a.TagData.at(index);
590         ++index;
591         switch (type) {
592             case('A') : 
593                 m_out << "A:" << tagData[index]; 
594                 ++index; 
595                 break;
596             
597             case('C') : 
598                 m_out << "i:" << (int)tagData[index]; 
599                 ++index; 
600                 break;
601             
602             case('c') : 
603                 m_out << "i:" << (int)tagData[index];
604                 ++index; 
605                 break;
606             
607             case('S') : 
608                 m_out << "i:" << BgzfData::UnpackUnsignedShort(&tagData[index]);
609                 index += 2; 
610                 break;
611                 
612             case('s') : 
613                 m_out << "i:" << BgzfData::UnpackSignedShort(&tagData[index]);
614                 index += 2; 
615                 break;
616             
617             case('I') :
618                 m_out << "i:" << BgzfData::UnpackUnsignedInt(&tagData[index]);
619                 index += 4; 
620                 break;
621             
622             case('i') : 
623                 m_out << "i:" << BgzfData::UnpackSignedInt(&tagData[index]);
624                 index += 4; 
625                 break;
626             
627             case('f') : 
628                 m_out << "f:" << BgzfData::UnpackFloat(&tagData[index]);
629                 index += 4; 
630                 break;
631             
632             case('d') : 
633                 m_out << "d:" << BgzfData::UnpackDouble(&tagData[index]);
634                 index += 8; 
635                 break;
636             
637             case('Z') :
638             case('H') : 
639                 m_out << type << ":";
640                 while (tagData[index]) {
641                     m_out << tagData[index];
642                     ++index;
643                 }
644                 ++index; 
645                 break;      
646         }
647
648         if ( tagData[index] == '\0') 
649             break;
650     }
651
652     m_out << endl;
653 }
654
655 void ConvertTool::ConvertToolPrivate::PrintWiggle(const BamAlignment& a) { 
656     ; 
657 }
658
659 bool ConvertTool::ConvertToolPrivate::RunPileupConversion(BamMultiReader* reader) {
660   
661     // check for valid BamMultiReader
662     if ( reader == 0 ) return false;
663   
664     // set up our pileup format 'visitor'
665     ConvertPileupFormatVisitor* v = new ConvertPileupFormatVisitor(m_references, 
666                                                                    m_settings->FastaFilename,
667                                                                    m_settings->IsPrintingPileupMapQualities, 
668                                                                    &m_out);
669
670     // set up PileupEngine
671     PileupEngine pileup;
672     pileup.AddVisitor(v);
673     
674     // iterate through data
675     BamAlignment al;
676     while ( reader->GetNextAlignment(al) )
677         pileup.AddAlignment(al);
678     pileup.Flush();
679     
680     // clean up
681     delete v;
682     v = 0;
683     
684     // return success
685     return true;
686 }       
687
688 // ---------------------------------------------
689 // ConvertTool implementation
690
691 ConvertTool::ConvertTool(void)
692     : AbstractTool()
693     , m_settings(new ConvertSettings)
694     , m_impl(0)
695 {
696     // set program details
697     Options::SetProgramInfo("bamtools convert", "converts BAM to a number of other formats", "-format <FORMAT> [-in <filename> -in <filename> ...] [-out <filename>] [other options]");
698     
699     // set up options 
700     OptionGroup* IO_Opts = Options::CreateOptionGroup("Input & Output");
701     Options::AddValueOption("-in",     "BAM filename", "the input BAM file(s)", "", m_settings->HasInput,   m_settings->InputFiles,     IO_Opts, Options::StandardIn());
702     Options::AddValueOption("-out",    "BAM filename", "the output BAM file",   "", m_settings->HasOutput,  m_settings->OutputFilename, IO_Opts, Options::StandardOut());
703     Options::AddValueOption("-format", "FORMAT", "the output file format - see README for recognized formats", "", m_settings->HasFormat, m_settings->Format, IO_Opts);
704    
705     OptionGroup* FilterOpts = Options::CreateOptionGroup("Filters");
706     Options::AddValueOption("-region", "REGION", "genomic region. Index file is recommended for better performance, and is read automatically if it exists as <filename>.bai. See \'bamtools help index\' for more  details on creating one", "", m_settings->HasRegion, m_settings->Region, FilterOpts);
707     
708     OptionGroup* PileupOpts = Options::CreateOptionGroup("Pileup Options");
709     Options::AddValueOption("-fasta", "FASTA filename", "FASTA reference file", "", m_settings->HasFastaFilename, m_settings->FastaFilename, PileupOpts, "");
710     Options::AddOption("-mapqual", "print the mapping qualities", m_settings->IsPrintingPileupMapQualities, PileupOpts);
711     
712     OptionGroup* SamOpts = Options::CreateOptionGroup("SAM Options");
713     Options::AddOption("-noheader", "omit the SAM header from output", m_settings->IsOmittingSamHeader, SamOpts);
714 }
715
716 ConvertTool::~ConvertTool(void) {
717     delete m_settings;
718     m_settings = 0;
719     
720     delete m_impl;
721     m_impl = 0;
722 }
723
724 int ConvertTool::Help(void) {
725     Options::DisplayHelp();
726     return 0;
727 }
728
729 int ConvertTool::Run(int argc, char* argv[]) {
730   
731     // parse command line arguments
732     Options::Parse(argc, argv, 1);
733     
734     // run internal ConvertTool implementation, return success/fail
735     m_impl = new ConvertToolPrivate(m_settings);
736     
737     if ( m_impl->Run() ) 
738         return 0;
739     else 
740         return 1;
741 }
742
743 // ---------------------------------------------
744 // ConvertPileupFormatVisitor implementation
745
746 ConvertPileupFormatVisitor::ConvertPileupFormatVisitor(const RefVector& references, 
747                                                        const string& fastaFilename,
748                                                        const bool isPrintingMapQualities,
749                                                        ostream* out)
750     : PileupVisitor()
751     , m_hasFasta(false)
752     , m_isPrintingMapQualities(isPrintingMapQualities)
753     , m_out(out)
754     , m_references(references)
755
756     // set up Fasta reader if file is provided
757     if ( !fastaFilename.empty() ) {
758       
759         // check for FASTA index
760         string indexFilename = "";
761         if ( Utilities::FileExists(fastaFilename + ".fai") ) 
762             indexFilename = fastaFilename + ".fai";
763       
764         // open FASTA file
765         if ( m_fasta.Open(fastaFilename, indexFilename) ) 
766             m_hasFasta = true;
767     }
768 }
769
770 ConvertPileupFormatVisitor::~ConvertPileupFormatVisitor(void) { 
771     // be sure to close Fasta reader
772     if ( m_hasFasta ) {
773         m_fasta.Close();
774         m_hasFasta = false;
775     }
776 }
777
778 void ConvertPileupFormatVisitor::Visit(const PileupPosition& pileupData ) {
779   
780     // skip if no alignments at this position
781     if ( pileupData.PileupAlignments.empty() ) return;
782   
783     // retrieve reference name
784     const string& referenceName = m_references[pileupData.RefId].RefName;
785     const int& position   = pileupData.Position;
786     
787     // retrieve reference base from FASTA file, if one provided; otherwise default to 'N'
788     char referenceBase('N');
789     if ( m_hasFasta && (pileupData.Position < m_references[pileupData.RefId].RefLength) ) {
790         if ( !m_fasta.GetBase(pileupData.RefId, pileupData.Position, referenceBase ) ) {
791             cerr << "Pileup error : Could not read reference base from FASTA file" << endl;
792             return;
793         }
794     }
795     
796     // get count of alleles at this position
797     const int numberAlleles = pileupData.PileupAlignments.size();
798     
799     // -----------------------------------------------------------
800     // build strings based on alleles at this positionInAlignment
801     
802     stringstream bases("");
803     stringstream baseQualities("");
804     stringstream mapQualities("");
805     
806     // iterate over alignments at this pileup position
807     vector<PileupAlignment>::const_iterator pileupIter = pileupData.PileupAlignments.begin();
808     vector<PileupAlignment>::const_iterator pileupEnd  = pileupData.PileupAlignments.end();
809     for ( ; pileupIter != pileupEnd; ++pileupIter ) {
810         const PileupAlignment pa = (*pileupIter);
811         const BamAlignment& ba = pa.Alignment;
812         
813         // if beginning of read segment
814         if ( pa.IsSegmentBegin )
815             bases << '^' << ( ((int)ba.MapQuality > 93) ? (char)126 : (char)((int)ba.MapQuality+33) );
816         
817         // if current base is not a DELETION
818         if ( !pa.IsCurrentDeletion ) {
819           
820             // get base at current position
821             char base = ba.QueryBases.at(pa.PositionInAlignment);
822             
823             // if base matches reference
824             if ( base == '=' || 
825                  toupper(base) == toupper(referenceBase) ||
826                  tolower(base) == tolower(referenceBase) ) 
827             {
828                 base = (ba.IsReverseStrand() ? ',' : '.' );
829             }
830             
831             // mismatches reference
832             else base = (ba.IsReverseStrand() ? tolower(base) : toupper(base) );
833             
834             // store base
835             bases << base;
836           
837             // if next position contains insertion
838             if ( pa.IsNextInsertion ) {
839                 bases << '+' << pa.InsertionLength;
840                 for (int i = 1; i <= pa.InsertionLength; ++i) {
841                     char insertedBase = (char)ba.QueryBases.at(pa.PositionInAlignment + i);
842                     bases << (ba.IsReverseStrand() ? (char)tolower(insertedBase) : (char)toupper(insertedBase) );
843                 }
844             }
845             
846             // if next position contains DELETION
847             else if ( pa.IsNextDeletion ) {
848                 bases << '-' << pa.DeletionLength;
849                 for (int i = 1; i <= pa.DeletionLength; ++i) {
850                     char deletedBase('N');
851                     if ( m_hasFasta && (pileupData.Position+i < m_references[pileupData.RefId].RefLength) ) {
852                         if ( !m_fasta.GetBase(pileupData.RefId, pileupData.Position+i, deletedBase ) ) {
853                             cerr << "Pileup error : Could not read reference base from FASTA file" << endl;
854                             return;
855                         }
856                     }
857                     bases << (ba.IsReverseStrand() ? (char)tolower(deletedBase) : (char)toupper(deletedBase) );
858                 }
859             }
860         }
861         
862         // otherwise, DELETION
863         else bases << '*';
864         
865         // if end of read segment
866         if ( pa.IsSegmentEnd ) bases << '$';
867         
868         // store current base quality
869         baseQualities << ba.Qualities.at(pa.PositionInAlignment);
870         
871         // save alignment map quality if desired
872         if ( m_isPrintingMapQualities )
873             mapQualities << ( ((int)ba.MapQuality > 93) ? (char)126 : (char)((int)ba.MapQuality+33) );
874     }
875     
876     // ----------------------
877     // print results 
878     
879     // tab-delimited
880     // <refName> <1-based pos> <refBase> <numberAlleles> <bases> <qualities> [mapQuals]
881     
882     const string TAB = "\t";
883     *m_out << referenceName       << TAB 
884            << position + 1        << TAB 
885            << referenceBase       << TAB 
886            << numberAlleles       << TAB 
887            << bases.str()         << TAB 
888            << baseQualities.str() << TAB
889            << mapQualities.str()  << endl;
890 }