Skip to content

Fix back-transformation of ranges in coords #2821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 25, 2018
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
* `coord_sf()` now accepts two new parameters, `label_graticule` and `label_axes`,
that can be used to specify which graticules to label on which side of the plot
(@clauswilke, #2846).

* `Coord` objects now have a function `backtransform_range()` that returns the
panel range in data coordinates. This change may affect developers of custom coords,
who now should implement this function. It may also affect developers of custom
geoms that use the `range()` function. In some applications, `backtransform_range()`
may be more appropriate.
(@clauswilke, #2821).

* `geom_sf()` now respects `lineend`, `linejoin`, and `linemitre` parameters
for lines and polygons (@alistaire47, #2826)
Expand Down
32 changes: 26 additions & 6 deletions R/coord-.r
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,21 @@
#' - `render_bg`: Renders background elements.
#' - `render_axis_h`: Renders the horizontal axes.
#' - `render_axis_v`: Renders the vertical axes.
#' - `backtransform_range(panel_params)`: Extracts the panel range provided
#' in `panel_params` (created by `setup_panel_params()`, see below) and
#' back-transforms to data coordinates. This back-transformation can be needed
#' for coords such as `coord_trans()` where the range in the transformed
#' coordinates differs from the range in the untransformed coordinates. Returns
#' a list of two ranges, `x` and `y`, and these correspond to the variables
#' mapped to the `x` and `y` aesthetics, even for coords such as `coord_flip()`
#' where the `x` aesthetic is shown along the y direction and vice versa.
#' - `range(panel_params)`: Extracts the panel range provided
#' in `panel_params` (created by `setup_panel_params()`, see below) and
#' back-transforms to data coordinates. This back-transformation is needed
#' for coords such as `coord_flip()`, `coord_polar()`, `coord_trans()` where
#' the range in the transformed coordinates differs from the range in the
#' untransformed coordinates.
#' returns it. Unlike `backtransform_range()`, this function does not perform
#' any back-transformation and instead returns final transformed coordinates. Returns
#' a list of two ranges, `x` and `y`, and these correspond to the variables
#' mapped to the `x` and `y` aesthetics, even for coords such as `coord_flip()`
#' where the `x` aesthetic is shown along the y direction and vice versa.
#' - `transform`: Transforms x and y coordinates.
#' - `distance`: Calculates distance.
#' - `is_linear`: Returns `TRUE` if the coordinate system is
Expand Down Expand Up @@ -84,11 +93,22 @@ Coord <- ggproto("Coord",
# transform range given in transformed coordinates
# back into range in given in (possibly scale-transformed)
# data coordinates
range = function(panel_params) {
backtransform_range = function(self, panel_params) {
warning(
"range backtransformation not implemented in this coord; plot may be wrong.",
"range backtransformation not implemented in this coord; results may be wrong.",
call. = FALSE
)
# return result from range function for backwards compatibility
# before ggplot2 3.0.1
self$range(panel_params)
},

# return range stored in panel_params
range = function(panel_params) {
warning(
"range calculation not implemented in this coord; results may be wrong.",
call. = FALSE
)
list(x = panel_params$x.range, y = panel_params$y.range)
},

Expand Down
4 changes: 4 additions & 0 deletions R/coord-cartesian-.r
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ CoordCartesian <- ggproto("CoordCartesian", Coord,
list(x = panel_params$x.range, y = panel_params$y.range)
},

backtransform_range = function(self, panel_params) {
self$range(panel_params)
},

transform = function(data, panel_params) {
rescale_x <- function(data) rescale(data, from = panel_params$x.range)
rescale_y <- function(data) rescale(data, from = panel_params$y.range)
Expand Down
6 changes: 6 additions & 0 deletions R/coord-flip.r
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ CoordFlip <- ggproto("CoordFlip", CoordCartesian,
CoordCartesian$transform(data, panel_params)
},

backtransform_range = function(self, panel_params) {
self$range(panel_params)
},

range = function(panel_params) {
# summarise_layout() expects the original x and y ranges here,
# not the ones we would get after flipping the axes
list(x = panel_params$y.range, y = panel_params$x.range)
},

Expand Down
10 changes: 9 additions & 1 deletion R/coord-map.r
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,19 @@ CoordMap <- ggproto("CoordMap", Coord,
out
},

range = function(panel_params) {
backtransform_range = function(panel_params) {
# range is stored in data coordinates and doesn't have to be back-transformed
list(x = panel_params$x.range, y = panel_params$y.range)
},

range = function(panel_params) {
# Range in projected coordinates:
# list(x = panel_params$x.proj, y = panel_params$y.proj)
# However, coord_map() does never really work with transformed coordinates,
# so return unprojected data coordinates here
list(x = panel_params$x.range, y = panel_params$y.range)
},

distance = function(x, y, panel_params) {
max_dist <- dist_central_angle(panel_params$x.range, panel_params$y.range)
dist_central_angle(x, y) / max_dist
Expand Down
2 changes: 1 addition & 1 deletion R/coord-munch.r
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ coord_munch <- function(coord, data, range, segment_length = 0.01) {
if (coord$is_linear()) return(coord$transform(data, range))

# range has theta and r values; get corresponding x and y values
ranges <- coord$range(range)
ranges <- coord$backtransform_range(range)

# Convert any infinite locations into max/min
# Only need to work with x and y because for munching, those are the
Expand Down
6 changes: 6 additions & 0 deletions R/coord-polar.r
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ CoordPolar <- ggproto("CoordPolar", Coord,
dist_polar(r, theta)
},

backtransform_range = function(self, panel_params) {
self$range(panel_params)
},

range = function(self, panel_params) {
# summarise_layout() expects that the x and y ranges here
# match the setting from self$theta and self$r
setNames(
list(panel_params$theta.range, panel_params$r.range),
c(self$theta, self$r)
Expand Down
9 changes: 8 additions & 1 deletion R/coord-transform.r
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,20 @@ CoordTrans <- ggproto("CoordTrans", Coord,
dist_euclidean(self$trans$x$transform(x), self$trans$y$transform(y)) / max_dist
},

range = function(self, panel_params) {
backtransform_range = function(self, panel_params) {
list(
x = self$trans$x$inverse(panel_params$x.range),
y = self$trans$y$inverse(panel_params$y.range)
)
},

range = function(self, panel_params) {
list(
x = panel_params$x.range,
y = panel_params$y.range
)
},

transform = function(self, data, panel_params) {
trans_x <- function(data) transform_value(self$trans$x, data, panel_params$x.range)
trans_y <- function(data) transform_value(self$trans$y, data, panel_params$y.range)
Expand Down
2 changes: 1 addition & 1 deletion R/geom-abline.r
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ geom_abline <- function(mapping = NULL, data = NULL,
#' @export
GeomAbline <- ggproto("GeomAbline", Geom,
draw_panel = function(data, panel_params, coord) {
ranges <- coord$range(panel_params)
ranges <- coord$backtransform_range(panel_params)

data$x <- ranges$x[1]
data$xend <- ranges$x[2]
Expand Down
2 changes: 1 addition & 1 deletion R/geom-hline.r
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ geom_hline <- function(mapping = NULL, data = NULL,
#' @export
GeomHline <- ggproto("GeomHline", Geom,
draw_panel = function(data, panel_params, coord) {
ranges <- coord$range(panel_params)
ranges <- coord$backtransform_range(panel_params)

data$x <- ranges$x[1]
data$xend <- ranges$x[2]
Expand Down
2 changes: 1 addition & 1 deletion R/geom-vline.r
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ geom_vline <- function(mapping = NULL, data = NULL,
#' @export
GeomVline <- ggproto("GeomVline", Geom,
draw_panel = function(data, panel_params, coord) {
ranges <- coord$range(panel_params)
ranges <- coord$backtransform_range(panel_params)

data$x <- data$xintercept
data$xend <- data$xintercept
Expand Down
10 changes: 10 additions & 0 deletions R/sf.R
Original file line number Diff line number Diff line change
Expand Up @@ -543,10 +543,20 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
)
},

backtransform_range = function(panel_params) {
# this does not actually return backtransformed ranges in the general case, needs fixing
warning(
"range backtransformation not implemented in this coord; results may be wrong.",
call. = FALSE
)
list(x = panel_params$x_range, y = panel_params$y_range)
},

range = function(panel_params) {
list(x = panel_params$x_range, y = panel_params$y_range)
},


# CoordSf enforces a fixed aspect ratio -> axes cannot be changed freely under faceting
is_free = function() FALSE,

Expand Down
39 changes: 39 additions & 0 deletions R/summarise-plot.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,45 @@
#'
#' These functions provide summarised information about built ggplot objects.
#'
#' There are three types of summary that can be obtained: A summary of the plot layout,
#' a summary of the plot coord, and a summary of plot layers.
#'
#' @section Layout summary:
#'
#' The function `summarise_layout()` returns a table that provides information about
#' the plot panel(s) in the built plot. The table has the following columns:
#'
#' \describe{
#' \item{`panel`}{A factor indicating the individual plot panels.}
#' \item{`row`}{Row number in the grid of panels.}
#' \item{`col`}{Column number in the grid of panels.}
#' \item{`vars`}{A list of lists. For each panel, the respective list
#' provides the variables and their values that specify the panel.}
#' \item{`xmin`, `xmax`}{The minimum and maximum values of the variable mapped to
#' the x aesthetic, in transformed coordinates.}
#' \item{`ymin`, `ymax`}{The minimum and maximum values of the variable mapped to
#' the y aesthetic, in transformed coordinates.}
#' \item{`xscale`}{The scale object applied to the x aesthetic.}
#' \item{`yscale`}{The scale object applied to the y aesthetic.}
#' }
#'
#' Importantly, the values for `xmin`, `xmax`, `ymin`, `ymax`, `xscale`, and `yscale`
#' are determined by the variables that are mapped to `x` and `y` in the `aes()` call.
#' So even if a coord changes how x and y are shown in the final plot (as is the case
#' for `coord_flip()` or `coord_polar()`), these changes have no effect on the results
#' returned by `summarise_plot()`.
#'
#' @section Coord summary:
#'
#' The function `summarise_coord()` returns information about the log base for
#' coordinates that are log-transformed in `coord_trans()`, and it also indicates
#' whether the coord has flipped the x and y axes.
#'
#' @section Layer summary:
#'
#' The function `summarise_layers()` returns a table with a single column, `mapping`, which
#' contains information about aesthetic mapping for each layer.
#'
#' @param p A ggplot_built object.
#'
#' @examples
Expand Down
17 changes: 13 additions & 4 deletions man/ggplot2-ggproto.Rd

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

2 changes: 1 addition & 1 deletion man/ggsf.Rd

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

46 changes: 46 additions & 0 deletions man/summarise_plot.Rd

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