Spaces:
Sleeping
Sleeping
use URL hashes
Browse files
app.R
CHANGED
@@ -13,15 +13,16 @@ library(overture)
|
|
13 |
source("utils.R")
|
14 |
source("inat-ranges.R")
|
15 |
|
|
|
16 |
|
17 |
ui <- page_sidebar(
|
18 |
shinybusy::add_busy_spinner(),
|
19 |
|
20 |
title = "iNaturalist Species Ranges",
|
21 |
sidebar = sidebar(
|
22 |
-
textInput("location", "Location", "
|
23 |
-
varSelectInput("rank", NULL, taxa, selected = "
|
24 |
-
textInput("taxon", NULL, "
|
25 |
actionButton("button", "Go")
|
26 |
),
|
27 |
card(
|
@@ -32,24 +33,16 @@ ui <- page_sidebar(
|
|
32 |
|
33 |
server <- function(input, output, session) {
|
34 |
output$map <- renderMaplibre({
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
} else {
|
39 |
-
aoi <- get_division(input$location)
|
40 |
-
meta <- richness(inat, aoi, rank = input$rank, taxon = input$taxon)
|
41 |
-
}
|
42 |
-
m <- richness_map(meta)
|
43 |
m
|
44 |
})
|
45 |
|
46 |
observeEvent(input$button, {
|
47 |
print(input$location)
|
48 |
aoi <- get_division(input$location)
|
49 |
-
|
50 |
-
|
51 |
-
jsonlite::write_json(meta, cache, auto_unbox = TRUE)
|
52 |
-
message(paste("rendering", meta$url))
|
53 |
|
54 |
session$reload()
|
55 |
})
|
|
|
13 |
source("utils.R")
|
14 |
source("inat-ranges.R")
|
15 |
|
16 |
+
duckdb_config(threads = 24) # I/O limited so overclock threads
|
17 |
|
18 |
ui <- page_sidebar(
|
19 |
shinybusy::add_busy_spinner(),
|
20 |
|
21 |
title = "iNaturalist Species Ranges",
|
22 |
sidebar = sidebar(
|
23 |
+
textInput("location", "Location", "United States"),
|
24 |
+
varSelectInput("rank", NULL, taxa, selected = "class"),
|
25 |
+
textInput("taxon", NULL, "Aves"),
|
26 |
actionButton("button", "Go")
|
27 |
),
|
28 |
card(
|
|
|
33 |
|
34 |
server <- function(input, output, session) {
|
35 |
output$map <- renderMaplibre({
|
36 |
+
aoi <- get_division(input$location)
|
37 |
+
url <- richness(inat, aoi, rank = input$rank, taxon = input$taxon)
|
38 |
+
m <- richness_map(url, aoi)
|
|
|
|
|
|
|
|
|
|
|
39 |
m
|
40 |
})
|
41 |
|
42 |
observeEvent(input$button, {
|
43 |
print(input$location)
|
44 |
aoi <- get_division(input$location)
|
45 |
+
richness(inat, aoi, rank = input$rank, taxon = input$taxon)
|
|
|
|
|
|
|
46 |
|
47 |
session$reload()
|
48 |
})
|
inat-ranges.R
CHANGED
@@ -23,8 +23,9 @@ taxa <- duckdbfs::open_dataset(
|
|
23 |
recursive = FALSE
|
24 |
)
|
25 |
|
26 |
-
|
27 |
-
|
|
|
28 |
|
29 |
# Also requires get_h3_aoi() from utils.R
|
30 |
richness <- function(inat, aoi, rank = NULL, taxon = NULL, zoom = 3) {
|
@@ -32,12 +33,25 @@ richness <- function(inat, aoi, rank = NULL, taxon = NULL, zoom = 3) {
|
|
32 |
"AWS_PUBLIC_ENDPOINT",
|
33 |
Sys.getenv("AWS_S3_ENDPOINT")
|
34 |
)
|
35 |
-
hash <-
|
36 |
s3 <- paste0("s3://public-data/cache/inat/", hash, ".h3j")
|
|
|
37 |
|
38 |
# check if hash exists
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
|
40 |
-
#
|
41 |
if (!is.null(rank) && !is.null(taxon)) {
|
42 |
taxa <- open_dataset(
|
43 |
glue("s3://public-inat/taxonomy/taxa.parquet"),
|
@@ -50,41 +64,33 @@ richness <- function(inat, aoi, rank = NULL, taxon = NULL, zoom = 3) {
|
|
50 |
inner_join(inat, by = "taxon_id")
|
51 |
}
|
52 |
|
53 |
-
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
mutate(height = n / max(n)) |>
|
63 |
-
duckdbfs::to_h3j(s3)
|
64 |
-
# write_dataset("s3://public-data/inat-tmp-ranges.parquet")
|
65 |
-
})
|
66 |
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
69 |
|
70 |
-
url
|
71 |
-
|
72 |
-
meta <- list(
|
73 |
-
X = center[1],
|
74 |
-
Y = center[2],
|
75 |
-
zoom = zoom,
|
76 |
-
url = url,
|
77 |
-
time = clock[[2]],
|
78 |
-
bounds = bounds
|
79 |
-
)
|
80 |
-
return(meta)
|
81 |
}
|
82 |
|
83 |
-
richness_map <- function(
|
|
|
84 |
m <-
|
85 |
maplibre() |>
|
86 |
add_draw_control() |>
|
87 |
-
add_h3j_source("h3j_source", url =
|
88 |
add_fill_extrusion_layer(
|
89 |
id = "h3j_layer",
|
90 |
source = "h3j_source",
|
@@ -101,7 +107,7 @@ richness_map <- function(meta) {
|
|
101 |
),
|
102 |
fill_extrusion_opacity = 0.7
|
103 |
) |>
|
104 |
-
fit_bounds(
|
105 |
|
106 |
return(m)
|
107 |
}
|
|
|
23 |
recursive = FALSE
|
24 |
)
|
25 |
|
26 |
+
get_hash <- function(aoi, rank, taxon) {
|
27 |
+
digest::digest(list(aoi, rank, taxon))
|
28 |
+
}
|
29 |
|
30 |
# Also requires get_h3_aoi() from utils.R
|
31 |
richness <- function(inat, aoi, rank = NULL, taxon = NULL, zoom = 3) {
|
|
|
33 |
"AWS_PUBLIC_ENDPOINT",
|
34 |
Sys.getenv("AWS_S3_ENDPOINT")
|
35 |
)
|
36 |
+
hash <- get_hash(aoi, rank, taxon)
|
37 |
s3 <- paste0("s3://public-data/cache/inat/", hash, ".h3j")
|
38 |
+
url <- gsub("s3://", glue("https://{public_endpoint}/"), s3)
|
39 |
|
40 |
# check if hash exists
|
41 |
+
cache_hit <- tryCatch(
|
42 |
+
{
|
43 |
+
duckdbfs::open_dataset(s3)
|
44 |
+
TRUE
|
45 |
+
},
|
46 |
+
error = function(e) FALSE,
|
47 |
+
finally = FALSE
|
48 |
+
)
|
49 |
+
|
50 |
+
if (cache_hit) {
|
51 |
+
return(url)
|
52 |
+
}
|
53 |
|
54 |
+
# Subset by taxon, if requested
|
55 |
if (!is.null(rank) && !is.null(taxon)) {
|
56 |
taxa <- open_dataset(
|
57 |
glue("s3://public-inat/taxonomy/taxa.parquet"),
|
|
|
64 |
inner_join(inat, by = "taxon_id")
|
65 |
}
|
66 |
|
67 |
+
inat <- inat |> rename(h3id = h4)
|
68 |
|
69 |
+
# Subset by area, if requested
|
70 |
+
if (nrow(aoi) > 0) {
|
71 |
+
inat <-
|
72 |
+
get_h3_aoi(aoi, precision = 4) |>
|
73 |
+
select(h3id) |>
|
74 |
+
inner_join(inat, by = "h3id")
|
75 |
+
}
|
|
|
|
|
|
|
|
|
76 |
|
77 |
+
# Aggregate to h4 hex
|
78 |
+
inat |>
|
79 |
+
distinct(taxon_id, h3id) |>
|
80 |
+
group_by(h3id) |>
|
81 |
+
summarise(n = n()) |>
|
82 |
+
mutate(height = n / max(n)) |>
|
83 |
+
duckdbfs::to_h3j(s3)
|
84 |
|
85 |
+
return(url)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
}
|
87 |
|
88 |
+
richness_map <- function(url, gdf) {
|
89 |
+
bounds <- as.vector(sf::st_bbox(gdf))
|
90 |
m <-
|
91 |
maplibre() |>
|
92 |
add_draw_control() |>
|
93 |
+
add_h3j_source("h3j_source", url = url) |>
|
94 |
add_fill_extrusion_layer(
|
95 |
id = "h3j_layer",
|
96 |
source = "h3j_source",
|
|
|
107 |
),
|
108 |
fill_extrusion_opacity = 0.7
|
109 |
) |>
|
110 |
+
fit_bounds(bounds, animate = TRUE)
|
111 |
|
112 |
return(m)
|
113 |
}
|
test.R
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
|
2 |
source("utils.R")
|
3 |
source("inat-ranges.R")
|
4 |
|
@@ -9,26 +8,25 @@ url <- "https://minio.carlboettiger.info/public-data/cache/inat/f0108ef86feababf
|
|
9 |
|
10 |
#x <- jsonlite::read_json(url)
|
11 |
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
)
|
32 |
|
33 |
|
34 |
htmlwidgets::saveWidget(m, "test2.html")
|
@@ -81,54 +79,44 @@ htmlwidgets::saveWidget(m, "mammals-richness.html")
|
|
81 |
|
82 |
# mutate(h3 = h3_cell_to_parent(h4, 3L))
|
83 |
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
|
98 |
|
99 |
library(htmlwidgets)
|
100 |
htmlwidgets::saveWidget(m, "example.html")
|
101 |
|
102 |
|
|
|
|
|
|
|
|
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
amphib = open_dataset("s3://public-inat/polygon/Amphibia.parquet", recursive = FALSE)
|
114 |
-
|
115 |
-
gdf <- amphib |>
|
116 |
-
filter(name == "Ambystoma californiense") |>
|
117 |
-
to_sf(crs=4326)
|
118 |
|
119 |
maplibre(center = c(-122.5, 37.8), zoom = 4) |>
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
|
133 |
|
134 |
# Access SVI
|
@@ -144,30 +132,29 @@ ca <- tracts |>
|
|
144 |
mutate(h4 = tolower(as.character(h4)))
|
145 |
|
146 |
out <- ca |>
|
147 |
-
|
148 |
-
|
149 |
|
150 |
|
151 |
# mutate(height = n / max(n)) |>
|
152 |
|
153 |
url = "https://minio.carlboettiger.info/public-data/cache/inat/cec4b3087f0b6c41ecc384da2521f97c.h3j"
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
)
|
|
|
|
|
1 |
source("utils.R")
|
2 |
source("inat-ranges.R")
|
3 |
|
|
|
8 |
|
9 |
#x <- jsonlite::read_json(url)
|
10 |
|
11 |
+
m = maplibre(center = c(-110, 37), zoom = 3) |>
|
12 |
+
add_draw_control() |>
|
13 |
+
add_h3j_source("h3j_source", url = url) |>
|
14 |
+
add_fill_extrusion_layer(
|
15 |
+
id = "h3j_layer",
|
16 |
+
source = "h3j_source",
|
17 |
+
tooltip = "n",
|
18 |
+
fill_extrusion_color = viridis_pal("height"),
|
19 |
+
fill_extrusion_height = list(
|
20 |
+
"interpolate",
|
21 |
+
list("linear"),
|
22 |
+
list("zoom"),
|
23 |
+
0,
|
24 |
+
0,
|
25 |
+
1,
|
26 |
+
list("*", 100000, list("get", "height"))
|
27 |
+
),
|
28 |
+
fill_extrusion_opacity = 0.7
|
29 |
+
)
|
|
|
30 |
|
31 |
|
32 |
htmlwidgets::saveWidget(m, "test2.html")
|
|
|
79 |
|
80 |
# mutate(h3 = h3_cell_to_parent(h4, 3L))
|
81 |
|
82 |
+
m <- maplibre(center = c(-110.5, 34.8), zoom = 4) |> add_draw_control()
|
83 |
+
richness_map(
|
84 |
+
m,
|
85 |
+
"https://minio.carlboettiger.info/public-data/inat-tmp-ranges.h3j"
|
86 |
+
)
|
87 |
|
88 |
|
89 |
+
install.packages(
|
90 |
+
'spDataLarge',
|
91 |
+
repos = 'https://nowosad.github.io/drat/',
|
92 |
+
type = 'source'
|
93 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
|
95 |
|
96 |
library(htmlwidgets)
|
97 |
htmlwidgets::saveWidget(m, "example.html")
|
98 |
|
99 |
|
100 |
+
amphib = open_dataset(
|
101 |
+
"s3://public-inat/polygon/Amphibia.parquet",
|
102 |
+
recursive = FALSE
|
103 |
+
)
|
104 |
|
105 |
+
gdf <- amphib |>
|
106 |
+
filter(name == "Ambystoma californiense") |>
|
107 |
+
to_sf(crs = 4326)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
|
109 |
maplibre(center = c(-122.5, 37.8), zoom = 4) |>
|
110 |
+
add_source(id = "gdf", gdf) |>
|
111 |
+
add_layer(
|
112 |
+
"gdf-layer",
|
113 |
+
type = "fill",
|
114 |
+
source = "gdf",
|
115 |
+
paint = list(
|
116 |
+
"fill-color" = "darkgreen",
|
117 |
+
"fill-opacity" = .9
|
118 |
+
)
|
119 |
+
)
|
|
|
|
|
120 |
|
121 |
|
122 |
# Access SVI
|
|
|
132 |
mutate(h4 = tolower(as.character(h4)))
|
133 |
|
134 |
out <- ca |>
|
135 |
+
inner_join(inat, by = "h4") |>
|
136 |
+
count(STATE, COUNTY, FIPS, h5)
|
137 |
|
138 |
|
139 |
# mutate(height = n / max(n)) |>
|
140 |
|
141 |
url = "https://minio.carlboettiger.info/public-data/cache/inat/cec4b3087f0b6c41ecc384da2521f97c.h3j"
|
142 |
+
maplibre() |>
|
143 |
+
add_draw_control() |>
|
144 |
+
add_h3j_source("h3j_source", url = url) |>
|
145 |
+
add_fill_extrusion_layer(
|
146 |
+
id = "h3j_layer",
|
147 |
+
source = "h3j_source",
|
148 |
+
tooltip = "n",
|
149 |
+
fill_extrusion_color = viridis_pal("height"),
|
150 |
+
fill_extrusion_height = list(
|
151 |
+
"interpolate",
|
152 |
+
list("linear"),
|
153 |
+
list("zoom"),
|
154 |
+
0,
|
155 |
+
0,
|
156 |
+
1,
|
157 |
+
list("*", 100000, list("get", "height"))
|
158 |
+
),
|
159 |
+
fill_extrusion_opacity = 0.7
|
160 |
+
)
|
|
utils.R
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
|
2 |
library(dplyr)
|
3 |
library(duckdbfs)
|
4 |
library(sf)
|
@@ -6,7 +5,9 @@ library(sf)
|
|
6 |
as_dataset.sf <- function(sf, ...) {
|
7 |
# cludgy way to get polygon into duckdb as spatial data
|
8 |
tmp <- tempfile(fileext = ".fgb")
|
9 |
-
sf |>
|
|
|
|
|
10 |
aoi <- duckdbfs::open_dataset(tmp, ...)
|
11 |
|
12 |
aoi
|
@@ -14,23 +15,20 @@ as_dataset.sf <- function(sf, ...) {
|
|
14 |
|
15 |
# promote bbox to polygon
|
16 |
as_poly <- function(aoi) {
|
17 |
-
|
18 |
crs <- st_crs(aoi)
|
19 |
-
if (crs$input != "EPSG:4326"
|
20 |
aoi <- aoi |> st_transform(4326)
|
21 |
-
|
22 |
-
}
|
23 |
if (inherits(aoi, "bbox")) {
|
24 |
-
aoi <- aoi |>
|
25 |
-
st_as_sfc() |>
|
26 |
-
st_as_sf() |>
|
27 |
rename(geom = x)
|
28 |
}
|
29 |
-
aoi
|
30 |
}
|
31 |
|
32 |
|
33 |
-
|
34 |
get_h3_aoi <- function(aoi, zoom = 0L, precision = 6L, upper = FALSE) {
|
35 |
duckdbfs::load_h3()
|
36 |
|
@@ -39,23 +37,30 @@ get_h3_aoi <- function(aoi, zoom = 0L, precision = 6L, upper = FALSE) {
|
|
39 |
precision <- as.integer(precision)
|
40 |
res <- paste0("h", precision)
|
41 |
|
42 |
-
if(inherits(aoi, "sf") || inherits(aoi, "bbox")) {
|
43 |
aoi <- as_poly(aoi)
|
44 |
aoi <- as_dataset.sf(aoi)
|
45 |
}
|
46 |
|
47 |
# multipolygon dump may not be needed for draw tools.
|
48 |
h3_aoi <- aoi |>
|
49 |
-
dplyr::mutate(
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
}
|
60 |
|
61 |
h3_aoi |>
|
@@ -64,68 +69,77 @@ get_h3_aoi <- function(aoi, zoom = 0L, precision = 6L, upper = FALSE) {
|
|
64 |
}
|
65 |
|
66 |
hex_res <- function(x) {
|
67 |
-
|
68 |
utils::head(1) |>
|
69 |
dplyr::mutate(res = h3_get_resolution(h3id)) |>
|
70 |
dplyr::pull(res)
|
71 |
}
|
72 |
|
73 |
-
hex_join <- function(x,y) {
|
74 |
res_x <- hex_res(x)
|
75 |
res_y <- hex_res(y)
|
76 |
|
77 |
if (res_x > res_y) {
|
78 |
-
y <- y |>
|
79 |
-
dplyr::mutate(
|
80 |
-
|
|
|
|
|
|
|
|
|
81 |
}
|
82 |
-
|
83 |
-
y <- y |>
|
84 |
-
dplyr::mutate(
|
|
|
|
|
|
|
|
|
85 |
}
|
86 |
|
87 |
dplyr::inner_join(x, y)
|
88 |
}
|
89 |
|
90 |
-
|
91 |
antimeridian_hexes <- function(zoom = 4, con = duckdbfs::cached_connection()) {
|
92 |
duckdbfs::load_h3(con)
|
93 |
duckdbfs::load_spatial(con)
|
94 |
-
DBI::dbExecute(
|
95 |
-
|
|
|
96 |
CREATE OR REPLACE TABLE antimeridian AS (
|
97 |
SELECT ST_GeomFromText(
|
98 |
'POLYGON ((170 85, 190 85, 190 -85, 170 -85, 170 85))'
|
99 |
) AS geometry
|
100 |
)
|
101 |
-
"
|
|
|
102 |
zoom <- as.integer(zoom)
|
103 |
-
|
104 |
-
am <-
|
105 |
dplyr::tbl(con, "antimeridian") |>
|
106 |
-
dplyr::mutate(
|
107 |
-
|
108 |
-
|
|
|
|
|
|
|
|
|
109 |
dplyr::select(h3id)
|
110 |
|
111 |
-
am
|
112 |
}
|
113 |
|
114 |
|
115 |
-
viridis_pal <-
|
116 |
-
function(column = "height",
|
117 |
-
|
118 |
-
|
119 |
-
max_v = 1) {
|
120 |
-
pal <- viridisLite::viridis(n)
|
121 |
-
fill_color = mapgl::step_expr(
|
122 |
column = column,
|
123 |
base = pal[1],
|
124 |
stops = pal[2:n],
|
125 |
-
values = seq(min_v, max_v, length.out = n-1),
|
126 |
na_color = "white"
|
127 |
)
|
128 |
-
|
129 |
-
fill_color
|
130 |
-
}
|
131 |
|
|
|
|
|
|
|
|
1 |
library(dplyr)
|
2 |
library(duckdbfs)
|
3 |
library(sf)
|
|
|
5 |
as_dataset.sf <- function(sf, ...) {
|
6 |
# cludgy way to get polygon into duckdb as spatial data
|
7 |
tmp <- tempfile(fileext = ".fgb")
|
8 |
+
sf |>
|
9 |
+
sf::st_transform(4326) |>
|
10 |
+
sf::write_sf(tmp, append = FALSE, quiet = TRUE)
|
11 |
aoi <- duckdbfs::open_dataset(tmp, ...)
|
12 |
|
13 |
aoi
|
|
|
15 |
|
16 |
# promote bbox to polygon
|
17 |
as_poly <- function(aoi) {
|
|
|
18 |
crs <- st_crs(aoi)
|
19 |
+
if (crs$input != "EPSG:4326") {
|
20 |
aoi <- aoi |> st_transform(4326)
|
21 |
+
}
|
|
|
22 |
if (inherits(aoi, "bbox")) {
|
23 |
+
aoi <- aoi |>
|
24 |
+
st_as_sfc() |>
|
25 |
+
st_as_sf() |>
|
26 |
rename(geom = x)
|
27 |
}
|
28 |
+
aoi
|
29 |
}
|
30 |
|
31 |
|
|
|
32 |
get_h3_aoi <- function(aoi, zoom = 0L, precision = 6L, upper = FALSE) {
|
33 |
duckdbfs::load_h3()
|
34 |
|
|
|
37 |
precision <- as.integer(precision)
|
38 |
res <- paste0("h", precision)
|
39 |
|
40 |
+
if (inherits(aoi, "sf") || inherits(aoi, "bbox")) {
|
41 |
aoi <- as_poly(aoi)
|
42 |
aoi <- as_dataset.sf(aoi)
|
43 |
}
|
44 |
|
45 |
# multipolygon dump may not be needed for draw tools.
|
46 |
h3_aoi <- aoi |>
|
47 |
+
dplyr::mutate(
|
48 |
+
poly = array_extract(unnest(st_dump(geom)), "geom"),
|
49 |
+
h3id = h3_polygon_wkt_to_cells(poly, {
|
50 |
+
precision
|
51 |
+
}),
|
52 |
+
h3id = unnest(h3id)
|
53 |
+
) |>
|
54 |
+
dplyr::mutate(
|
55 |
+
h0 = h3_h3_to_string(h3_cell_to_parent(h3id, {
|
56 |
+
zoom
|
57 |
+
})),
|
58 |
+
h3id = h3_h3_to_string(h3id)
|
59 |
+
)
|
60 |
+
|
61 |
+
if (upper) {
|
62 |
+
h3_aoi <- h3_aoi |>
|
63 |
+
dplyr::mutate(h0 = toupper(h0), h3id = toupper(h3id))
|
64 |
}
|
65 |
|
66 |
h3_aoi |>
|
|
|
69 |
}
|
70 |
|
71 |
hex_res <- function(x) {
|
72 |
+
x |>
|
73 |
utils::head(1) |>
|
74 |
dplyr::mutate(res = h3_get_resolution(h3id)) |>
|
75 |
dplyr::pull(res)
|
76 |
}
|
77 |
|
78 |
+
hex_join <- function(x, y) {
|
79 |
res_x <- hex_res(x)
|
80 |
res_y <- hex_res(y)
|
81 |
|
82 |
if (res_x > res_y) {
|
83 |
+
y <- y |>
|
84 |
+
dplyr::mutate(
|
85 |
+
h3id = unnest(h3_cell_to_children(h3id, {
|
86 |
+
res_x
|
87 |
+
})),
|
88 |
+
h3id = toupper(h3id)
|
89 |
+
)
|
90 |
}
|
91 |
+
if (res_x < res_y) {
|
92 |
+
y <- y |>
|
93 |
+
dplyr::mutate(
|
94 |
+
h3id = h3_cell_to_parent(h3id, {
|
95 |
+
res_x
|
96 |
+
})
|
97 |
+
)
|
98 |
}
|
99 |
|
100 |
dplyr::inner_join(x, y)
|
101 |
}
|
102 |
|
|
|
103 |
antimeridian_hexes <- function(zoom = 4, con = duckdbfs::cached_connection()) {
|
104 |
duckdbfs::load_h3(con)
|
105 |
duckdbfs::load_spatial(con)
|
106 |
+
DBI::dbExecute(
|
107 |
+
con,
|
108 |
+
"
|
109 |
CREATE OR REPLACE TABLE antimeridian AS (
|
110 |
SELECT ST_GeomFromText(
|
111 |
'POLYGON ((170 85, 190 85, 190 -85, 170 -85, 170 85))'
|
112 |
) AS geometry
|
113 |
)
|
114 |
+
"
|
115 |
+
)
|
116 |
zoom <- as.integer(zoom)
|
117 |
+
|
118 |
+
am <-
|
119 |
dplyr::tbl(con, "antimeridian") |>
|
120 |
+
dplyr::mutate(
|
121 |
+
h3id = unnest(
|
122 |
+
h3_polygon_wkt_to_cells_string(geometry, {
|
123 |
+
zoom
|
124 |
+
})
|
125 |
+
)
|
126 |
+
) |>
|
127 |
dplyr::select(h3id)
|
128 |
|
129 |
+
am
|
130 |
}
|
131 |
|
132 |
|
133 |
+
viridis_pal <-
|
134 |
+
function(column = "height", n = 61, min_v = 0, max_v = 1) {
|
135 |
+
pal <- viridisLite::viridis(n)
|
136 |
+
fill_color = mapgl::step_expr(
|
|
|
|
|
|
|
137 |
column = column,
|
138 |
base = pal[1],
|
139 |
stops = pal[2:n],
|
140 |
+
values = seq(min_v, max_v, length.out = n - 1),
|
141 |
na_color = "white"
|
142 |
)
|
|
|
|
|
|
|
143 |
|
144 |
+
fill_color
|
145 |
+
}
|