diff --git a/d4tools/src/plot/cli.yml b/d4tools/src/plot/cli.yml index 76d9097..0e23328 100644 --- a/d4tools/src/plot/cli.yml +++ b/d4tools/src/plot/cli.yml @@ -14,9 +14,20 @@ args: short: R long: resolution help: Specify the resolution of the output image + value_name: widthxheight - region: required: true short: r long: region help: Regions to be plotted - value_name: chr[:start[-end]] \ No newline at end of file + value_name: chr[:start[-end]] + - nbins: + short: n + long: nbins + help: Number of bins for data downsampling + value_name: n + - bin-width: + short: w + long: bin-width + help: Width of each bin in base pairs + value_name: width diff --git a/d4tools/src/plot/mod.rs b/d4tools/src/plot/mod.rs index 7918bfa..a8535b5 100644 --- a/d4tools/src/plot/mod.rs +++ b/d4tools/src/plot/mod.rs @@ -54,6 +54,25 @@ fn downsample_data( .collect()) } +fn actual_range_length( + path: &str, + chr: &str, + mut range: (u32, u32), +) -> Result> { + let input: d4::D4TrackReader = d4::D4TrackReader::open(path)?; + let target = input + .header() + .chrom_list() + .iter() + .find(|this| this.name == chr) + .unwrap(); + + range.0 = range.0.min(target.size as u32); + range.1 = range.1.max(range.0).min(target.size as u32); + + Ok(range.1 as u64 - range.0 as u64 + 1) +} + pub fn entry_point(args: Vec) -> Result<(), Box> { let yaml = load_yaml!("cli.yml"); let matches = App::from_yaml(yaml) @@ -86,7 +105,48 @@ pub fn entry_point(args: Vec) -> Result<(), Box> }) .unwrap(); - let data = downsample_data(input, &chr, (left, right), 256)?; + let range_length = actual_range_length(input, &chr, (left, right))?; + + let nbins = matches.value_of("nbins").map(|v| v.parse::().unwrap()); + let bin_width = matches + .value_of("bin-width") + .map(|v| v.parse::().unwrap()); + + let nbins = match (nbins, bin_width) { + (Some(n), None) => { + if n == 0 { + panic!("Invalid nbins: cannot be zero"); + } + n + } + (None, Some(width)) => { + if width == 0 { + panic!("Invalid bin-width: cannot be zero"); + } + (range_length - 1) / width + 1 + } + (Some(n), Some(width)) => { + if n == 0 { + panic!("Invalid nbins: cannot be zero"); + } + if width == 0 { + panic!("Invalid bin-width: cannot be zero"); + } + let calculated_nbins = (range_length - 1) / width + 1; + if n != calculated_nbins { + panic!( + "Conflicting options: --nbins ({}) and --bin-width ({}) do not match.", + n, width + ); + } + n + } + (None, None) => 256, + }; + + let nbins = nbins as usize; + + let data = downsample_data(input, &chr, (left, right), nbins)?; let pos_range = plotters::data::fitting_range(data.iter().map(|x| &x.0)); let mut data_range = plotters::data::fitting_range(data.iter().map(|x| &x.1));