GithubHelp home page GithubHelp logo

mapgl's Introduction

mapgl mapgl website

The mapgl R package makes the latest versions of Mapbox GL JS and MapLibre GL JS available to R users. The package interface is designed to make the powerful capabilities of both libraries available in R mapping projects, but also feel similar to users coming from other R mapping packages.

Install from CRAN:

install.packages("mapgl")

Or, install the development version from GitHub:

remotes::install_github("walkerke/mapgl")

Read through these vignettes to learn how to use the package:

Recommended training and how to learn more

If you find this project useful in your work and would like to ensure continued development of the package, you can provide support in the following ways:

To stay on top of package updates / new features and to get information about mapgl trainings, be sure to sign up for the Walker Data mailing list here.

mapgl's People

Contributors

walkerke avatar cboettig avatar stefanlinner avatar danielvartan avatar etiennebacher avatar bbest avatar yutannihilation avatar kmcd39 avatar olivroy avatar mstavro avatar rwparsons avatar ngoodkind avatar

Stargazers

Ai Dream World avatar  avatar Roy Francis avatar LBrenes avatar Altay Yuzeir avatar  avatar paintbrush avatar Universe Technologies avatar Sander Hackey avatar Aleksander Bang-Larsen avatar Dalong fedrick naanlong avatar JanuaryPersimmon2024 avatar Sigma avatar Dimitrios Kapetanios avatar  avatar SG avatar Arif Aşıroğlu avatar Yuta Sato avatar Jessie Reese avatar  avatar Bob Walker avatar vivek avatar Samer Mouksassi avatar Jay Matsushiba avatar  avatar Cody James avatar Gabriel Schaldach Morgado avatar Avery Hill avatar Alberto Rico avatar Sasha Bogdanovic avatar Wilson Yung avatar  avatar Guilherme Felitti avatar Felix Kraus avatar Eric Book avatar Ricardo Theodoro avatar Brent Kaplan avatar Insang Song avatar Diego Ripley avatar Dương Đình Quang avatar Andrei-WongE avatar André Leite avatar Brancen Gregory avatar Bastián Olea Herera avatar Jessé Burlamaque avatar Jimmy Briggs avatar Sam Herniman avatar Liam Brown avatar Kari Cummings avatar  avatar  avatar Anders Kiledal avatar Robin Lovelace avatar Sebastian Di Geronimo avatar Stéphane Guillou avatar  avatar Markus Lang avatar  avatar  avatar Eivind Gard Lund avatar Nora avatar Harry BM avatar Heeyoung Lee avatar Evgeny Noi avatar Zhanchao Yang avatar Darius Görgen avatar  avatar Simon Norris avatar  avatar Santiago Mota avatar Alexander Severinsen avatar  avatar Alexis Merot avatar  avatar  avatar  avatar Brent T avatar Jinseob Kim avatar Philip Orlando avatar  avatar Parker Barnes avatar Sebastian avatar Etienne RIFA avatar Anatolii Tsyplenkov avatar Anatoly Chernov avatar Jesus M. Castagnetto avatar Mauricio "Pachá" Vargas Sepúlveda avatar Joshua Kunst avatar Rich Pauloo avatar  avatar Veerle van Leemput avatar Sean Crotty avatar Benjamin Butler avatar Victor Perrier avatar John Coene avatar Keisuke ANDO avatar Anthony Bernardi avatar Patrick Elungat avatar Stephen Sanders avatar intothejunglecode avatar

Watchers

 avatar  avatar  avatar Antony Barja  avatar Germano Costa Neto avatar Callum avatar SpatialGuy7 avatar

mapgl's Issues

compare does not inherit bounds argument

Hi,

When one creates the compare slider widget with two maps that were created with the bounds argument, the compare widget ignores the bounds and drops center and zoom to default values. See the example below.

How it works now

library(mapgl)
library(sf)

# Load example dataset
nc <-
  sf::st_read(system.file("shape/nc.shp", package = "sf"))[1, ] |>
  sf::st_transform(crs = 4326)

# Option 1 — Widget bbox set up with bounds argument ---------------------
# Create map 1
m1 <-
  maplibre(bound = nc) |> # bbox is setup with bounds
    add_fill_layer(
  id = "nc_data",
  source = nc,
  fill_color = "blue",
  fill_opacity = 0.5
)

# Create map 2
m2 <-
  maplibre(bound = nc) |> # bbox is setup with bounds
  add_fill_layer(
    id = "nc_data",
    source = nc,
    fill_color = "red",
    fill_opacity = 0.5
  )

# The bound argument of maplibregl object is ignored
compare(m1, m2)

The workaround

# Option 2 — Widget bbox set up with center and zoom arguments -----------------
nc_centroid <-
  sf::st_centroid(nc) |>
  sf::st_coordinates()

m1 <-
  maplibre(
    center = c(nc_centroid[1], nc_centroid[2]),
    zoom = 10
  ) |>
  add_fill_layer(
    id = "nc_data",
    source = nc,
    fill_color = "blue",
    fill_opacity = 0.5
  )

m2 <-
  maplibre(
    center = c(nc_centroid[1], nc_centroid[2]),
    zoom = 10
  ) |>
  add_fill_layer(
    id = "nc_data",
    source = nc,
    fill_color = "red",
    fill_opacity = 0.5
  )

# Now the center argument of maplibregl object is used
compare(m1, m2)

Session Info

#> R version 4.4.1 (2024-06-14 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 10 x64 (build 19045)
#> 
#> Matrix products: default
#> 
#> 
#> locale:
#> [1] LC_COLLATE=English_United States.utf8 
#> [2] LC_CTYPE=English_United States.utf8   
#> [3] LC_MONETARY=English_United States.utf8
#> [4] LC_NUMERIC=C                          
#> [5] LC_TIME=English_United States.utf8    
#> 
#> time zone: Pacific/Auckland
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] sf_1.0-17   mapgl_0.1.4
#> 
#> loaded via a namespace (and not attached):
#>  [1] terra_1.7-78       cli_3.6.3          knitr_1.48         rlang_1.1.4       
#>  [5] xfun_0.46          DBI_1.2.3          KernSmooth_2.23-24 glue_1.7.0        
#>  [9] htmltools_0.5.8.1  e1071_1.7-14       rmarkdown_2.27     grid_4.4.1        
#> [13] evaluate_0.24.0    classInt_0.4-10    fastmap_1.2.0      base64enc_0.1-3   
#> [17] yaml_2.3.10        lifecycle_1.0.4    compiler_4.4.1     codetools_0.2-20  
#> [21] fs_1.6.4           htmlwidgets_1.6.4  Rcpp_1.0.13        digest_0.6.36     
#> [25] class_7.3-22       reprex_2.1.1       magrittr_2.0.3     tools_4.4.1       
#> [29] withr_3.0.1        proxy_0.4-27       geojsonsf_2.0.3    units_0.8-5

`fit_bounds()` should default to `animate = FALSE`

The default behavior of fit_bounds() flies in to a given bounding box. animate = TRUE is probably a useful option in Shiny, but not for most interactive analyses. I'll need to expose animate as a top-level argument and set the default to FALSE.

Feature Request: Update tooltip with `set_*` function

Hi,

I have the following use case: A user can change the fill_color variable by clicking a button. This already works fine using the set_paint_property() function. However, I would also like to set a new tooltip variable (as suggested by the outcommented code). It would be great if this could be done using a set_*() function that does not require redrawing the fill_layer. Currently I'm accomplishing this by first clear_layer("fill") and then again add_fill_layer(...) with the updated tooltip. However, this results in redrawing the fill_layer and with a set-update it would be much nicer and more performant.

library(mapgl)
library(sf)
library(shiny)

nc <- st_read(system.file("shape/nc.shp", package="sf"))

ui <-
  fluidPage(
    actionButton("click", "Click"),
    mapboxglOutput("map")
  )

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

  output$map <- renderMapboxgl({
    mapboxgl() %>%
      fit_bounds(
        nc,
        animate = FALSE
      ) |>
      add_fill_layer(
        "fill",
        source = nc,
        fill_color = interpolate(
          column = "AREA",
          values = c(min(nc[["AREA"]]), max(nc[["AREA"]])),
          stops = c("#f7fbff", "#08306b"),
          na_color = "lightgrey"
        ),
        tooltip = "AREA"
      )
  })

  myMapProxy <- mapboxgl_proxy("map", session)

  observeEvent(input$click, {
    myMapProxy |>
      set_paint_property(
        "fill",
        "fill-color",
        interpolate(
          column = "PERIMETER",
          values = c(min(nc[["PERIMETER"]]), max(nc[["PERIMETER"]])),
          stops = c("#f7fbff", "#08306b"),
          na_color = "lightgrey"
        )
      ) #|>
      # set_*_property(
      #   "fill",
      #   "tooltip",
      #   "PERIMETER"
      # )
  })
}

shinyApp(
  ui = ui,
  server = server
)

Out of curiosity: Is it generally also possible to update the source dataset using a set_* function?

Thanks again for your great work, very appreaciated!

Changing `fill-color` with `set_paint_property()` removes `fill_color` in `hover_options`

Hi,

I think I'm experiencing a bug. When clicking the "Click" button, the fill color should change to interpolate from PERIMETER instead of AREA. This works as intended. However, after clicking the "Click" button, the yellow fill color of hover_options disappeared.

library(mapgl)
library(sf)
library(shiny)

nc <- st_read(system.file("shape/nc.shp", package="sf"))

ui <-
  fluidPage(
    actionButton("click", "Click"),
    mapboxglOutput("map")
  )

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

  output$map <- renderMapboxgl({
    mapboxgl() %>%
      fit_bounds(
        nc,
        animate = FALSE
      ) |>
      add_fill_layer(
        "fill",
        source = nc,
        fill_color = interpolate(
          column = "AREA",
          values = c(min(nc[["AREA"]]), max(nc[["AREA"]])),
          stops = c("#f7fbff", "#08306b"),
          na_color = "lightgrey"
        ),
        hover_options = list(
          fill_color = "yellow"
        )
      )
  })

  myMapProxy <- mapboxgl_proxy("map", session)

  observeEvent(input$click, {
    myMapProxy |>
      set_paint_property(
        "fill",
        "fill-color",
        interpolate(
          column = "PERIMETER",
          values = c(min(nc[["PERIMETER"]]), max(nc[["PERIMETER"]])),
          stops = c("#f7fbff", "#08306b"),
          na_color = "lightgrey"
        )
      )
  })
}

shinyApp(
  ui = ui,
  server = server
)

Thanks a lot for your work! The package is great!

proxy functions should be deferred until the shiny session is flushed by default

This bug was identified in the discussions of #12.

Default values for shiny inputs that are observed to modify the map via proxy function are not currently applied. This is because they are not deferred until the shiny session is flushed as is the default by leaflet. See the that the proxy function includes a deferUntilFlush argument (https://github.com/rstudio/leaflet/blob/92bc272caa9a268140e75ede1966bcdc7d585636/R/utils.R#L136) and the invocation of this check this value and defers the change until the shiny session is flushed (https://github.com/rstudio/leaflet/blob/92bc272caa9a268140e75ede1966bcdc7d585636/R/utils.R#L194).

reprex:

By default this app should show only one polygon on the map (with the current default inputs for the 'area_range'. If you reduce the lower bound of the input range, it updates the map but this should happen for the default view of the map.

library(shiny)
library(bslib)
library(colourpicker)
library(dplyr)
library(sf)
library(shinyWidgets)
devtools::load_all()

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.042), 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)

I think the solution would be to make some wrapper, like leaflet has invokeRemote() which checks the shiny session and defers if necessary, rather than each shiny util, like set_filter(), directly calling proxy$session$sendCustomMessage().

Thanks!

Rex

styling for draw controls

Hi Kyle, would it be possible to allow passing custom css for the draw control? or could you make an example (as your time allows) on how to do this?

I am able to change most things about the styling using just mapgl controls and passing new styles to them (see example below) but not able to change the layout and positioning....what i want is the toolbar horizontal and bottom-center.

Also, currently for me the default top_left works great but it goes under legends I may have on top_left.... all other positions seem to send the draw_control out of the map

image

clear_layer() doesnt clear popups

Hi Kyle

in shiny, when using clear_layer("x") with mapboxgl_proxy... the layer x seems to clear but the popup does not.

What I was trying to do was:

-1- render a base map with renderMapboxgl()
-2- select a field from a dropdown menu and add_circle_layer using that field with mapboxgl_proxy(). Field is numeric, circle size is based on field.

if i switch to a different numeric field in the dropdown...the old popups stay but read "undefined"

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

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

Reset zoom level

Hello, would it be possible to add a function to show a button for resetting the zoom level?

Use empty/solid color basemap

I am trying to create a map using shifted geometry for the US (i.e. Alaska and Hawaii moved), and need to have an empty/solid color basemap option for displaying it. I can't seem to find an option for doing this in mapboxgl() or maplibre().

variable sized icons/circles/markers

Hi Kyle,

is it possible already or in the roadmap to be able to pass a list or vector of sizes for circles/symbols/markers?

To clarify my question:

I'm able to color circles by a continuous variable like so:

add_circle_layer(
id = "elevation",
source = data_sf,
circle_radius = 5,
circle_color = interpolate(
column = "Elevation_m",
values = c(min(data$Elevation_m), max(data$Elevation_m)),
stops = c("#FFF7FB", "#023858")
),
circle_opacity = 1
)

I would also like to be able to pass variable sizes to radius here for example

set_view() doesn´t update the location of the mapboxgl_proxy()

Hi Kyle,

When I try to switch the location of a mapboxgl_proxy("map") in the shiny app, it keeps the map in the original location of the proxy map.

It also doesn´t work with fly_to().

mapboxgl_proxy("map") %>% set_view(center=c (-98.20715351039499,19.047733964562703),zoom=15)

Quit a legend after adding it

I have developed a shiny app where I can switch from different layers, for example, heatmap, population, and locations.
I use the function clear_layer() to quit the polygons when I switch from one layer to another; however, there's no function to remove the legend added through add_legend(), even if I add an option in the shiny app to render the map again the legend is still there.

Is there a way to remove the legend? Thanks!

Popup legends in shiny

The function hover or tooltip works fine with mapboxgl in my console, but when I render it on a shiny app it doesn´t show any legends. Cheers for all your work Kyle.

Produce Error Message When Render Fails

I am trying to use add_circle_layer() with ~17k points, categorized by type. It fails to render when points are used altogether, but when separated by type, some types render and some types don't. I've tried everything (testing for valid st, removing duplicate geometries, checking for null geometries), but can't find any difference that is causing this behavior. Moreover, everything plots just fine using other methods in R. Having an error message to point out why the mapboxgl() map is failing to render would be helpful.

More new feature requests :) -- synced maps and save static image button

Requesting some other new features that seem implementable in maplibre/mapbox js gl!

  • Synced maps! To parallel implementation for leaflet in, i.e., leafsync::sync.
  • Add button to save static image of maps with some defaults and option to hide map controls (a leaflet parallel would be leaflet.extras2::addEasyprint).

Thanks for the "before_id" addition based on the other issue :) this is getting cool, fast.

Allow layering of basemaps and setting of z-indices (if possible with maplibre/mapbox gl js)

Hi!

I'm making plans to switch from leaflet to either deck.gl or maplibre gl js for a lot of my interactive mapping / shiny development work.

Using R/leaflet, I made a practice of initializing maps with different layers: one w/o labels and another with labels separate, and I put the data between them by setting z-indices.

For example, in leaflet, I had:

leaflet() %>%
    addMapPane("tileLabels", # place name labels
               zIndex = 599) %>%
    addProviderTiles(providers$CartoDB.PositronNoLabels) %>%
    addProviderTiles(providers$CartoDB.PositronOnlyLabels,
                     group = 'Place names',
                     options =
                       providerTileOptions(
                         pane = "tileLabels",
                       )) 

And then I could also add map panes for the data with a z-index between the NoLabel layer and the OnlyLabel layer, and give user ability to toggle labels on/off, and have labels appear above the data by default.

I don't see a way to layer provider tiles or basemaps in any R interface to maplibre or other gl js packages, like mapbox or deck.gl. If it's possible, i'd love to see features to layer basemaps and set z-indices using mapgl!!!!

Thank you! I use many of your packages quite frequently.

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

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)

Shinyapp using mapgl/maplibre crashes when deployed

Hi!

Deploying the shiny app i was developing using mapgl and maplibre and carto, as created with:

mapgl::maplibre(
     style = carto_style("dark-matter")
    ,preserveDrawingBuffer = TRUE # per GH issue, important for capture::capture
  )

However, when I deploy the app, it loads initially -- the map even renders! However, the app crashes as the map is drawn and I get the "Disconnected from server" message.

The app works locally, and logs from shinyapps.io show nothing unusual and no errors. However, when i inspect the page on either chrome or firefox, I get errors from maplibregl.js, including:

"Source map error: Error: request failed with status 404"

And--

TypeError: HTMLWidgets.find(...) is undefined
    <anonymous> [shinyapp_url]/_w_cd3d9e1d/maplibregl-binding-0.1.4/maplibregl.js:933

I've also tried this with json deployed with the app and read with jsonlite but got the same errors. I'll also include these other errors from chrome (i'm sorry, i'm not experienced debugging html/javascript so not sure what is relevant)--

image

Thank you!

How to Implement Tiles?

Hi @walkerke,
Recently, I have been thinking about migrating my web map development to MapLibre as an alternative to Leaflet. However, I'm having some difficulty when using tiles. How can I implement the same example with MapLibre GL?

library(leaflet)
url_tile = "https://earthengine.googleapis.com/v1/projects/earthengine-legacy/maps/3dd20ff6c80036e9f973edd88a9038bf-e0f6a3aa654d1adf849392fff5ccae40/tiles/{z}/{x}/{y}"

leaflet() |> 
  addTiles() |> 
  addTiles(urlTemplate = url_tile)

image

url_tile = "https://earthengine.googleapis.com/v1/projects/earthengine-legacy/maps/3dd20ff6c80036e9f973edd88a9038bf-e0f6a3aa654d1adf849392fff5ccae40/tiles/{z}/{x}/{y}"
maplibre(
  style = maptiler_style(
    style_name = "openstreetmap",
    api_key = "hdV4nOYKQa4di4tH4qr2")) |> 
  add_fullscreen_control() |> 
  add_navigation_control() |> 
  fit_bounds(bbox = churo) |> 
  add_image_source(
    id = "rgb",
    url = url_tile)

image

Handle categorical variables in `step_expr()`

I've been enjoying learning to use {mapgl} after having used {leaflet} almost entirely for my interactive mapping needs. It would be great if the step_expr function could be modified to accommodate categorical data (i.e., not converting numerical data into categories) when using add_fill_layer since I have some use cases where this would be very helpful.

Not sure how it works on the backend, but may alternatively be easier to create a new function to handle categorical variables instead.

Change documentation for interpolate() from c() to list()

The documentation for interpolate currently suggests changing type by using:
c(“exponential”, base)
or
c(“cubic-bezier”, x1, y1, x2, y2)
… which currently result in errors rendering due to numeric values being coerced to characters. Using list() prevents the coercion and enables changing the type.

Might be a consideration for a broader overhaul. stopifnot() might also be useful, but I think a fix to the documentation is easier in the meantime. If anything I hope this post is useful for anyone else trying to change interpolation type.

Allow updating of layer aesthetics with content from data.

Building off of the example in the map-design vignette: I'd like to be able to update the fill-color of existing polygons.

I'm not sure if this is intended to be the job of set_layout_property() or set_paint_property() but here is an adapted bit of code from the vignette:

library(shiny)
library(bslib)
library(colourpicker)
library(sf)

nc <- st_read(system.file("shape/nc.shp", package="sf"))
nc$var1 <- rnorm(n = nrow(nc))
nc$var2 <- rnorm(n = nrow(nc))

ui <- page_sidebar(
  title = "mapgl with Shiny",
  sidebar = sidebar(
    radioButtons("outcome", "pick outcome var:", choices = c("var1", "var2"), select = "var1")
  ),
  card(
    full_screen = TRUE,
    maplibreOutput("map")
  )
)

server <- function(input, output, session) {
  output$map <- renderMaplibre({
    maplibre(style = carto_style("positron")) |> 
      fit_bounds(nc, animate = FALSE) |> 
      add_fill_layer(id = "nc_data",
                     source = nc,
                     fill_color = interpolate(
                       # I could swap the `coloumn` arg for input$outcome but ofc this means it has to re-render 
                       # the map every time it's updated 👎
                       column = "var1",
                       values = c(-3, 3),
                       stops = c("lightblue", "darkblue"),
                       na_color = "lightgrey"
                     ),
                     fill_opacity = 0.5)
  })
  
  observeEvent(input$outcome, {
    maplibre_proxy("map") |>
      set_layout_property("nc_data", "fill-color", interpolate(
        column = input$outcome,
        values = c(-3, 3),
        stops = c("lightblue", "darkblue"),
        na_color = "lightgrey"
      ))
  })
}

shinyApp(ui, server)

I'd like to be able to change the outcome variable being visualised in the fill-color of a fill_layer. I'd also like to be able to affect the legend but I see that there is an issue (#10) that may address this.

Thanks!

Compare() in Shiny

Hello,

I can't seem to get compare() working in a Shiny app. I've tried to make a minimal reproducible example here. It gives the error: Warning in renderWidget(instance) : Ignoring explicitly provided widget ID "compare-container-db634"; Shiny doesn't use them.

# Load libraries
library(shiny)
library(mapgl)

# Define UI
ui <- fluidPage(
  titlePanel("Map Comparison"),
  mainPanel(
    mapboxglOutput("compare_map", height = "800px")
  )
)

# Define Server logic
server <- function(input, output, session) {
  
  output$compare_map <- renderMapboxgl({
    m1 <- mapboxgl(style = mapbox_style("light"))
    m2 <- mapboxgl(style = mapbox_style("dark"))
    
    # Use the compare function as documented
    compare(m1, m2)
  })
}

# Run the application
shinyApp(ui = ui, server = server)

PMTiles source integration?

Hi! First, so excited to see that you are developing tools for maplibre in R!
It parallels well with work I've been doing to explore 1) creating PMTiles with tippecanoe, 2) storing them in an S3 bucket, and 3) adding them as a vector source to a maplibre map.

I see you have the add_vector_source() function set up for MapTiler tiles, and I am wondering if it is a small lift to allow it to render PMTiles read in directly from an S3 bucket link?

I have an html script adapted from the PMTiles source and protocol example (here: https://maplibre.org/maplibre-gl-js/docs/examples/pmtiles/), that works as a standalone.
Would it be possible to integrate the PMTiles plugin with this work? Possibly in the add_vector_source() function?

If I can help move toward a pull request, let me know if that's something could see including in this package.
Thanks!

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Map with PMTiles from S3 link</title>
    <meta property="og:description" content="Initialize a map in an HTML element with MapLibre GL JS. Add PM tiles plugin and protocol" />
    <meta charset='utf-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel='stylesheet' href='https://unpkg.com/[email protected]/dist/maplibre-gl.css' />
    <script src='https://unpkg.com/[email protected]/dist/maplibre-gl.js'></script>
    <script src="https://unpkg.com/[email protected]/dist/index.js"></script>
    <style>
        body { margin: 0; padding: 0; }
        html, body, #map { height: 100%; }
    </style>
</head>
<body>
<div id="map"></div>
<script>

    // add the PMTiles plugin to the maplibregl global.
    const protocol = new pmtiles.Protocol();
    maplibregl.addProtocol('pmtiles', (request) => {
        return new Promise((resolve, reject) => {
            const callback = (err, data) => {
                if (err) {
                    reject(err);
                } else {
                    resolve({data});
                }
            };
            protocol.tile(request, callback);
        });
    });

    const PMTILES_URL = 'https://vector-tiles-testing.s3.us-east-2.amazonaws.com/brmpo-demographics.pmtiles';

    const p = new pmtiles.PMTiles(PMTILES_URL);
    // this is so we share one instance across the JS code and the map renderer
    protocol.add(p);

    // we first fetch the header so we can get the center lon, lat of the map.
    p.getHeader().then(h => {
        const map = new maplibregl.Map({
            container: 'map',
            zoom: h.maxZoom - 2,
            center: [h.centerLon, h.centerLat],
            style: {
                version: 8,
                sources: {
                    'brmpo_demo_tiles': {
                        type: 'vector',
                        url: `pmtiles://${PMTILES_URL}`,
                        attribution: '© <a href="https://openstreetmap.org">OpenStreetMap</a>'
                    }
                },
                layers: [
                    {
                        'id': 'demo',
                        'source': 'brmpo_demo_tiles',
                        'source-layer': 'brmpo_tract',
                        'type': 'fill',
                        'paint': {
                            'fill-color': [
                                "rgb",10,10,10
                                ],
                            'fill-outline-color': 'white',
                            'fill-opacity': .8

                        }
                    }
                ]
            }
        });
       
    });

</script>
</body>
</html>

`set_filter()` not working within rhino framework

This could well be a me problem as I'm just figuring out my way around mapgl today.

I'm working on a larger app which uses rhino but have found that I can't get mapgl::set_filter() to work. I've made a smaller app here - https://github.com/RWParsons/test-app. This has a rhino app with a map shown in the main module https://github.com/RWParsons/test-app/blob/main/app/main.R but the filter does not trigger changes on the map.

I have made the same app but without the modules/rhino framework and it works fine: https://github.com/RWParsons/test-app/blob/main/normal-app.R

Any help would be greatly appreciated

Thanks for your work on this package, @walkerke, I'm very keen be able to work mapgl into this project of mine 😄

blank Viewer in RStudio

When using the package functions to create an interactive map with e.g. something like the following from RStudio

library(mapgl)
maplibre()

the Viewer pane keeps blank. This is probably no mapgl issue, but is it expected?

When exporting/saving as a webpage the map is visible as expected in the browser.

set_style() reverts back to original map in shiny

Hi Kyle,

I'm looking to update the basemap style using mapboxgl_proxy() and set_style() in a shiny app. The style changes fine, but the map seems to revert back to the original data that was displayed when it was first rendered.

For instance, in the reprex below, if you choose a different polygon and then update the style to "satellite-streets", the polygon and label data will disappear altogether at first, but when changing the style back to "standard" will revert back to the data that was initially provided to renderMapboxgl().

Ideally I'd like set_style() to just change the basemap without affecting any of the data that's loaded:

library(shiny)
library(mapgl)
library(sf)
library(dplyr)

nc <- st_read(system.file("shape/nc.shp", package = "sf"),
              quiet = TRUE)

ui <- fillPage(
  div(
    selectInput(
      "select_polygon",
      label = "Select a polygon",
      choices = unique(nc$NAME)
    ),
    selectInput("style", "Choose Map Style:",
                choices = c("Standard" = "standard", 
                            "Satellite Streets" = "satellite-streets")),
    style = "position: absolute; top: 10px; left: 10px; z-index: 1000;"
  ),
  tagList(
    mapboxglOutput("map", width = "100%", height = "100vh")
  )
)

server <- function(input, output, session) {
  
  output$map <- renderMapboxgl({
    
    initial_data <- nc %>% filter(NAME == "Ashe")
    
    mapboxgl(
      style = mapbox_style("satellite-streets"),
      center = c(-2, 53),
      zoom = 7,
      access_token = app_config$mapbox_key
    ) |>
      add_fill_layer(
        id = "chosen_polygon",
        source = initial_data,
        fill_opacity = 1,
        fill_color = "red",
        fill_outline_color = "black",
        hover_options = list(
          fill_color = "white",
          fill_opacity = 0.5,
          fill_outline_color = "yellow"
        )
      ) |>
      add_symbol_layer(
        id = "chosen_polygon_label",
        source = initial_data,
        text_field = get_column("NAME"),
        text_size = 24
      ) |>
      fit_bounds(
        nc,
        padding = 50,
        animate = TRUE
      )
    
  })
  
  observeEvent(input$select_polygon, {
    
    hold <- nc %>% filter(NAME == input$select_polygon)
    
    mapboxgl_proxy("map") |>
      clear_layer("chosen_polygon") |>
      clear_layer("chosen_polygon_label") |>
      add_fill_layer(
        id = "chosen_polygon",
        source = hold,
        fill_opacity = 1,
        fill_color = "red",
        fill_outline_color = "black",
        hover_options = list(
          fill_color = "white",
          fill_opacity = 0.5,
          fill_outline_color = "yellow"
        )
      ) |>
      add_symbol_layer(
        id = "chosen_polygon_label",
        source = hold,
        text_field = get_column("NAME"),
        text_size = 24
      ) |>
      fit_bounds(
        nc,
        padding = 50,
        animate = TRUE
      )
    
  })
  
  observeEvent(input$style, {
    mapboxgl_proxy("map") %>%
      set_style(mapbox_style(input$style))
  })
  
}  

runApp(shinyApp(ui, server))

I'm using the dev version of mapgl that I downloaded yesterday. Any solution would be much appreciated! Thanks in advance!

Clear_markers() doesn´t erase the markers in the shiny session

I have written a simple code of an app that asks the user the lat and long of a point, then the user can plot it in the map through a button and also erase it through another button; however, the function clear_markers() doesn´t erase the marker in the proxy map. I have tried clear_layer() too but neither works.

Here is my code, cheers:

ui <- dashboardPage(skin="red",
                    dashboardHeader(title="Mapa interactivo"),
                    dashboardSidebar(
                      sidebarMenu(menuItem(
                        "Análisis", 
                        tabName = "isos", 
                        icon = icon("map-pin"),
                        textInput("latitud", "Latitud:",width = "250px"),
                        textInput("longitud", "Longitud:",width = "250px"),
                        actionButton("agregar", "Nuevo punto",width = "192px"),
                        actionButton("buffer", "Buffer (400m)",width = "192px"),
                        br(),
                        actionButton("borrar", "Borrar",style=estilo,width = "192px")))
                      ,collapsed=T),
                    dashboardBody(
                      tags$style(type = "text/css", "#mapa {height: calc(100vh - 80px) !important;}"),
                      mapboxglOutput("mapa")
                    ))

server <- function(input, output, session) {
  markers <- reactiveVal(data.frame(lat = numeric(0), lon = numeric(0),idp=numeric(0)))
  buffer_layer <- reactiveVal(NULL)

  output$mapa <- renderMapboxgl({
    mapboxgl(center = c( -99.16719645546601,19.427190799035163),zoom=10,style = mapbox_style("dark"),access_token = token) })
    
    
    
    observeEvent(input$agregar, {
      lat <- as.numeric(input$latitud)
      lon <- as.numeric(input$longitud)
      
      #current_markers <- markers()
      #new_marker <- data.frame(lat = lat, lon = lon)
      #markers(rbind(current_markers, new_marker))
      
      if (!is.na(lat) && !is.na(lon)) {
        current_markers <- markers()
        new_marker <- data.frame(lat = lat, lon = lon,idp=1)
        markers(rbind(current_markers, new_marker))
        #coords <- markers()
        points_sf <- st_as_sf(markers(), coords = c("lon", "lat"), crs = 4326)
        mapboxgl_proxy("mapa") %>% add_markers(points_sf,marker_id="idp")
      } else {
        showModal(modalDialog(
          title = "Error",
          "Por favor, ingresa valores válidos para latitud y longitud."
        ))
      }
    })
    
    observeEvent(input$buffer, {
      if (nrow(markers()) > 0) {
        coords <- markers()
        sf_use_s2(T)
        points_sf <- st_as_sf(coords, coords = c("lon", "lat"), crs = 4326)
        buffer_sf <- st_buffer(points_sf, dist = 400)
        buffer_sf$Clave_buffer<-rownames(buffer_sf)
        sf_use_s2(F)
        
        mapboxgl_proxy("mapa") %>%
          add_fill_layer(id="Clave_buffer",source=buffer_sf,fill_color  = "blue", fill_opacity = 0.2)
      } else {
        showModal(modalDialog(
          title = "Error",
          "Por favor, agrega al menos un marcador antes de crear el buffer."
        ))
      }
    })

    
    observeEvent(input$borrar, {
      mapboxgl_proxy("mapa") %>% clear_layer("Clave_buffer") %>% clear_markers()
      markers(data.frame(lat = numeric(0), lon = numeric(0)))
      buffer_layer(NULL)
    })}

shinyApp(ui,server)
```


Add Source issue for set terrain

the example provided in the set terrain documentaion -

map <- mapboxgl(style = "mapbox://styles/mapbox/satellite-streets-v12",
center = c(-111.658106, 40.582623),
zoom = 10,
pitch = 45,
bearing = 180)

map <- add_source(map, id = "mapbox-dem", type = "raster-dem",
url = "mapbox://mapbox.mapbox-terrain-dem-v1",
tileSize = 512, maxzoom = 14)

map <- set_terrain(map, source = "mapbox-dem", exaggeration = 1.5)

leads to this error -
Error in add_source(map, id = "mapbox-dem", type = "raster-dem", url = "mapbox://mapbox.mapbox-terrain-dem-v1", :
argument "data" is missing, with no default

graceful handling for missing in add_circle_layer

if one of the values in the column is NA... no circles are rendered

good behaviour could be to have a na.rm type argument, if TRUE, dont render NA circles, if FALSE, NAs can be rendered as a separate category

Mapbox won't render in Quarto

At the moment, Quarto can't accommodate JS libraries served over a CDN because it expects a local resource path. Follow up on options for Mapbox GL JS re: licensing and how I can handle this.

mapboxgl_proxy bugs

Hi Kyle,

Loving mapgl so far and the material from your workshops! Thanks so much for all the hard work on it.

I've been playing with mapboxgl_proxy in a shiny app I'm that building, and came across a couple of (what I think are) bugs. Here's a reprex to help explain:

library(shiny)
library(mapgl)
library(sf)
library(dplyr)

nc <- st_read(system.file("shape/nc.shp", package = "sf"),
              quiet = TRUE)

ui <- fillPage(
  div(
    selectInput(
      "select_polygon",
      label = "Select a polygon",
      choices = unique(nc$NAME)
    ),
    style = "position: absolute; top: 10px; left: 10px; z-index: 1000;"
  ),
  tagList(
    mapboxglOutput("map", width = "100%", height = "100vh")
    )
)

server <- function(input, output, session) {
  
  selected_polygon_reactive <- reactiveVal(NULL)
  
  output$map <- renderMapboxgl({
    
    initial_data <- nc %>% filter(NAME == "Ashe")
    
    mapboxgl(
      style = mapbox_style("satellite-streets"),
      center = c(-2, 53),
      zoom = 7,
      access_token = app_config$mapbox_key
    ) |>
      add_fill_layer(
        id = "chosen_polygon",
        source = initial_data,
        fill_opacity = 1,
        fill_color = "red",
        fill_outline_color = "black",
        hover_options = list(
          fill_color = "white",
          fill_opacity = 0.5,
          fill_outline_color = "yellow"
        )
      ) |>
      add_symbol_layer(
        id = "chosen_polygon_label",
        source = initial_data,
        text_field = get_column("NAME"),
        text_size = 24
      ) |>
      fit_bounds(
        nc,
        padding = 50,
        animate = TRUE
      )
    
  })
  
  observeEvent(input$select_polygon, {
    
    hold <- nc %>% filter(NAME == input$select_polygon)
    
    selected_polygon_reactive(hold)
    
  }, ignoreInit = TRUE)
  
  observeEvent(selected_polygon_reactive(), {

    mapboxgl_proxy("map") |>
      clear_layer("chosen_polygon") |>
      clear_layer("chosen_polygon_label") |>
      add_fill_layer(
        id = "chosen_polygon",
        source = selected_polygon_reactive(),
        fill_opacity = 1,
        fill_color = "red",
        fill_outline_color = "black",
        hover_options = list(
          fill_color = "white",
          fill_opacity = 0.5,
          fill_outline_color = "yellow"
        )
      ) |>
      add_symbol_layer(
        id = "chosen_polygon_label",
        source = selected_polygon_reactive(),
        text_field = get_column("NAME"),
        text_size = 24
      ) |>
      fit_bounds(
        nc,
        padding = 50,
        animate = TRUE
      )

  })

}  

runApp(shinyApp(ui, server))

The app should behave in the following way: When changing the selectInput, the data gets filtered to the selected polygon, the map gets cleared, and the newly selected polygon along with its label gets shown.

When running this app on the latest CRAN release, it loads fine. However, when changing the selectInput, the "chosen_polygon" fill layer gets cleared and updated fine, but the "chosen_polygon_label" only gets removed and does not show up for the new polygon.

When running this app on the latest development version from github, the "chosen_polygon" and "chosen_polygon_label" both get cleared but neither the polygon nor the label show up.

I'm using an intermediary selected_polygon_reactive() in this example because in my actual app I'm retrieving data from a database in response to a selectInput, and therefore don't load all the data I need into the app on first load. Therefore I can't use the filtering functionality that might otherwise be an option.

Any help would be much appreciated! Thanks in advance

Allow Multiple Legends to be Displayed

I have an application where I desire to have a legend displaying values from a choropleth map and another legend displaying values for a circle layer on top of the choropleth map. However, only the last legend generated displays. I tried using unique_id to have them display as different elements in the CSS, but this didn't make a difference.

Viridislite palettes not working with match_expr

I have been having an issue with my first mapgl map where virdislite palettes appear to be failing with match_expr(). If you cut off the trailing FF's, the palettes work fine.

reprex:

library(mapgl)
library(sf)
library(dplyr)

magma_pal <- viridisLite::magma(6)
magma_substr_pal <- substr(magma_pal, 1, 7)


nc <- st_read(system.file('shape/nc.shp', package = 'sf'))
nc <- nc %>% mutate(zz = sample(letters[1:6], nrow(.), replace = T))


# magma doesn't work 
mapboxgl() %>%
  fit_bounds(nc, animate = F) %>%
  add_fill_layer(
    id = 'GC Zones',
    source = nc,
    fill_color = match_expr(
      column = 'zz',
      values = unique(nc$zz),
      stops = magma_pal
      )
    ) %>%
  add_legend(
    'GC Zones',
    values = unique(nc$zz),
    colors = magma_pal,
    type = 'categorical'
  )


# dropping last FFs makes it work though
mapboxgl() %>%
  fit_bounds(nc, animate = F) %>%
  add_fill_layer(
    id = 'GC Zones',
    source = nc,
    fill_color = match_expr(
      column = 'zz',
      values = unique(nc$zz),
      stops = magma_substr_pal
    )
  ) %>%
  add_legend(
    'GC Zones',
    values = unique(nc$zz),
    colors = magma_substr_pal,
    type = 'categorical'
  )

sessionInfo

R version 4.2.2 (2022-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 22631)

Matrix products: default

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

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

other attached packages:
[1] dplyr_1.1.3 sf_1.0-12   mapgl_0.1.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.10        rstudioapi_0.14    magrittr_2.0.3     units_0.8-1        tidyselect_1.2.0  
 [6] viridisLite_0.4.2  R6_2.5.1           rlang_1.1.0        fastmap_1.1.1      fansi_1.0.4       
[11] tools_4.2.2        grid_4.2.2         utf8_1.2.3         KernSmooth_2.23-20 cli_3.6.0         
[16] e1071_1.7-13       DBI_1.2.3          htmltools_0.5.8.1  class_7.3-20       yaml_2.3.8        
[21] digest_0.6.35      tibble_3.2.1       lifecycle_1.0.4    purrr_1.0.1        htmlwidgets_1.6.4 
[26] vctrs_0.6.1        glue_1.6.2         geojsonsf_2.0.3    proxy_0.4-27       pillar_1.9.0      
[31] compiler_4.2.2     generics_0.1.3     classInt_0.4-9     jsonlite_1.8.8     pkgconfig_2.0.3   

Altering arguments in add_heatmap_layer() for extremely high densities of points

Hi Kyle, I am really enjoying your 3 part workshop on mapgl! I am trying to utilize the package for Machine Learning predicted points of marine plastic off of windward Oahu. There is 171,000 points of predicted plastic in the bounding box. Is there something within the arguments of add_heatmap_layer() that can fix this?

My code:

knitr::opts_chunk$set(echo = TRUE)
library(tidyverse)
library(here)
library(mapgl)
library(sf)
library(tidycensus)

Read in Data

plastics <- read_csv(here("Plastic.csv"))

Turn Lat & Longs to point geometries

plastics_sf <- plastics %<>% 
  st_as_sf(coords = c("longitude", "latitude")) %>% 
  st_sf(crs = 4326)

Pull in Hawaii Data

hawaii <- get_acs(
  state = "HI",
  county = "Honolulu",
  geography = "tract",
  variables = "B19013_001",
  geometry = TRUE,
  year = 2020
)

Base map

hi_map <- mapboxgl(
  style = mapbox_style("light"),
  bounds = hawaii
)

output:

Screen.Recording.2024-08-07.at.12.42.32.PM.mp4

Here is an output I got with hex bins using tmap

Screen Shot 2024-07-26 at 2 03 36 PM

Thanks!

Feature Request: Link legends to layers using layer groups

Is it currently possible to link legends to map layers, like the leaflet package does via the group argument? For example, if I have a map with two layers using two color scales, I’d like to bind one legend to each layer, rather than having two legends appear added to one another.

As always, thanks for this great package.

Integrate a layer switcher (outside Shiny)

A layer switcher would be a nice-to-have, as other mapping libraries in R have one. It's pretty straightforward to do this in Shiny with set_layout_property() but an option to do this outside Shiny would be useful. Look into this.

Mapbox becomes sluggish when calling `clear_layer()` and `add_fill_layer()` many times

Hi,

I use the combination of clear_layer() and add_fill_layer() a lot to update my mapboxgl without redrawing the whole map. One thing I noticed is that if you call clear_layer() and add_fill_layer() many (!) times, the map gets a bit sluggish. Why is this? My understanding is that the combination of clear_layer() and add_fill_layer() just replaces the previous layers and doesn't add any complexity to the map, so it shouldn't affect performance. Or am I missing something here?

The following example should illustrate the problem. Note how fast and responsive the tooltips are when you hover over the map immediately after starting the application. Once you have clicked on the button, the tooltips appear much slower and sluggish.

library(mapgl)
library(sf)
library(shiny)

nc <- st_read(system.file("shape/nc.shp", package="sf"))

ui <-
  fluidPage(
    actionButton("click", "Click"),
    mapboxglOutput("map")
  )

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

  output$map <- renderMapboxgl({
    mapboxgl() %>%
      fit_bounds(
        nc,
        animate = FALSE
      ) |>
      add_fill_layer(
        "fill",
        source = nc,
        fill_color = interpolate(
          column = "AREA",
          values = c(min(nc[["AREA"]]), max(nc[["AREA"]])),
          stops = c("#f7fbff", "#08306b"),
          na_color = "lightgrey"
        ),
        tooltip = "AREA"
      )
  })

  myMapProxy <- mapboxgl_proxy("map", session)

  observeEvent(input$click, {

    for(i in 1:200){
      myMapProxy |>
        clear_layer("fill") |>
        add_fill_layer(
          "fill",
          source = nc,
          fill_color = interpolate(
            column = "AREA",
            values = c(min(nc[["AREA"]]), max(nc[["AREA"]])),
            stops = c("#f7fbff", "#08306b"),
            na_color = "lightgrey"
          ),
          tooltip = "AREA"
        )
    }

  })
}

shinyApp(
  ui = ui,
  server = server
)

`add_image_source()` doesn't accept colour tab in `spatRaster`

There are a couple problem that I've identified in add_image_source()

Here is an example using a sample of data that I'm working with:

test_data <- structure(list(x = c(146.704635285663, 143.470715055695, 145.815307222422, 
                                  147.917355371901, 145.060725835429, 144.117499101689, 148.186848724398, 
                                  139.805605461732, 148.456342076895, 149.04922745239, 140.641034854474, 
                                  144.575637800934, 147.351419331656, 143.335968379447, 143.335968379447, 
                                  141.314768235717, 145.545813869925, 152.714337046353, 147.593963348904, 
                                  150.747035573123, 146.327344592167, 148.779734099892, 148.96837944664, 
                                  147.432267337406, 144.198347107438, 150.908731584621, 144.198347107438, 
                                  142.931728350701, 144.548688465685, 144.710384477183, 148.213798059648, 
                                  141.422565576716, 147.782608695652, 140.506288178225, 143.01257635645, 
                                  140.425440172476, 143.282069708947, 144.791232482932, 140.398490837226, 
                                  151.636363636364, 151.98670499461, 144.494789795185, 141.98850161696, 
                                  144.872080488681, 143.01257635645, 142.716133668703, 145.680560546173, 
                                  144.602587136184, 144.683435141933, 142.177146963708), y = c(-22.270970176069, 
                                                                                               -21.6241861300755, -22.7560582105641, -22.567412863816, -28.6040639597557, 
                                                                                               -20.3306180380884, -20.3306180380884, -18.6597592526051, -23.5375889328063, 
                                                                                               -25.1545490477902, -20.573162055336, -15.9648257276321, -20.573162055336, 
                                                                                               -18.0399245418613, -16.6385591088753, -21.0043514193317, -24.6964103485447, 
                                                                                               -26.6098131512756, -28.3076212720086, -25.0467517067912, -21.5433381243263, 
                                                                                               -23.3758929213079, -23.5375889328063, -27.1218505210205, -17.1775458138699, 
                                                                                               -25.3162450592885, -21.1121487603306, -23.1063995688106, -23.1872475745598, 
                                                                                               -16.8541537908732, -20.4384153790873, -23.4567409270571, -24.9120050305426, 
                                                                                               -21.1929967660798, -20.8426554078333, -24.2382716492993, -16.9080524613726, 
                                                                                               -27.2296478620194, -22.7021595400647, -24.8581063600431, -26.5559144807761, 
                                                                                               -26.4481171397772, -23.1333489040604, -23.4028422565577, -14.267017606899, 
                                                                                               -18.471113905857, -18.4441645706073, -24.0765756378009, -19.2795939633489, 
                                                                                               -19.6568846568451), pred = c(305.917283580099, 357.147308791887, 
                                                                                                                            376.899080177541, 311.293558669515, 385.302194465796, 311.406970170659, 
                                                                                                                            213.397097913667, 381.62892998555, 380.620131247899, 323.160634223337, 
                                                                                                                            353.569875088294, 252.367408752526, 200.205388159247, 329.665758425744, 
                                                                                                                            303.616701525762, 347.401323384892, 388.910507958226, 163.814361430662, 
                                                                                                                            361.271026317014, 254.712761913955, 279.277148678091, 372.939247974351, 
                                                                                                                            369.336082811912, 345.740485904605, 250.928162986335, 246.393765060807, 
                                                                                                                            337.069420587925, 380.044721245275, 407.590072013831, 221.797793265574, 
                                                                                                                            222.250923169777, 362.431972284566, 380.155056796395, 368.03688613144, 
                                                                                                                            340.701795340508, 395.745150086577, 307.098348063308, 377.360232941649, 
                                                                                                                            368.020435732177, 320.938285611497, 174.648842951226, 374.875642089735, 
                                                                                                                            362.421003870795, 406.694805719537, 385.248720216423, 349.138641118029, 
                                                                                                                            178.077076149705, 400.417789912908, 248.847855237357, 350.543773979372
                                                                                               ), id = c(119188L, 129383L, 110793L, 114199L, 4932L, 149130L, 
                                                                                                         149281L, 170610L, 97078L, 66001L, 145388L, 194585L, 145637L, 
                                                                                                         177759L, 190621L, 138950L, 75096L, 38399L, 9545L, 68258L, 130767L, 
                                                                                                         99973L, 97097L, 29644L, 186468L, 62739L, 137392L, 104512L, 103145L, 
                                                                                                         189183L, 147669L, 98269L, 70863L, 136007L, 141475L, 83781L, 188708L, 
                                                                                                         27749L, 111544L, 72086L, 39270L, 40788L, 104003L, 99352L, 202832L, 
                                                                                                         172885L, 173304L, 87022L, 163546L, 158565L), col = c("#FA4A29", 
                                                                                                                                                              "#E41E1D", "#E1191C", "#F74627", "#E1191D", "#F74627", "#FE9E43", 
                                                                                                                                                              "#E1191D", "#E1191D", "#F23D24", "#E6221D", "#FD8238", "#FEA647", 
                                                                                                                                                              "#F03823", "#FA4C29", "#E8291F", "#E0191D", "#FEBD57", "#E31A1C", 
                                                                                                                                                              "#FD7F37", "#FD6730", "#E2191C", "#E21A1C", "#E92A1F", "#FD8339", 
                                                                                                                                                              "#FD873A", "#ED3221", "#E1191D", "#DF181D", "#FE9941", "#FE9841", 
                                                                                                                                                              "#E31A1C", "#E1191D", "#E21A1C", "#EB2F20", "#E0191D", "#F94928", 
                                                                                                                                                              "#E1191C", "#E21A1C", "#F33F25", "#FEB650", "#E2191C", "#E31A1C", 
                                                                                                                                                              "#DF181D", "#E1191D", "#E8271E", "#FEB34D", "#DF181D", "#FD8539", 
                                                                                                                                                              "#E7251E")), row.names = c(NA, -50L), class = c("tbl_df", "tbl", 
                                                                                                                                                                                                              "data.frame"))

tb <- data.frame(value = test_data$id, col = test_data$col)

test_raster <- tidyterra::as_spatraster(dplyr::select(test_data, x, y, id), crs = 4326)

terra::coltab(test_raster) <- tb

plot(test_raster) # works

maplibre() |>
  # add rasters
  add_image_source(
    id = "acute_raster_data",
    data = test_raster
  ) |>
  add_raster_layer(
    id = "acute_raster_layer",
    source = "acute_raster_data"
  )

This is just a sample of 50 rows from the input data. The full data is of an Australian state and looks fine when running plot(test_raster):

image

One of the issues is that this raster has colour layer that add_image_source() should accept. However, after it is projected to EPSG:4326 here:

data <- terra::project(data, "EPSG:4326")

This causes it to lose the it's attribute and terra::has.colors(data) then returns FALSE. This could arguably being a feature request to terra to but I think it could be solved if the coltb is created before the data are projected in add_image_source().

The second problem I found is that even if I skip the projection, with these data, I get an error when running:

terra::writeRaster(data, png_path, overwrite = TRUE, NAflag = 255, datatype = "INT1U")

I don't think the rgb values are out of range but it fails to write the image and then nothing is shown on the map.

If you would like, I can make a PR to solve the first issue with the projection but I'm not sure what to do about the problem with writeRaster (and you're probably much more familiar with this).

Cheers,
Rex

Address search bar

Hello, would it be possible to have a function for including a search bar to look up a given address or location?

No option to fit title length in add_legend() for continuous values

I am adding legends to fill layers in mapgl, but can't find a way to get the box to auto fit the width of the title when using continuous values. Legends for categorical values seem to do this by default. Ideally, the width will be auto by default for continuous legends as well so that it can accommodate dynamic titles in a Shiny app.

Continuous:
image

Categorical:
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.