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

`set_filter()` does not work with `"in"` operator on a vector for inputs. #16

Closed RWParsons closed 4 months ago

RWParsons commented 4 months ago

As mentioned in the discussion of #12, it seems that the "in" operator does not work as desired when used for filtering the source data against a vector of possible values.

reprex:

library(shiny)
library(bslib)
library(colourpicker)
library(dplyr)
library(sf)
library(shinyWidgets)
library(mapgl)

ui <- bootstrapPage(
  sliderInput("slider", "min value:", value = 0, min = -3, max = 3),
  numericRangeInput("area_range", label = "numeric range input for area", value = c(0.03, 0.25), step = 0.01),
  maplibreOutput("map")
)

server <- function(input, output, session) {
  nc <- st_read(system.file("shape/nc.shp", package = "sf"))
  nc$var1 <- rnorm(n = nrow(nc))
  nc$CNTY_ID <- as.character(nc$CNTY_ID)

  output$map <- renderMaplibre({
    maplibre() |>
      fit_bounds(nc, animate = FALSE) |>
      add_fill_layer(
        id = "polygon_layer",
        source = nc,
        fill_color = "blue",
        fill_opacity = 0.5,
        tooltip = "AREA"
      )
  })

  observe({
    ids <- nc |>
      filter(
        AREA >= min(input$area_range),
        AREA <= max(input$area_range)
      ) |>
      pull(CNTY_ID)

    cat(length(ids))

    maplibre_proxy("map") |>
      set_filter(
        "polygon_layer",
        list("in", "CNTY_ID", ids)
      )
  })
}

shinyApp(ui, server)

Note that the filter works when the ids is of length 1: change the range to be from 0.03 to 0.043 to observe this effect on the map.

I'm not at all familiar with the mapbox API but perhaps it needs to be wrapped with some "any" condition as described here? https://stackoverflow.com/questions/62477327/how-to-use-in-expression-in-mapbox-gl-map-setfilter

Thanks!

Rex

walkerke commented 4 months ago

So if you change this line:

list("in", "CNTY_ID", ids)

To:

c("in", "CNTY_ID", ids)

It does work.

This is because Mapbox expects a single array for in like ["in", "property_name", "value1", "value2", "value3", ...]

For example:

> values <- c("a", "b", "c")
> jsonlite::toJSON(list("in", "column", values), auto_unbox = TRUE)
["in","column",["a","b","c"]] 
> jsonlite::toJSON(c("in", "column", values), auto_unbox = TRUE)
["in","column","a","b","c"] 

I'd like to think of ways to make this easier for users, but I want to keep the powerful capabilities of Mapbox's expression system exposed. So we'll want to think this through.

RWParsons commented 4 months ago

Perfect - thanks @walkerke!