walkerke / mapgl

R interface to Mapbox GL JS v3 and Maplibre GL JS
https://walker-data.com/mapgl
Other
91 stars 5 forks source link

Mapbox_proxy() doesn´t load polygons from add_vector_source() #36

Closed enriquevaa closed 1 month ago

enriquevaa commented 2 months ago

I have detected a bug: when I call a layer stored in my Mapbox studio from an add_vector_source() with mapbox_proxy() it doesn´t load in the map, but If I call it from renderMapboxgl() works fine.

Here are two reproducible examples. The first one does work, however, the second one hasn´t since the update of mapgl.

ageb_cdmx = st_read("data/ageb_cdmx.shp")
layers<-c("None","AGEBS")

ui <- page_sidebar(
  title = "Layers Bug",
  sidebar = sidebar(
    width = 350,
    selectInput("layer", "Select layer",layers,selected = "None")
  ),
  card(
    full_screen = TRUE,
    card_header("Map"),
    mapboxglOutput("map")
  ),
)

##This one works
server <- function(input, output, session) {

  output$map <- renderMapboxgl({
    mapboxgl(style = mapbox_style("dark"),access_token = token) %>% fit_bounds(t3b_cdmx)  
  })

  observeEvent(input$layer, {
    if (input$layer== "AGEBS") {
      mapboxgl_proxy("map") %>%add_fill_layer(
            id = "AGEB",
            source =ageb_cdmx,
            fill_color = interpolate(
              column = "Densidad",
              type = "linear",
              values = c(0,1, 15000, 30000),
              stops = c("lightgrey","#edf8b1", "#7fcdbb", "#2c7fb8"),
              na_color = "lightgrey",

            ),
            fill_opacity = 0.7,
            tooltip = "tooltip") %>% 
        add_layers_control(collapsible = TRUE) %>% 
        add_continuous_legend(
          "Densidad por AGEB CDMX (2020)",
          values = c("3k", "30k", "50k"),
          colors = c("#edf8b1", "#7fcdbb", "#2c7fb8"),
          position = "bottom-right"
        )
    } 

    if (input$layer== "None") {
      mapboxgl_proxy("map") %>% clear_layer("AGEB") %>% clear_controls() %>% clear_legend()
    } 

  })  

}

shinyApp(ui, server)

##This one doesn´t show any polygons when I call them with add_vector_source()

server <- function(input, output, session) {

  output$map <- renderMapboxgl({
    mapboxgl(style = mapbox_style("dark"),access_token = token) %>% fit_bounds(t3b_cdmx)  
  })

  observeEvent(input$layer, {
    if (input$layer== "AGEBS") {
      mapboxgl_proxy("map") %>%add_vector_source(id="ageb_cdmx",url="mapbox://enriquevaa.tile") %>% add_fill_layer(
        id = "AGEB",
        source ="ageb_cdmx",
        source_layer = "AGEB_Mapbox_CDMX-5l1f2s",
        fill_color = interpolate(
          column = "Densidad",
          type = "linear",
          values = c(0,1, 15000, 30000),
          stops = c("lightgrey","#edf8b1", "#7fcdbb", "#2c7fb8"),
          na_color = "lightgrey",

        ),
        fill_opacity = 0.7,
        tooltip = "tooltip") %>% 
        add_layers_control(collapsible = TRUE) %>% 
        add_continuous_legend(
          "Densidad por AGEB CDMX (2020)",
          values = c("3k", "30k", "50k"),
          colors = c("#edf8b1", "#7fcdbb", "#2c7fb8"),
          position = "bottom-right"
        )
    } 

    if (input$layer== "None") {
      mapboxgl_proxy("map") %>% clear_layer("AGEB") %>% clear_controls() %>% clear_legend()
    } 

  })  

}

shinyApp(ui, server)
walkerke commented 2 months ago

Is your tileset shared publicly? I can't seem to pull it in.

enriquevaa commented 2 months ago

I made it public already: url="mapbox://enriquevaa.5t18pfv7" source_layer="AGEB_Mapbox_CDMX-5l1f2s"

walkerke commented 2 months ago

Yeah, there's something wrong here with how sources are handled in a proxy session. I think I know how to fix, I'll post here.

walkerke commented 2 months ago

I think I have an approach that works locally, but I can't get your layer to display outside a proxy session.

When I try this:

mapboxgl() %>%
  add_vector_source(id="ageb_cdmx",url="mapbox://enriquevaa.5t18pfv7") %>%  
  add_fill_layer(
    id = "AGEB",
    source ="ageb_cdmx",
    source_layer = "AGEB_Mapbox_CDMX-567l1f2s",
    fill_color = interpolate(
      column = "Densidad",
      type = "linear",
      values = c(0,1, 15000, 30000),
      stops = c("lightgrey","#edf8b1", "#7fcdbb", "#2c7fb8"),
      na_color = "lightgrey",

    ),
    fill_opacity = 0.7,
    tooltip = "tooltip")

I see: Error: Source layer "AGEB_Mapbox_CDMX-567l1f2s" does not exist on source "ageb_cdmx" as specified by style layer "AGEB"

Do you see the same?

walkerke commented 2 months ago

Here's an approach I would recommend for now:

Add the source on map load outside the proxy session, then selectively display / clear the layer within the proxy session.

This will require re-installing from GitHub as I was passing through source-layer incorrectly to JavaScript within proxy sessions.

library(shiny)
library(mapgl)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      actionButton("add_layer", "Add contours"),
      actionButton("clear_layer", "Remove contours")
    ),
    mainPanel(
      mapboxgl::mapboxglOutput("map")
    )
  )
)

server <- function(input, output, session) {
  output$map <- mapboxgl::renderMapboxgl({
    mapboxgl(style = mapbox_style("light"), zoom = 13, center = c(-122.447303, 37.753574)) |> 
      add_vector_source(
        id = "mapbox-contours",
        url = "mapbox://mapbox.mapbox-terrain-v2"
      )
  })

  observeEvent(input$add_layer, {
    mapboxgl_proxy("map") |>
      add_line_layer(
        id = "terrain-data",
        source = "mapbox-contours",
        source_layer = "contour", 
        line_color = '#ff69b4',
        line_width = 1
      )
  })

  observeEvent(input$clear_layer, {
    mapboxgl_proxy("map") |> 
      clear_layer("terrain-data")
  })
}

shinyApp(ui, server)

I'm not closing this as I can't yet get a source added within a proxy session to communicate with add_layer() correctly unless it is added using the source argument directly.

enriquevaa commented 2 months ago

I think I have an approach that works locally, but I can't get your layer to display outside a proxy session.

When I try this:

mapboxgl() %>%
  add_vector_source(id="ageb_cdmx",url="mapbox://enriquevaa.5t18pfv7") %>%  
  add_fill_layer(
    id = "AGEB",
    source ="ageb_cdmx",
    source_layer = "AGEB_Mapbox_CDMX-567l1f2s",
    fill_color = interpolate(
      column = "Densidad",
      type = "linear",
      values = c(0,1, 15000, 30000),
      stops = c("lightgrey","#edf8b1", "#7fcdbb", "#2c7fb8"),
      na_color = "lightgrey",

    ),
    fill_opacity = 0.7,
    tooltip = "tooltip")

I see: Error: Source layer "AGEB_Mapbox_CDMX-567l1f2s" does not exist on source "ageb_cdmx" as specified by style layer "AGEB"

Do you see the same?

I think is the name of the layer. This one is the correct: "AGEB_Mapbox_CDMX-5l1f2s"

walkerke commented 2 months ago

Try this with the latest GitHub version of mapgl:

library(bslib)
library(mapgl)
library(magrittr)
library(shiny)

layers<-c("None","AGEBS")

ui <- page_sidebar(
  title = "Layers Bug",
  sidebar = sidebar(
    width = 350,
    selectInput("layer", "Select layer",layers,selected = "None")
  ),
  card(
    full_screen = TRUE,
    card_header("Map"),
    mapboxglOutput("map")
  ),
)

server <- function(input, output, session) {

  output$map <- renderMapboxgl({
    mapboxgl(style = mapbox_style("dark"), 
             center = mapboxapi::mb_geocode("Mexico City, Mexico"),
             zoom = 9) %>%
      add_vector_source(id="ageb_cdmx",url="mapbox://enriquevaa.5t18pfv7")
  })

  observeEvent(input$layer, {
    if (input$layer== "AGEBS") {
      mapboxgl_proxy("map") %>% 
        add_fill_layer(
        id = "AGEB",
        source ="ageb_cdmx",
        source_layer = "AGEB_Mapbox_CDMX-5l1f2s",
        fill_color = interpolate(
          column = "Densidad",
          type = "linear",
          values = c(0,1, 15000, 30000),
          stops = c("lightgrey","#edf8b1", "#7fcdbb", "#2c7fb8"),
          na_color = "lightgrey",

        ),
        fill_opacity = 0.7,
        tooltip = "tooltip") %>% 
        add_layers_control(collapsible = TRUE) %>% 
        add_continuous_legend(
          "Densidad por AGEB CDMX (2020)",
          values = c("3k", "30k", "50k"),
          colors = c("#edf8b1", "#7fcdbb", "#2c7fb8"),
          position = "bottom-right"
        )
    } 

    if (input$layer== "None") {
      mapboxgl_proxy("map") %>% clear_layer("AGEB") %>% clear_controls() %>% clear_legend()
    } 

  })  

}

shinyApp(ui, server)
enriquevaa commented 2 months ago

This works great! Thank you

enriquevaa commented 2 months ago

@walkerke I have detected another bug related to add_vector_source(). When you use add_fill_layer, hover_options doesn´t work.

In the first case, it doesn´t work; however, it does in the second chunk of code.

Thanks!

##With vector_source
mapboxgl(center = c( -99.133469,19.43267787),zoom = 10) %>%add_vector_source(id="ageb_cdmx",url="mapbox://enriquevaa.5t18pfv7") %>% add_fill_layer(
  id = "AGEB",
  source ="ageb_cdmx",
  source_layer = "AGEB_Mapbox_CDMX-5l1f2s",
  fill_opacity = 0.7,
  fill_color = interpolate(
    column = "Densidad",
    type = "linear",
    values = c(0,1, 15000, 30000),
    stops = c("lightgrey","#edf8b1", "#7fcdbb", "#2c7fb8"),
    na_color = "lightgrey"),
  hover_options = list(
    fill_color = "yellow",
    fill_opacity = 1
  ))

##Without vector_source
mapboxgl(center = c( -99.133469,19.43267787),zoom = 10) %>%  add_fill_layer(
  id = "AGEB",
  source =ageb_cdmx,
  fill_opacity = 0.7,
  fill_color = interpolate(
    column = "Densidad",
    type = "linear",
    values = c(0,1, 15000, 30000),
    stops = c("lightgrey","#edf8b1", "#7fcdbb", "#2c7fb8"),
    na_color = "lightgrey"),
  hover_options = list(
    fill_color = "yellow",
    fill_opacity = 1
  ))
walkerke commented 2 months ago

Yeah, I don't have hover effects for vector tile sources enabled right now. I can see how it can be done (I use setFeatureState() under the hood) but it also requires an id in the vector tileset and I can't find any good examples anywhere of how that id gets generated. For GeoJSON sources, it's straightforward as we use generateId: true which takes care of it for us.

Have you seen any GL JS examples that show how to do hover effects for vector tile sources?

bt-maps commented 2 months ago

Some good info on setFeatureState() here

https://blog.mapbox.com/going-live-with-electoral-maps-a-guide-to-feature-state-b520e91a22d

walkerke commented 1 month ago

The hover tooltips now work! See the example here:

https://walker-data.com/mapboxapi/articles/dynamic-maps.html#styling-vector-tiles-with-mapbox-gl-js-and-mapboxer

The catch is that your vector tileset still needs an id for its features. Mapbox Tiling Service does it by default, and tippecanoe can do it. I don't know about tilesets created in other ways.

walkerke commented 1 month ago

@enriquevaa I also just tried your example above and the hover options do work with the vector source.