oswaldosantos / ggsn

R package to add north symbols and scale bars to maps created with ggplot or ggmap
http://oswaldosantos.github.io/ggsn/
GNU General Public License v2.0
161 stars 9 forks source link

`north` not showing if data not in `ggplot` but in `geom_sf` only #28

Closed statnmap closed 6 years ago

statnmap commented 6 years ago

First of all, thank you for this package.
When I have multiple layers to plot, I use to write my ggplot layers as is:

ggplot() +
  geom_sf(data = data1) +
  geom_sf(data = data2)

In that case, your scalebar continues to work but not the north arrow.
For instance with your example:

dsn <- system.file('extdata', package = 'ggsn')
# Map in geographic coordinates
map <- st_read(dsn, 'sp', quiet = TRUE)
# Map in projected coordinates
map2 <- st_transform(map, 31983)

ggplot() +
  geom_sf(data = map2, aes(fill = nots)) +
  north(map2, symbol = 16, scale = 0.15) +
  scale_fill_brewer(name = 'Animal abuse\nnotifications', palette = 8) +
  scalebar(map2, dist = 5, dd2km = FALSE)

I do not see where this comes from.

oswaldosantos commented 6 years ago

You can use north2:

gg_map <- ggplot() +
  geom_sf(data = map2, aes(fill = nots)) +
  scale_fill_brewer(name = 'Animal abuse\nnotifications', palette = 8) +
  scalebar(map2, dist = 5, dd2km = FALSE)
north2(gg_map, x = .7, y = .9, symbol = 16, scale = 0.15)
statnmap commented 6 years ago

Ok. Indeed, you wrote it in the doc of north2...

North symbols are included in the plot with the annotation_custom function, which do not works when used together with an empty call to ggplot (see last example). When it is convenient to use an empty call to ggplot, use north2 instead.

Thanks.

statnmap commented 6 years ago

Indeed, I think this is more a problem of ggmap::inset, because if you really use:

  return(annotation_custom(symbol, xmin = x.min, xmax = x.max, ymin = y.min, 
                           ymax = y.max))

Then it works for ggplot() empty call. But not for ggmap.
Maybe can you add a test whether this is a {sf} object or not. I do not know if there is a parameter somewhere to find if the call to ggplot was empty or not.

alt_north <- function (data = NULL, location = "topright", scale = 0.1, symbol = 1, 
                       x.min, x.max, y.min, y.max, anchor = NULL) 
{
  if (is.null(data)) {
    if (is.null(x.min) | is.null(x.max) | is.null(y.min) | 
        is.null(y.max)) {
      stop("If data is not defined, x.min, x.max, y.min and y.max must be.")
    }
    data <- data.frame(long = c(x.min, x.max), lat = c(y.min, 
                                                       y.max))
  }
  if (any(class(data) %in% "sf")) {
    xmin <- sf::st_bbox(data)["xmin"]
    xmax <- sf::st_bbox(data)["xmax"]
    ymin <- sf::st_bbox(data)["ymin"]
    ymax <- sf::st_bbox(data)["ymax"]
    scale.x <- (xmax - xmin) * scale
    scale.y <- (ymax - ymin) * scale
  }
  else {
    xmin <- min(data$long)
    xmax <- max(data$long)
    ymin <- min(data$lat)
    ymax <- max(data$lat)
    scale.x <- (xmax - xmin) * scale
    scale.y <- (ymax - ymin) * scale
  }
  if (location == "bottomleft") {
    if (is.null(anchor)) {
      x.min <- xmin
      y.min <- ymin
    }
    else {
      x.min <- anchor["x"]
      y.min <- anchor["y"]
    }
    x.max <- x.min + scale.x
    y.max <- y.min + scale.y
  }
  if (location == "bottomright") {
    if (is.null(anchor)) {
      x.max <- xmax
      y.min <- ymin
    }
    else {
      x.max <- anchor["x"]
      y.min <- anchor["y"]
    }
    x.min <- x.max - scale.x
    y.max <- y.min + scale.y
  }
  if (location == "topleft") {
    if (is.null(anchor)) {
      x.min <- xmin
      y.max <- ymax
    }
    else {
      x.min <- anchor["x"]
      y.max <- anchor["y"]
    }
    x.max <- x.min + scale.x
    y.min <- y.max - scale.y
  }
  if (location == "topright") {
    if (is.null(anchor)) {
      x.max <- xmax
      y.max <- ymax
    }
    else {
      x.max <- anchor["x"]
      y.max <- anchor["y"]
    }
    x.min <- x.max - scale.x
    y.min <- y.max - scale.y
  }
  symbol <- sprintf("%02.f", symbol)
  symbol <- png::readPNG(paste0(system.file("symbols", package = "ggsn"), 
                                "/", symbol, ".png"))
  symbol <- grid::rasterGrob(symbol, interpolate = TRUE)
  if (any(class(data) %in% "sf")) {
    return(annotation_custom(symbol, xmin = x.min, xmax = x.max, ymin = y.min, 
                           ymax = y.max))
  } else {
    return(ggmap::inset(symbol, xmin = x.min, xmax = x.max, ymin = y.min, 
                        ymax = y.max))
  }
}

And the example:

ggplot() +
  geom_sf(data = map2, aes(fill = nots)) +
  alt_north(map2, symbol = 16, scale = 0.15) +
  scale_fill_brewer(name = 'Animal abuse\nnotifications', palette = 8) +
  scalebar(map2, dist = 5, dd2km = FALSE)
oswaldosantos commented 6 years ago

I was using ggmap::inset to avoid problems of annotation_custom with non Cartesian coordinates. This problems seems to be solved in recent ggplot versions, so I came back to annotation_custom in ggsn v0.4.10.