diff --git a/.hgtags b/.hgtags deleted file mode 100644 index 1f1defc..0000000 --- a/.hgtags +++ /dev/null @@ -1,5 +0,0 @@ -6c5d3b27dd40c6fde5982005b728a790fe4da7c7 0.1 -aeb3c72cc00e172a58bc47bab1c9cd77dd605f56 0.2 -a15644c6c712ba05dbbc65d3ec40f78a2b8c4f08 0.3 -1b207621f381c9d04b12a4e90a6521a4f8326661 0.5 -8e9db5bd2a94dbebea01e70514896f827546d99d 0.6 diff --git a/CHANGELOG b/CHANGELOG index c064e70..2e574da 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +0.14: Enable multiple unit systems and adding and removing conversions + - Changed documentation to roxygen2 style + 0.13: Replaces packageStartupMessage with a warning (when unable to load XML files at load time) 0.12: Addresses further install issues for Windows diff --git a/DESCRIPTION b/DESCRIPTION index 433ca26..fab8d48 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,13 +1,19 @@ Package: udunits2 Type: Package Title: Udunits-2 Bindings for R -Version: 0.13 -Date: 2016-11-16 -Author: James Hiebert -Maintainer: James Hiebert +Version: 0.14 +Date: 2017-01-31 +Authors@R: c( + person("James", "Hiebert", email="hiebert@uvic.ca", role=c("aut", "cre")), + person("Swechhya", "Bista", email="Swechhya.Bista@cytel.com", role="aut"), + person("Bill", "Denney", email="wdenney@humanpredictions.com", role="aut")) Description: Provides simple bindings to Unidata's udunits library. URL: https://github.com/pacificclimate/Rudunits2 https://www.unidata.ucar.edu/software/udunits/ SystemRequirements: udunits-2 License: GPL-2 LazyLoad: yes -Depends: R (>= 2.10.0) +Depends: + R (>= 2.10.0) +Suggests: + testthat +RoxygenNote: 5.0.1 diff --git a/NAMESPACE b/NAMESPACE index 0c084f7..81e689c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,2 +1,28 @@ -exportPattern("^ud.*") -useDynLib(udunits2, R_ut_are_convertible, R_ut_convert, R_ut_get_name, R_ut_get_symbol, R_ut_is_parseable, R_ut_set_encoding, R_ut_init) +# Generated by roxygen2: do not edit by hand + +export(ud.add.system) +export(ud.are.convertible) +export(ud.convert) +export(ud.free.system) +export(ud.get.name) +export(ud.get.symbol) +export(ud.have.unit.system) +export(ud.is.parseable) +export(ud.list.systems) +export(ud.remove.unit) +export(ud.set.conversion) +export(ud.set.encoding) +useDynLib(udunits2,R_ut_are_convertible) +useDynLib(udunits2,R_ut_convert) +useDynLib(udunits2,R_ut_free_system) +useDynLib(udunits2,R_ut_get_name) +useDynLib(udunits2,R_ut_get_symbol) +useDynLib(udunits2,R_ut_has_system) +useDynLib(udunits2,R_ut_init) +useDynLib(udunits2,R_ut_is_parseable) +useDynLib(udunits2,R_ut_list_systems) +useDynLib(udunits2,R_ut_remove_unit) +useDynLib(udunits2,R_ut_set_conversion) +useDynLib(udunits2,R_ut_set_encoding) +useDynLib(udunits2,R_ut_system_cleanup) +useDynLib(udunits2,R_ut_system_count) diff --git a/R/ud.functions.R b/R/ud.functions.R index 1990288..6ebd22a 100644 --- a/R/ud.functions.R +++ b/R/ud.functions.R @@ -1,45 +1,129 @@ +#' @useDynLib udunits2 R_ut_init .onLoad <- function(libname, pkgname) { ## By default, configure udunits with path set (presumably) by the ## user through the UDUNITS2_XML_PATH environment variable - .C('R_ut_init', as.integer(0)) - if (!ud.have.unit.system()) { + .C('R_ut_init', as.character(''), as.character(''), as.integer(0)) + if (!ud.have.unit.system("default")) { ## Failing that, override it with the in-package XML file p0 <- system.file("share/udunits2.xml", package="udunits2") Sys.setenv(UDUNITS2_XML_PATH=p0) - .C('R_ut_init', as.integer(1)) + .C('R_ut_init',as.character(''), as.character(''), as.integer(1)) ## If *that* fails, give the user some instructions for how to remedy ## the problem - if (!ud.have.unit.system()) { + if (!ud.have.unit.system("default")) { warning( "Failed to read udunits system database: udunits2 will not work properly.\nPlease set the UDUNITS2_XML_PATH environment variable before attempting to load the package") } } } +#' @useDynLib udunits2 R_ut_init .onAttach <- function(libname, pkgname) { msg <- "udunits system database read" p0 <- Sys.getenv("UDUNITS2_XML_PATH") if (p0 != "") { + .C('R_ut_init', as.character(''), as.character(''), as.integer(0)) msg <- paste(msg, "from", p0) - } + } packageStartupMessage(msg) } +#' @useDynLib udunits2 R_ut_system_cleanup +.onUnload <- function(libpath){ + # .C('R_ut_system_cleanup') #To delete all the created systems. + library.dynam.unload("udunits2", libpath) +} + +#' @title Determine whether two units may be converted between each +#' other +#' +#' @description This function takes udunits compatible strings and +#' determines whether or not it is possible to convert between them. +#' +#' @param u1 A character string which is parseable into a udunits +#' compatible unit. +#' @param u2 Another character string which is also parseable into a +#' udunits compatible unit. +#' @param system.name The unit system name to use. +#' @details Even if two units are parseable and recognized by the +#' udunits library, it may or may not be possible to convert from one +#' to another. For example, it makes sense to convert from celsius to +#' kelvin, however not from celsius to kilograms. This function +#' allows the user to check if two units are of the same system and if +#' there exists a defined conversion between the two. +#' @return Returns a logical: \code{TRUE} if the units can be converted +#' between each other, \code{FALSE} if either of the arguments is not +#' parseable by udunits, or if no conversion is possible. +#' @references See the udunits function ut_are_convertible: +#' \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#ut_005fare_005fconvertible_0028_0029} +#' and the main uninits webpage: +#' \url{http://www.unidata.ucar.edu/software/udunits/} +#' @author James Hiebert \email{hiebert@uvic.ca} +#' @seealso \code{\link{ud.is.parseable}} +#' @examples +#' ud.are.convertible("miles", "km") # TRUE +#' ud.are.convertible("grams", "kilograms") # TRUE +#' ud.are.convertible("celsius", "grams") # FALSE +#' ud.are.convertible("not", "parseable") # FALSE +#' @export +#' @useDynLib udunits2 R_ut_are_convertible ud.are.convertible <- -function(u1, u2) { - if (! (ud.is.parseable(u1) && ud.is.parseable(u2))) { +function(u1, u2, system.name = "default") { + if (! (ud.is.parseable(u1, system.name) && ud.is.parseable(u2, system.name))) { return(FALSE) } rv <- .C('R_ut_are_convertible', as.character(u1), as.character(u2), - convertible=logical(1)) + convertible=logical(1), + as.character(system.name)) return(rv$convertible) } +#' @title Convert numeric types from one unit to another +#' @description This function takes the numeric argument \code{x}, +#' quantified in units \code{u1} and converts it to be of units +#' \code{u2}. +#' @param x Some argument which is convertible to a numeric type by +#' \code{as.double}. +#' @param u1 A character string which is parseable into a udunits +#' compatible unit. +#' @param u2 Another character string which is also parseable into a +#' udunits compatible unit and for which there exists a defined +#' transformation from the units represented by u1. +#' @param system.name The unit system name to use. +#' @details This function uses the udunits function +#' \code{cv_convert_doubles} to convert the argument from one set of +#' units to another. +#' @return Returns a numeric type having converted from one unit to +#' another. The attributes of the original argument \code{x} (e.g. +#' class, dimensions, etc.) are preserved and then re-applied to the +#' return value of the transformation as such: +#' +#' \code{attributes(rv) <- attributes(x)} +#' +#' If either of unit \code{u1} or \code{u2} is unparseable, or there +#' does not exist a conversion from one to the other the function +#' raises an error. +#' @references Unidata's udunits reference: +#' \url{http://www.unidata.ucar.edu/software/udunits/} +#' +#' API guide for cv_convert_doubles: +#' \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-cv_005fconvert_005fdoubles-39} +#' @author James Hiebert \email{hiebert@uvic.ca} +#' @seealso \code{\link{ud.are.convertible}} +#' @examples +#' x <- seq(10) +#' ud.convert(x, "miles", "km") # c(1.609344, 3.218688, 4.828032, ...) +#' x <- c(-40, 0, 100) +#' ud.convert(x, "celsius", "degree_fahrenheit") # c(-40, 32, 212) +#' err <- try(ud.convert(100,"miles", "grams")) # Error +#' err <- try(ud.convert(NA, "not", "parseable")) # Error +#' @export +#' @useDynLib udunits2 R_ut_convert ud.convert <- -function(x, u1, u2) { - if (! ud.are.convertible(u1, u2)) { +function(x, u1, u2, system.name="default") { + if (! ud.are.convertible(u1, u2, system.name)) { stop(paste("Units", u1, "and", u2, "are not convertible")) } ## Filter out NA's before passing them to the C function @@ -49,54 +133,329 @@ function(x, u1, u2) { len <- length(i) c.rv <- .C('R_ut_convert', - as.double(x)[i], - as.integer(len), - as.character(u1), - as.character(u2), - converted=double(len) - ) + as.double(x)[i], + as.integer(len), + as.character(u1), + as.character(u2), + converted=double(len), + as.character(system.name)) rv[i] <- c.rv$converted ## If it's a matrix/vector or anything else, convert it back to it's original type attributes(rv) <- attributes(x) return(rv) } +#' @title Retrieve the udunits name or symbol from the database for a +#' given units string +#' @description Retrieve the udunits name or symbol from the database +#' for a given units string. +#' @param unit.string A character string which is parseable into a +#' udunits compatible unit. +#' @param system.name The unit system name to use. +#' @details This function retrieves the udunits name or symbol from the +#' udunits database and returns it. It uses the udunits functions +#' ut_get_name and ut_get_symbol respectively. +#' @return Returns a character string stating the udunits's name/symbol +#' for the given unit, or an empty character string if the unit does +#' not map to a name/symbol for the default character set. If the unit +#' is unparseable, the function raises an error. +#' @references Unidata's udunits reference: +#' \url{http://www.unidata.ucar.edu/software/udunits/} +#' +#' API guide for ut_get_name: +#' \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-ut_005fget_005fname-66} +#' +#' +#' API guide for ut_get_symbol: +#' \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-ut_005fget_005fsymbol-67} +#' +#' @author James Hiebert \email{hiebert@uvic.ca} +#' @note More often than not units do not have names or symbols that +#' are returned by the base functions. This depends entirely on what +#' is defined in the units data base, which is--as of API version +#' 2--an XML database which ships with the library. See Unidata's +#' website for more information about the XML database: +#' \url{http://www.unidata.ucar.edu/software/udunits/udunits-2-units.html}. +#' All in all, don't put too much stock in them, for they are for +#' convenience only. If your application \emph{requires} certain names +#' and symbols to be present, the XML database is local and editable. +#' @examples +#' units.to.display <- c("celsius", # has no name, messed up symbol (maybe a bug in R?) +#' "kg", +#' "hr", # has no symbol +#' "K", +#' "degrees", +#' "m", +#' "ohm") +#' for (u in units.to.display) { +#' print(ud.get.name(u)) +#' print(ud.get.symbol(u)) +#' } +#' @export +#' @useDynLib udunits2 R_ut_get_name ud.get.name <- -function(unit.string) { - stopifnot(ud.is.parseable(unit.string)) +function(unit.string, system.name="default") { + stopifnot(ud.is.parseable(unit.string, system.name)) rv <- .C('R_ut_get_name', as.character(unit.string), - ud.name=character(length=1)) + ud.name=character(length=1), + as.character(system.name)) return(rv$ud.name) } +#' @rdname ud.get.name +#' @export +#' @useDynLib udunits2 R_ut_get_symbol ud.get.symbol <- -function(unit.string) { - stopifnot(ud.is.parseable(unit.string)) +function(unit.string, system.name="default") { + stopifnot(ud.is.parseable(unit.string, system.name)) rv <- .C('R_ut_get_symbol', as.character(unit.string), - ud.symbol=character(length=1)) + ud.symbol=character(length=1), + as.character(system.name)) return(rv$ud.symbol) } +#' @title Determine whether a unit string is parseable by the udunits +#' library +#' @description Determine whether a unit string is parseable and +#' recognized by the udunits library. +#' @param unit.string A character string representing a type of units +#' which may be parseable by the udunits library. +#' @param system.name The unit system name to use. +#' @details \code{ud.is.parseable} uses udunit's function +#' \code{ut_parse} to determine whether or not the given unit string +#' is parseable. If \code{ut_parse} returns NULL, then +#' \code{ud.is.parseable} will return \code{FALSE}. +#' @return Returns a logical: \code{TRUE} if the units is parseable and +#' recognized by the udunits library, \code{FALSE} otherwise. +#' @references Unidata's udunits reference: +#' \url{http://www.unidata.ucar.edu/software/udunits/} +#' +#' API guide for ut_parse: +#' \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-ut_005fparse-43} +#' +#' @author James Hiebert \email{hiebert@uvic.ca} +#' @note There is a note in the \code{ut_parse} docs about how the +#' argument string must have no leading or trailing whitespace. We +#' make sure in this package to always call \code{ut_trim} on any +#' strings before they are passed to \code{ut_parse}. The package +#' user need not strip whitespace before-hand. +#' @seealso \code{\link{ud.are.convertible}} +#' @examples +#' ud.is.parseable("K") # TRUE +#' ud.is.parseable(" K ") # TRUE +#' ud.is.parseable("miles") # TRUE +#' ud.is.parseable("Not parseable") # FALSE +#' @export +#' @useDynLib udunits2 R_ut_is_parseable ud.is.parseable <- -function(unit.string) { +function(unit.string, system.name = "default") { rv <- .C('R_ut_is_parseable', as.character(unit.string), - parseable=logical(1)) + parseable=logical(1), + as.character(system.name)) return(rv$parseable) } +#' @title Set the udunits package level encoding type +#' @description Sets the encoding type parameter for the current +#' \code{system.name}. +#' @param enc.string A character string representing the encoding type. +#' Valid strings are \code{utf8},\code{ascii},\code{iso-8859-1},and +#' \code{latin1} (an alias for ISO-8859-1). +#' @param system.name The unit system name to use. +#' @details Encoding type is a parameter to nearly all of the functions +#' in the udunits library. By default, the R udunits2 pacakge sets +#' the encoding type to UTF-8, however this package allows the user to +#' set other encoding types which are supported by the udunits +#' library. It presently suports UTF-8, ASCII, and ISO-8859-1. +#' @return Returns no value. Raises an error if it is not given a valid +#' encoding string. +#' @references Unidata's udunits reference: +#' \url{http://www.unidata.ucar.edu/software/udunits/} +#' +#' API guide chapter on data types: +#' \url{https://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#Types} +#' @author James Hiebert \email{hiebert@uvic.ca} +#' @examples +#' valid.enc.strings <- c('utf8', 'ascii', 'iso-8859-1', 'latin1') +#' lapply(valid.enc.strings, ud.set.encoding) +#' err <- try(ud.set.encoding("This will fail")) +#' @export +#' @useDynLib udunits2 R_ut_set_encoding ud.set.encoding <- -function(enc.string) { +function(enc.string, system.name="default") { .C('R_ut_set_encoding', - as.character(enc.string)) + as.character(enc.string), + as.character(system.name)) return() } +#' Check if a system is loaded. +#' @param system.name The unit system name to check. +#' @return \code{TRUE} if the system specified by system.name is +#' present, otherwise \code{FALSE}. +#' @seealso \code{\link{ud.add.system}} +#' @examples +#' ud.have.unit.system("default") # TRUE +#' ud.have.unit.system("notloaded") # FALSE +#' @author Swechhya Bista \email{Swechhya.Bista@cytel.com} +#' @export +#' @useDynLib udunits2 R_ut_has_system ud.have.unit.system <- -function() { +function(system.name) { rv <- .C('R_ut_has_system', - exists=logical(1)) + exists=logical(1), + as.character(system.name)) return(rv$exists) } + +#' Delete the system given by the user +#' @param system.name The unit system name to use. +#' @return \code{TRUE} if system.name is successfully deleted, otherwise +#' returns an error. +#' @author Swechhya Bista \email{Swechhya.Bista@cytel.com} +#' @export +#' @useDynLib udunits2 R_ut_free_system +ud.free.system <- +function(system.name){ + rv <- .C('R_ut_free_system', + as.character(system.name), + deleted = logical(1)) + return(rv$deleted) +} + +#' Add a unit system +#' @param file.name The XML file to read for unit information. If +#' \code{file.name} is blank, the system default xml is used. +#' @param system.name The unit system name to use. If system.name is +#' null the the name of the system is set to "default". +#' @author Swechhya Bista \email{Swechhya.Bista@cytel.com} +#' @seealso \code{\link{ud.list.systems}}, \code{\link{ud.free.system}} +#' @export +#' @useDynLib udunits2 R_ut_init +ud.add.system <- function(file.name, system.name){ + rv <- .C('R_ut_init', + as.character(file.name), + as.character(system.name), + as.integer(1)) +} + +#' List the loaded systems +#' @return A character vector containing the system names. If no +#' systems have been loaded it returns \code{NULL}. +#' @author Swechhya Bista \email{Swechhya.Bista@cytel.com} +#' @seealso \code{\link{ud.add.system}}, \code{\link{ud.free.system}} +#' @export +#' @useDynLib udunits2 R_ut_system_count R_ut_list_systems +ud.list.systems <- function(){ + rv <- .C('R_ut_system_count', + count = integer(1)) + if(rv$count != 0){ + rv <- .C('R_ut_list_systems', + systems = character(rv$count)) + + return(rv$systems) + } else { + return(NULL) + } +} + +#' Set a conversion rule between two units +#' @param unit1.text,unit2.text The text to define the units. The text +#' may take the form of "optional blank, optional floating point +#' nonnegative number, optional blank, unit text, optional blank". The +#' blanks are ignored; if the floating point number is not given, then +#' it is assumed to be \code{1}. +#' @param system.name The unit system name to use. +#' @param function.name The function to use to convert between +#' \code{unit1.text} and \code{unit2.text}. See the details for more +#' information. +#' @details The functions are applied so that if the units are given as +#' \code{unit1.text = "a x"} and \code{unit2.text = "b y"} where "a" +#' and "b" are numbers: +#' \itemize{ +#' \item{scale}{x = b/a * y} +#' \item{offset}{x = (b - a) * y} +#' \item{log}{x = log(a/b)(y); log(a/b) is log base a/b} +#' \item{invert}{x = 1/y} +#' } +#' +#' If both unit text names are not defined, then both are created and +#' they are mapped to each other. If one is not defined, it is created +#' and mapped to the other. If both are defined, then an error is raised. +#' @return \code{TRUE} if conversion setting is successful, otherwise +#' returns an error. +#' @author Swechhya Bista \email{Swechhya.Bista@cytel.com} +#' @examples +#' \dontrun{ +#' ud.set.conversion("1 moleaspirin", "180.157 gram") # Convert grams to moles of aspirin +#' } +#' @export +#' @useDynLib udunits2 R_ut_set_conversion +ud.set.conversion <- function(unit1.text, unit2.text, system.name="default", + function.name=c("scale", "offset", "log", "invert")) { + function.name <- match.arg(function.name) + units <- extract.unit.parts(c(unit1.text, unit2.text)) + rv <- .C('R_ut_set_conversion', + as.character(units$text[1]), + as.character(units$text[2]), + as.double(units$value[1]), + as.double(units$value[2]), + as.character(system.name), + as.character(function.name), + set = logical(1) + ) + return(rv$set) +} + +#' Remove a unit from a loaded system +#' @param unit.name The unit name to remove from a system +#' @param system.name The unit system name to use. +#' @return \code{TRUE} if the unit is successfully removed, otherwise +#' returns an error. +#' @author Swechhya Bista \email{Swechhya.Bista@cytel.com} +#' @examples +#' \dontrun{ +#' ud.remove.unit("miles") +#' } +#' @export +#' @useDynLib udunits2 R_ut_remove_unit +ud.remove.unit <- function(unit.name, system.name="default"){ + rv <- .C("R_ut_remove_unit", + as.character(unit.name), + removed = logical(1), + as.character(system.name)) + return(rv$removed) +} + +#' Extract the text and value part from the unit text +#' @param unitdef A unit definition in the form of "optional blank, +#' optional floating point nonnegative number, optional blank, unit +#' text, optional blank". Blanks (e.g. spaces, tabs, etc.) are +#' ignored. +#' @param assumed.value The value to assume if the optional floating +#' point nonnegative number is not given in the \code{unitdef}. +#' @return A list with two elements: \code{value} and \code{text}. +#' @note This function is called from ud.set.conversion +#' @author William Denney \email{wdenney@humanpredictions.com} +extract.unit.parts <- function(unitdef, assumed.value=1) { + if (is.factor(unitdef)) { + unitdef <- as.character(unitdef) + } + if (!is.character(unitdef)) { + stop("unitdef must be a character scalar or vector") + } + pattern.unit <- "^[[:blank:]]*([[:digit:]]*\\.?[[:digit:]]+)?[[:blank:]]*([[:alnum:]_]+)[[:blank:]]*$" + mask.unit <- grepl(pattern.unit, unitdef, perl=TRUE) + if (any(!mask.unit)) { + stop("Invalid unit: ", paste0('"', unitdef[!mask.unit], '"', collapse=", "), ". ", + "Units must follow either the pattern (nonnegative number, optional spaces, alphanumeric or underscore string) or (alphanumeric or underscore string).") + } + unit.value <- as.numeric(gsub(pattern.unit, "\\1", unitdef)) + unit.value[is.na(unit.value)] <- assumed.value + unit.text <- gsub(pattern.unit, "\\2", unitdef) + list(value=unit.value, + text=unit.text) +} diff --git a/R/udunits2.R b/R/udunits2.R new file mode 100644 index 0000000..d315594 --- /dev/null +++ b/R/udunits2.R @@ -0,0 +1,53 @@ +#' @title udunits-2 bindings for R +#' @description This package provides simple bindings to version 2 of Unidata's udunits library +#' @details +#' +#' \tabular{ll}{ +#' Package: \tab udunits2\cr +#' Type: \tab Package\cr +#' Version: \tab 0.6\cr +#' Date: \tab 2011-02-11\cr +#' License: \tab GPL-2\cr +#' LazyLoad: \tab yes\cr +#' } +#' +#' This package provides simple bindings to the version 2 API of +#' Unidata's udunits library. While the entire API is not supported, we +#' have chosen to boil it down to a few simple functions to be able to +#' exploit the most useful functionality that the library provides. This +#' package provides the following functions to work within a unit +#' system: +#' +#' \itemize{ +#' \item \code{\link{ud.is.parseable}} +#' \item \code{\link{ud.get.name}} +#' \item \code{\link{ud.get.symbol}} +#' \item \code{\link{ud.are.convertible}} +#' \item \code{\link{ud.convert}} +#' } +#' +#' Functions for creating and removing units within a system: +#' +#' \itemize{ +#' \item \code{\link{ud.remove.unit}} +#' \item \code{\link{ud.set.conversion}} +#' } +#' +#' Functions for creating and removing unit systems: +#' +#' \itemize{ +#' \item \code{\link{ud.add.system}} +#' \item \code{\link{ud.free.system}} +#' \item \code{\link{ud.have.unit.system}} +#' \item \code{\link{ud.list.systems}} +#' \item \code{\link{ud.set.encoding}} +#' } +#' +#' Please see the respective function help pages for further details and usage. +#' @author James Hiebert +#' @keywords unitdata units climate meteorology +#' @name udunits2-package +#' @aliases udunits2 +#' @references +#' Unidata's udunits web page: \url{http://www.unidata.ucar.edu/software/udunits/} +NULL diff --git a/man/extract.unit.parts.Rd b/man/extract.unit.parts.Rd new file mode 100644 index 0000000..62b9544 --- /dev/null +++ b/man/extract.unit.parts.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R +\name{extract.unit.parts} +\alias{extract.unit.parts} +\title{Extract the text and value part from the unit text} +\usage{ +extract.unit.parts(unitdef, assumed.value = 1) +} +\arguments{ +\item{unitdef}{A unit definition in the form of "optional blank, +optional floating point nonnegative number, optional blank, unit +text, optional blank". Blanks (e.g. spaces, tabs, etc.) are +ignored.} + +\item{assumed.value}{The value to assume if the optional floating +point nonnegative number is not given in the \code{unitdef}.} +} +\value{ +A list with two elements: \code{value} and \code{text}. +} +\description{ +Extract the text and value part from the unit text +} +\note{ +This function is called from ud.set.conversion +} +\author{ +William Denney \email{wdenney@humanpredictions.com} +} + diff --git a/man/ud.add.system.Rd b/man/ud.add.system.Rd new file mode 100644 index 0000000..38f94be --- /dev/null +++ b/man/ud.add.system.Rd @@ -0,0 +1,25 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R +\name{ud.add.system} +\alias{ud.add.system} +\title{Add a unit system} +\usage{ +ud.add.system(file.name, system.name) +} +\arguments{ +\item{file.name}{The XML file to read for unit information. If +\code{file.name} is blank, the system default xml is used.} + +\item{system.name}{The unit system name to use. If system.name is +null the the name of the system is set to "default".} +} +\description{ +Add a unit system +} +\author{ +Swechhya Bista \email{Swechhya.Bista@cytel.com} +} +\seealso{ +\code{\link{ud.list.systems}}, \code{\link{ud.free.system}} +} + diff --git a/man/ud.are.convertible.Rd b/man/ud.are.convertible.Rd index 8a2899e..bae5cdb 100644 --- a/man/ud.are.convertible.Rd +++ b/man/ud.are.convertible.Rd @@ -1,40 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R \name{ud.are.convertible} \alias{ud.are.convertible} -\title{Determine whether two units may be converted between each other} -\description{ - This function takes udunits compatible strings and determines whether - or not it is possible to convert between them. -} +\title{Determine whether two units may be converted between each + other} \usage{ -ud.are.convertible(u1, u2) +ud.are.convertible(u1, u2, system.name = "default") } \arguments{ - \item{u1}{A character string which is parseable into a udunits compatible unit.} - \item{u2}{Another character string which is also parseable into a udunits compatible unit.} -} -\details{ - Even if two units are parseable and recognized by the udunits library, - it may or may not be possible to convert from one to another. For - example, it makes sense to convert from celsius to kelvin, however not - from celsius to kilograms. This function allows the user to check if - two units are of the same system and if there exists a defined conversion between - the two. +\item{u1}{A character string which is parseable into a udunits +compatible unit.} + +\item{u2}{Another character string which is also parseable into a +udunits compatible unit.} + +\item{system.name}{The unit system name to use.} } \value{ - Returns a logical: \code{True} if the units can be converted between - each other, \code{False} if either of the arguments is not parseable - by udunits, or if no conversion is possible. +Returns a logical: \code{TRUE} if the units can be converted + between each other, \code{FALSE} if either of the arguments is not + parseable by udunits, or if no conversion is possible. } -\references{ - See the udunits function ut_are_convertible: - \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#ut_005fare_005fconvertible_0028_0029} - and the main uninits webpage: \url{http://www.unidata.ucar.edu/software/udunits/} -} -\author{ - James Hiebert \email{hiebert@uvic.ca} +\description{ +This function takes udunits compatible strings and + determines whether or not it is possible to convert between them. } -\seealso{ - \code{\link{ud.is.parseable}} +\details{ +Even if two units are parseable and recognized by the + udunits library, it may or may not be possible to convert from one + to another. For example, it makes sense to convert from celsius to + kelvin, however not from celsius to kilograms. This function + allows the user to check if two units are of the same system and if + there exists a defined conversion between the two. } \examples{ ud.are.convertible("miles", "km") # TRUE @@ -42,3 +39,16 @@ ud.are.convertible("grams", "kilograms") # TRUE ud.are.convertible("celsius", "grams") # FALSE ud.are.convertible("not", "parseable") # FALSE } +\author{ +James Hiebert \email{hiebert@uvic.ca} +} +\references{ +See the udunits function ut_are_convertible: + \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#ut_005fare_005fconvertible_0028_0029} + and the main uninits webpage: + \url{http://www.unidata.ucar.edu/software/udunits/} +} +\seealso{ +\code{\link{ud.is.parseable}} +} + diff --git a/man/ud.convert.Rd b/man/ud.convert.Rd index b5a84bb..f8993fb 100644 --- a/man/ud.convert.Rd +++ b/man/ud.convert.Rd @@ -1,43 +1,45 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R \name{ud.convert} \alias{ud.convert} \title{Convert numeric types from one unit to another} -\description{This function takes the numeric argument \code{x}, - quantified in units \code{u1} and converts it to be of units \code{u2}. -} \usage{ -ud.convert(x, u1, u2) +ud.convert(x, u1, u2, system.name = "default") } \arguments{ - \item{x}{Some argument which is convertible to a numeric type by \code{as.double}.} - \item{u1}{A character string which is parseable into a udunits compatible unit.} - \item{u2}{Another character string which is also parseable into a - udunits compatible unit and for which there exists a defined - transformation from the units represented by u1.} -} -\details{ - This function uses the udunits function \code{cv_convert_doubles} to - convert the argument from one set of units to another. +\item{x}{Some argument which is convertible to a numeric type by +\code{as.double}.} + +\item{u1}{A character string which is parseable into a udunits +compatible unit.} + +\item{u2}{Another character string which is also parseable into a +udunits compatible unit and for which there exists a defined +transformation from the units represented by u1.} + +\item{system.name}{The unit system name to use.} } \value{ - Returns a numeric type having converted from one unit to another. The - attributes of the original argument \code{x} (e.g. class, dimensions, - etc.) are preserved and then re-applied to the return value of the - transformation as such: +Returns a numeric type having converted from one unit to + another. The attributes of the original argument \code{x} (e.g. + class, dimensions, etc.) are preserved and then re-applied to the + return value of the transformation as such: + \code{attributes(rv) <- attributes(x)} - If either of unit \code{u1} or \code{u2} is unparseable, or there does - not exist a conversion from one to the other the function raises an error. + + If either of unit \code{u1} or \code{u2} is unparseable, or there + does not exist a conversion from one to the other the function + raises an error. } -\references{ - Unidata's udunits reference: - \url{http://www.unidata.ucar.edu/software/udunits/} - API guide for cv_convert_doubles: - \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-cv_005fconvert_005fdoubles-39} +\description{ +This function takes the numeric argument \code{x}, + quantified in units \code{u1} and converts it to be of units + \code{u2}. } -\author{ - James Hiebert \email{hiebert@uvic.ca} -} -\seealso{ - \code{\link{ud.are.convertible}} +\details{ +This function uses the udunits function + \code{cv_convert_doubles} to convert the argument from one set of + units to another. } \examples{ x <- seq(10) @@ -47,3 +49,17 @@ ud.convert(x, "celsius", "degree_fahrenheit") # c(-40, 32, 212) err <- try(ud.convert(100,"miles", "grams")) # Error err <- try(ud.convert(NA, "not", "parseable")) # Error } +\author{ +James Hiebert \email{hiebert@uvic.ca} +} +\references{ +Unidata's udunits reference: +\url{http://www.unidata.ucar.edu/software/udunits/} + +API guide for cv_convert_doubles: +\url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-cv_005fconvert_005fdoubles-39} +} +\seealso{ +\code{\link{ud.are.convertible}} +} + diff --git a/man/ud.free.system.Rd b/man/ud.free.system.Rd new file mode 100644 index 0000000..287198e --- /dev/null +++ b/man/ud.free.system.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R +\name{ud.free.system} +\alias{ud.free.system} +\title{Delete the system given by the user} +\usage{ +ud.free.system(system.name) +} +\arguments{ +\item{system.name}{The unit system name to use.} +} +\value{ +\code{TRUE} if system.name is successfully deleted, otherwise + returns an error. +} +\description{ +Delete the system given by the user +} +\author{ +Swechhya Bista \email{Swechhya.Bista@cytel.com} +} + diff --git a/man/ud.get.name.Rd b/man/ud.get.name.Rd index ffd8d81..7283ff9 100644 --- a/man/ud.get.name.Rd +++ b/man/ud.get.name.Rd @@ -1,50 +1,47 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R \name{ud.get.name} \alias{ud.get.name} \alias{ud.get.symbol} -\title{Retrieve the udunits name or symbol from the database for a given - units string} -\description{ - Retrieve the udunits name or symbol from the database for a given - units string. -} +\title{Retrieve the udunits name or symbol from the database for a + given units string} \usage{ -ud.get.name(unit.string) +ud.get.name(unit.string, system.name = "default") + +ud.get.symbol(unit.string, system.name = "default") } \arguments{ - \item{unit.string}{A character string which is parseable into a udunits compatible unit.} -} -\details{ - This function retrieves the udunits name or symbol from the udunits - database and returns it. It uses the udunits functions ut_get_name - and ut_get_symbol respectively. +\item{unit.string}{A character string which is parseable into a +udunits compatible unit.} + +\item{system.name}{The unit system name to use.} } \value{ - Returns a character string stating the udunits's name/symbol for the - given unit, or an empty character string if the unit does not map to a - name/symbol for the default character set. - If the unit is unparseable, the function raises an error. +Returns a character string stating the udunits's name/symbol + for the given unit, or an empty character string if the unit does + not map to a name/symbol for the default character set. If the unit + is unparseable, the function raises an error. } -\references{ - Unidata's udunits reference: - \url{http://www.unidata.ucar.edu/software/udunits/} - API guide for ut_get_name: - \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-ut_005fget_005fname-66} - API guide for ut_get_symbol: \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-ut_005fget_005fsymbol-67} +\description{ +Retrieve the udunits name or symbol from the database + for a given units string. +} +\details{ +This function retrieves the udunits name or symbol from the + udunits database and returns it. It uses the udunits functions + ut_get_name and ut_get_symbol respectively. } -\author{James Hiebert \email{hiebert@uvic.ca}} - \note{ - More often than not units do not have names or symbols that are - returned by the base functions. This depends entirely on what is - defined in the units data base, which is--as of API version 2--an XML - database which ships with the library. See Unidata's website for more - information about the XML database: +More often than not units do not have names or symbols that + are returned by the base functions. This depends entirely on what + is defined in the units data base, which is--as of API version + 2--an XML database which ships with the library. See Unidata's + website for more information about the XML database: \url{http://www.unidata.ucar.edu/software/udunits/udunits-2-units.html}. All in all, don't put too much stock in them, for they are for convenience only. If your application \emph{requires} certain names and symbols to be present, the XML database is local and editable. } - \examples{ units.to.display <- c("celsius", # has no name, messed up symbol (maybe a bug in R?) "kg", @@ -53,9 +50,23 @@ units.to.display <- c("celsius", # has no name, messed up symbol (maybe a bug in "degrees", "m", "ohm") - for (u in units.to.display) { print(ud.get.name(u)) print(ud.get.symbol(u)) } } +\author{ +James Hiebert \email{hiebert@uvic.ca} +} +\references{ +Unidata's udunits reference: + \url{http://www.unidata.ucar.edu/software/udunits/} + + API guide for ut_get_name: + \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-ut_005fget_005fname-66} + + + API guide for ut_get_symbol: + \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-ut_005fget_005fsymbol-67} +} + diff --git a/man/ud.have.unit.system.Rd b/man/ud.have.unit.system.Rd index 032ce50..f39e177 100644 --- a/man/ud.have.unit.system.Rd +++ b/man/ud.have.unit.system.Rd @@ -1,35 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R \name{ud.have.unit.system} \alias{ud.have.unit.system} -\title{Determine whether udunits has loaded its units database} -\description{ - This function check wether or not udunits has successfully found and - loaded its run-time XML units database. -} +\title{Check if a system is loaded.} \usage{ -ud.have.unit.system() +ud.have.unit.system(system.name) } -\details{ - At package load time, Rudunits attempts to load a unit system from an - XML units database from the file system. This might be installed with - the system library (e.g. through apt or yum), or the user can use - their own. The file-system location is configured using the - UDUNITS2_XML_PATH environment variable. - - This package will attempt to load the path contained in - UDUNITS2_XML_PATH. If it's empty, it will attempt to load it from the - system library. Failing that it will attempt to load its own XML - database that ships with the package (from udunits source). - - One can call \code{ud.have.unit.system} to confirm that the units - database has been loaded successfully. +\arguments{ +\item{system.name}{The unit system name to check.} } \value{ - Returns a logical: \code{True} if udunits has successfully found and - loaded the XML units database, \code{False} otherwise. +\code{TRUE} if the system specified by system.name is + present, otherwise \code{FALSE}. } -\author{ - James Hiebert \email{hiebert@uvic.ca} +\description{ +Check if a system is loaded. } \examples{ -ud.have.unit.system() # TRUE +ud.have.unit.system("default") # TRUE +ud.have.unit.system("notloaded") # FALSE +} +\author{ +Swechhya Bista \email{Swechhya.Bista@cytel.com} } +\seealso{ +\code{\link{ud.add.system}} +} + diff --git a/man/ud.is.parseable.Rd b/man/ud.is.parseable.Rd index 515f4f8..522a6c7 100644 --- a/man/ud.is.parseable.Rd +++ b/man/ud.is.parseable.Rd @@ -1,44 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R \name{ud.is.parseable} \alias{ud.is.parseable} -\title{Determine whether a unit string is parseable by the udunits library} -\description{ - Determine whether a unit string is parseable and recognized by the udunits library. -} +\title{Determine whether a unit string is parseable by the udunits + library} \usage{ -ud.is.parseable(unit.string) +ud.is.parseable(unit.string, system.name = "default") } \arguments{ - \item{unit.string}{A character string representing a type of units - which may be parseable by the udunits library} -} -\details{ - \code{ud.is.parseable} uses udunit's function \code{ut_parse} to - determine whether or not the given unit string is parseable. If - \code{ut_parse} returns NULL, then \code{ud.is.parseable} will return \code{FALSE}. +\item{unit.string}{A character string representing a type of units +which may be parseable by the udunits library.} + +\item{system.name}{The unit system name to use.} } \value{ - Returns a logical: \code{True} if the units is parseable and - recognized by the udunits library, \code{False} otherwise. +Returns a logical: \code{TRUE} if the units is parseable and + recognized by the udunits library, \code{FALSE} otherwise. } -\references{ - Unidata's udunits reference: - \url{http://www.unidata.ucar.edu/software/udunits/} - API guide for ut_parse: - \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-ut_005fparse-43} +\description{ +Determine whether a unit string is parseable and + recognized by the udunits library. } -\author{ - James Hiebert \email{hiebert@uvic.ca} +\details{ +\code{ud.is.parseable} uses udunit's function + \code{ut_parse} to determine whether or not the given unit string + is parseable. If \code{ut_parse} returns NULL, then + \code{ud.is.parseable} will return \code{FALSE}. } \note{ - There is a note in the \code{ut_parse} docs about how the - argument string must have no leading or trailing whitespace. We make - sure in this package to always call \code{ut_trim} on any strings - before they are passed to \code{ut_parse}. The package user need not - strip whitespace before-hand. -} - -\seealso{ - \code{\link{ud.are.convertible}} +There is a note in the \code{ut_parse} docs about how the + argument string must have no leading or trailing whitespace. We + make sure in this package to always call \code{ut_trim} on any + strings before they are passed to \code{ut_parse}. The package + user need not strip whitespace before-hand. } \examples{ ud.is.parseable("K") # TRUE @@ -46,3 +40,17 @@ ud.is.parseable(" K ") # TRUE ud.is.parseable("miles") # TRUE ud.is.parseable("Not parseable") # FALSE } +\author{ +James Hiebert \email{hiebert@uvic.ca} +} +\references{ +Unidata's udunits reference: + \url{http://www.unidata.ucar.edu/software/udunits/} + + API guide for ut_parse: + \url{http://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#index-ut_005fparse-43} +} +\seealso{ +\code{\link{ud.are.convertible}} +} + diff --git a/man/ud.list.systems.Rd b/man/ud.list.systems.Rd new file mode 100644 index 0000000..1109e6c --- /dev/null +++ b/man/ud.list.systems.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R +\name{ud.list.systems} +\alias{ud.list.systems} +\title{List the loaded systems} +\usage{ +ud.list.systems() +} +\value{ +A character vector containing the system names. If no + systems have been loaded it returns \code{NULL}. +} +\description{ +List the loaded systems +} +\author{ +Swechhya Bista \email{Swechhya.Bista@cytel.com} +} +\seealso{ +\code{\link{ud.add.system}}, \code{\link{ud.free.system}} +} + diff --git a/man/ud.remove.unit.Rd b/man/ud.remove.unit.Rd new file mode 100644 index 0000000..c658bc2 --- /dev/null +++ b/man/ud.remove.unit.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R +\name{ud.remove.unit} +\alias{ud.remove.unit} +\title{Remove a unit from a loaded system} +\usage{ +ud.remove.unit(unit.name, system.name = "default") +} +\arguments{ +\item{unit.name}{The unit name to remove from a system} + +\item{system.name}{The unit system name to use.} +} +\value{ +\code{TRUE} if the unit is successfully removed, otherwise + returns an error. +} +\description{ +Remove a unit from a loaded system +} +\examples{ +\dontrun{ +ud.remove.unit("miles") +} +} +\author{ +Swechhya Bista \email{Swechhya.Bista@cytel.com} +} + diff --git a/man/ud.set.conversion.Rd b/man/ud.set.conversion.Rd new file mode 100644 index 0000000..1a1b0df --- /dev/null +++ b/man/ud.set.conversion.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R +\name{ud.set.conversion} +\alias{ud.set.conversion} +\title{Set a conversion rule between two units} +\usage{ +ud.set.conversion(unit1.text, unit2.text, system.name = "default", + function.name = c("scale", "offset", "log", "invert")) +} +\arguments{ +\item{unit1.text, unit2.text}{The text to define the units. The text +may take the form of "optional blank, optional floating point +nonnegative number, optional blank, unit text, optional blank". The +blanks are ignored; if the floating point number is not given, then +it is assumed to be \code{1}.} + +\item{system.name}{The unit system name to use.} + +\item{function.name}{The function to use to convert between +\code{unit1.text} and \code{unit2.text}. See the details for more +information.} +} +\value{ +\code{TRUE} if conversion setting is successful, otherwise + returns an error. +} +\description{ +Set a conversion rule between two units +} +\details{ +The functions are applied so that if the units are given as + \code{unit1.text = "a x"} and \code{unit2.text = "b y"} where "a" + and "b" are numbers: + \itemize{ + \item{scale}{x = b/a * y} + \item{offset}{x = (b - a) * y} + \item{log}{x = log(a/b)(y); log(a/b) is log base a/b} + \item{invert}{x = 1/y} + } + + If both unit text names are not defined, then both are created and + they are mapped to each other. If one is not defined, it is created + and mapped to the other. If both are defined, then an error is raised. +} +\examples{ +\dontrun{ +ud.set.conversion("1 moleaspirin", "180.157 gram") # Convert grams to moles of aspirin +} +} +\author{ +Swechhya Bista \email{Swechhya.Bista@cytel.com} +} + diff --git a/man/ud.set.encoding.Rd b/man/ud.set.encoding.Rd index 9fc2154..69be616 100644 --- a/man/ud.set.encoding.Rd +++ b/man/ud.set.encoding.Rd @@ -1,38 +1,46 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ud.functions.R \name{ud.set.encoding} \alias{ud.set.encoding} \title{Set the udunits package level encoding type} -\description{ - This function sets the encoding type parameter which is global to the - R udunits2 package. -} \usage{ -ud.set.encoding(enc.string) +ud.set.encoding(enc.string, system.name = "default") } \arguments{ - \item{enc.string}{A character string representing the encoding type. - Valid strings are \code{utf8},\code{ascii},\code{iso-8859-1},and - \code{latin1} (an alias for ISO-8859-1).} -} -\details{ - Encoding type is a parameter to nearly all of the functions in the - udunits library. By default, the R udunits2 pacakge sets the encoding - type to UTF-8, however this package allows the user to set other - encoding types which are supported by the udunits library. It - presently suports UTF-8, ASCII, and ISO-8859-1 +\item{enc.string}{A character string representing the encoding type. +Valid strings are \code{utf8},\code{ascii},\code{iso-8859-1},and +\code{latin1} (an alias for ISO-8859-1).} + +\item{system.name}{The unit system name to use.} } \value{ - Returns no value. Raises an error if it is not given a valid encoding string. +Returns no value. Raises an error if it is not given a valid + encoding string. } -\references{ - Unidata's udunits reference: - \url{http://www.unidata.ucar.edu/software/udunits/} - API guide chapter on data types: - \url{https://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#Types} +\description{ +Sets the encoding type parameter for the current + \code{system.name}. +} +\details{ +Encoding type is a parameter to nearly all of the functions + in the udunits library. By default, the R udunits2 pacakge sets + the encoding type to UTF-8, however this package allows the user to + set other encoding types which are supported by the udunits + library. It presently suports UTF-8, ASCII, and ISO-8859-1. } -\author{James Hiebert \email{hiebert@uvic.ca}} - \examples{ valid.enc.strings <- c('utf8', 'ascii', 'iso-8859-1', 'latin1') lapply(valid.enc.strings, ud.set.encoding) err <- try(ud.set.encoding("This will fail")) } +\author{ +James Hiebert \email{hiebert@uvic.ca} +} +\references{ +Unidata's udunits reference: + \url{http://www.unidata.ucar.edu/software/udunits/} + + API guide chapter on data types: + \url{https://www.unidata.ucar.edu/software/udunits/udunits-2.1.24/udunits2lib.html#Types} +} + diff --git a/man/udunits2-package.Rd b/man/udunits2-package.Rd index 20e270c..d83ee1f 100644 --- a/man/udunits2-package.Rd +++ b/man/udunits2-package.Rd @@ -1,9 +1,12 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/udunits2.R \name{udunits2-package} -\alias{udunits2-package} \alias{udunits2} -\docType{package} +\alias{udunits2-package} \title{udunits-2 bindings for R} -\description{This package provides simple bindings to version 2 of Unidata's udunits library} +\description{ +This package provides simple bindings to version 2 of Unidata's udunits library +} \details{ \tabular{ll}{ Package: \tab udunits2\cr @@ -13,34 +16,49 @@ Date: \tab 2011-02-11\cr License: \tab GPL-2\cr LazyLoad: \tab yes\cr } -This package provides simple bindings to the version 2 API of Unidata's -udunits library. While the entire API is not supported, we have chosen -to boil it down to a few simple functions to be able to exploit the most -useful functionality that the library provides. This package provides -the following functions: + +This package provides simple bindings to the version 2 API of +Unidata's udunits library. While the entire API is not supported, we +have chosen to boil it down to a few simple functions to be able to +exploit the most useful functionality that the library provides. This +package provides the following functions to work within a unit +system: + \itemize{ - \item \code{ud.is.parseable} - \item \code{ud.get.name} - \item \code{ud.get.symbol} - \item \code{ud.are.convertible} - \item \code{ud.convert} + \item \code{\link{ud.is.parseable}} + \item \code{\link{ud.get.name}} + \item \code{\link{ud.get.symbol}} + \item \code{\link{ud.are.convertible}} + \item \code{\link{ud.convert}} } + +Functions for creating and removing units within a system: + +\itemize{ + \item \code{\link{ud.remove.unit}} + \item \code{\link{ud.set.conversion}} +} + +Functions for creating and removing unit systems: + +\itemize{ + \item \code{\link{ud.add.system}} + \item \code{\link{ud.free.system}} + \item \code{\link{ud.have.unit.system}} + \item \code{\link{ud.list.systems}} + \item \code{\link{ud.set.encoding}} +} + Please see the respective function help pages for further details and usage. } \author{ - James Hiebert - -Maintainer: James Hiebert +James Hiebert } \references{ - Unidata's udunits web page: - \url{http://www.unidata.ucar.edu/software/udunits/} +Unidata's udunits web page: \url{http://www.unidata.ucar.edu/software/udunits/} } -\keyword{unitdata} -\keyword{units} \keyword{climate} \keyword{meteorology} -\seealso{ - \code{\link{ud.is.parseable} \link{ud.get.name} \link{ud.get.symbol} - \link{ud.are.convertible} \link{ud.convert}} -} +\keyword{unitdata} +\keyword{units} + diff --git a/src/udunits2_R.c b/src/udunits2_R.c index 75f2a2d..3409ddb 100644 --- a/src/udunits2_R.c +++ b/src/udunits2_R.c @@ -10,202 +10,741 @@ #include #include /* FILENAME_MAX */ -ut_system *sys = NULL; -static ut_encoding enc; +typedef struct Sys_List{ + ut_system *sys; //Pointer to the system + ut_encoding enc; //Encoding for the system + char *sys_name; //The name of the system + struct Sys_List *next; //Pointer to the next node + +}Sys_List; + +struct Sys_List * head = NULL;//Pointer to the head of the list +int Total_Sys = 0; // Total number of systems added/loaded /* From the enum comments in udunits2.h */ -const char * ut_status_strings[] = { - "Success", - "An argument violates the function's contract", - "Unit, prefix, or identifier already exists", - "No such unit exists", - "Operating-system error. See \"errno\".", - "The units belong to different unit-systems", - "The operation on the unit(s) is meaningless", - "The unit-system doesn't have a unit named \"second\"", - "An error occurred while visiting a unit", - "A unit can't be formatted in the desired manner", - "string unit representation contains syntax error", - "string unit representation contains unknown word", - "Can't open argument-specified unit database", - "Can't open environment-specified unit database", - "Can't open installed, default, unit database", - "Error parsing unit specification" +const char * ut_status_strings[] = +{ + "Success", + "An argument violates the function's contract", + "Unit, prefix, or identifier already exists", + "No such unit exists", + "Operating-system error. See \"errno\".", + "The units belong to different unit-systems", + "The operation on the unit(s) is meaningless", + "The unit-system doesn't have a unit named \"second\"", + "An error occurred while visiting a unit", + "A unit can't be formatted in the desired manner", + "string unit representation contains syntax error", + "string unit representation contains unknown word", + "Can't open argument-specified unit database", + "Can't open environment-specified unit database", + "Can't open installed, default, unit database", + "Error parsing unit specification" }; -void handle_error(const char *calling_function) { - ut_status stat; - stat = ut_get_status(); - error("Error in function %s: %s", calling_function, ut_status_strings[stat]); +void handle_error(const char *calling_function) +{ + ut_status stat; + stat = ut_get_status(); + error("Error in function %s: %s", calling_function, ut_status_strings[stat]); } -void R_ut_init(const int *print_warning_on_failure) { - ut_status stat; - ut_set_error_message_handler((ut_error_message_handler) Rvprintf); - if (sys != NULL) { - ut_free_system(sys); - } - ut_set_error_message_handler(ut_ignore); - sys = ut_read_xml(NULL); - ut_set_error_message_handler((ut_error_message_handler) Rvprintf); - if (sys == NULL) { - stat = ut_get_status(); - if (*print_warning_on_failure) - ut_handle_error_message("Warning in R_ut_init: %s\n", ut_status_strings[stat]); +/*Function to check if a system is defined or not, if the system is defined then +it will return TRUE(or 1), else FALSE(or 0) will be returned */ +void R_ut_has_system(int *exists, char * const *sys_name) +{ + Sys_List * current_sys_node; + int found = 1; + size_t length = strlen(*sys_name); + + if(head == NULL || sys_name == NULL) //If there are no systems defined or system name is null + { + *exists = 0; + return; + } + + current_sys_node = head; + + + while(current_sys_node != NULL) + { + found = strncmp(*sys_name, current_sys_node->sys_name, length); + + if(found == 0) // if the system name is found + { + *exists = 1; + return; + } + else//if system name is not found. + { + current_sys_node = current_sys_node->next; + } + } + + + *exists = 0; return; - } - enc = UT_UTF8; - return; } -void R_ut_has_system(int *exists) { - if (sys != NULL) { - *exists = 1; - } - else { - *exists = 0; - } - return; -} - -/* Take an encoding string and set the global var enc */ -void R_ut_set_encoding(const char * const *enc_string) { - - size_t length = strlen(*enc_string); - - if (strncmp(*enc_string, "utf8", length) == 0) { - enc = UT_UTF8; - } - else if (strncmp(*enc_string, "ascii", length) == 0) { - enc = UT_ASCII; - } - else if (strncmp(*enc_string, "iso-8859-1", length) == 0 || - strncmp(*enc_string, "latin1", length) == 0) { - enc = UT_LATIN1; - } - else { - error("Valid encoding string parameters are ('utf8'|'ascii'|'iso-8859-1','latin1')"); - } - return; -} - -void R_ut_is_parseable(char * const *units_string, int *parseable) { - ut_unit *result; - int one = 1; - - if (sys == NULL) { - R_ut_init(&one); - } - - ut_trim(*units_string, enc); - result = ut_parse(sys, *units_string, enc); - if (result == NULL) { - *parseable = 0; - } - else { - *parseable = 1; - } - ut_free(result); - return; -} - -void R_ut_are_convertible(char * const *ustring1, char * const *ustring2, int *convertible) { - ut_unit *u1, *u2; - int one = 1; - - if (sys == NULL) { - R_ut_init(&one); - } - - ut_trim(*ustring1, enc); ut_trim(*ustring2, enc); - u1 = ut_parse(sys, *ustring1, enc); - u2 = ut_parse(sys, *ustring2, enc); - - if (!(u1 && u2)) { - handle_error("R_ut_are_convertible"); - } - - if (ut_are_convertible(u1, u2) == 0) { - *convertible = 0; - } - else { - *convertible = 1; - } - ut_free(u1); ut_free(u2); - return; -} - -void R_ut_convert(const double *x, int *count, char * const *units_from, char * const *units_to, double *rv) { - ut_unit *from, *to; - cv_converter *conv; - int one = 1; - - if (sys == NULL) { - R_ut_init(&one); - } - - ut_trim(*units_from, enc); ut_trim(*units_to, enc); - - from = ut_parse(sys, *units_from, enc); - if (from == NULL) { - handle_error("R_ut_convert"); +/*Function to check if a system is defined or not, if the system is defined then +the system will be returned, else a null value will be returned */ +void R_ut_get_system(char *sys_name, Sys_List **named_sys) +{ + Sys_List * current_sys_node; + int found = 1; + size_t length = strlen(sys_name); + + if(head == NULL || sys_name == NULL) //If there are no systems defined or system name is null + { + *named_sys = NULL; + error("System '%s' not defined.", sys_name); + return; + } + + *named_sys = NULL; + current_sys_node = head; + while(current_sys_node != NULL) + { + found = strncmp(sys_name, current_sys_node->sys_name, length); + + if(found == 0) // if the system name is found + { + *named_sys = current_sys_node; + break; + } + else//if system name is not found. + { + current_sys_node = current_sys_node->next; + } + } + + if(current_sys_node == NULL) + { + error("System '%s' not defined.", sys_name); + } + return; - } +} + + +void R_ut_init(char * const *file_name, char * const *sys_name, const int *print_warning_on_failure) +{ + ut_status stat; + + char ** system_name; + + int *exist = NULL; + + Sys_List * current_sys_node, *new_sys_node; + + current_sys_node = new_sys_node = NULL; + + ut_set_error_message_handler((ut_error_message_handler) Rvprintf); + + + //If system name is blank then the system is named as default + if(sys_name == NULL || *sys_name == NULL || strlen(*sys_name) == 0) + { + system_name = (char**) R_alloc(1, sizeof(char*)); + *system_name = (char*) R_alloc(8, sizeof(char)); + strcpy(*system_name, "default"); + } + else + { + system_name = (char**) R_alloc(1, sizeof(char*)); + *system_name = (char*) R_alloc((strlen(*sys_name)+1), sizeof(char)); + strcpy(*system_name, *sys_name); + + } + + + if (head == NULL) // No system have yet been loaded + { + new_sys_node = (Sys_List *) Calloc(sizeof(Sys_List), Sys_List); + head = new_sys_node; + } + else //traverse the list to check if system with same name exist, if not add a new system in the list + { + exist = (int *) Calloc(sizeof(int), int); + + //Check if a system with same name exist + //R_ut_has_system(exist, sys_name); + R_ut_has_system(exist, system_name); + + //If system with same name does not exist then add the system to the end of the system list + //Else give an error + if(*exist == 0) + { + current_sys_node = head; + + while(current_sys_node->next != NULL) + { + current_sys_node = current_sys_node->next; + } + new_sys_node = (Sys_List *) Calloc(sizeof(Sys_List), Sys_List); + current_sys_node->next = new_sys_node; + + Free(exist); + } + else// System with same name exists give user the error message + { + error("System '%s' already defined.", *system_name); + Free(exist); + return; + } + + } + + new_sys_node->next = NULL; + new_sys_node->enc = UT_UTF8;//Default encoding + + //Copy system name to the system list + new_sys_node->sys_name = (char*)Calloc((strlen(*system_name)+1) * sizeof(char), char); + + strcpy(new_sys_node->sys_name, *system_name); + + ut_set_error_message_handler(ut_ignore); + + + if(file_name == NULL || *file_name == NULL || strlen(*file_name) == 0) + { + new_sys_node->sys = ut_read_xml(NULL); + } + else + { + new_sys_node->sys = ut_read_xml(*file_name); + } + + ut_set_error_message_handler((ut_error_message_handler) Rvprintf); + + if (new_sys_node->sys == NULL)//If system creation fails + { + stat = ut_get_status(); + + if (*print_warning_on_failure) + ut_handle_error_message("Warning in R_ut_init: %s\n", ut_status_strings[stat]); + + if(current_sys_node != NULL) + current_sys_node->next = NULL; + + if(head == new_sys_node)//If head was pointing to the new system + head = NULL; + + Free(new_sys_node->sys_name); + Free(new_sys_node); + + return; + } + + Total_Sys ++; //Increase the count of systems + - to = ut_parse(sys, *units_to, enc); - if (from == NULL) { - handle_error("R_ut_convert"); return; - } - conv = ut_get_converter(from, to); - if (conv == NULL) { - handle_error("R_ut_convert"); +} + + + +/* Take an encoding string and set the global var enc *///--updated for multiple systems +/*Take an encoding string and set the encoding for the unit system given by the user */ +void R_ut_set_encoding(const char * const *enc_string, char * const *sys_name) +{ + Sys_List *current_sys_node = NULL; + + size_t length = strlen(*enc_string); + + if(sys_name != NULL ) + { + //Check if the system name is defined or not + R_ut_get_system(*sys_name, ¤t_sys_node); + } + + if(current_sys_node == NULL) + { + return; + } + + if (strncmp(*enc_string, "utf8", length) == 0) + { + current_sys_node->enc = UT_UTF8; + } + else if (strncmp(*enc_string, "ascii", length) == 0) + { + current_sys_node->enc = UT_ASCII; + } + else if (strncmp(*enc_string, "iso-8859-1", length) == 0 || + strncmp(*enc_string, "latin1", length) == 0) + { + current_sys_node->enc = UT_LATIN1; + } + else + { + error("Valid encoding string parameters are ('utf8'|'ascii'|'iso-8859-1','latin1')"); + } + + return; - } - cv_convert_doubles(conv, x, (size_t) *count, rv); +} + +void R_ut_is_parseable(char * const *units_string, int *parseable, char * const *sys_name) +{ + ut_unit *result; + //int one = 1; + Sys_List *current_sys_node = NULL; + + if(sys_name != NULL ) + { + //Check if the system name is defined or not + R_ut_get_system(*sys_name, ¤t_sys_node); + + } - // Cleanup - cv_free(conv); - ut_free(to); - ut_free(from); - return; + if (current_sys_node == NULL)// + { + return; + } + + ut_trim(*units_string, current_sys_node->enc); + result = ut_parse(current_sys_node->sys, *units_string, current_sys_node->enc); + + if (result == NULL) + { + *parseable = 0; + } + else + { + *parseable = 1; + } + + ut_free(result); + + return; } -void R_ut_get_name(char * const *ustring, char **rstring) { - ut_unit *u; - char *trimmed; - char *s; - trimmed = ut_trim(*ustring, enc); - u = ut_parse(sys, trimmed, enc); +void R_ut_are_convertible(char * const *ustring1, char * const *ustring2, + int *convertible, char * const *sys_name) +{ + ut_unit *u1, *u2; + + Sys_List *current_sys_node = NULL; + + if(sys_name != NULL ) + { + //Check if the system name is defined or not + R_ut_get_system(*sys_name, ¤t_sys_node); + } + + + if (current_sys_node == NULL)// + { + return; + } + + ut_trim(*ustring1, current_sys_node->enc); + ut_trim(*ustring2, current_sys_node->enc); + u1 = ut_parse(current_sys_node->sys, *ustring1, current_sys_node->enc); + u2 = ut_parse(current_sys_node->sys, *ustring2, current_sys_node->enc); + + if (!(u1 && u2)) + { + handle_error("R_ut_are_convertible"); + } + + if (ut_are_convertible(u1, u2) == 0) + { + *convertible = 0; + } + else + { + *convertible = 1; + } + + ut_free(u1); + ut_free(u2); + return; +} - if (!u) { - handle_error("R_ut_get_name"); - } +void R_ut_convert(const double *x, int *count, char * const *units_from, + char * const *units_to, double *rv, char * const *sys_name) +{ + ut_unit *from, *to; + cv_converter *conv; + + Sys_List *current_sys_node = NULL; + + if(sys_name != NULL ) + { + //Check if the system name is defined or not + R_ut_get_system(*sys_name, ¤t_sys_node); + } + + if (current_sys_node == NULL)// + { + return; + } + + ut_trim(*units_from, current_sys_node->enc); + ut_trim(*units_to, current_sys_node->enc); + + from = ut_parse(current_sys_node->sys, *units_from, current_sys_node->enc); + if (from == NULL) + { + handle_error("R_ut_convert"); + return; + } + + to = ut_parse(current_sys_node->sys, *units_to, current_sys_node->enc); + if (from == NULL) + { + handle_error("R_ut_convert"); + return; + } + conv = ut_get_converter(from, to); + if (conv == NULL) + { + handle_error("R_ut_convert"); + return; + } + + cv_convert_doubles(conv, x, (size_t) *count, rv); + + // Cleanup + cv_free(conv); + ut_free(to); + ut_free(from); + return; +} + +void R_ut_get_name(char * const *ustring, char **rstring, char * const *sys_name) +{ + ut_unit *u; + char *trimmed; + char *s; + Sys_List *current_sys_node = NULL; + + if(sys_name != NULL ) + { + //Check if the system name is defined or not + R_ut_get_system(*sys_name, ¤t_sys_node); + } + + if (current_sys_node == NULL) + { + return; + } + + trimmed = ut_trim(*ustring, current_sys_node->enc); + u = ut_parse(current_sys_node->sys, trimmed, current_sys_node->enc); + + if (!u) + { + handle_error("R_ut_get_name"); + } + + s = (char *) ut_get_name(u, current_sys_node->enc); // FIXME: ut_get_name seems to allocate the string... does it need to be free-ed? + + if (s == NULL) return; + else *rstring = s; + + return; +} + +void R_ut_get_symbol(char * const *ustring, char **rstring, char * const *sys_name) +{ + ut_unit *u; + char *trimmed; + char *s; + + Sys_List *current_sys_node = NULL; + + if(sys_name != NULL ) + { + //Check if the system name is defined or not + R_ut_get_system(*sys_name, ¤t_sys_node); + } + + + if (current_sys_node == NULL) + { + return; + } + + trimmed = ut_trim(*ustring, current_sys_node->enc); + u = ut_parse(current_sys_node->sys, trimmed, current_sys_node->enc); + + if (!u) + { + handle_error("R_ut_get_symbol"); + } + + s = (char *) ut_get_symbol(u, current_sys_node->enc); // FIXME: ut_get_symbol seems to allocate the string... does it need to be free-ed? + + if (s == NULL) return; + else *rstring = s; + + return; +} + + +/*Function to remove a defined unit system*/ +void R_ut_free_system(char * const *sys_name, int* deleted) +{ + Sys_List *current_sys_node, *prev_sys_node; + current_sys_node = prev_sys_node = NULL; + + if(sys_name != NULL ) + { + //Check if the system name is defined or not + R_ut_get_system(*sys_name, ¤t_sys_node); + } + + + if(current_sys_node == NULL)//If the name is not defined + { + *deleted = 0; + return; + } + + + if(current_sys_node == head)//If the system is the first system in the list + { + head = head->next; + } + else//If the system is not the first system on the list go to 1 node prior to the system containing name + { + prev_sys_node = head; + + while(prev_sys_node->next != current_sys_node) + { + prev_sys_node = prev_sys_node->next; + } + + prev_sys_node->next = current_sys_node->next; + } - s = (char *) ut_get_name(u, enc); // FIXME: ut_get_name seems to allocate the string... does it need to be free-ed? + //Free the system + ut_free_system(current_sys_node->sys); - if (s == NULL) return; - else *rstring = s; + //Free the system name + Free(current_sys_node->sys_name); - return; + //Deallocate memory from the list + Free(current_sys_node); + + //Decrease the count of systems + Total_Sys --; + + //Set deleted to true + *deleted = 1; + + return; +} + +/*Get the total number of defined systems*/ +void R_ut_system_count(int *count) +{ + *count = Total_Sys; + return; +} + + +/*Function to get the list of system names*/ +void R_ut_list_systems(char ** SystemList) +{ + Sys_List * current_sys_node = NULL; + + int count = 0; + + if(head == NULL)//If no system is yet defined. + { + SystemList = NULL; + return; + } + + current_sys_node = head; + + for(count = 0; count < Total_Sys; count++)//Traverse the system linked list and get the names + { + SystemList[count] = (char*) R_alloc((strlen(current_sys_node->sys_name)+1), sizeof(char)); + strcpy(SystemList[count], current_sys_node->sys_name); + current_sys_node = current_sys_node->next; + } + + return; } -void R_ut_get_symbol(char * const *ustring, char **rstring) { - ut_unit *u; - char *trimmed; - char *s; - trimmed = ut_trim(*ustring, enc); - u = ut_parse(sys, trimmed, enc); +/*Function to delete the complete list of system.*/ +void R_ut_system_cleanup() +{ + Sys_List *current_sys_node = NULL; - if (!u) { - handle_error("R_ut_get_symbol"); - } + while(head != NULL) + { + current_sys_node = head; + head = head->next; - s = (char *) ut_get_symbol(u, enc); // FIXME: ut_get_symbol seems to allocate the string... does it need to be free-ed? + ut_free_system(current_sys_node->sys); + Free(current_sys_node->sys_name); + Free(current_sys_node); + current_sys_node = NULL; + } + + Total_Sys = 0; + return; + +} + +/*Function to remove a unit from the system*/ +void R_ut_remove_unit(char * const *unit_name, int *removed, char * const * sys_name) +{ + Sys_List *current_sys_node = NULL; + ut_unit *del_unit = NULL; + ut_status stat; + + if(sys_name != NULL ) + { + //Check if the system name is defined or not + R_ut_get_system(*sys_name, ¤t_sys_node); + } + + if(current_sys_node == NULL) + { + *removed = 0; + return; + } + + //Check if the unit is defined in the system + del_unit = ut_get_unit_by_name(current_sys_node->sys, *unit_name); + + if(del_unit == NULL)//If the unit is not defined print the error + { + error("Unit '%s' not defined in the system.", *unit_name); + *removed = 0; + return; + } + + //remove the mapping of the unit + ut_unmap_name_to_unit(current_sys_node->sys, *unit_name, current_sys_node->enc); + stat = ut_get_status(); + + if(stat != UT_SUCCESS)//If unmapping is unsuccessful + { + ut_handle_error_message("Warning in R_ut_remove_unit: %s\n", ut_status_strings[stat]); + *removed = 0; + return; + } + + //Remove the memory allocated for the system + ut_free(del_unit); + *removed = 1; + + return; +} + + + +/*Function to get and set the appropriate conversion function specified by the user*/ +void get_unit(const char *func_type, double quant1, double quant2, const ut_unit* u1, ut_unit** u2) +{ + //Get the length of function + size_t length = strlen(func_type); + + if (strncmp(func_type, "scale", length) == 0) + { + *u2 = ut_scale((quant1/quant2), u1); + } + else if(strncmp(func_type, "offset", length) == 0) + { + *u2 = ut_offset(u1, (quant1-quant2)); + } + else if(strncmp(func_type, "log", length) == 0) + { + *u2 = ut_log((quant1/quant2), u1); + } + else if(strncmp(func_type, "invert", length) == 0) + { + *u2 = ut_invert(u1); + } + else + { + error("Enter valid function type. Valid function types are ('scale'|'offset'|'invert'|'log')"); + } + return; + +} - if (s == NULL) return; - else *rstring = s; - return; + +/*Function to add a new units and set conversion to a loaded system */ +void R_ut_set_conversion(char * const *u1_name, char * const *u2_name, double *u1_quant, + double *u2_quant, char * const * sys_name, char * const * func_type, int *set) +{ + + Sys_List *current_sys_node = NULL; + ut_unit *u1_unit, *u2_unit; + u1_unit = u2_unit = NULL; + + if(sys_name != NULL ) + { + //Check if the system name is defined or not + R_ut_get_system(*sys_name, ¤t_sys_node); + } + + if(current_sys_node == NULL) + { + *set = 0; + return; + } + + if(func_type != NULL) + { + size_t length = strlen(*func_type); + + if(!(strncmp(*func_type, "scale", length) == 0 || strncmp(*func_type, "offset", length) == 0 || + strncmp(*func_type, "log", length) == 0 || strncmp(*func_type, "invert", length) == 0)) + { + error("Enter valid function type. Valid function types are ('scale'|'offset'|'invert'|'log')"); + return; + } + } + + //Trim any leading or trailing whitespaces + ut_trim(*u1_name, current_sys_node->enc); + ut_trim(*u2_name, current_sys_node->enc); + + //Check if the unit is already defined in the system + u1_unit = ut_get_unit_by_name(current_sys_node->sys, *u1_name); + u2_unit = ut_get_unit_by_name(current_sys_node->sys, *u2_name); + + if(u1_unit == NULL && u2_unit == NULL)//if both the units are not defined. + { + u1_unit = ut_new_base_unit(current_sys_node->sys); + ut_map_name_to_unit(*u1_name, current_sys_node->enc, u1_unit); + ut_map_unit_to_name(u1_unit, *u1_name, current_sys_node->enc); + get_unit(*func_type, *u1_quant, *u2_quant, u1_unit, &(u2_unit)); + ut_map_name_to_unit(*u2_name, current_sys_node->enc, u2_unit); + ut_map_unit_to_name(u2_unit, *u2_name, current_sys_node->enc); + } + else if(u1_unit == NULL)//if the u1_unit is defined + { + get_unit(*func_type, *u2_quant, *u1_quant, u2_unit, &(u1_unit)); + ut_map_name_to_unit(*u1_name, current_sys_node->enc, u1_unit); + ut_map_unit_to_name(u1_unit, *u1_name, current_sys_node->enc); + } + else if(u2_unit == NULL)//if the u2_unit is defined + { + get_unit(*func_type, *u1_quant, *u2_quant, u1_unit, &(u2_unit)); + ut_map_name_to_unit(*u2_name, current_sys_node->enc, u2_unit); + ut_map_unit_to_name(u2_unit, *u2_name, current_sys_node->enc); + } + else//if both the units are defined + { + *set = 0; + error("Both the units are already defined. However, you can remove the unit from the system using 'ud.remove.unit' function and reset the conversion."); + } + + *set = 1; + return; + } +