Spaces:
Sleeping
Sleeping
:see_no_evil:
Browse files- app.py +27 -20
- utilities.py +5 -31
app.py
CHANGED
@@ -7,6 +7,7 @@ from utilities import *
|
|
7 |
import leafmap.maplibregl as leafmap
|
8 |
import requests
|
9 |
import geopandas as gpd
|
|
|
10 |
|
11 |
st.set_page_config(page_title="Redlining & GBIF", layout="wide")
|
12 |
st.title("Redlining & GBIF")
|
@@ -84,7 +85,6 @@ with st.form("my_form"):
|
|
84 |
@st.cache_data
|
85 |
def compute_hexes(_gdf, gdf_name, rank, taxa, zoom, distinct_taxa = ""):
|
86 |
|
87 |
-
# FIXME check if dest exists in cache
|
88 |
dest = unique_path(gdf_name, rank, taxa, zoom, distinct_taxa)
|
89 |
bucket = "public-gbif"
|
90 |
url = base_url + f"/{bucket}/" + dest
|
@@ -93,7 +93,10 @@ def compute_hexes(_gdf, gdf_name, rank, taxa, zoom, distinct_taxa = ""):
|
|
93 |
if response.status_code != 404:
|
94 |
return url
|
95 |
|
96 |
-
sel = con
|
|
|
|
|
|
|
97 |
|
98 |
sel = (sel
|
99 |
.rename(hex = "h" + str(zoom)) # h3 == 41,150 hexes. h5 == 2,016,830 hexes
|
@@ -119,13 +122,14 @@ def compute_hexes(_gdf, gdf_name, rank, taxa, zoom, distinct_taxa = ""):
|
|
119 |
|
120 |
|
121 |
|
122 |
-
import altair as alt
|
123 |
-
|
124 |
@st.cache_data
|
125 |
def bar_chart(gdf_name, rank, taxa, zoom, distinct_taxa = ""):
|
126 |
sel = con.read_parquet("s3://public-gbif/app/redlined_cities_gbif.parquet")
|
127 |
-
sel = sel
|
128 |
-
|
|
|
|
|
|
|
129 |
if gdf_name != "All":
|
130 |
sel = sel.filter(_.city == gdf_name)
|
131 |
|
@@ -136,7 +140,7 @@ def bar_chart(gdf_name, rank, taxa, zoom, distinct_taxa = ""):
|
|
136 |
else:
|
137 |
sel = sel.agg(n = _.count(), area = _.area.sum())
|
138 |
sel = (sel
|
139 |
-
.mutate(density = _.n /_.area)
|
140 |
.group_by(_.grade)
|
141 |
.agg(mean = _.density.mean(),sd = _.density.std())
|
142 |
.order_by(_.mean.desc())
|
@@ -168,27 +172,30 @@ if nunique:
|
|
168 |
mapcol, chartcol = st.columns([4,1])
|
169 |
|
170 |
if submitted:
|
171 |
-
gdf = get_polygon(gdf_name, area_source, con)
|
172 |
-
url = compute_hexes(gdf, gdf_name, rank, taxa, zoom, distinct_taxa = distinct_taxa)
|
173 |
-
layer = HexagonLayer(url, v_scale)
|
174 |
-
|
175 |
-
|
176 |
-
m = leafmap.Map(style=terrain_styling(), center=[-120, 37.6], zoom=2, pitch=35, bearing=10)
|
177 |
-
if gdf is not None:
|
178 |
-
m.add_gdf(gdf[[gdf.geometry.name]], "fill", paint = {"fill-opacity": 0.2}) # adds area of interest & zooms in
|
179 |
-
m.add_pmtiles(mappinginequality, style=redlines, visible=True, opacity = 0.9, fit_bounds=False)
|
180 |
-
m.add_deck_layers([layer])
|
181 |
-
m.add_layer_control()
|
182 |
-
|
183 |
with mapcol:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
184 |
m.to_streamlit()
|
|
|
185 |
with chartcol:
|
186 |
-
st.markdown("Mean number of " + count + " by redline grade")
|
187 |
bar_chart(gdf_name, rank, taxa, zoom, distinct_taxa = distinct_taxa)
|
|
|
188 |
|
189 |
|
|
|
190 |
st.divider()
|
191 |
|
|
|
192 |
'''
|
193 |
## Overview
|
194 |
|
|
|
7 |
import leafmap.maplibregl as leafmap
|
8 |
import requests
|
9 |
import geopandas as gpd
|
10 |
+
import altair as alt
|
11 |
|
12 |
st.set_page_config(page_title="Redlining & GBIF", layout="wide")
|
13 |
st.title("Redlining & GBIF")
|
|
|
85 |
@st.cache_data
|
86 |
def compute_hexes(_gdf, gdf_name, rank, taxa, zoom, distinct_taxa = ""):
|
87 |
|
|
|
88 |
dest = unique_path(gdf_name, rank, taxa, zoom, distinct_taxa)
|
89 |
bucket = "public-gbif"
|
90 |
url = base_url + f"/{bucket}/" + dest
|
|
|
93 |
if response.status_code != 404:
|
94 |
return url
|
95 |
|
96 |
+
sel = (con
|
97 |
+
.read_parquet("s3://public-gbif/app/redlined_cities_gbif.parquet")
|
98 |
+
.filter(_[rank] == taxa)
|
99 |
+
)
|
100 |
|
101 |
sel = (sel
|
102 |
.rename(hex = "h" + str(zoom)) # h3 == 41,150 hexes. h5 == 2,016,830 hexes
|
|
|
122 |
|
123 |
|
124 |
|
|
|
|
|
125 |
@st.cache_data
|
126 |
def bar_chart(gdf_name, rank, taxa, zoom, distinct_taxa = ""):
|
127 |
sel = con.read_parquet("s3://public-gbif/app/redlined_cities_gbif.parquet")
|
128 |
+
sel = (sel
|
129 |
+
.filter(_[rank] == taxa)
|
130 |
+
.mutate(geom = _.geom.convert('EPSG:4326', 'ESRI:54009'))
|
131 |
+
.mutate(area = _.geom.area())
|
132 |
+
)
|
133 |
if gdf_name != "All":
|
134 |
sel = sel.filter(_.city == gdf_name)
|
135 |
|
|
|
140 |
else:
|
141 |
sel = sel.agg(n = _.count(), area = _.area.sum())
|
142 |
sel = (sel
|
143 |
+
.mutate(density = _.n /_.area * 10000) # per hectre
|
144 |
.group_by(_.grade)
|
145 |
.agg(mean = _.density.mean(),sd = _.density.std())
|
146 |
.order_by(_.mean.desc())
|
|
|
172 |
mapcol, chartcol = st.columns([4,1])
|
173 |
|
174 |
if submitted:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
175 |
with mapcol:
|
176 |
+
gdf = get_polygon(gdf_name, area_source, con)
|
177 |
+
url = compute_hexes(gdf, gdf_name, rank, taxa, zoom, distinct_taxa = distinct_taxa)
|
178 |
+
layer = HexagonLayer(url, v_scale)
|
179 |
+
digest = hashlib.md5(str(layer).encode()).hexdigest()
|
180 |
+
print(digest)
|
181 |
+
|
182 |
+
m = leafmap.Map(style=terrain_styling(), center=[-120, 37.6], zoom=2, pitch=35, bearing=10)
|
183 |
+
if gdf is not None:
|
184 |
+
m.add_gdf(gdf[[gdf.geometry.name]], "fill", paint = {"fill-opacity": 0.2}) # adds area of interest & zooms in
|
185 |
+
m.add_pmtiles(mappinginequality, style=redlines, visible=True, opacity = 0.9, fit_bounds=False)
|
186 |
+
m.add_deck_layers([layer])
|
187 |
+
m.add_layer_control()
|
188 |
m.to_streamlit()
|
189 |
+
|
190 |
with chartcol:
|
|
|
191 |
bar_chart(gdf_name, rank, taxa, zoom, distinct_taxa = distinct_taxa)
|
192 |
+
st.markdown("Mean density of " + count + " by redline grade, count per hectre")
|
193 |
|
194 |
|
195 |
+
|
196 |
st.divider()
|
197 |
|
198 |
+
|
199 |
'''
|
200 |
## Overview
|
201 |
|
utilities.py
CHANGED
@@ -24,7 +24,7 @@ def set_source_secrets(con):
|
|
24 |
if secret is None:
|
25 |
secret = st.secrets["SOURCE_SECRET"]
|
26 |
|
27 |
-
|
28 |
if key is None:
|
29 |
key = st.secrets["SOURCE_KEY"]
|
30 |
|
@@ -110,32 +110,6 @@ def HexagonLayer(data, v_scale = 1):
|
|
110 |
get_fill_color="[255 - value, 255, value]",
|
111 |
)
|
112 |
|
113 |
-
def DeckGlobe(layer):
|
114 |
-
view_state = pdk.ViewState(latitude=51.47, longitude=0.45, zoom=0)
|
115 |
-
view = pdk.View(type="_GlobeView", controller=True, width=1000, height=600)
|
116 |
-
COUNTRIES = "https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_admin_0_scale_rank.geojson"
|
117 |
-
|
118 |
-
layers = [
|
119 |
-
pdk.Layer(
|
120 |
-
"GeoJsonLayer",
|
121 |
-
id="base-map",
|
122 |
-
data=COUNTRIES,
|
123 |
-
stroked=False,
|
124 |
-
filled=True,
|
125 |
-
get_fill_color=[200, 200, 200],
|
126 |
-
),
|
127 |
-
layer,
|
128 |
-
]
|
129 |
-
deck = pdk.Deck(
|
130 |
-
views=[view],
|
131 |
-
initial_view_state=view_state,
|
132 |
-
layers=layers,
|
133 |
-
map_provider=None,
|
134 |
-
# Note that this must be set for the globe to be opaque
|
135 |
-
parameters={"cull": True},
|
136 |
-
)
|
137 |
-
return deck
|
138 |
-
|
139 |
|
140 |
def terrain_styling():
|
141 |
maptiler_key = os.getenv("MAPTILER_KEY")
|
@@ -192,7 +166,6 @@ def get_city(name = "Oakland", con = ibis.duckdb.connect()):
|
|
192 |
return gdf
|
193 |
|
194 |
|
195 |
-
@st.cache_data
|
196 |
def get_polygon(name = "New Haven",
|
197 |
source = "City",
|
198 |
_con = ibis.duckdb.connect()):
|
@@ -211,9 +184,10 @@ import hashlib
|
|
211 |
import pandas as pd
|
212 |
def unique_path(gdf_name, rank, taxa, zoom, distinct_taxa):
|
213 |
#gdf_hash = str(pd.util.hash_pandas_object(gdf).sum())
|
214 |
-
text = gdf_name
|
215 |
-
|
216 |
-
sig
|
|
|
217 |
dest = "cache/gbif_" + sig + ".json"
|
218 |
return dest
|
219 |
|
|
|
24 |
if secret is None:
|
25 |
secret = st.secrets["SOURCE_SECRET"]
|
26 |
|
27 |
+
key = os.getenv("SOURCE_KEY")
|
28 |
if key is None:
|
29 |
key = st.secrets["SOURCE_KEY"]
|
30 |
|
|
|
110 |
get_fill_color="[255 - value, 255, value]",
|
111 |
)
|
112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
114 |
def terrain_styling():
|
115 |
maptiler_key = os.getenv("MAPTILER_KEY")
|
|
|
166 |
return gdf
|
167 |
|
168 |
|
|
|
169 |
def get_polygon(name = "New Haven",
|
170 |
source = "City",
|
171 |
_con = ibis.duckdb.connect()):
|
|
|
184 |
import pandas as pd
|
185 |
def unique_path(gdf_name, rank, taxa, zoom, distinct_taxa):
|
186 |
#gdf_hash = str(pd.util.hash_pandas_object(gdf).sum())
|
187 |
+
text = [gdf_name, rank, taxa, str(zoom), distinct_taxa]
|
188 |
+
sig = "-".join(text)
|
189 |
+
print(sig)
|
190 |
+
sig = hashlib.sha1(sig.encode()).hexdigest()
|
191 |
dest = "cache/gbif_" + sig + ".json"
|
192 |
return dest
|
193 |
|