Skip to content

Commit 24714e3

Browse files
authored
Merge pull request #7 from boettiger-lab/feat/update_app
fixing chatbot and bar charts
2 parents e615c68 + 87ac526 commit 24714e3

4 files changed

Lines changed: 55 additions & 13 deletions

File tree

app/app.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import streamlit as st
22
from cng.h3 import *
3-
from ibis import _
43
import importlib
54
from datetime import time
5+
import traceback
6+
import ibis.selectors as s
7+
from ibis import _
8+
import ibis
9+
import openai
610

711
st.set_page_config(layout="wide",
812
page_title="TPL Conservation Almanac",
@@ -50,6 +54,13 @@
5054
county_choice = 'All'
5155
st.divider()
5256

57+
with st.sidebar:
58+
with st.popover("ℹ️ Help"):
59+
st.markdown(help_message)
60+
if st.button("🧹 Clear Filters", type="secondary", help = 'Reset all the filters to their default state.'):
61+
st.rerun()
62+
st.divider()
63+
5364
legend, position, bg_color, fontsize, shape_type, controls = get_legend(paint, leafmap_choice)
5465
# get all the ids that correspond to the filter
5566
gdf = filter_data(tpl_table, state_choice, county_choice, year_range)
@@ -65,6 +76,7 @@
6576
'''
6677
Mapping queries:
6778
- Show me the most expensive protected site
79+
- Show me protected areas with high levels of carbon in Florida
6880
- Show me sites owned, managed or sponsored by the Trust for Public Land
6981
'''
7082

@@ -165,11 +177,11 @@ def run_sql(query, llm_choice):
165177

166178
# extract ids, columns, bounds if present
167179
if "fid" in llm_output.columns and not llm_output.empty:
168-
ids = list(set(llm_output['fid'].tolist()))
180+
unique_ids = list(set(llm_output['fid'].tolist()))
169181
llm_cols = extract_columns(sql_query)
170-
bounds = llm_output.total_bounds.tolist()
182+
llm_bounds = llm_output.total_bounds.tolist()
171183
else:
172-
ids, llm_cols = [], []
184+
unique_ids, llm_cols = [], []
173185
not_mapping = True
174186

175187
except Exception as e:
@@ -196,13 +208,16 @@ def run_sql(query, llm_choice):
196208

197209
# define PMTiles style dict (if we didn't already do so using the chatbot)
198210
if 'style' not in locals():
199-
if one_state:
211+
if one_state or ('llm_output' in locals()):
200212
# filter to ids in that state
201213
style = tpl_style(unique_ids, paint, pmtiles)
202214
else:
203215
# selected all states, so no need to filter
204216
style=tpl_style_default(paint, pmtiles)
205-
bounds = get_bounds(state_choice, county_choice, m)
217+
if 'llm_output' in locals():
218+
bounds = llm_bounds
219+
else:
220+
bounds = get_bounds(state_choice, county_choice, m)
206221

207222
# add pmtiles to map (using user-specified module)
208223
if leafmap_choice == "maplibregl":
@@ -277,7 +292,7 @@ def run_sql(query, llm_choice):
277292
get_bar(gdf_tpl, style_choice, 'year', 'total_amount', paint,'Year','Acquisition Cost ($)',"Yearly investment ($) in protected area")
278293

279294
with col2:
280-
gdf_landvote = group_data(gdf_landvote.filter(_.status == 'Pass'), 'Measure Cost')
295+
gdf_landvote = group_data(gdf_landvote.filter(gdf_landvote.status == 'Pass'), 'Measure Cost')
281296
get_bar(gdf_landvote, style_choice, 'year', 'total_amount', paint, 'Year','Funds Approved ($)','Yearly funds from conservation ballot measures')
282297

283298
st.divider()

app/system_prompt.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,19 @@ example_assistant: {{"sql_query":
6565
LEFT JOIN carbon
6666
USING (h8)
6767
GROUP BY "fid", "site", geom"
68-
ORDER BY "mean_carbon" DESC LIMIT 10;
68+
ORDER BY "mean_carbon" DESC LIMIT 100;
6969
"explanation":"I joined `conservation_almanac` with `carbon` to retrieve irrecoverable carbon levels for protected areas in the `conservation_almanac`. I returned 10 areas with highest levels of carbon.
7070
}}
7171

72+
## Example:
73+
example_user: "Show me protected areas with high species richness"
74+
example_assistant: {{"sql_query":
75+
SELECT "fid", "geom", "site", AVG("richness") AS "mean_richness"
76+
FROM conservation_almanac
77+
LEFT JOIN richness
78+
USING (h8)
79+
GROUP BY "fid", "site", geom"
80+
ORDER BY "mean_richness" DESC LIMIT 100;
81+
"explanation":"I joined `conservation_almanac` with `richness` to retrieve species richness for protected areas in the `conservation_almanac`. I returned 100 areas with highest levels of carbon.
82+
}}
83+

app/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import re
66
from leafmap.foliumap import PMTilesMapLibreTooltip
77
from branca.element import Template
8+
import pandas as pd
9+
import datetime
810

911
def get_counties(state_selection):
1012
if state_selection != 'All':
@@ -112,6 +114,14 @@ def tpl_style(ids, paint, pmtiles):
112114
}
113115
return style
114116

117+
118+
119+
120+
def extract_columns(sql_query):
121+
# Find all substrings inside double quotes
122+
columns = list(dict.fromkeys(re.findall(r'"(.*?)"', sql_query)))
123+
return columns
124+
115125
def get_colorbar(gdf, paint):
116126
"""
117127
Extracts color hex codes and value range (vmin, vmax) from paint

app/variables.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from cng.utils import *
66
from cng.h3 import *
77
from minio import Minio
8-
import streamlit
8+
import streamlit as st
99
from datetime import datetime, timedelta
1010
import streamlit
1111
import re
@@ -16,9 +16,9 @@
1616
set_secrets(con)
1717

1818
# Get signed URLs to access license-controlled layers
19-
# key = st.secrets["MINIO_KEY"]
20-
# secret = st.secrets["MINIO_SECRET"]
21-
# client = Minio("minio.carlboettiger.info", key, secret)
19+
key = st.secrets["MINIO_KEY"]
20+
secret = st.secrets["MINIO_SECRET"]
21+
client = Minio("minio.carlboettiger.info", key, secret)
2222

2323
pmtiles = "https://minio.carlboettiger.info/public-tpl/conservation_almanac/tpl.pmtiles"
2424
tpl_z8_url = "https://minio.carlboettiger.info/public-tpl/conservation_almanac/z8/tpl_h3_z8.parquet"
@@ -159,6 +159,11 @@
159159
# ['Forestry','Historical','Unknown','Other','Farming','Recreation','Environment','Scenic','RAN'],
160160
# }
161161

162+
help_message = '''
163+
- ❌ Safari/iOS not fully supported. For Safari/iOS users, change the **Leafmap module** from MapLibre to Folium in **(Map Settings)** below.
164+
- 📊 Use this sidebar to color-code the map by different attributes **(Group by)**
165+
- 💬 For a more tailored experience, query our dataset of protected areas and their precomputed metrics for each of the displayed layers, using the experimental chatbot. The language model tries to answer natural language questions by drawing only from curated datasets (listed below).
166+
'''
162167

163168
#maplibregl tooltip
164169
tooltip_cols = ['fid','state','site','sponsor','program','county','year','manager',
@@ -249,9 +254,9 @@
249254
openrouter_api = st.secrets["OPENROUTER_API_KEY"]
250255

251256
llm_options = {
257+
"gpt-oss-20b": ChatOpenAI(model = "openai/gpt-oss-20b:free", api_key=openrouter_api, base_url = "https://openrouter.ai/api/v1", temperature=0),
252258
"mistral-small-3.2-24b-instruct": ChatOpenAI(model = "mistralai/mistral-small-3.2-24b-instruct:free", api_key=openrouter_api, base_url = "https://openrouter.ai/api/v1", temperature=0),
253259
"devstral-small-2505": ChatOpenAI(model = "mistralai/devstral-small-2505:free", api_key=openrouter_api, base_url = "https://openrouter.ai/api/v1", temperature=0),
254-
"gpt-oss-20b": ChatOpenAI(model = "openai/gpt-oss-20b:free", api_key=openrouter_api, base_url = "https://openrouter.ai/api/v1", temperature=0),
255260
"deepseek-r1t2-chimera": ChatOpenAI(model = "tngtech/deepseek-r1t2-chimera:free", api_key=openrouter_api, base_url = "https://openrouter.ai/api/v1", temperature=0),
256261
"kimi-dev-72b": ChatOpenAI(model = "moonshotai/kimi-dev-72b:free", api_key=openrouter_api, base_url = "https://openrouter.ai/api/v1", temperature=0),
257262
"hunyuan-a13b-instruct": ChatOpenAI(model = "tencent/hunyuan-a13b-instruct:free", api_key=openrouter_api, base_url = "https://openrouter.ai/api/v1", temperature=0),

0 commit comments

Comments
 (0)