Skip to content

Implement default crs for non-sf objects in coord_sf(). #3659

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 31 commits into from
Jun 24, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
38d4e10
Implement default crs for non-sf objects in coord_sf().
clauswilke Dec 6, 2019
64794d1
make limits work
clauswilke Dec 6, 2019
0a885b8
cleanup code, write documentation
clauswilke Dec 7, 2019
829b081
more accurately specify CRS
clauswilke Dec 7, 2019
b482d7b
handle missing or infinite values in sf_transform_xy().
clauswilke Dec 7, 2019
fe31077
fix package build
clauswilke Dec 7, 2019
325a458
properly reset bbox at beginning of plot generation
clauswilke Dec 8, 2019
6110b02
cleanup
clauswilke Dec 8, 2019
c583ced
check that the coord is of type CoordSf before
clauswilke Dec 9, 2019
aece1e4
scale limit improvements
clauswilke Dec 15, 2019
63a52d7
Register bounding box even for stat_sf_coordinates. Gives better defa…
clauswilke Dec 15, 2019
94c495d
Merge branch 'master' into coord-sf
clauswilke Jan 12, 2020
7ea08ae
Merge branch 'master' into coord-sf
clauswilke Feb 1, 2020
6831ab2
finalize handling of limits, improve documentation
clauswilke Feb 1, 2020
b590d64
unit tests for new coord_sf() features
clauswilke Feb 2, 2020
edcae20
alternative limit methods
clauswilke Feb 3, 2020
0942ae4
ensure point data is always numeric
clauswilke Feb 3, 2020
c5dd56a
expand documentation
clauswilke Feb 6, 2020
1e485e9
merge in master
clauswilke Feb 6, 2020
371af57
delete space
clauswilke Feb 6, 2020
9843333
capitalize crs
clauswilke Feb 6, 2020
11fa427
check against incorrect mapping
clauswilke Feb 11, 2020
385e4b9
update docs
clauswilke Feb 11, 2020
45b6b38
more limits methods
clauswilke Feb 11, 2020
4743813
better limits methods
clauswilke Feb 11, 2020
da26d4d
simplify error message
clauswilke Feb 12, 2020
5d39f18
Merge branch 'master' into coord-sf
clauswilke Feb 12, 2020
e4cdcd6
Merge branch 'master' into coord-sf
clauswilke Mar 9, 2020
9e97812
Merge branch 'master' into coord-sf
clauswilke Jun 23, 2020
ee0fb99
fix error message if scale limits inversion problem
clauswilke Jun 24, 2020
942d74c
reword warnings and error messages.
clauswilke Jun 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 54 additions & 11 deletions R/coord-sf.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#' @format NULL
CoordSf <- ggproto("CoordSf", CoordCartesian,

# default crs to be used
default_crs = 4326, # default is WGS 84

# Find the first CRS if not already supplied
setup_params = function(self, data) {
if (!is.null(self$crs)) {
Expand Down Expand Up @@ -41,15 +44,20 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
},

transform = function(self, data, panel_params) {
# we need to transform all non-sf data into the correct coordinate system
source_crs <- self$default_crs
target_crs <- panel_params$crs

# normalize geometry data, it should already be in the correct crs here
data[[ geom_column(data) ]] <- sf_rescale01(
data[[ geom_column(data) ]],
panel_params$x_range,
panel_params$y_range
)

# Assume x and y supplied directly already in common CRS
# transform and normalize regular position data
data <- transform_position(
data,
sf_transform_xy(data, target_crs, source_crs),
function(x) sf_rescale01_x(x, panel_params$x_range),
function(x) sf_rescale01_x(x, panel_params$y_range)
)
Expand Down Expand Up @@ -165,23 +173,33 @@ 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)
backtransform_range = function(self, panel_params) {
target_crs <- self$default_crs
source_crs <- panel_params$crs

x <- panel_params$x_range
y <- panel_params$y_range
data <- list(x = c(x, x), y = c(y, rev(y)))
data <- sf_transform_xy(data, target_crs, source_crs)
list(x = range(data$x), y = range(data$y))
},

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,

# for regular geoms (such as geom_path, geom_polygon, etc.), CoordSf is non-linear
is_linear = function() FALSE,

distance = function(self, x, y, panel_params) {
d <- self$backtransform_range(panel_params)
max_dist <- dist_euclidean(d$x, d$y)
dist_euclidean(x, y) / max_dist
},

aspect = function(self, panel_params) {
if (isTRUE(sf::st_is_longlat(panel_params$crs))) {
# Contributed by @edzer
Expand Down Expand Up @@ -375,20 +393,44 @@ CoordSf <- ggproto("CoordSf", CoordCartesian,
}
)

## helper functions to transform and normalize geometry and position data
# transform position data (columns x and y in a data frame)
sf_transform_xy <- function(data, target_crs, source_crs) {
if (identical(target_crs, source_crs) ||
is.null(target_crs) || is.null(source_crs) || is.null(data) ||
!all(c("x", "y") %in% names(data))) {
return(data)
}

sf_data <- sf::st_sfc(
sf::st_multipoint(cbind(data$x, data$y)),
crs = source_crs
)
sf_data_trans <- sf::st_transform(sf_data, target_crs)[[1]]
data$x <- sf_data_trans[, 1]
data$y <- sf_data_trans[, 2]
data
}

# normalize geometry data (variable x is geometry column)
sf_rescale01 <- function(x, x_range, y_range) {
if (is.null(x)) {
return(x)
}

sf::st_normalize(x, c(x_range[1], y_range[1], x_range[2], y_range[2]))
}

# normalize position data (variable x is x or y position)
sf_rescale01_x <- function(x, range) {
(x - range[1]) / diff(range)
}



#' @param crs Use this to select a specific coordinate reference system (CRS).
#' If not specified, will use the CRS defined in the first layer.
#' @param default_crs TODO: document
#' @param datum CRS that provides datum to use when generating graticules
#' @param label_axes Character vector or named list of character values
#' specifying which graticule lines (meridians or parallels) should be labeled on
Expand Down Expand Up @@ -417,7 +459,7 @@ sf_rescale01_x <- function(x, range) {
#' @export
#' @rdname ggsf
coord_sf <- function(xlim = NULL, ylim = NULL, expand = TRUE,
crs = NULL, datum = sf::st_crs(4326),
crs = NULL, default_crs = 4326, datum = sf::st_crs(4326),
label_graticule = waiver(),
label_axes = waiver(),
ndiscr = 100, default = FALSE, clip = "on") {
Expand Down Expand Up @@ -457,6 +499,7 @@ coord_sf <- function(xlim = NULL, ylim = NULL, expand = TRUE,
limits = list(x = xlim, y = ylim),
datum = datum,
crs = crs,
default_crs = default_crs,
label_axes = label_axes,
label_graticule = label_graticule,
ndiscr = ndiscr,
Expand Down
17 changes: 16 additions & 1 deletion R/stat-sf-coordinates.R
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,27 @@ stat_sf_coordinates <- function(mapping = aes(), data = NULL, geom = "point",
#' @export
StatSfCoordinates <- ggproto(
"StatSfCoordinates", Stat,
compute_group = function(data, scales, fun.geometry = NULL) {

default_crs = NULL, # if set to null, take default from coord

compute_layer = function(self, data, params, layout) {
# extract default crs if not set manually
if (is.null(self$default_crs)) {
self$default_crs <- layout$coord$default_crs
}
ggproto_parent(Stat, self)$compute_layer(data, params, layout)
},

compute_group = function(self, data, scales, fun.geometry = NULL) {
if (is.null(fun.geometry)) {
fun.geometry <- function(x) sf::st_point_on_surface(sf::st_zm(x))
}

points_sfc <- fun.geometry(data$geometry)
# transform to the coords default crs if possible
if (!(is.null(self$default_crs) || is.na(sf::st_crs(points_sfc)))) {
points_sfc <- sf::st_transform(points_sfc, self$default_crs)
}
coordinates <- sf::st_coordinates(points_sfc)
data$x <- coordinates[, "X"]
data$y <- coordinates[, "Y"]
Expand Down