walkerke / mapgl

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

Viridislite palettes not working with match_expr #26

Closed ryanscharf closed 1 month ago

ryanscharf commented 1 month ago

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   
walkerke commented 1 month ago

Yeah, I've noticed this too. Unfortunately this is an underlying issue with the JS libraries, they can't handle 8-character hex color codes with transparency appended.

I'd be open to suggestions about this. I'm torn between making color palettes "easier" (like other R mapping packages do) and preserving the flexibility of Mapbox / MapLibre which I really like.

A possible patch in the interim is to detect if a stop is a hex code (so starting with #) then truncating the string before sending to JS.

ryanscharf commented 1 month ago

As a user coming from tmap/leaflet, it feels natural for me to want to use palettes in the same way in mapgl as I do elsewhere. Do other packages just do a substr if the length is >7?

walkerke commented 1 month ago

Just pushed a fix in my latest commit. This code now works:

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

magma_pal <- viridisLite::magma(6)

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

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'
  )