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

scalebar() fails if 'data' contains columns other than latitude and longitude #47

Closed DesiQuintans closed 5 years ago

DesiQuintans commented 5 years ago

In ggsn v0.5.0, the presence of extra columns will make scalebar() fail when it is time to pipe it together with a ggplot object:

library(ggmap)
library(ggsn)

coords <- 
    data.frame(stringsAsFactors = FALSE,
               long  = c(150.750034, 150.6857303, 150.533333, 150.416667, 150.279,
                         150.3730633, 150.5625494, 150.65),
               lat   = c(-33.613769, -33.5801886, -33.5, -33.55, -33.514, 
                         -33.7094058, -33.7002999, -33.666667),
               label = c("WSU", "Grose Vale", "Bilpin", "Mt Tomah", "Bell",
                         "Katoomba", "Springwood", "Hawkesbury Heights"))

head(coords)

#>       long       lat      label
#> 1 150.7500 -33.61377        WSU
#> 2 150.6857 -33.58019 Grose Vale
#> 3 150.5333 -33.50000     Bilpin
#> 4 150.4167 -33.55000   Mt Tomah
#> 5 150.2790 -33.51400       Bell
#> 6 150.3731 -33.70941   Katoomba

my_map <- qmplot(long, lat, data = coords, geom = "blank", 
                 maptype = "terrain-background", zoom = 11)

my_map + 
    scalebar(data = coords, dist = 5, dist_unit = "km", model = "WGS84", 
             transform = TRUE)
#> `data` must be uniquely named but has duplicate columns

And a blank map (white rectangle) is generated. But when the extra column(s) are removed:

my_map + 
    scalebar(data = coords[c("long", "lat")], dist = 5, dist_unit = "km", 
             model = "WGS84", transform = TRUE)

oswaldosantos commented 5 years ago

With ggmap, it is convenient not to use the argument data. You can avoid the problem with:

my_map + 
    scalebar(x.min = min(coords$long),
             x.max = max(coords$long),
             y.min = min(coords$lat),
             y.max = max(coords$lat),
             dist = 5, dist_unit = "km", model = "WGS84", 
             transform = TRUE)

You proposed solution (commit 0e97ec2) works for this specific code but creates bugs with others. Try this with the scalebar of your commit:

data(domestic_violence)
mp <- as_Spatial(domestic_violence)
mp@data$id <- 1:nrow(mp@data)
mp_df <- merge(tidy(mp), mp, by = "id")
ggplot(mp_df, aes(long, lat, group = group, fill = Scaled)) +
    geom_polygon() +
    geom_path(color = "black", size = .05) +
    coord_equal() +
    scalebar(mp_df, dist = 4, dist_unit = "km",
             transform = TRUE, model = "WGS84") +
    blank() +
    scale_fill_continuous(low = "#fff7ec", high = "#7F0000")
DesiQuintans commented 5 years ago

Ah I see. I'm guessing you've tried to automate this process before when it comes to a ggmap pipeline?

oswaldosantos commented 5 years ago

Yes, that's the solution I found. Do you have a better one in mind?

DesiQuintans commented 5 years ago

Unfortunately not. It should be possible to inherit data from the ggplot object by writing scalebar() as a new kind of geom and inheriting it from aes() (vignette), turning scalebar() into a generic with a default method for df/ggplot and a method for sf. Perhaps that's a nice place for someone else to jump in, since I haven't extended ggplot2 before.