Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export(scale_x_units)
export(scale_y_units)
export(set_units)
export(ud_are_convertible)
export(ud_convert)
export(unitless)
export(units_options)
export(valid_udunits)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
* Improve `keep_units()` helper for more general usage scenarios;
#394 @d-morrison addressing #392

* Add `ud_convert()` to convert units of a vector; #399 @dlebauer addressing #398

# version 0.8-5

* avoid -Wformat-security warning on CRAN
Expand Down
12 changes: 6 additions & 6 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ ud_compare <- function(x, y, xn, yn) {
.Call('_units_ud_compare', PACKAGE = 'units', x, y, xn, yn)
}

ud_convert <- function(val, from, to) {
.Call('_units_ud_convert', PACKAGE = 'units', val, from, to)
ud_convertible <- function(from, to) {
.Call('_units_ud_convertible', PACKAGE = 'units', from, to)
}

ud_convert_doubles <- function(val, from, to) {
.Call('_units_ud_convert_doubles', PACKAGE = 'units', val, from, to)
}

ud_map_names <- function(names, inunit) {
Expand Down Expand Up @@ -57,10 +61,6 @@ R_ut_get_symbol <- function(unit) {
.Call('_units_R_ut_get_symbol', PACKAGE = 'units', unit)
}

R_ut_are_convertible <- function(a, b) {
.Call('_units_R_ut_are_convertible', PACKAGE = 'units', a, b)
}

R_ut_scale <- function(unit, factor) {
.Call('_units_R_ut_scale', PACKAGE = 'units', unit, factor)
}
Expand Down
49 changes: 34 additions & 15 deletions R/udunits.R
Original file line number Diff line number Diff line change
@@ -1,31 +1,50 @@
#' Test if two units are convertible
#' \pkg{udunits2} utilities
#'
#' Parses and checks whether units can be converted by UDUNITS-2. Units may not
#' be convertible either because they are different magnitudes or because one
#' (or both) units are not defined in the database.
#' Some \pkg{udunits2} utilities are exposed to the user. These functions are
#' useful for checking whether units are convertible or converting between units
#' without having to create \pkg{units} objects.
#'
#' @param x character or object of class \code{symbolic_units}, for the symbol
#' of the first unit.
#' @param y character or object of class \code{symbolic_units}, for the symbol
#' of the second unit.
#' @param from character or object of class \code{symbolic_units},
#' for the symbol of the original unit.
#' @param to character or object of class \code{symbolic_units},
#' for the symbol of the unit to convert.
#'
#' @return boolean, \code{TRUE} if both units exist and are convertible.
#' @return \code{ud_are_convertible}
#' returns \code{TRUE} if both units exist and are convertible,
#' \code{FALSE} otherwise.
#'
#' @name udunits2
#' @export
ud_are_convertible <- function(from, to) {
ud_convertible(ud_char(from), ud_char(to))
}

#' @param x numeric vector
#'
#' @return \code{ud_convert}
#' returns a numeric vector with \code{x} converted to new unit.
#'
#' @name udunits2
#' @export
#'
#' @examples
#' ud_are_convertible("m", "km")
#' ud_convert(100, "m", "km")
#'
#' a <- set_units(1:3, m/s)
#' ud_are_convertible(units(a), "km/h")
#' ud_are_convertible("s", "kg")
ud_are_convertible = function(x, y) {
stopifnot(inherits(x, c("character", "symbolic_units")), inherits(y, c("character", "symbolic_units")))
res <- try(R_ut_are_convertible(
R_ut_parse(ud_char(x)), R_ut_parse(ud_char(y))), silent = TRUE)
! inherits(res, "try-error") && res
#' ud_convert(1:3, units(a), "km/h")
#'
#' ud_are_convertible("degF", "degC")
#' ud_convert(32, "degF", "degC")
ud_convert <- function(x, from, to) {
ud_convert_doubles(x, ud_char(from), ud_char(to))
}

ud_char <- function(x) {
if (is.character(x)) return(x)
if (!inherits(x, "symbolic_units")) stop("not a unit")

Check warning on line 46 in R/udunits.R

View check run for this annotation

Codecov / codecov/patch

R/udunits.R#L46

Added line #L46 was not covered by tests

res <- if (length(x$numerator))
paste(x$numerator, collapse=" ") else "1"
if (length(x$denominator))
Expand Down
29 changes: 0 additions & 29 deletions man/ud_are_convertible.Rd

This file was deleted.

45 changes: 45 additions & 0 deletions man/udunits2.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 18 additions & 18 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,28 @@ BEGIN_RCPP
return rcpp_result_gen;
END_RCPP
}
// ud_convert
NumericVector ud_convert(NumericVector val, std::string from, std::string to);
RcppExport SEXP _units_ud_convert(SEXP valSEXP, SEXP fromSEXP, SEXP toSEXP) {
// ud_convertible
LogicalVector ud_convertible(std::string from, std::string to);
RcppExport SEXP _units_ud_convertible(SEXP fromSEXP, SEXP toSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< std::string >::type from(fromSEXP);
Rcpp::traits::input_parameter< std::string >::type to(toSEXP);
rcpp_result_gen = Rcpp::wrap(ud_convertible(from, to));
return rcpp_result_gen;
END_RCPP
}
// ud_convert_doubles
NumericVector ud_convert_doubles(NumericVector val, std::string from, std::string to);
RcppExport SEXP _units_ud_convert_doubles(SEXP valSEXP, SEXP fromSEXP, SEXP toSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< NumericVector >::type val(valSEXP);
Rcpp::traits::input_parameter< std::string >::type from(fromSEXP);
Rcpp::traits::input_parameter< std::string >::type to(toSEXP);
rcpp_result_gen = Rcpp::wrap(ud_convert(val, from, to));
rcpp_result_gen = Rcpp::wrap(ud_convert_doubles(val, from, to));
return rcpp_result_gen;
END_RCPP
}
Expand Down Expand Up @@ -160,18 +172,6 @@ BEGIN_RCPP
return rcpp_result_gen;
END_RCPP
}
// R_ut_are_convertible
LogicalVector R_ut_are_convertible(SEXP a, SEXP b);
RcppExport SEXP _units_R_ut_are_convertible(SEXP aSEXP, SEXP bSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< SEXP >::type a(aSEXP);
Rcpp::traits::input_parameter< SEXP >::type b(bSEXP);
rcpp_result_gen = Rcpp::wrap(R_ut_are_convertible(a, b));
return rcpp_result_gen;
END_RCPP
}
// R_ut_scale
SEXP R_ut_scale(SEXP unit, double factor);
RcppExport SEXP _units_R_ut_scale(SEXP unitSEXP, SEXP factorSEXP) {
Expand Down Expand Up @@ -298,7 +298,8 @@ static const R_CallMethodDef CallEntries[] = {
{"_units_ud_init", (DL_FUNC) &_units_ud_init, 1},
{"_units_ud_set_encoding", (DL_FUNC) &_units_ud_set_encoding, 1},
{"_units_ud_compare", (DL_FUNC) &_units_ud_compare, 4},
{"_units_ud_convert", (DL_FUNC) &_units_ud_convert, 3},
{"_units_ud_convertible", (DL_FUNC) &_units_ud_convertible, 2},
{"_units_ud_convert_doubles", (DL_FUNC) &_units_ud_convert_doubles, 3},
{"_units_ud_map_names", (DL_FUNC) &_units_ud_map_names, 2},
{"_units_ud_unmap_names", (DL_FUNC) &_units_ud_unmap_names, 1},
{"_units_ud_map_symbols", (DL_FUNC) &_units_ud_map_symbols, 2},
Expand All @@ -308,7 +309,6 @@ static const R_CallMethodDef CallEntries[] = {
{"_units_R_ut_new_dimensionless_unit", (DL_FUNC) &_units_R_ut_new_dimensionless_unit, 0},
{"_units_R_ut_get_name", (DL_FUNC) &_units_R_ut_get_name, 1},
{"_units_R_ut_get_symbol", (DL_FUNC) &_units_R_ut_get_symbol, 1},
{"_units_R_ut_are_convertible", (DL_FUNC) &_units_R_ut_are_convertible, 2},
{"_units_R_ut_scale", (DL_FUNC) &_units_R_ut_scale, 2},
{"_units_R_ut_offset", (DL_FUNC) &_units_R_ut_offset, 2},
{"_units_R_ut_multiply", (DL_FUNC) &_units_R_ut_multiply, 2},
Expand Down
28 changes: 18 additions & 10 deletions src/udunits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,24 @@ IntegerVector ud_compare(NumericVector x, NumericVector y,
}

// [[Rcpp::export]]
NumericVector ud_convert(NumericVector val, std::string from, std::string to) {
LogicalVector ud_convertible(std::string from, std::string to) {
bool convertible = false;

ut_unit *u_from = ut_parse(sys, ut_trim(from.data(), enc), enc);
ut_unit *u_to = ut_parse(sys, ut_trim(to.data(), enc), enc);

if (u_from == NULL || u_to == NULL)
goto finished; // #nocov
convertible = ut_are_convertible(u_from, u_to) != 0;

finished:
ut_free(u_from);
ut_free(u_to);
return convertible;
}

// [[Rcpp::export]]
NumericVector ud_convert_doubles(NumericVector val, std::string from, std::string to) {
if (val.size() == 0) return val;

ut_unit *u_from = ut_parse(sys, ut_trim(from.data(), enc), enc);
Expand Down Expand Up @@ -222,15 +239,6 @@ CharacterVector R_ut_get_symbol(SEXP unit) {
return CharacterVector::create(s);
}

// [[Rcpp::export]]
LogicalVector R_ut_are_convertible(SEXP a, SEXP b) {
ut_unit *u1 = ut_unwrap(a);
ut_unit *u2 = ut_unwrap(b);
if (u1 == NULL || u2 == NULL)
return false; // #nocov
return ut_are_convertible(u1, u2) != 0;
}

// # nocov start

// [[Rcpp::export]]
Expand Down
8 changes: 0 additions & 8 deletions tests/testthat/test_conversion.R
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,6 @@ test_that("NA as units generate warnings", {
expect_error(set_units(NA_real_, NA, mode="standard"), "a missing value for units is not allowed")
})

test_that("ud_are_convertible return the expected value", {
x <- 1:10 * as_units("m")
expect_type(ud_are_convertible("m", "km"), "logical")
expect_true(ud_are_convertible("m", "km"))
expect_true(ud_are_convertible(units(x), "km"))
expect_false(ud_are_convertible("s", "kg"))
})

test_that("set_units keeps names even with unit conversion (#305)", {
expect_named(set_units(c(a=1), "m"), "a")
expect_named(
Expand Down
25 changes: 25 additions & 0 deletions tests/testthat/test_udunits.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
test_that("ud_are_convertible return the expected value", {
x <- 1:10 * as_units("m")
expect_type(ud_are_convertible("m", "km"), "logical")
expect_true(ud_are_convertible("m", "km"))
expect_true(ud_are_convertible(units(x), "km"))
expect_false(ud_are_convertible("s", "kg"))
})

test_that("ud_convert works with simple conversions", {
x <- 1:10 * as_units("m")
expect_equal(ud_convert(1, "m", "km"), 1/1000)
expect_equal(ud_convert(as.numeric(x), units(x), "km"), as.numeric(x)/1000)
expect_equal(ud_convert(1, "km", "m"), 1000)
expect_equal(ud_convert(32, "degF", "degC"), 0)
expect_equal(ud_convert(0, "degC", "K"), 273.15)
})

test_that("ud_convert works with vectors", {
expect_equal(ud_convert(1:2, "m", "km"), 1:2/1000)
expect_equal(ud_convert(c(32, 212), "degF", "degC"), c(0, 100))
})

test_that("ud_convert returns Error for incompatible units", {
expect_error(ud_convert(100, "m", "kg"), "Units not convertible")
})
Loading