library(shiny) library(bslib) library(shinybusy) source("utils.R") source("inat-ranges.R") public_endpoint <- Sys.getenv("AWS_PUBLIC_ENDPOINT", Sys.getenv("AWS_S3_ENDPOINT")) # intialize data load_h3() load_spatial() duckdbfs::duckdb_secrets() inat <- open_dataset("s3://public-inat/hex") taxa <- open_dataset(glue("s3://public-inat/taxonomy/taxa.parquet"), recursive = FALSE) |> rename(taxon_id = id) cache <- tempfile(fileext = ".json") # I/O limited, we can have many more processes than CPU threads con <- duckdbfs::cached_connection() DBI::dbExecute(con, "SET threads = 128;") ###### User interface ###### ui <- page_sidebar( title = "iNaturalist Rangemaps", markdown("Visualize species richness from [iNaturalist Range map datasets](https://www.inaturalist.org/pages/range_maps). Pan & zoom the map over the desired area and hit 'map richness', or draw the desired area with the draw tool. Filter by specific taxonomic ranks or view all 100,293 mapped species. Note that larger areas will be slower to compute. (Area selections that overlap the antimerdian may create visual artefacts). "), shinybusy::add_busy_spinner(), sidebar = sidebar( card( markdown("Filter by taxonomic group or toggle off to see all species."), input_switch("filter", "filter taxa:", value = TRUE), varSelectInput("rank", NULL, taxa, selected = "class"), textInput("taxon", NULL, "Aves") ), actionButton("get_features", "Map richness") ), card( maplibreOutput("map"), ) ) ###### Server ###### server <- function(input, output, session) { # observeEvent(input$map_bbox, { }) # We can react to any zoom/pan on the map output$map <- renderMaplibre({ # Hacky -- we sidecar the metadata here if(file.exists(cache)) { meta <- jsonlite::read_json(cache) print(meta$url) } else { meta <- list(X = -110, Y = 37, zoom = 4, url = paste0("https://", public_endpoint, "/public-data/inat-tmp-ranges.h3j")) } m <- richness_map(meta) m }) observeEvent(input$get_features, { # Use the bbox as the focal area unless user has selected a focal area drawn_features <- get_drawn_features(mapboxgl_proxy("map")) if(nrow(drawn_features) > 0) { aoi <- drawn_features } else if (!is.null(input$map_bbox)){ aoi <- sf::st_bbox(unlist(input$map_bbox), crs = 4326) } else { aoi <- spData::us_states } rank <- taxon <- NULL if (input$filter) { rank <- input$rank taxon <- input$taxon } message("Computing richness...") meta <- richness(inat, aoi, rank, taxon, zoom = input$map_zoom) jsonlite::write_json(meta, cache, auto_unbox = TRUE) message(paste("rendering", meta$url)) session$reload() }) } shinyApp(ui, server)