Skip to content

Commit

Permalink
feat: Add aux field to tooltip (#226)
Browse files Browse the repository at this point in the history
* Add aux field to Alignment structs

* Format

* Show aux fields in tooltip

* Show aux fields and flags in tooltip for plot-bam
  • Loading branch information
fxwiegand committed Dec 25, 2021
1 parent 5d88403 commit cfc8fe5
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 36 deletions.
55 changes: 50 additions & 5 deletions src/bam/plot/bam_plot.html.tera
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,57 @@
let decompressed = LZString.decompressFromUTF16(plot);
let unpacker = new jsonm.Unpacker();
unpacker.setMaxDictSize(100000);
let unpacked_specs = unpacker.unpack(JSON.parse(decompressed));
unpacked_specs["width"] = $(window).width() - 100;
unpacked_specs["title"] = bams[i - 1];
decompressed_plots.push(unpacked_specs);
let specs = unpacker.unpack(JSON.parse(decompressed));
specs["width"] = $(window).width() - 100;
specs["title"] = bams[i - 1];
let aux_keys = new Set([]);
specs.data[1].values.forEach(function(a) {
if (a.row > 0) {
for (var key in a.aux) {
aux_keys.add(key);
}
if (Array.isArray(a.flags)) {
let flags = {};
a.flags.forEach(function(b) {
if (b === 1) {
flags[b] = "template having multiple segments in sequencing";
} else if (b === 2) {
flags[b] = "each segment properly aligned according to the aligner";
} else if (b === 4) {
flags[b] = "segment unmapped";
} else if (b === 8) {
flags[b] = "next segment in the template unmapped";
} else if (b === 16) {
flags[b] = "SEQ being reverse complemented";
} else if (b === 32) {
flags[b] = "SEQ of the next segment in the template being reverse complemented";
} else if (b === 64) {
flags[b] = "the first segment in the template";
} else if (b === 128) {
flags[b] = "the last segment in the template";
} else if (b === 256) {
flags[b] = "secondary alignment";
} else if (b === 512) {
flags[b] = "not passing filters, such as platform/vendor quality controls";
} else if (b === 1024) {
flags[b] = "PCR or optical duplicate";
} else if (b === 2048) {
flags[b] = "vega lite lines";
}
});
a.flags = flags;
}
}
});

let aux_tooltip = "";
for (var a of aux_keys) {
aux_tooltip = `${aux_tooltip}, \"${a}\": (datum[\"aux\"] || {})[\"${a}\"]`;
}
specs.marks[2].encode.update.tooltip.signal = specs.marks[2].encode.update.tooltip.signal.slice(0, -1) + aux_tooltip + "}";
decompressed_plots.push(specs);
let plot_id = `#plot_${i}`;
vegaEmbed(plot_id, unpacked_specs);
vegaEmbed(plot_id, specs);
i++;
}

Expand Down
74 changes: 44 additions & 30 deletions src/bcf/report/js/table-report.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,45 +25,59 @@ $(document).ready(function () {
let description = JSON.parse(d);

for (let t = 1; t <= vis_len; t++) {
let aux_keys = new Set([]);
let specs = $(this).data('vis' + t.toString());
specs.data[1].values.forEach(function(a) {
if (a.row > 0 && Array.isArray(a.flags)) {
let flags = {};
a.flags.forEach(function(b) {
if (b === 1) {
flags[b] = "template having multiple segments in sequencing";
} else if (b === 2) {
flags[b] = "each segment properly aligned according to the aligner";
} else if (b === 4) {
flags[b] = "segment unmapped";
} else if (b === 8) {
flags[b] = "next segment in the template unmapped";
} else if (b === 16) {
flags[b] = "SEQ being reverse complemented";
} else if (b === 32) {
flags[b] = "SEQ of the next segment in the template being reverse complemented";
} else if (b === 64) {
flags[b] = "the first segment in the template";
} else if (b === 128) {
flags[b] = "the last segment in the template";
} else if (b === 256) {
flags[b] = "secondary alignment";
} else if (b === 512) {
flags[b] = "not passing filters, such as platform/vendor quality controls";
} else if (b === 1024) {
flags[b] = "PCR or optical duplicate";
} else if (b === 2048) {
flags[b] = "vega lite lines";
}
});
a.flags = flags;
if (a.row > 0) {
for (var key in a.aux) {
aux_keys.add(key);
}
if (Array.isArray(a.flags)) {
let flags = {};
a.flags.forEach(function(b) {
if (b === 1) {
flags[b] = "template having multiple segments in sequencing";
} else if (b === 2) {
flags[b] = "each segment properly aligned according to the aligner";
} else if (b === 4) {
flags[b] = "segment unmapped";
} else if (b === 8) {
flags[b] = "next segment in the template unmapped";
} else if (b === 16) {
flags[b] = "SEQ being reverse complemented";
} else if (b === 32) {
flags[b] = "SEQ of the next segment in the template being reverse complemented";
} else if (b === 64) {
flags[b] = "the first segment in the template";
} else if (b === 128) {
flags[b] = "the last segment in the template";
} else if (b === 256) {
flags[b] = "secondary alignment";
} else if (b === 512) {
flags[b] = "not passing filters, such as platform/vendor quality controls";
} else if (b === 1024) {
flags[b] = "PCR or optical duplicate";
} else if (b === 2048) {
flags[b] = "vega lite lines";
}
});
a.flags = flags;
}
}
});

let aux_tooltip = "";
for (var a of aux_keys) {
aux_tooltip = `${aux_tooltip}, \"${a}\": (datum[\"aux\"] || {})[\"${a}\"]`;
}
specs.marks[2].encode.update.tooltip.signal = specs.marks[2].encode.update.tooltip.signal.slice(0, -1) + aux_tooltip + "}";
specs.title = 'Sample: ' + $(this).data('vis-sample' + t.toString());
specs.width = $('#vis1').width() - 40;
vegaEmbed('#vis' + t.toString(), specs);
}



$('.spinner-border').hide();

$("#sidebar").empty();
Expand Down
42 changes: 41 additions & 1 deletion src/bcf/report/table_report/alignment_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use self::rust_htslib::bam::FetchDefinition;
use crate::bcf::report::table_report::fasta_reader::read_fasta;
use crate::common::Region;
use anyhow::Result;
use itertools::Itertools;
use rust_htslib::bam::record::CigarStringView;
use rust_htslib::{bam, bam::Read};
use serde::Serialize;
use std::collections::HashMap;
use std::path::Path;

#[derive(Serialize, Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -35,6 +37,7 @@ pub struct Alignment {
tid: i32,
mate_tid: i32,
mapq: u8,
aux: HashMap<String, String>,
}

#[derive(Serialize, Clone, Debug, PartialEq)]
Expand All @@ -49,6 +52,7 @@ pub struct AlignmentNucleobase {
pub read_end: u32,
pub mapq: u8,
pub cigar: String,
aux: HashMap<String, String>,
}

#[derive(Serialize, Clone, Debug, PartialEq)]
Expand All @@ -62,6 +66,7 @@ pub struct AlignmentMatch {
pub read_end: u32,
pub mapq: u8,
pub cigar: String,
aux: HashMap<String, String>,
}

pub fn decode_flags(code: u16) -> Vec<u16> {
Expand Down Expand Up @@ -115,7 +120,12 @@ fn make_alignment(record: &bam::Record) -> Alignment {
//Länge
let le = record.seq().len() as u16;

//Sequenz
let aux: HashMap<String, String> = record
.aux_iter()
.map(|r| r.unwrap())
.map(|(r, v)| (String::from_utf8(r.to_owned()).unwrap(), aux_to_string(v)))
.collect();

let seq = record.seq().as_bytes();
let sequenz = String::from_utf8(seq).unwrap();

Expand All @@ -139,6 +149,7 @@ fn make_alignment(record: &bam::Record) -> Alignment {
tid,
mate_tid: mtid,
mapq: record.mapq(),
aux,
}
}

Expand Down Expand Up @@ -177,6 +188,7 @@ pub fn make_nucleobases<P: AsRef<Path> + std::fmt::Debug>(
read_end: (temp_snippet.mate_pos + 100) as u32,
mapq: snippet.mapq,
cigar: snippet.cigar.to_string(),
aux: snippet.aux.clone(),
};

matches.push(pairing);
Expand Down Expand Up @@ -278,6 +290,7 @@ pub fn make_nucleobases<P: AsRef<Path> + std::fmt::Debug>(
read_end: re as u32,
mapq: snippet.mapq,
cigar: snippet.cigar.to_string(),
aux: snippet.aux.clone(),
};

if from as f64 <= (base.start_position + 0.5)
Expand Down Expand Up @@ -323,6 +336,7 @@ pub fn make_nucleobases<P: AsRef<Path> + std::fmt::Debug>(
read_end: read_end as u32,
mapq: snippet.mapq,
cigar: snippet.cigar.to_string(),
aux: snippet.aux.clone(),
};

read_offset += 1;
Expand Down Expand Up @@ -450,6 +464,7 @@ fn make_markers(
read_end: read_end as u32,
mapq: snip.mapq,
cigar: snip.cigar.to_string(),
aux: snip.aux.clone(),
});
}

Expand All @@ -464,6 +479,7 @@ fn make_markers(
read_end: read_end as u32,
mapq: snip.mapq,
cigar: snip.cigar.to_string(),
aux: snip.aux,
};
(mtch, base)
}
Expand Down Expand Up @@ -498,5 +514,29 @@ fn end_mismatch_detection(snip: Alignment, match_start: i64, match_count: i64) -
read_end: re as u32,
mapq: snip.mapq,
cigar: snip.cigar.to_string(),
aux: snip.aux,
}
}

fn aux_to_string(aux: rust_htslib::bam::record::Aux) -> String {
match aux {
rust_htslib::bam::record::Aux::Char(c) => String::from_utf8(vec![c]).unwrap(),
rust_htslib::bam::record::Aux::I8(i) => i.to_string(),
rust_htslib::bam::record::Aux::U8(i) => i.to_string(),
rust_htslib::bam::record::Aux::I16(i) => i.to_string(),
rust_htslib::bam::record::Aux::U16(i) => i.to_string(),
rust_htslib::bam::record::Aux::I32(i) => i.to_string(),
rust_htslib::bam::record::Aux::U32(i) => i.to_string(),
rust_htslib::bam::record::Aux::Float(i) => i.to_string(),
rust_htslib::bam::record::Aux::Double(i) => i.to_string(),
rust_htslib::bam::record::Aux::String(s) => s.to_owned(),
rust_htslib::bam::record::Aux::HexByteArray(i) => i.to_string(),
rust_htslib::bam::record::Aux::ArrayI8(a) => a.iter().join(","),
rust_htslib::bam::record::Aux::ArrayU8(a) => a.iter().join(","),
rust_htslib::bam::record::Aux::ArrayU16(a) => a.iter().join(","),
rust_htslib::bam::record::Aux::ArrayI16(a) => a.iter().join(","),
rust_htslib::bam::record::Aux::ArrayU32(a) => a.iter().join(","),
rust_htslib::bam::record::Aux::ArrayI32(a) => a.iter().join(","),
rust_htslib::bam::record::Aux::ArrayFloat(a) => a.iter().join(","),
}
}

0 comments on commit cfc8fe5

Please sign in to comment.