]> git.donarmstrong.com Git - samtools.git/blob - bam2depth.c
87a4c5b6cf379c026c6883d95a44c700cc3d5364
[samtools.git] / bam2depth.c
1 /* This program demonstrates how to generate pileup from multiple BAMs
2  * simutaneously, to achieve random access and to use the BED interface.
3  * To compile this program separately, you may:
4  *
5  *   gcc -g -O2 -Wall -o bam2depth -D_MAIN_BAM2DEPTH bam2depth.c -L. -lbam -lz
6  */
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <unistd.h>
11 #include "bam.h"
12
13 typedef struct {     // auxiliary data structure
14         bamFile fp;      // the file handler
15         bam_iter_t iter; // NULL if a region not specified
16         int min_mapQ, min_len; // mapQ filter; length filter
17 } aux_t;
18
19 void *bed_read(const char *fn); // read a BED or position list file
20 void bed_destroy(void *_h);     // destroy the BED data structure
21 int bed_overlap(const void *_h, const char *chr, int beg, int end); // test if chr:beg-end overlaps
22
23 // This function reads a BAM alignment from one BAM file.
24 static int read_bam(void *data, bam1_t *b) // read level filters better go here to avoid pileup
25 {
26         aux_t *aux = (aux_t*)data; // data in fact is a pointer to an auxiliary structure
27         int ret = aux->iter? bam_iter_read(aux->fp, aux->iter, b) : bam_read1(aux->fp, b);
28         if (!(b->core.flag&BAM_FUNMAP)) {
29                 if ((int)b->core.qual < aux->min_mapQ) b->core.flag |= BAM_FUNMAP;
30                 else if (aux->min_len && bam_cigar2qlen(&b->core, bam1_cigar(b)) < aux->min_len) b->core.flag |= BAM_FUNMAP;
31         }
32         return ret;
33 }
34
35 #ifdef _MAIN_BAM2DEPTH
36 int main(int argc, char *argv[])
37 #else
38 int main_depth(int argc, char *argv[])
39 #endif
40 {
41         int i, n, tid, beg, end, pos, *n_plp, baseQ = 0, mapQ = 0, min_len = 0;
42         const bam_pileup1_t **plp;
43         char *reg = 0; // specified region
44         void *bed = 0; // BED data structure
45         bam_header_t *h = 0; // BAM header of the 1st input
46         aux_t **data;
47         bam_mplp_t mplp;
48
49         // parse the command line
50         while ((n = getopt(argc, argv, "r:b:q:Q:l:")) >= 0) {
51                 switch (n) {
52                         case 'l': min_len = atoi(optarg); break; // minimum query length
53                         case 'r': reg = strdup(optarg); break;   // parsing a region requires a BAM header
54                         case 'b': bed = bed_read(optarg); break; // BED or position list file can be parsed now
55                         case 'q': baseQ = atoi(optarg); break;   // base quality threshold
56                         case 'Q': mapQ = atoi(optarg); break;    // mapping quality threshold
57                 }
58         }
59         if (optind == argc) {
60                 fprintf(stderr, "Usage: bam2depth [-r reg] [-q baseQthres] [-Q mapQthres] [-l minQLen] [-b in.bed] <in1.bam> [...]\n");
61                 return 1;
62         }
63
64         // initialize the auxiliary data structures
65         n = argc - optind; // the number of BAMs on the command line
66         data = calloc(n, sizeof(void*)); // data[i] for the i-th input
67         beg = 0; end = 1<<30; tid = -1;  // set the default region
68         for (i = 0; i < n; ++i) {
69                 bam_header_t *htmp;
70                 data[i] = calloc(1, sizeof(aux_t));
71                 data[i]->fp = bam_open(argv[optind+i], "r"); // open BAM
72                 data[i]->min_mapQ = mapQ;                    // set the mapQ filter
73                 data[i]->min_len  = min_len;                 // set the qlen filter
74                 htmp = bam_header_read(data[i]->fp);         // read the BAM header
75                 if (i == 0) {
76                         h = htmp; // keep the header of the 1st BAM
77                         if (reg) bam_parse_region(h, reg, &tid, &beg, &end); // also parse the region
78                 } else bam_header_destroy(htmp); // if not the 1st BAM, trash the header
79                 if (tid >= 0) { // if a region is specified and parsed successfully
80                         bam_index_t *idx = bam_index_load(argv[optind+i]);  // load the index
81                         data[i]->iter = bam_iter_query(idx, tid, beg, end); // set the iterator
82                         bam_index_destroy(idx); // the index is not needed any more; phase out of the memory
83                 }
84         }
85
86         // the core multi-pileup loop
87         mplp = bam_mplp_init(n, read_bam, (void**)data); // initialization
88         n_plp = calloc(n, sizeof(int)); // n_plp[i] is the number of covering reads from the i-th BAM
89         plp = calloc(n, sizeof(void*)); // plp[i] points to the array of covering reads (internal in mplp)
90         while (bam_mplp_auto(mplp, &tid, &pos, n_plp, plp) > 0) { // come to the next covered position
91                 if (pos < beg || pos >= end) continue; // out of range; skip
92                 if (bed && bed_overlap(bed, h->target_name[tid], pos, pos + 1) == 0) continue; // not in BED; skip
93                 fputs(h->target_name[tid], stdout); printf("\t%d", pos+1); // a customized printf() would be faster
94                 for (i = 0; i < n; ++i) { // base level filters have to go here
95                         int j, m = 0;
96                         for (j = 0; j < n_plp[i]; ++j) {
97                                 const bam_pileup1_t *p = plp[i] + j; // DON'T modfity plp[][] unless you really know
98                                 if (p->is_del || p->is_refskip) ++m; // having dels or refskips at tid:pos
99                                 else if (bam1_qual(p->b)[p->qpos] < baseQ) ++m; // low base quality
100                         }
101                         printf("\t%d", n_plp[i] - m); // this the depth to output
102                 }
103                 putchar('\n');
104         }
105         free(n_plp); free(plp);
106         bam_mplp_destroy(mplp);
107
108         bam_header_destroy(h);
109         for (i = 0; i < n; ++i) {
110                 bam_close(data[i]->fp);
111                 if (data[i]->iter) bam_iter_destroy(data[i]->iter);
112                 free(data[i]);
113         }
114         free(data); free(reg);
115         if (bed) bed_destroy(bed);
116         return 0;
117 }