rggplot2r-sfggmap

Is ggmap/sf still plotting point in wrong place?


Is this old bug still unresolved, or is there a reasonable workaround yet?

Some cities:

require(ggplot2)
require(ggmap)
require(sf)

cities = sf::read_sf('{
  "type": "FeatureCollection",
  "name": "cities",
  "crs": {
    "type": "name",
    "properties": {
      "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
    }
  },
  "features": [{
      "type": "Feature",
      "properties": {
        "city": "London"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [-0.13, 51.51]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "city": "Rome"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [12.48, 41.89]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "city": "Stockholm"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [18.07, 59.33]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "city": "Istanbul"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [28.96, 41.01]
      }
    }
  ]
}
')

And a ggmap basemap:

bb = c(left = -11, bottom = 35, right = 42, top = 65)
europe = ggmap::get_stamenmap(bbox = bb, maptype = 'toner-lite', zoom = 3)

ggmap(europe) + 
  geom_sf(data = cities, inherit.aes = FALSE, col = 'red', size = 3)

enter image description here


Solution

  • I think the problem is that ggmap uses the co-ordinate object CoordMap to decide where to place pixels of a raster. By switching to coord_sf (which happens when you add a geom_sf layer), you are knocking the raster out of alignment. If you extract the co-ordinates of your points you can simply overlay them as a geom_point layer:

    ggmap(europe) + 
      geom_point(data = as.data.frame(st_coordinates(cities)), aes(X, Y), 
                 inherit.aes = FALSE, col = 'red', size = 3)
    

    enter image description here

    If you want to be able to draw more complex sf objects, then I would stay away from ggmap and use maptiles:

    library(maptiles)
    library(tidyterra)
    
    bb <- c(left = -11, bottom = 35, right = 42, top = 65)
    
    europe <- matrix(bb, 2, byrow = TRUE) |>
      st_multipoint()       |> 
      st_sfc(crs = 4326)    |>
      st_transform(3857) |>
      get_tiles(provider = "Stadia.Stamen.TonerLite", zoom = 4, crop = TRUE)
    
    ggplot() +
      geom_spatraster_rgb(data = europe) +
      geom_sf(data = cities, size = 3, color = "red") +
      coord_sf(crs = 3857, expand = FALSE, ylim = st_bbox(europe)[c(2, 4)]) 
    

    enter image description here