Skip to content

coord_sf() scale function breaks #5442

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
May 20, 2024

Conversation

teunbrand
Copy link
Collaborator

This PR aims to fix #4622.

Briefly, it adds three conventions of scales that work in other coordinate systems. It allows:

  • breaks = function() to take long/lat limits and compute breaks.
  • breaks = NULL to remove axes and gridlines.
  • n.breaks = x to set a desired number of breaks.

Demonstrations:

devtools::load_all("~/packages/ggplot2")
#> ℹ Loading ggplot2

nc <- sf::st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
p <- ggplot(nc) +
  geom_sf()

p + scale_x_continuous(breaks = breaks_width(1))

p + scale_x_continuous(breaks = NULL)

p + scale_x_continuous(n.breaks = 9)

I have to note that it doesn't fix the case where out-of-bounds breaks get censored. This remains an error:

p + scale_x_continuous(breaks = -c(75, 78, 80, 82, 85),
                       labels = LETTERS[1:5])
#> Error in `fixup_graticule_labels()` at ggplot2/R/ggproto.R:180:16:
#> ! Breaks and labels along x direction are different lengths

Created on 2023-09-27 with reprex v2.0.2

Comment on lines +608 to +623
if (!is.null(crs)) {
# Atomic breaks input are assumed to be in long/lat coordinates.
# To preserve that assumption for function breaks, the bounding box
# needs to be translated to long/lat coordinates.
if (!is_named(bbox)) {
names(bbox) <- c("xmin", "ymin", "xmax", "ymax")
}
# Convert bounding box to long/lat coordinates
bbox <- sf::st_as_sfc(sf::st_bbox(bbox, crs = crs))
bbox <- sf::st_bbox(sf::st_transform(bbox, 4326))
bbox <- as.numeric(bbox)

# If any bbox is NA the transformation has probably failed.
# (.e.g from IGH to long/lat). In this case, just provide full long/lat.
bbox[is.na(bbox)] <- c(-180, -90, 180, 90)[is.na(bbox)]
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd really like some more sf-experienced eyes on this bit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clauswilke you are perhaps the one best fitting the "sf-experienced" moniker :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't looked carefully yet and I'm not entirely sure I even understand what this code is meant to do, but one thing I notice right away is hard-coded CRS values (line 617) and limits (line 622). Should this respect the choice of default_crs in coord_sf()? Also, I'm worried about just transforming a bounding box. This can give all sorts of unexpected results for weird coordinate systems. That's why I added different limits methods implemented in calc_limits_bbox(). Should that be used here, and the choice of the limit method respected (see argument lims_method in coord_sf())? Finally, I added the function sf_transform_xy() to transform coordinates will imposing various checks. Is that useful here? (The answer to several of my questions may well be "no", just wanted to be sure I raised them so we've thought it all through carefully.)

Copy link
Collaborator Author

@teunbrand teunbrand Oct 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Claus, these are definitely helpful comments to think about. I guess the main thing I don't know if the transformation/reprojection of line segments describing a bounding box in {sf} just applies to the start/end coordinates or is interpolated somehow.
I'll try to answer some of the points, but for some I have to experiment a little bit more.

what this code is meant to do

It is supposed to provide a limits argument to ScaleContinuous$get_breaks(). The catch here is that the default calculated limits can be in a different metric (e.g. metres), whereas st_graticule() only takes in lon/lat break positions.

Should this respect the choice of default_crs in coord_sf()?

No, not currently. Because we're using the default st_graticule(..., datum = st_crs(4326)), only lon/lat degrees are sensible break positions.

Also, I'm worried about just transforming a bounding box.

Me too. I don't fully understand the nuances involved, which is why I appreciate the second set of eyes here.

Should that (calc_limits_bbox(), red.) be used here

That is a good thing to try out, but I have to find some examples where the results might diverge.

I added the function sf_transform_xy() to transform coordinates will imposing various checks. Is that useful here?

This is also a good thing to try out, but I might not have to use it if I use calc_limits_bbox().

@thomasp85
Copy link
Member

can this be included in the next release or are there unresolved questions?

@teunbrand
Copy link
Collaborator Author

I think this a step in the right direction and shouldn't make anything worse, so I'd like to include this. However, these might be some typical quirks with regards to projection, like if you want to place ticks at longitude -180/180 it might be that the scale limits are calculated as -179.99/179.99 and the ticks aren't drawn.

Copy link
Member

@thomasp85 thomasp85 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. As said, it is a step in the right direction even though there may be issues to iron out still

@teunbrand teunbrand merged commit b74570c into tidyverse:main May 20, 2024
11 checks passed
@teunbrand teunbrand deleted the sf_scale_function_breaks branch May 20, 2024 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

geom_sf(): add support for scale_x_continuous(n.breaks = )
3 participants