diff --git a/DESCRIPTION b/DESCRIPTION index 5885603..70b1135 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Package: units -Version: 0.8-5.5 +Version: 0.8-5.6 Title: Measurement Units for R Vectors Authors@R: c(person("Edzer", "Pebesma", role = c("aut", "cre"), email = "edzer.pebesma@uni-muenster.de", comment = c(ORCID = "0000-0001-8049-7069")), person("Thomas", "Mailund", role = "aut", email = "mailund@birc.au.dk"), @@ -41,7 +41,7 @@ SystemRequirements: udunits-2 License: GPL-2 URL: https://r-quantities.github.io/units/, https://github.com/r-quantities/units BugReports: https://github.com/r-quantities/units/issues -RoxygenNote: 7.3.1 +RoxygenNote: 7.3.2 Roxygen: list(old_usage = TRUE) Encoding: UTF-8 Config/testthat/edition: 3 diff --git a/NEWS.md b/NEWS.md index 03b1e54..8e6f0a2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,8 @@ * Fix `hist()` error; #368 +* Add support for `lims()` in `ggplot2` scales; #370 + # version 0.8-5 * avoid -Wformat-security warning on CRAN diff --git a/R/scale_units.R b/R/scale_units.R index a433891..20c5add 100644 --- a/R/scale_units.R +++ b/R/scale_units.R @@ -57,7 +57,7 @@ scale_x_units <- function(..., guide = ggplot2::waiver(), position = "bottom", palette = identity, ..., guide = guide, position = position, - super = MakeScaleContinuousPositionUnits() + super = make_scale_units() ) sc$units <- as_units(unit) set_sec_axis(sec.axis, sc) @@ -76,16 +76,16 @@ scale_y_units <- function(..., guide = ggplot2::waiver(), position = "left", palette = identity, ..., guide = guide, position = position, - super = MakeScaleContinuousPositionUnits() + super = make_scale_units() ) sc$units <- as_units(unit) set_sec_axis(sec.axis, sc) } -MakeScaleContinuousPositionUnits <- function() { +make_scale_units <- function(parent=ggplot2::ScaleContinuousPosition) { ggplot2::ggproto( "ScaleContinuousPositionUnits", - ggplot2::ScaleContinuousPosition, + parent, units = NULL, map = function(self, x, limits = self$get_limits()) { @@ -95,16 +95,19 @@ MakeScaleContinuousPositionUnits <- function() { else units(x) <- as_units(1, self$units) x <- drop_units(x) } - ggplot2::ggproto_parent( - ggplot2::ScaleContinuousPosition, self)$map(x, limits) + ggplot2::ggproto_parent(parent, self)$map(x, limits) }, transform = function(self, x) { if (!is.null(self$units)) units(x) <- as_units(1, self$units) - new_x <- ggplot2::ggproto_parent( - ggplot2::ScaleContinuousPosition, self)$transform(drop_units(x)) + if (inherits(self$limits, "units")) { + units(self$limits) <- units(x) + self$limits <- drop_units(self$limits) + } + + new_x <- ggplot2::ggproto_parent(parent, self)$transform(drop_units(x)) as_units(new_x, units(x)) }, @@ -135,3 +138,18 @@ scale_type.units <- function(x) { " Please, attach it using 'library(units)' to properly show scales with units.") c("units", "continuous") } + +utils::globalVariables("caller_env") + +# registered in .onLoad() +limits.units <- function(lims, var, call = caller_env()) { + if (length(lims) != 2) + stop("`", var, "` must be a two-element vector", call.=FALSE) + + trans <- if (!any(is.na(lims)) && lims[1] > lims[2]) + "reverse" else "identity" + name <- paste0("scale_", var, "_units") + sc <- match.fun(name)(limits=lims, transform=trans) + sc$call <- if (!is.null(call)) call else str2lang(paste0(name, "()")) + sc +} diff --git a/R/tidyverse.R b/R/tidyverse.R index c40c36d..a3f3414 100644 --- a/R/tidyverse.R +++ b/R/tidyverse.R @@ -88,6 +88,7 @@ register_all_s3_methods <- function() { register_s3_method("vctrs::vec_ptype2", "units.units") register_s3_method("vctrs::vec_cast", "units.units") register_s3_method("ggplot2::scale_type", "units") + register_s3_method("ggplot2::limits", "units") } register_s3_method <- function(generic, class, fun=NULL) { diff --git a/tests/testthat/_snaps/plot/ggplot2-limits-other-units.svg b/tests/testthat/_snaps/plot/ggplot2-limits-other-units.svg new file mode 100644 index 0000000..295c0c4 --- /dev/null +++ b/tests/testthat/_snaps/plot/ggplot2-limits-other-units.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2.5 +5.0 +7.5 +10.0 + + + + + + + + + +0 +5000 +10000 +15000 +20000 +a [m] +a [m] +ggplot2 limits other units + + diff --git a/tests/testthat/_snaps/plot/ggplot2-limits-via-scale-with-units.svg b/tests/testthat/_snaps/plot/ggplot2-limits-via-scale-with-units.svg new file mode 100644 index 0000000..eb8ff88 --- /dev/null +++ b/tests/testthat/_snaps/plot/ggplot2-limits-via-scale-with-units.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2.5 +5.0 +7.5 +10.0 + + + + + + + + + +0 +5 +10 +15 +20 +a [m] +a [m] +ggplot2 limits via scale with units + + diff --git a/tests/testthat/_snaps/plot/ggplot2-limits-via-scale.svg b/tests/testthat/_snaps/plot/ggplot2-limits-via-scale.svg new file mode 100644 index 0000000..e298f6b --- /dev/null +++ b/tests/testthat/_snaps/plot/ggplot2-limits-via-scale.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2.5 +5.0 +7.5 +10.0 + + + + + + + + + +0 +5 +10 +15 +20 +a [m] +a [m] +ggplot2 limits via scale + + diff --git a/tests/testthat/_snaps/plot/ggplot2-limits-via-xlim.svg b/tests/testthat/_snaps/plot/ggplot2-limits-via-xlim.svg new file mode 100644 index 0000000..1d1a613 --- /dev/null +++ b/tests/testthat/_snaps/plot/ggplot2-limits-via-xlim.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2.5 +5.0 +7.5 +10.0 + + + + + + + + + +0 +5 +10 +15 +20 +a [m] +a [m] +ggplot2 limits via xlim + + diff --git a/tests/testthat/test_plot.R b/tests/testthat/test_plot.R index 590867b..6b53ff2 100644 --- a/tests/testthat/test_plot.R +++ b/tests/testthat/test_plot.R @@ -79,4 +79,23 @@ test_that("axis transformations do not affect displayed units", { vdiffr::expect_doppelganger("ggplot2 trans + unit", p2) }) +test_that("axis limits can be changed", { + skip_if_not_installed("vdiffr") + skip_if_not_installed("ggplot2", "3.5.0") + library(ggplot2) + + df <- data.frame(a = set_units(1:10, "m")) + + p0 <- ggplot(df, aes(y=a, x=a)) + geom_point() + p1 <- p0 + scale_x_units(limits=c(0, 20)) + p2 <- p0 + scale_x_units(limits=set_units(c(0, 20), "m")) + p3 <- p0 + xlim(set_units(c(0, 20), "m")) + p4 <- p0 + xlim(set_units(c(0, 20), "km")) + + vdiffr::expect_doppelganger("ggplot2 limits via scale", p1) + vdiffr::expect_doppelganger("ggplot2 limits via scale with units", p2) + vdiffr::expect_doppelganger("ggplot2 limits via xlim", p3) + vdiffr::expect_doppelganger("ggplot2 limits other units", p4) +}) + do.call(units_options, units:::.default_options)