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 @@
+
+
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 @@
+
+
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 @@
+
+
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 @@
+
+
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)