Spaces:
Build error
Build error
pgzmnk
commited on
Commit
•
6a0f6c2
1
Parent(s):
49af784
Refactor how geometry is written to table.
Browse files- app.py +3 -1
- utils/duckdb_queries.py +8 -1
- utils/indicators.py +31 -28
app.py
CHANGED
@@ -2,8 +2,10 @@ import gradio as gr
|
|
2 |
|
3 |
from utils import duckdb_queries as dq
|
4 |
from utils.gradio import get_window_url_params
|
5 |
-
from utils.indicators import
|
6 |
|
|
|
|
|
7 |
|
8 |
with gr.Blocks() as demo:
|
9 |
with gr.Column():
|
|
|
2 |
|
3 |
from utils import duckdb_queries as dq
|
4 |
from utils.gradio import get_window_url_params
|
5 |
+
from utils.indicators import IndexGenerator
|
6 |
|
7 |
+
# Instantiate outside gradio app to avoid re-initializing GEE, which is slow
|
8 |
+
indexgenerator = IndexGenerator()
|
9 |
|
10 |
with gr.Blocks() as demo:
|
11 |
with gr.Column():
|
utils/duckdb_queries.py
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import os
|
2 |
|
3 |
import duckdb
|
@@ -27,6 +28,12 @@ def get_project_geometry(project_name):
|
|
27 |
"SELECT geometry FROM project WHERE name = ? LIMIT 1", [project_name]
|
28 |
).fetchall()
|
29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
|
31 |
def get_project_scores(project_name, start_year, end_year):
|
32 |
return con.execute(
|
@@ -42,7 +49,7 @@ def check_if_project_exists_for_year(project_name, year):
|
|
42 |
).fetchall()[0][0]
|
43 |
|
44 |
|
45 |
-
def write_score_to_temptable():
|
46 |
con.sql(
|
47 |
"CREATE OR REPLACE TABLE _temptable AS SELECT *, (value * area) AS score FROM (SELECT year, project_name, AVG(value) AS value, area FROM df GROUP BY year, project_name, area ORDER BY project_name)"
|
48 |
)
|
|
|
1 |
+
import json
|
2 |
import os
|
3 |
|
4 |
import duckdb
|
|
|
28 |
"SELECT geometry FROM project WHERE name = ? LIMIT 1", [project_name]
|
29 |
).fetchall()
|
30 |
|
31 |
+
def get_project_centroid(project_name):
|
32 |
+
# Workaround to get centroid of project
|
33 |
+
# To-do: refactor to only use DuckDB spatial extension
|
34 |
+
_geom = get_project_geometry(project_name)
|
35 |
+
_polygon = json.dumps(json.loads(_geom[0][0])['features'][0]['geometry'])
|
36 |
+
return con.sql(f"SELECT ST_X(ST_Centroid(ST_GeomFromGeoJSON('{_polygon}'))) AS longitude, ST_Y(ST_Centroid(ST_GeomFromGeoJSON('{_polygon}'))) AS latitude;").fetchall()[0]
|
37 |
|
38 |
def get_project_scores(project_name, start_year, end_year):
|
39 |
return con.execute(
|
|
|
49 |
).fetchall()[0][0]
|
50 |
|
51 |
|
52 |
+
def write_score_to_temptable(df):
|
53 |
con.sql(
|
54 |
"CREATE OR REPLACE TABLE _temptable AS SELECT *, (value * area) AS score FROM (SELECT year, project_name, AVG(value) AS value, area FROM df GROUP BY year, project_name, area ORDER BY project_name)"
|
55 |
)
|
utils/indicators.py
CHANGED
@@ -14,7 +14,6 @@ from utils import duckdb_queries as dq
|
|
14 |
from . import logging
|
15 |
|
16 |
GEE_SERVICE_ACCOUNT = "climatebase-july-2023@ee-geospatialml-aquarry.iam.gserviceaccount.com"
|
17 |
-
ROI_RADIUS = 20000
|
18 |
INDICES_FILE = "indices.yaml"
|
19 |
|
20 |
|
@@ -23,28 +22,21 @@ class IndexGenerator:
|
|
23 |
A class to generate indices and compute zonal means.
|
24 |
|
25 |
Args:
|
26 |
-
centroid (tuple): The centroid coordinates (latitude, longitude) of the region of interest.
|
27 |
-
year (int): The year for which indices are generated.
|
28 |
-
roi_radius (int, optional): The radius (in meters) for creating a buffer around the centroid as the region of interest. Defaults to 20000.
|
29 |
-
project_name (str, optional): The name of the project. Defaults to "".
|
30 |
map (geemap.Map, optional): Map object for mapping. Defaults to None (i.e. no map created)
|
31 |
"""
|
32 |
|
33 |
def __init__(
|
34 |
self,
|
35 |
-
indices_file,
|
36 |
map=None,
|
37 |
):
|
38 |
# Authenticate to GEE & DuckDB
|
39 |
self._authenticate_ee(GEE_SERVICE_ACCOUNT)
|
40 |
|
41 |
# Set instance variables
|
42 |
-
self.indices = self._load_indices(
|
43 |
self.map = map
|
44 |
-
if self.map is not None
|
45 |
-
|
46 |
-
else:
|
47 |
-
self.show = False
|
48 |
|
49 |
def _cloudfree(self, gee_path, daterange):
|
50 |
"""
|
@@ -149,15 +141,16 @@ class IndexGenerator:
|
|
149 |
return out[index_config.get("bandname")]
|
150 |
return out
|
151 |
|
152 |
-
def generate_composite_index_df(self, year, indices=[]):
|
|
|
153 |
data = {
|
154 |
"metric": indices,
|
155 |
"year": year,
|
156 |
-
"centroid":
|
157 |
-
"project_name":
|
158 |
"value": list(map(self.zonal_mean_index, indices, repeat(year))),
|
159 |
-
"area": self.roi.area().getInfo(), # m^2
|
160 |
-
"geojson":
|
161 |
# to-do: coefficient
|
162 |
}
|
163 |
|
@@ -177,15 +170,30 @@ class IndexGenerator:
|
|
177 |
ee.Initialize(credentials)
|
178 |
logging.info("Authenticated to Google Earth Engine.")
|
179 |
|
180 |
-
def
|
181 |
dfs = []
|
182 |
logging.info(years)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
for year in years:
|
184 |
logging.info(year)
|
185 |
self.project_name = project_name
|
186 |
-
df = self.generate_composite_index_df(year, list(self.indices.keys()))
|
187 |
dfs.append(df)
|
188 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
|
190 |
# h/t: https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/12
|
191 |
def _latlon_to_config(self, longitudes=None, latitudes=None):
|
@@ -234,8 +242,8 @@ class IndexGenerator:
|
|
234 |
return zoom, b_box["center"]
|
235 |
|
236 |
def show_project_map(self, project_name):
|
237 |
-
|
238 |
-
features = json.loads(
|
239 |
geometry = features[0]["geometry"]
|
240 |
longitudes = np.array(geometry["coordinates"])[0, :, 0]
|
241 |
latitudes = np.array(geometry["coordinates"])[0, :, 1]
|
@@ -279,10 +287,10 @@ class IndexGenerator:
|
|
279 |
years.append(year)
|
280 |
|
281 |
if len(years) > 0:
|
282 |
-
df = self.
|
283 |
|
284 |
# Write score table to `_temptable`
|
285 |
-
dq.write_score_to_temptable()
|
286 |
|
287 |
# Create `bioindicator` table IF NOT EXISTS.
|
288 |
dq.get_or_create_bioindicator_table()
|
@@ -294,8 +302,3 @@ class IndexGenerator:
|
|
294 |
return scores
|
295 |
|
296 |
|
297 |
-
# Instantiate outside gradio app to avoid re-initializing GEE, which is slow
|
298 |
-
indexgenerator = IndexGenerator(
|
299 |
-
roi_radius=ROI_RADIUS,
|
300 |
-
indices_file=INDICES_FILE,
|
301 |
-
)
|
|
|
14 |
from . import logging
|
15 |
|
16 |
GEE_SERVICE_ACCOUNT = "climatebase-july-2023@ee-geospatialml-aquarry.iam.gserviceaccount.com"
|
|
|
17 |
INDICES_FILE = "indices.yaml"
|
18 |
|
19 |
|
|
|
22 |
A class to generate indices and compute zonal means.
|
23 |
|
24 |
Args:
|
|
|
|
|
|
|
|
|
25 |
map (geemap.Map, optional): Map object for mapping. Defaults to None (i.e. no map created)
|
26 |
"""
|
27 |
|
28 |
def __init__(
|
29 |
self,
|
|
|
30 |
map=None,
|
31 |
):
|
32 |
# Authenticate to GEE & DuckDB
|
33 |
self._authenticate_ee(GEE_SERVICE_ACCOUNT)
|
34 |
|
35 |
# Set instance variables
|
36 |
+
self.indices = self._load_indices(INDICES_FILE)
|
37 |
self.map = map
|
38 |
+
self.show = True if self.map is not None else False
|
39 |
+
|
|
|
|
|
40 |
|
41 |
def _cloudfree(self, gee_path, daterange):
|
42 |
"""
|
|
|
141 |
return out[index_config.get("bandname")]
|
142 |
return out
|
143 |
|
144 |
+
def generate_composite_index_df(self, year, project_geometry, indices=[]):
|
145 |
+
|
146 |
data = {
|
147 |
"metric": indices,
|
148 |
"year": year,
|
149 |
+
"centroid": "",
|
150 |
+
"project_name": "",
|
151 |
"value": list(map(self.zonal_mean_index, indices, repeat(year))),
|
152 |
+
"area": self.roi.area().getInfo(), # m^2 to-do: calculate with duckdb
|
153 |
+
"geojson": "",
|
154 |
# to-do: coefficient
|
155 |
}
|
156 |
|
|
|
170 |
ee.Initialize(credentials)
|
171 |
logging.info("Authenticated to Google Earth Engine.")
|
172 |
|
173 |
+
def _calculate_yearly_index(self, years, project_name):
|
174 |
dfs = []
|
175 |
logging.info(years)
|
176 |
+
project_geometry = dq.get_project_geometry(project_name)
|
177 |
+
project_centroid = dq.get_project_centroid(project_name)
|
178 |
+
# to-do: refactor to involve less transformations
|
179 |
+
_polygon = json.dumps(json.loads(project_geometry[0][0])['features'][0]['geometry'])
|
180 |
+
# to-do: don't use self.roi and instead pass patameter strategically
|
181 |
+
self.roi = ee.Geometry.Polygon(json.loads(_polygon)['coordinates'])
|
182 |
+
|
183 |
+
# to-do: pararelize?
|
184 |
for year in years:
|
185 |
logging.info(year)
|
186 |
self.project_name = project_name
|
187 |
+
df = self.generate_composite_index_df(year, project_geometry, list(self.indices.keys()))
|
188 |
dfs.append(df)
|
189 |
+
|
190 |
+
# Concatenate all dataframes
|
191 |
+
df_concat = pd.concat(dfs)
|
192 |
+
df_concat['centroid'] = project_centroid
|
193 |
+
df_concat['project_name'] = project_name
|
194 |
+
df_concat['geojson'] = project_geometry
|
195 |
+
breakpoint()
|
196 |
+
return df_concat
|
197 |
|
198 |
# h/t: https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/12
|
199 |
def _latlon_to_config(self, longitudes=None, latitudes=None):
|
|
|
242 |
return zoom, b_box["center"]
|
243 |
|
244 |
def show_project_map(self, project_name):
|
245 |
+
project_geometry = dq.get_project_geometry(project_name)
|
246 |
+
features = json.loads(project_geometry[0][0].replace("'", '"'))["features"]
|
247 |
geometry = features[0]["geometry"]
|
248 |
longitudes = np.array(geometry["coordinates"])[0, :, 0]
|
249 |
latitudes = np.array(geometry["coordinates"])[0, :, 1]
|
|
|
287 |
years.append(year)
|
288 |
|
289 |
if len(years) > 0:
|
290 |
+
df = self._calculate_yearly_index(years, project_name)
|
291 |
|
292 |
# Write score table to `_temptable`
|
293 |
+
dq.write_score_to_temptable(df)
|
294 |
|
295 |
# Create `bioindicator` table IF NOT EXISTS.
|
296 |
dq.get_or_create_bioindicator_table()
|
|
|
302 |
return scores
|
303 |
|
304 |
|
|
|
|
|
|
|
|
|
|