diff --git a/NEWS.md b/NEWS.md index 77216314..5d9a7117 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # metR (development version) +## New features + +- The contour functions gain a `clip` argument to only show contours in an area defined by a polygon. + # metR 0.14.1 ## Breaking Changes diff --git a/R/geom_contour_fill.R b/R/geom_contour_fill.R index 704db231..86280e70 100644 --- a/R/geom_contour_fill.R +++ b/R/geom_contour_fill.R @@ -66,6 +66,8 @@ geom_contour_fill <- function(mapping = NULL, data = NULL, breaks = MakeBreaks(), bins = NULL, binwidth = NULL, + proj = NULL, + clip = NULL, kriging = FALSE, global.breaks = TRUE, na.fill = FALSE, @@ -87,6 +89,8 @@ geom_contour_fill <- function(mapping = NULL, data = NULL, na.rm = FALSE, na.fill = na.fill, kriging = kriging, + proj = proj, + clip = clip, global.breaks = global.breaks, ... ) diff --git a/R/geom_contour_tanaka.R b/R/geom_contour_tanaka.R index 69d7a449..5ffabef7 100644 --- a/R/geom_contour_tanaka.R +++ b/R/geom_contour_tanaka.R @@ -80,6 +80,9 @@ geom_contour_tanaka <- function(mapping = NULL, data = NULL, dark = "gray20", range = c(0.01, 0.5), smooth = 0, + proj = NULL, + clip = NULL, + kriging = FALSE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) { @@ -102,6 +105,9 @@ geom_contour_tanaka <- function(mapping = NULL, data = NULL, dark = dark, range = range, smooth = smooth, + kriging = kriging, + proj = proj, + clip = clip, ... ) ) diff --git a/R/stat_contour2.r b/R/stat_contour2.r index d943c677..d873d494 100644 --- a/R/stat_contour2.r +++ b/R/stat_contour2.r @@ -10,6 +10,11 @@ #' data or for each grouping. #' @param kriging Logical indicating whether to perform ordinary kriging before contouring. #' Use this if you want to use contours with irregularly spaced data. +#' @param proj The projection to which to project the contours to. +#' It can be either a projection string or a function to apply to the whole +#' contour dataset. +#' @param clip A simple features object to be used as a clip. Contours are only +#' drawn in the interior of this polygon. #' #' @export #' @section Computed variables: @@ -24,6 +29,8 @@ stat_contour2 <- function(mapping = NULL, data = NULL, breaks = MakeBreaks(), bins = NULL, binwidth = NULL, + proj = NULL, + clip = NULL, kriging = FALSE, global.breaks = TRUE, na.rm = FALSE, @@ -47,6 +54,8 @@ stat_contour2 <- function(mapping = NULL, data = NULL, binwidth = binwidth, global.breaks = global.breaks, kriging = kriging, + proj = proj, + clip = clip, ... ) ) @@ -94,7 +103,7 @@ StatContour2 <- ggplot2::ggproto("StatContour2", ggplot2::Stat, breaks = scales::fullseq, complete = TRUE, na.rm = FALSE, circular = NULL, xwrap = NULL, ywrap = NULL, na.fill = FALSE, global.breaks = TRUE, - proj = NULL, kriging = FALSE) { + proj = NULL, kriging = FALSE, clip = NULL) { if (isFALSE(global.breaks)) { breaks <- setup_breaks(data, breaks = breaks, @@ -179,6 +188,17 @@ StatContour2 <- ggplot2::ggproto("StatContour2", ggplot2::Stat, } } + + if (!is.null(clip)) { + if (!is.na(sf::st_crs(clip))) { + sf::st_crs(clip) <- NA + } + clip <- sf::st_union(clip) + contours <- contours[, clip_contours(x, y, clip, type = "LINESTRING"), by = setdiff(colnames(contours), c("x", "y", "dx", "dy"))] + contours[, group := interaction(group, L)] + } + + return(contours) } ) diff --git a/R/stat_contour_fill.R b/R/stat_contour_fill.R index ccb0d424..d13364b7 100644 --- a/R/stat_contour_fill.R +++ b/R/stat_contour_fill.R @@ -7,6 +7,8 @@ stat_contour_fill <- function(mapping = NULL, data = NULL, bins = NULL, binwidth = NULL, global.breaks = TRUE, + proj = NULL, + clip = NULL, kriging = FALSE, na.fill = FALSE, show.legend = NA, @@ -28,6 +30,8 @@ stat_contour_fill <- function(mapping = NULL, data = NULL, binwidth = binwidth, global.breaks = global.breaks, kriging = kriging, + proj = proj, + clip = clip, ... ) ) @@ -82,7 +86,7 @@ StatContourFill <- ggplot2::ggproto("StatContourFill", ggplot2::Stat, breaks = scales::fullseq, complete = TRUE, na.rm = FALSE, xwrap = NULL, ywrap = NULL, na.fill = FALSE, global.breaks = TRUE, - proj = NULL, kriging = FALSE) { + proj = NULL, kriging = FALSE, clip = NULL) { data.table::setDT(data) if (isFALSE(global.breaks)) { @@ -102,7 +106,6 @@ StatContourFill <- ggplot2::ggproto("StatContourFill", ggplot2::Stat, warningf("The data must be a complete regular grid.", call. = FALSE) return(data.frame()) } else { - # data <- data.table::setDT(tidyr::complete(data, x, y, fill = list(z = NA))) data <- .complete(data, x, y) } } @@ -156,9 +159,17 @@ StatContourFill <- ggplot2::ggproto("StatContourFill", ggplot2::Stat, } } + if (!is.null(clip)) { + if (!is.na(sf::st_crs(clip))) { + sf::st_crs(clip) <- NA + } + clip <- sf::st_union(clip) + cont <- cont[, clip_contours(x, y, clip), by = setdiff(colnames(cont), c("x", "y"))] + cont[, subgroup := interaction(subgroup, L)] + } - cont + cont } ) @@ -223,3 +234,22 @@ pretty_isoband_levels <- function(isoband_levels, dig.lab = 3) { # and open at their upper boundary sprintf("(%s, %s]", label_low, label_high) } + + +clip_contours <- function(x, y, clip, type = "POLYGON") { + + xy <- sf::st_linestring(x = matrix(c(x, y), ncol = 2)) |> + sf::st_cast(type) |> + sf::st_intersection(clip) + + if (length(xy) == 0) { + return(NULL) + } + xy <- sf::st_coordinates(xy) + + L <- do.call(interaction, lapply(seq(3, ncol(xy)), function(i) xy[, i])) + list(x = xy[, 1], + y = xy[, 2], + L = L) + +} diff --git a/man/geom_contour2.Rd b/man/geom_contour2.Rd index 8704bf14..f932709c 100644 --- a/man/geom_contour2.Rd +++ b/man/geom_contour2.Rd @@ -39,6 +39,8 @@ stat_contour2( breaks = MakeBreaks(), bins = NULL, binwidth = NULL, + proj = NULL, + clip = NULL, kriging = FALSE, global.breaks = TRUE, na.rm = FALSE, @@ -137,6 +139,13 @@ the default plot specification, e.g. \code{\link[ggplot2:borders]{borders()}}.} \code{ggproto} \code{Geom} subclass or as a string naming the geom stripped of the \code{geom_} prefix (e.g. \code{"point"} rather than \code{"geom_point"})} +\item{proj}{The projection to which to project the contours to. +It can be either a projection string or a function to apply to the whole +contour dataset.} + +\item{clip}{A simple features object to be used as a clip. Contours are only +drawn in the interior of this polygon.} + \item{kriging}{Logical indicating whether to perform ordinary kriging before contouring. Use this if you want to use contours with irregularly spaced data.} } diff --git a/man/geom_contour_fill.Rd b/man/geom_contour_fill.Rd index 656e68cf..acf5cde3 100644 --- a/man/geom_contour_fill.Rd +++ b/man/geom_contour_fill.Rd @@ -16,6 +16,8 @@ geom_contour_fill( breaks = MakeBreaks(), bins = NULL, binwidth = NULL, + proj = NULL, + clip = NULL, kriging = FALSE, global.breaks = TRUE, na.fill = FALSE, @@ -33,6 +35,8 @@ stat_contour_fill( bins = NULL, binwidth = NULL, global.breaks = TRUE, + proj = NULL, + clip = NULL, kriging = FALSE, na.fill = FALSE, show.legend = NA, @@ -81,6 +85,13 @@ to the paired geom/stat.} \item{binwidth}{Distance between breaks.} +\item{proj}{The projection to which to project the contours to. +It can be either a projection string or a function to apply to the whole +contour dataset.} + +\item{clip}{A simple features object to be used as a clip. Contours are only +drawn in the interior of this polygon.} + \item{kriging}{Logical indicating whether to perform ordinary kriging before contouring. Use this if you want to use contours with irregularly spaced data.} diff --git a/man/geom_contour_tanaka.Rd b/man/geom_contour_tanaka.Rd index 19f79e77..5c3c1cc2 100644 --- a/man/geom_contour_tanaka.Rd +++ b/man/geom_contour_tanaka.Rd @@ -20,6 +20,9 @@ geom_contour_tanaka( dark = "gray20", range = c(0.01, 0.5), smooth = 0, + proj = NULL, + clip = NULL, + kriging = FALSE, na.rm = FALSE, show.legend = NA, inherit.aes = TRUE @@ -81,6 +84,16 @@ and returns breaks as output \item{smooth}{numeric indicating the degree of smoothing of illumination and size. Larger} +\item{proj}{The projection to which to project the contours to. +It can be either a projection string or a function to apply to the whole +contour dataset.} + +\item{clip}{A simple features object to be used as a clip. Contours are only +drawn in the interior of this polygon.} + +\item{kriging}{Logical indicating whether to perform ordinary kriging before contouring. +Use this if you want to use contours with irregularly spaced data.} + \item{na.rm}{If \code{FALSE}, the default, missing values are removed with a warning. If \code{TRUE}, missing values are silently removed.} diff --git a/tests/testthat/Rplots.pdf b/tests/testthat/Rplots.pdf index 821f25da..18c8c619 100644 Binary files a/tests/testthat/Rplots.pdf and b/tests/testthat/Rplots.pdf differ