File size: 2,975 Bytes
66a02d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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"),
      textInput("aoi", "Location", "California"),
    ),
    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
    # Cache at the URL level instead?
    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)