Spaces:
Runtime error
Runtime error
dynamic pmtiles filterer
Browse files
app.R
CHANGED
@@ -3,7 +3,8 @@ library(bslib)
|
|
3 |
library(markdown)
|
4 |
library(shinychat)
|
5 |
library(mapgl)
|
6 |
-
library(
|
|
|
7 |
library(duckdbfs)
|
8 |
library(fontawesome)
|
9 |
library(bsicons)
|
@@ -14,10 +15,10 @@ duckdbfs::load_spatial()
|
|
14 |
|
15 |
css <- HTML("<link rel='stylesheet' type='text/css' href='https://demos.creative-tim.com/material-dashboard/assets/css/material-dashboard.min.css?v=3.2.0'>")
|
16 |
|
17 |
-
pmtiles <- "https://data.source.coop/cboettig/us-boundaries/mappinginequality.pmtiles"
|
18 |
|
19 |
# Define the UI
|
20 |
ui <- page_sidebar(
|
|
|
21 |
tags$head(css),
|
22 |
titlePanel("Demo App"),
|
23 |
card(
|
@@ -27,7 +28,8 @@ ui <- page_sidebar(
|
|
27 |
"Which county has the highest average social vulnerability?",
|
28 |
width = "100%"),
|
29 |
div(
|
30 |
-
actionButton("user_msg", "", icon = icon("paper-plane"),
|
|
|
31 |
class = "align-text-bottom"),
|
32 |
col_widths = c(11, 1)),
|
33 |
fill = FALSE
|
@@ -38,28 +40,31 @@ ui <- page_sidebar(
|
|
38 |
plotOutput("chart1"),
|
39 |
plotOutput("chart2"),
|
40 |
),
|
41 |
-
|
42 |
-
|
|
|
43 |
),
|
44 |
|
45 |
-
gt_output("table"),
|
46 |
|
|
|
47 |
|
48 |
-
card(fill =
|
49 |
-
|
50 |
-
|
51 |
accordion(
|
52 |
open = FALSE,
|
53 |
-
accordion_panel(
|
|
|
|
|
54 |
verbatimTextOutput("sql_code"),
|
55 |
),
|
56 |
-
accordion_panel(
|
|
|
|
|
57 |
textOutput("explanation"),
|
58 |
)
|
59 |
-
),
|
60 |
-
|
61 |
-
col_widths = c(2, 8, 2)
|
62 |
-
)
|
63 |
),
|
64 |
|
65 |
sidebar = sidebar(
|
@@ -68,13 +73,18 @@ ui <- page_sidebar(
|
|
68 |
input_switch("svi", "Social Vulnerability", value = FALSE),
|
69 |
input_switch("richness", "Biodiversity Richness", value = FALSE),
|
70 |
input_switch("rsr", "Biodiversity Range Size Rarity", value = FALSE),
|
71 |
-
# width = 350,
|
72 |
),
|
|
|
73 |
theme = bs_theme(version = "5")
|
74 |
)
|
75 |
|
76 |
-
|
77 |
-
|
|
|
|
|
|
|
|
|
|
|
78 |
|
79 |
con <- duckdbfs::cached_connection()
|
80 |
schema <- DBI::dbGetQuery(con, "PRAGMA table_info(svi)")
|
@@ -84,9 +94,8 @@ You are a helpful agent who always replies strictly in JSON-formatted text.
|
|
84 |
Your task is to translate the users question into a SQL query that will be run
|
85 |
against the "svi" table in a duckdb database. The duckdb database has a
|
86 |
spatial extension which understands PostGIS operations as well.
|
87 |
-
|
88 |
-
|
89 |
-
Be careful to limit any return to no more than 50 rows.
|
90 |
|
91 |
The table schema is <schema>
|
92 |
|
@@ -100,41 +109,60 @@ Format your answer as follows:
|
|
100 |
}
|
101 |
', .open = "<", .close = ">")
|
102 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
# Define the server
|
104 |
server <- function(input, output, session) {
|
105 |
|
106 |
-
chat <- ellmer::chat_vllm(
|
107 |
-
base_url = "https://llm.nrp-nautilus.io/",
|
108 |
-
model = "llama3",
|
109 |
-
api_key = Sys.getenv("NRP_API_KEY"),
|
110 |
-
system_prompt = system_prompt
|
111 |
-
)
|
112 |
-
|
113 |
observeEvent(input$user_msg, {
|
114 |
stream <- chat$chat(input$chat)
|
115 |
|
116 |
-
|
117 |
-
|
118 |
|
119 |
-
|
|
|
|
|
120 |
output$explanation <- renderText(response$explanation)
|
121 |
|
|
|
122 |
df <- DBI::dbGetQuery(con, response$query)
|
123 |
|
|
|
124 |
df <- df |> select(-any_of("Shape"))
|
125 |
output$table <- render_gt(df, height = 300)
|
126 |
|
|
|
127 |
})
|
128 |
|
129 |
output$map <- renderMaplibre({
|
130 |
-
m <- maplibre(center=c(-92.9, 41.3), zoom=3)
|
131 |
|
132 |
if (input$redlines) {
|
133 |
m <- m |>
|
134 |
add_fill_layer(
|
135 |
id = "redlines",
|
136 |
source = list(type = "vector",
|
137 |
-
url = paste0("pmtiles://", pmtiles)),
|
138 |
source_layer = "mappinginequality",
|
139 |
fill_color = list("get", "fill")
|
140 |
)
|
@@ -163,18 +191,17 @@ server <- function(input, output, session) {
|
|
163 |
if (input$svi) {
|
164 |
m <- m |>
|
165 |
add_fill_layer(
|
166 |
-
id = "
|
167 |
source = list(type = "vector",
|
168 |
url = paste0("pmtiles://", "https://data.source.coop/cboettig/social-vulnerability/svi2020_us_tract.pmtiles")),
|
169 |
source_layer = "SVI2000_US_tract",
|
|
|
170 |
fill_opacity = 0.5,
|
171 |
fill_color = interpolate(column = "RPL_THEMES",
|
172 |
values = c(0, 1),
|
173 |
stops = c("lightblue", "darkblue"),
|
174 |
na_color = "lightgrey")
|
175 |
)
|
176 |
-
|
177 |
-
|
178 |
}
|
179 |
m})
|
180 |
|
|
|
3 |
library(markdown)
|
4 |
library(shinychat)
|
5 |
library(mapgl)
|
6 |
+
library(dplyr)
|
7 |
+
library(ggplot2)
|
8 |
library(duckdbfs)
|
9 |
library(fontawesome)
|
10 |
library(bsicons)
|
|
|
15 |
|
16 |
css <- HTML("<link rel='stylesheet' type='text/css' href='https://demos.creative-tim.com/material-dashboard/assets/css/material-dashboard.min.css?v=3.2.0'>")
|
17 |
|
|
|
18 |
|
19 |
# Define the UI
|
20 |
ui <- page_sidebar(
|
21 |
+
fillable = FALSE, # do not squeeze to vertical screen space
|
22 |
tags$head(css),
|
23 |
titlePanel("Demo App"),
|
24 |
card(
|
|
|
28 |
"Which county has the highest average social vulnerability?",
|
29 |
width = "100%"),
|
30 |
div(
|
31 |
+
actionButton("user_msg", "", icon = icon("paper-plane"),
|
32 |
+
class = "btn-primary btn-sm align-bottom"),
|
33 |
class = "align-text-bottom"),
|
34 |
col_widths = c(11, 1)),
|
35 |
fill = FALSE
|
|
|
40 |
plotOutput("chart1"),
|
41 |
plotOutput("chart2"),
|
42 |
),
|
43 |
+
col_widths = c(8, 4),
|
44 |
+
row_heights = c("600px"),
|
45 |
+
max_height = "700px"
|
46 |
),
|
47 |
|
|
|
48 |
|
49 |
+
gt_output("table"),
|
50 |
|
51 |
+
card(fill = TRUE,
|
52 |
+
card_header(fa("robot")),
|
53 |
+
|
54 |
accordion(
|
55 |
open = FALSE,
|
56 |
+
accordion_panel(
|
57 |
+
title = "show sql",
|
58 |
+
icon = fa("terminal"),
|
59 |
verbatimTextOutput("sql_code"),
|
60 |
),
|
61 |
+
accordion_panel(
|
62 |
+
title = "explain",
|
63 |
+
icon = fa("user", prefer_type="solid"),
|
64 |
textOutput("explanation"),
|
65 |
)
|
66 |
+
),
|
67 |
+
|
|
|
|
|
68 |
),
|
69 |
|
70 |
sidebar = sidebar(
|
|
|
73 |
input_switch("svi", "Social Vulnerability", value = FALSE),
|
74 |
input_switch("richness", "Biodiversity Richness", value = FALSE),
|
75 |
input_switch("rsr", "Biodiversity Range Size Rarity", value = FALSE),
|
|
|
76 |
),
|
77 |
+
|
78 |
theme = bs_theme(version = "5")
|
79 |
)
|
80 |
|
81 |
+
|
82 |
+
|
83 |
+
|
84 |
+
repo <- ""
|
85 |
+
pmtiles <- ""
|
86 |
+
parquet <- "https://data.source.coop/cboettig/social-vulnerability/svi2020_us_tract.parquet"
|
87 |
+
svi <- open_dataset(parquet, tblname = "svi")
|
88 |
|
89 |
con <- duckdbfs::cached_connection()
|
90 |
schema <- DBI::dbGetQuery(con, "PRAGMA table_info(svi)")
|
|
|
94 |
Your task is to translate the users question into a SQL query that will be run
|
95 |
against the "svi" table in a duckdb database. The duckdb database has a
|
96 |
spatial extension which understands PostGIS operations as well.
|
97 |
+
Include semantically meaningful columns like COUNTY and STATE name.
|
98 |
+
|
|
|
99 |
|
100 |
The table schema is <schema>
|
101 |
|
|
|
109 |
}
|
110 |
', .open = "<", .close = ">")
|
111 |
|
112 |
+
chat <- ellmer::chat_vllm(
|
113 |
+
base_url = "https://llm.nrp-nautilus.io/",
|
114 |
+
model = "llama3",
|
115 |
+
api_key = Sys.getenv("NRP_API_KEY"),
|
116 |
+
system_prompt = system_prompt,
|
117 |
+
api_args = list(temperature = 0)
|
118 |
+
)
|
119 |
+
|
120 |
+
# helper utilities
|
121 |
+
df <- tibble()
|
122 |
+
# faster/more scalable to pass maplibre the ids to refilter pmtiles,
|
123 |
+
# than to pass it the full geospatial/sf object
|
124 |
+
filter_column <- function(full_data, filtered_data, id_col = "FIPS") {
|
125 |
+
if (nrow(filtered_data) < 1) return(NULL)
|
126 |
+
values <- full_data |>
|
127 |
+
inner_join(filtered_data, copy = TRUE) |>
|
128 |
+
pull(id_col)
|
129 |
+
# maplibre syntax for the filter of PMTiles
|
130 |
+
list("in", list("get", id_col), list("literal", values))
|
131 |
+
}
|
132 |
+
|
133 |
# Define the server
|
134 |
server <- function(input, output, session) {
|
135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
observeEvent(input$user_msg, {
|
137 |
stream <- chat$chat(input$chat)
|
138 |
|
139 |
+
# optional, remember previous discussion
|
140 |
+
#chat_append("chat", stream)
|
141 |
|
142 |
+
# Parse response
|
143 |
+
response <- jsonlite::fromJSON(stream)
|
144 |
+
output$sql_code <- renderText(stringr::str_wrap(response$query, width = 60))
|
145 |
output$explanation <- renderText(response$explanation)
|
146 |
|
147 |
+
# Actually execute the SQL query generated:
|
148 |
df <- DBI::dbGetQuery(con, response$query)
|
149 |
|
150 |
+
# don't display shape column in render
|
151 |
df <- df |> select(-any_of("Shape"))
|
152 |
output$table <- render_gt(df, height = 300)
|
153 |
|
154 |
+
# We need to somehow trigger this df to update the map.
|
155 |
})
|
156 |
|
157 |
output$map <- renderMaplibre({
|
158 |
+
m <- maplibre(center = c(-92.9, 41.3), zoom = 3, height = "400")
|
159 |
|
160 |
if (input$redlines) {
|
161 |
m <- m |>
|
162 |
add_fill_layer(
|
163 |
id = "redlines",
|
164 |
source = list(type = "vector",
|
165 |
+
url = paste0("pmtiles://", "https://data.source.coop/cboettig/us-boundaries/mappinginequality.pmtiles")),
|
166 |
source_layer = "mappinginequality",
|
167 |
fill_color = list("get", "fill")
|
168 |
)
|
|
|
191 |
if (input$svi) {
|
192 |
m <- m |>
|
193 |
add_fill_layer(
|
194 |
+
id = "svi_layer",
|
195 |
source = list(type = "vector",
|
196 |
url = paste0("pmtiles://", "https://data.source.coop/cboettig/social-vulnerability/svi2020_us_tract.pmtiles")),
|
197 |
source_layer = "SVI2000_US_tract",
|
198 |
+
filter = filter_column(svi, df, "FIPS"),
|
199 |
fill_opacity = 0.5,
|
200 |
fill_color = interpolate(column = "RPL_THEMES",
|
201 |
values = c(0, 1),
|
202 |
stops = c("lightblue", "darkblue"),
|
203 |
na_color = "lightgrey")
|
204 |
)
|
|
|
|
|
205 |
}
|
206 |
m})
|
207 |
|