walkerke / mapboxapi

R interface to Mapbox web services
https://walker-data.com/mapboxapi/
Other
110 stars 8 forks source link

Feature suggestion: `layer_static_mapbox()` function wrapping `ggspatial::layer_spatial()` #17

Closed elipousson closed 2 years ago

elipousson commented 2 years ago

Thanks for a great package!

I recently ran into difficulty trying to use the get_static_tiles() function because I wasn't sure how to set an appropriate zoom level when returning tiles for areas of varied sizes. I had previously used the snapbox package and appreciated how the scale_ratio parameter from the layer_snapbox() function really simplified the process of getting a consistent look.

I was trying to figure out how the two packages handled this differently and realized it would be relatively straightforward to adapt a few key functions from snapbox and create an equivalent layer_static_mapbox() function for mapboxapi. You can see all the changes in this commit but I figured an explanation would be helpful before asking if you were interested in a pull request. Here are the changes:

if (all(is.null(c(width, height)))) {
  asp <- as.numeric(abs(bbox$xmax - bbox$xmin) / abs(bbox$ymax - bbox$ymin))
  max_size <- min(1280, round(1280 * scale))
  width <- min(max_size, round(max_size * asp))
  height <- min(max_size, round(max_size / asp))
}

If this feature suggestion is welcome, it may be appropriate to credit Anthony North and Miles McBain since portions of the code are adapted from their generously MIT-licensed package. Let me know what you think!

walkerke commented 2 years ago

Thanks @elipousson - this is a really interesting adaptation. I'll review when I get the chance! I ended up adopting the raster tiles API for basemaps rather than the static images API because it made a bit more sense to me and snapbox already accomplished it. That said, however - snapbox has not been updated in two years and it isn't on CRAN, so this may make sense as a feature.

I'll review and ping you here if I'd like a PR, which I anticipate I will!

elipousson commented 2 years ago

Some trial and error this morning makes me think that layer_static_mapbox() is not assigning the CRS the way I had expected. I should have more time later this week or over the weekend to try to figure it out and fix it (unless the issue is obvious when you have a chance to review). I also added support for bbox class objects to location_to_bbox() as well as bbox character strings.

elipousson commented 2 years ago

I think the issue was just that I didn't set the CRS for the location bbox to the appropriate coordinate reference system. I think it works!

elipousson commented 2 years ago

I just discovered what I believe is an outstanding issue with the CRS after attempting to use layer_static_mapbox in combination with some OSM building footprint data. The API behind osmdata is error-prone so I wasn't able to create a reprex but I compared the results from mapboxapi and snapbox and you can see the difference below. I should have time over the weekend or next week to chase down this bug.

Here is the version from mapboxapi::layer_static_mapbox:

Screen Shot 2022-04-07 at 9 22 15 AM

And here is the version from snapbox::layer_mapbox:

Screen Shot 2022-04-07 at 10 24 42 AM
walkerke commented 2 years ago

Sounds good, thanks! Yeah I was going to ask you about that - the basemaps returned by layer_static_mapbox() are a bit narrow. For example:

library(tigris)
library(mapboxapi)
library(ggspatial)
library(ggplot2)

baltimore <- tracts("MD", "Baltimore city", cb = TRUE, year = 2020)

# Map with layer_static_mapbox()
ggplot() + 
  layer_static_mapbox(
    location = baltimore,
    buffer_dist = 1000,
    style_id = "streets-v11",
    username = "mapbox"
  ) + 
  geom_sf(
    data = baltimore,
    alpha = 0.7
  ) + 
  theme_void()

image

Vs. get_static_tiles():

# Map with get_static_tiles()
baltimore_tiles <- get_static_tiles(
  location = baltimore, 
  zoom = 11,
  buffer_dist = 1000, 
  style_id = "streets-v11",
  username = "mapbox"
)

ggplot() + 
  layer_spatial(data = baltimore_tiles) + 
  geom_sf(
    data = baltimore,
    alpha = 0.7
  ) + 
  theme_void()

image

elipousson commented 2 years ago

Thankfully, this turned out to be an easier fix than I had expected. I had set it up to use the lon/lat bounding box to set the width/height but the extent was based on a Mercator projected bounding box. I reworked it so the width/height is set based on the aspect ratio of the projected bounding box and it seems like it is working as expected.

Hope it is OK but I also implemented a style_url parameter to allow easier copy/pasting from Mapbox Studio into R without needing to separate the username and style_id. If you don't want it in the pull request, just let me know and I'll reverse those changes.

walkerke commented 2 years ago

@elipousson looks much better now! image