diff --git a/.Rbuildignore b/.Rbuildignore index ac00a31..38931f6 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -4,3 +4,5 @@ ^_pkgdown\.yml$ ^docs$ ^pkgdown$ +^LICENSE\.md$ +^README\.Rmd$ diff --git a/DESCRIPTION b/DESCRIPTION index afb111e..83d9d51 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,11 +1,11 @@ Type: Package Package: cpp11bigwig -Title: Read bigWig Files. +Title: Read-only access to bigWig Files Version: 0.1.0 Authors@R: person("Jay", "Hesselberth", , "jay.hesselberth@gmail.com", role = c("aut", "cre")) Description: cpp11bigwig wraps libBigWig by @dpryan79. -License: MIT +License: MIT + file LICENSE URL: https://rnabioco.github.io/cpp11bigwig/, https://github.com/rnabioco/cpp11bigwig Suggests: diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a7a4a --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +YEAR: 2024 +COPYRIGHT HOLDER: cpp11bigwig authors diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..82f1ef6 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2024 cpp11bigwig authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/R/cpp11.R b/R/cpp11.R index 616309a..b544a3d 100644 --- a/R/cpp11.R +++ b/R/cpp11.R @@ -1,5 +1,5 @@ # Generated by cpp11: do not edit by hand -read_bigwig_impl <- function(bwfname, chrom, start, end) { - .Call(`_cpp11bigwig_read_bigwig_impl`, bwfname, chrom, start, end) +read_bigwig_cpp <- function(bwfname, chrom, start, end, raw) { + .Call(`_cpp11bigwig_read_bigwig_cpp`, bwfname, chrom, start, end, raw) } diff --git a/R/read_bigwig.r b/R/read_bigwig.r index 1376e09..230532d 100644 --- a/R/read_bigwig.r +++ b/R/read_bigwig.r @@ -4,6 +4,7 @@ #' @param chrom read data for specific chromosome #' @param start start position for data #' @param end end position for data +#' @param raw return raw data, single-base intervals #' #' @return \code{data.frame} #' @@ -15,15 +16,15 @@ #' read_bigwig(bw) #' #' @export -read_bigwig <- function(bwfile, chrom = "", start = -1, end = -1) { +read_bigwig <- function(bwfile, chrom = NULL, start = NULL, end = NULL, raw = FALSE) { if (!file.exists(bwfile)) { stop("File does not exist: ", bwfile) } - # if ((!is.null(start) && start < 0) || (!is.null(end) && end < 0)) { - # stop("`start` and `end` must both be >= 0") - # } + if ((!is.null(start) && start < 0) || (!is.null(end) && end < 0)) { + stop("`start` and `end` must both be >= 0") + } - read_bigwig_impl(bwfile, chrom, start, end) + read_bigwig_cpp(bwfile, chrom, start, end, raw) } diff --git a/man/read_bigwig.Rd b/man/read_bigwig.Rd index 6d3344b..983d271 100644 --- a/man/read_bigwig.Rd +++ b/man/read_bigwig.Rd @@ -4,7 +4,7 @@ \alias{read_bigwig} \title{Read data from bigWig files.} \usage{ -read_bigwig(bwfile, chrom = "", start = -1, end = -1) +read_bigwig(bwfile, chrom = NULL, start = NULL, end = NULL, raw = FALSE) } \arguments{ \item{bwfile}{filename for bigWig file} @@ -14,6 +14,8 @@ read_bigwig(bwfile, chrom = "", start = -1, end = -1) \item{start}{start position for data} \item{end}{end position for data} + +\item{raw}{return raw data, single-base intervals} } \value{ \code{data.frame} diff --git a/src/cpp11.cpp b/src/cpp11.cpp index b87af9f..117c3c8 100644 --- a/src/cpp11.cpp +++ b/src/cpp11.cpp @@ -6,16 +6,16 @@ #include // cpp11bigwig.cpp -writable::data_frame read_bigwig_impl(std::string bwfname, std::string chrom, int start, int end); -extern "C" SEXP _cpp11bigwig_read_bigwig_impl(SEXP bwfname, SEXP chrom, SEXP start, SEXP end) { +writable::data_frame read_bigwig_cpp(std::string bwfname, sexp chrom, sexp start, sexp end, sexp raw); +extern "C" SEXP _cpp11bigwig_read_bigwig_cpp(SEXP bwfname, SEXP chrom, SEXP start, SEXP end, SEXP raw) { BEGIN_CPP11 - return cpp11::as_sexp(read_bigwig_impl(cpp11::as_cpp>(bwfname), cpp11::as_cpp>(chrom), cpp11::as_cpp>(start), cpp11::as_cpp>(end))); + return cpp11::as_sexp(read_bigwig_cpp(cpp11::as_cpp>(bwfname), cpp11::as_cpp>(chrom), cpp11::as_cpp>(start), cpp11::as_cpp>(end), cpp11::as_cpp>(raw))); END_CPP11 } extern "C" { static const R_CallMethodDef CallEntries[] = { - {"_cpp11bigwig_read_bigwig_impl", (DL_FUNC) &_cpp11bigwig_read_bigwig_impl, 4}, + {"_cpp11bigwig_read_bigwig_cpp", (DL_FUNC) &_cpp11bigwig_read_bigwig_cpp, 5}, {NULL, NULL, 0} }; } diff --git a/src/cpp11bigwig.cpp b/src/cpp11bigwig.cpp index ed686b7..5b23ac0 100644 --- a/src/cpp11bigwig.cpp +++ b/src/cpp11bigwig.cpp @@ -1,11 +1,12 @@ #include #include + using namespace cpp11; #include "bigWig.h" [[cpp11::register]] -writable::data_frame read_bigwig_impl(std::string bwfname, std::string chrom, int start, int end) { +writable::data_frame read_bigwig_cpp(std::string bwfname, sexp chrom, sexp start, sexp end, sexp raw) { //http://stackoverflow.com/questions/347949/how-to-convert-a-stdstring-to-const-char-or-char std::vector bwfile(bwfname.begin(), bwfname.end()) ; @@ -14,11 +15,12 @@ writable::data_frame read_bigwig_impl(std::string bwfname, std::string chrom, in const char mode = 'r' ; bigWigFile_t *bwf = NULL; - // XXX change NULL to a CURL callback. see libBigWig demos + + // NULL can be a CURL callback. see libBigWig demos bwf = bwOpen(&bwfile[0], NULL, &mode) ; if (!bwf) - stop("Failed to open file: %s\n", bwfname.c_str()) ; + stop("Failed to open file: '%s'\n", bwfname.c_str()) ; std::vector chroms ; std::vector starts ; @@ -30,19 +32,22 @@ writable::data_frame read_bigwig_impl(std::string bwfname, std::string chrom, in int nchrom = bwf->cl->nKeys ; for (int nc = 0; nccl->chrom[nc] ; - std::string cur_chrom_c = cur_chrom ; + char* bw_chrom = bwf->cl->chrom[nc] ; + std::string bw_chrom_c(bw_chrom) ; - if (!chrom.empty() && chrom != cur_chrom_c) continue ; + if (!Rf_isNull(chrom)) { + std::string r_chrom = as_cpp(chrom) ; + if (r_chrom != bw_chrom_c) continue ; + } // set maximum boundaries if start / end are not specified - if (start == -1) start = 0 ; - if (end == -1) end = bwf->cl->len[nc]; + int bw_start = Rf_isNull(start) ? 0 : as_cpp(start) ; + int bw_end = Rf_isNull(end) ? bwf->cl->len[nc] : as_cpp(end) ; - intervals = bwGetValues(bwf, cur_chrom, start, end, 0) ; + intervals = bwGetValues(bwf, bw_chrom, bw_start, bw_end, 0) ; if (!intervals) - stop("Failed to retreived intervals for %s\n", chrom.c_str()) ; + stop("Failed to retreived intervals for %s\n", bw_chrom) ; int nint = intervals->l ; for(int i=0; i= 0 + +--- + + `start` and `end` must both be >= 0 + diff --git a/tests/testthat/test-read.R b/tests/testthat/test-read.R index e2a34a2..418ce63 100644 --- a/tests/testthat/test-read.R +++ b/tests/testthat/test-read.R @@ -1,15 +1,41 @@ -test_that("output have expected shape", { - out <- read_bigwig(test_path("data/test.bw")) - expect_equal(ncol(out), 4) - expect_equal(nrow(out), 154) +test_that("results have expected shape", { + # -- raw output ------- + bw <- test_path("data/test.bw") + + res <- read_bigwig(bw) + expect_equal(ncol(res), 4) + expect_equal(nrow(res), 154) + + # chrom + expect_equal(nrow(read_bigwig(bw, chrom = "1")), 54) + expect_equal(nrow(read_bigwig(bw, chrom = "10")), 100) + + # `chrom` must be a string + expect_snapshot_error(read_bigwig(bw, chrom = 10)) + + # 0 rows, chrom doesn't exist + expect_equal(nrow(read_bigwig(bw, chrom = "100")), 0) + + # start/end + expect_equal(nrow(read_bigwig(bw, chrom = "1", start = 1, end = 100)), 2) + expect_equal(nrow(read_bigwig(bw, chrom = "1", start = 100, end = 100)), 0) + expect_equal(nrow(read_bigwig(bw, chrom = "10", start = 100, end = 1000)), 100) + expect_equal(nrow(read_bigwig(bw, chrom = "10", start = 200, end = 250)), 50) + + # values + res <- read_bigwig(bw, chrom = "10", start = 100, end = 1000) + expect_equal(length(unique(res$value)), 1) + expect_equal(unique(res$value), 2) + + # -- bedgraph output ----- }) test_that("missing file causes error", { - expect_error(read_bigwig("missing.bw")) + expect_snapshot_error(read_bigwig("missing.bw")) }) test_that("negative coords causes error", { - expect_error(read_bigwig(test_path("data/test.bw"), start = -1)) - expect_error(read_bigwig(test_path("data/test.bw"), end = -1)) + expect_snapshot_error(read_bigwig(test_path("data/test.bw"), start = -1)) + expect_snapshot_error(read_bigwig(test_path("data/test.bw"), end = -1)) })