6

The sf package seems like a much more user-friendly approach to working with spatial data than, say, sp. For example, if I have a set of latitude/longitude coordinates, I can plot easily with the development version of ggplot2:

library(sf)
devtools::install_github("tidyverse/ggplot2")
library(ggplot2)

# generate some data
set.seed(123)

y = rnorm(10, mean=40, sd=20)
x = rnorm(10, mean=-100, sd=30)

# collect to data.frame
xy = data.frame(x=x,y=y)

# create sf object
xy.sf = sf::st_as_sf(xy, coords=c("x", "y"), crs=4269)

# plot points
ggplot(data=xy.sf) + geom_sf()

The ggplot2::geom_sf function knows that the xy.sf object's geometry is a set of points and so I don't need to invoke, e.g., ggplot2::geom_point().

However, suppose I want to add another geom based on the set of points.

For example, if I want to generate a contour layer to show where points are concentrated, I would use ggplot2::geom_density2d or ggplot2::stat_density2d, as suggested in this answer and this answer.

However, the following code

ggplot(data=xy.sf) +
    geom_sf() +
    geom_density2d(data=xy.sf, aes(x=x,y=y,colour=..level..))

produces the following image

test countour map

Note that the countour lines seem to have coordinates reversed!

I tried fiddling with the above code, but can't get it to work. I realize the sf package is fairly new, but the map is so close to being right! Any ideas?

Edit: Forgot to add session info

> sessionInfo()
R version 3.4.0 (2017-04-21)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252 
[2] LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ggplot2_2.2.1.9000 sf_0.4-3          

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.9       magrittr_1.5      maps_3.1.1        units_0.4-4      
 [5] MASS_7.3-47       munsell_0.4.3     geosphere_1.5-5   colorspace_1.3-2 
[9] lattice_0.20-35   rjson_0.2.15      jpeg_0.1-8        rlang_0.1.1      
[13] stringr_1.2.0     udunits2_0.13     plyr_1.8.4        tools_3.4.0      
[17] rgdal_1.2-5       grid_3.4.0        gtable_0.2.0      png_0.1-7        
[21] DBI_0.5-1         ggthemes_3.3.0    lazyeval_0.2.0    assertthat_0.1   
[25] digest_0.6.12     tibble_1.3.1      ggmap_2.6.1       reshape2_1.4.2   
[29] mapproj_1.2-4     labeling_0.3      sp_1.2-4          stringi_1.1.2    
[33] compiler_3.4.0    RgoogleMaps_1.4.1 scales_0.4.1      proto_1.0.0
juan
  • 398
  • 1
  • 14
  • I get the "correct" plot and can't reproduce this behavior on OSX Sierra, R 3.4.0, ggplot2_2.2.1.9000, and sf_0.4-3. (The latest version of `sf` is 0.5-1, but it requires GDAL 2.0 or greater, which I haven't installed yet.) – eipi10 Jul 10 '17 at 20:34
  • I get the correct plot also, with `sf` 0.5.1 – GGamba Jul 10 '17 at 20:44
  • Just upgraded to GDAL 2.2.0 and `sf` 0.5.1. Still getting correct plot. However, I can reproduce your plot if I reverse the coordinates explicitly, i.e., `geom_density2d(aes(x=y, y=x, colour=..level..))`. – eipi10 Jul 10 '17 at 21:12
  • Thanks, @eipi10!! I re-loaded the example and it works now. It seems that originally I created the vectors `x` as `y` and `y` as `x`, which didn't matter for the `data.frame`, but does matter for `geom_density2d`. This solved the problem I was having with my "real" data, in which I kept getting the error `x not found`. That's because `geom_density2d` is not getting `x` from `xy.sf` but from the vector `x` itself! When I delete `x`, I get the same error. – juan Jul 11 '17 at 14:37

1 Answers1

2

For future reference, I thought I'd post my findings as an answer:

It turns out that adding a layer after geom_sf, e.g., geom_density2d, does not inherit the mapping aesthetic applied to geom_sf.

Note that this works as expected:

ggplot(data=xy.sf, aes(x=x, y=y)) +
    geom_sf() +
    geom_density2d(aes(colour=..level..))

but only because x,y exist as objects separate from xy.sf.

Thus, the following throws an error:

rm(x,y)
ggplot(data=xy.sf, aes(x=x, y=y)) +
    geom_sf() +
    geom_density2d(aes(colour=..level..))

Error in FUN(X[[i]], ...) : object 'x' not found

Of course, this can be found in the documentation!

"geom_sf uses a unique aesthetic: geometry ... Unlike other aesthetics, geometry will never be inherited from the plot."

So the workaround I found is to use the x,y objects themselves, or a pure data.frame version of the sf object; e.g., geom_density2d(data=xy, aes(x,y,colour=..level..)).

juan
  • 398
  • 1
  • 14
  • for future reference, make sure `sf` objects have the same CRS (i.e., convert with `st_transform()`), then either `mutate()` or `extract()` the geometry column into Lat-Long or Easting-Northing, which you then feed into `aes()` as x & y. – grad student Jan 16 '23 at 14:55