Spaces:
Build error
Build error
Merge pull request #13 from buuck/default_projects
Browse files- app.py +13 -6
- utils/duckdb_queries.py +1 -1
- utils/indicators.py +33 -25
app.py
CHANGED
@@ -17,27 +17,34 @@ with gr.Blocks() as demo:
|
|
17 |
with gr.Row():
|
18 |
view_btn = gr.Button(value="Show project map")
|
19 |
calc_btn = gr.Button(value="Calculate!")
|
20 |
-
# save_btn = gr.Button(value="Save")
|
21 |
results_df = gr.Dataframe(
|
22 |
headers=["Year", "Project Name", "Score"],
|
23 |
datatype=["number", "str", "number"],
|
24 |
-
label="
|
25 |
)
|
26 |
calc_btn.click(
|
27 |
-
indexgenerator.
|
28 |
-
inputs=[start_year, end_year
|
29 |
outputs=results_df,
|
30 |
)
|
31 |
view_btn.click(
|
32 |
fn=indexgenerator.show_project_map,
|
33 |
-
inputs=[project_name],
|
34 |
outputs=[m1],
|
35 |
)
|
36 |
|
37 |
def update_project_dropdown_list(url_params):
|
38 |
username = url_params.get("username", "default")
|
39 |
projects = dq.list_projects_by_author(author_id=username)
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
# Get url params
|
43 |
url_params = gr.JSON({"username": "default"}, visible=False, label="URL Params")
|
|
|
17 |
with gr.Row():
|
18 |
view_btn = gr.Button(value="Show project map")
|
19 |
calc_btn = gr.Button(value="Calculate!")
|
|
|
20 |
results_df = gr.Dataframe(
|
21 |
headers=["Year", "Project Name", "Score"],
|
22 |
datatype=["number", "str", "number"],
|
23 |
+
label="Scores by year",
|
24 |
)
|
25 |
calc_btn.click(
|
26 |
+
indexgenerator.calculate_score,
|
27 |
+
inputs=[start_year, end_year],
|
28 |
outputs=results_df,
|
29 |
)
|
30 |
view_btn.click(
|
31 |
fn=indexgenerator.show_project_map,
|
|
|
32 |
outputs=[m1],
|
33 |
)
|
34 |
|
35 |
def update_project_dropdown_list(url_params):
|
36 |
username = url_params.get("username", "default")
|
37 |
projects = dq.list_projects_by_author(author_id=username)
|
38 |
+
# Initialize the first project in the list
|
39 |
+
project_names = projects['name'].tolist()
|
40 |
+
return gr.Dropdown.update(choices=project_names)
|
41 |
+
|
42 |
+
# Change the project name in the index generator object when the
|
43 |
+
# user selects a new project
|
44 |
+
project_name.change(
|
45 |
+
indexgenerator.set_project,
|
46 |
+
inputs=project_name
|
47 |
+
)
|
48 |
|
49 |
# Get url params
|
50 |
url_params = gr.JSON({"username": "default"}, visible=False, label="URL Params")
|
utils/duckdb_queries.py
CHANGED
@@ -18,7 +18,7 @@ else:
|
|
18 |
# to-do: pass con through decorator
|
19 |
def list_projects_by_author(author_id):
|
20 |
return con.execute(
|
21 |
-
"SELECT DISTINCT name FROM project WHERE authorId = ? AND geometry
|
22 |
[author_id],
|
23 |
).df()
|
24 |
|
|
|
18 |
# to-do: pass con through decorator
|
19 |
def list_projects_by_author(author_id):
|
20 |
return con.execute(
|
21 |
+
"SELECT DISTINCT name FROM project WHERE (authorId = ? OR authorId = 'default') AND (geometry IS NOT NULL)",
|
22 |
[author_id],
|
23 |
).df()
|
24 |
|
utils/indicators.py
CHANGED
@@ -34,10 +34,26 @@ class IndexGenerator:
|
|
34 |
# Authenticate to GEE & DuckDB
|
35 |
self._authenticate_ee(GEE_SERVICE_ACCOUNT)
|
36 |
|
|
|
|
|
|
|
|
|
37 |
# Use defined subset of indices
|
38 |
all_indices = self._load_indices(INDICES_FILE)
|
39 |
self.indices = {k: all_indices[k] for k in indices}
|
40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
def _cloudfree(self, gee_path, daterange):
|
42 |
"""
|
43 |
Internal method to generate a cloud-free composite.
|
@@ -59,7 +75,8 @@ class IndexGenerator:
|
|
59 |
)
|
60 |
return composite_cloudfree.clip(self.roi)
|
61 |
|
62 |
-
|
|
|
63 |
# Read index configurations
|
64 |
with open(indices_file, "r") as stream:
|
65 |
try:
|
@@ -146,7 +163,7 @@ class IndexGenerator:
|
|
146 |
logging.info(f"Calculated zonal mean for {index_key}.")
|
147 |
return out
|
148 |
|
149 |
-
def generate_composite_index_df(self, year,
|
150 |
data = {
|
151 |
"metric": indices,
|
152 |
"year": year,
|
@@ -175,36 +192,28 @@ class IndexGenerator:
|
|
175 |
ee.Initialize(credentials)
|
176 |
logging.info("Authenticated to Google Earth Engine.")
|
177 |
|
178 |
-
def _calculate_yearly_index(self, years
|
179 |
dfs = []
|
180 |
logging.info(years)
|
181 |
-
project_geometry = dq.get_project_geometry(project_name)
|
182 |
-
project_centroid = dq.get_project_centroid(project_name)
|
183 |
-
# to-do: refactor to involve less transformations
|
184 |
-
_polygon = json.dumps(
|
185 |
-
json.loads(project_geometry[0][0])["features"][0]["geometry"]
|
186 |
-
)
|
187 |
-
# to-do: don't use self.roi and instead pass patameter strategically
|
188 |
-
self.roi = ee.Geometry.Polygon(json.loads(_polygon)["coordinates"])
|
189 |
|
190 |
# to-do: pararelize?
|
191 |
for year in years:
|
192 |
logging.info(year)
|
193 |
-
self.project_name = project_name
|
194 |
df = self.generate_composite_index_df(
|
195 |
-
year, project_geometry, list(self.indices.keys())
|
196 |
)
|
197 |
dfs.append(df)
|
198 |
|
199 |
# Concatenate all dataframes
|
200 |
df_concat = pd.concat(dfs)
|
201 |
-
df_concat["centroid"] = str(project_centroid)
|
202 |
-
df_concat["project_name"] = project_name
|
203 |
-
df_concat["geojson"] = str(project_geometry)
|
204 |
return df_concat.round(2)
|
205 |
|
206 |
-
# h/t: https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/12
|
207 |
-
|
|
|
208 |
"""Function documentation:\n
|
209 |
Basic framework adopted from Krichardson under the following thread:
|
210 |
https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/7
|
@@ -249,9 +258,8 @@ class IndexGenerator:
|
|
249 |
# Finally, return the zoom level and the associated boundary-box center coordinates
|
250 |
return zoom, b_box["center"]
|
251 |
|
252 |
-
def show_project_map(self
|
253 |
-
|
254 |
-
features = json.loads(project_geometry[0][0].replace("'", '"'))["features"]
|
255 |
geometry = features[0]["geometry"]
|
256 |
longitudes = np.array(geometry["coordinates"])[0, :, 0]
|
257 |
latitudes = np.array(geometry["coordinates"])[0, :, 1]
|
@@ -287,15 +295,15 @@ class IndexGenerator:
|
|
287 |
|
288 |
return fig
|
289 |
|
290 |
-
def
|
291 |
years = []
|
292 |
for year in range(start_year, end_year):
|
293 |
-
row_exists = dq.check_if_project_exists_for_year(project_name, year)
|
294 |
if not row_exists:
|
295 |
years.append(year)
|
296 |
|
297 |
if len(years) > 0:
|
298 |
-
df = self._calculate_yearly_index(years
|
299 |
|
300 |
# Write score table to `_temptable`
|
301 |
dq.write_score_to_temptable(df)
|
@@ -306,5 +314,5 @@ class IndexGenerator:
|
|
306 |
# UPSERT project record
|
307 |
dq.upsert_project_record()
|
308 |
logging.info("upserted records into motherduck")
|
309 |
-
scores = dq.get_project_scores(project_name, start_year, end_year)
|
310 |
return scores
|
|
|
34 |
# Authenticate to GEE & DuckDB
|
35 |
self._authenticate_ee(GEE_SERVICE_ACCOUNT)
|
36 |
|
37 |
+
self.project_name = None
|
38 |
+
self.project_geometry = None
|
39 |
+
self.project_centroid = None
|
40 |
+
|
41 |
# Use defined subset of indices
|
42 |
all_indices = self._load_indices(INDICES_FILE)
|
43 |
self.indices = {k: all_indices[k] for k in indices}
|
44 |
|
45 |
+
def set_project(self, project_name):
|
46 |
+
self.project_name = project_name
|
47 |
+
self.project_geometry = dq.get_project_geometry(self.project_name)
|
48 |
+
self.project_centroid = dq.get_project_centroid(self.project_name)
|
49 |
+
|
50 |
+
# to-do: refactor to involve fewer transformations
|
51 |
+
_polygon = json.dumps(
|
52 |
+
json.loads(self.project_geometry[0][0])["features"][0]["geometry"]
|
53 |
+
)
|
54 |
+
# to-do: don't use self.roi and instead pass patameter strategically
|
55 |
+
self.roi = ee.Geometry.Polygon(json.loads(_polygon)["coordinates"])
|
56 |
+
|
57 |
def _cloudfree(self, gee_path, daterange):
|
58 |
"""
|
59 |
Internal method to generate a cloud-free composite.
|
|
|
75 |
)
|
76 |
return composite_cloudfree.clip(self.roi)
|
77 |
|
78 |
+
@staticmethod
|
79 |
+
def _load_indices(indices_file):
|
80 |
# Read index configurations
|
81 |
with open(indices_file, "r") as stream:
|
82 |
try:
|
|
|
163 |
logging.info(f"Calculated zonal mean for {index_key}.")
|
164 |
return out
|
165 |
|
166 |
+
def generate_composite_index_df(self, year, indices=[]):
|
167 |
data = {
|
168 |
"metric": indices,
|
169 |
"year": year,
|
|
|
192 |
ee.Initialize(credentials)
|
193 |
logging.info("Authenticated to Google Earth Engine.")
|
194 |
|
195 |
+
def _calculate_yearly_index(self, years):
|
196 |
dfs = []
|
197 |
logging.info(years)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
|
199 |
# to-do: pararelize?
|
200 |
for year in years:
|
201 |
logging.info(year)
|
|
|
202 |
df = self.generate_composite_index_df(
|
203 |
+
year, self.project_geometry, list(self.indices.keys())
|
204 |
)
|
205 |
dfs.append(df)
|
206 |
|
207 |
# Concatenate all dataframes
|
208 |
df_concat = pd.concat(dfs)
|
209 |
+
df_concat["centroid"] = str(self.project_centroid)
|
210 |
+
df_concat["project_name"] = self.project_name
|
211 |
+
df_concat["geojson"] = str(self.project_geometry)
|
212 |
return df_concat.round(2)
|
213 |
|
214 |
+
# h/t: https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/12\
|
215 |
+
@staticmethod
|
216 |
+
def _latlon_to_config(longitudes=None, latitudes=None):
|
217 |
"""Function documentation:\n
|
218 |
Basic framework adopted from Krichardson under the following thread:
|
219 |
https://community.plotly.com/t/dynamic-zoom-for-mapbox/32658/7
|
|
|
258 |
# Finally, return the zoom level and the associated boundary-box center coordinates
|
259 |
return zoom, b_box["center"]
|
260 |
|
261 |
+
def show_project_map(self):
|
262 |
+
features = json.loads(self.project_geometry[0][0].replace("'", '"'))["features"]
|
|
|
263 |
geometry = features[0]["geometry"]
|
264 |
longitudes = np.array(geometry["coordinates"])[0, :, 0]
|
265 |
latitudes = np.array(geometry["coordinates"])[0, :, 1]
|
|
|
295 |
|
296 |
return fig
|
297 |
|
298 |
+
def calculate_score(self, start_year, end_year):
|
299 |
years = []
|
300 |
for year in range(start_year, end_year):
|
301 |
+
row_exists = dq.check_if_project_exists_for_year(self.project_name, year)
|
302 |
if not row_exists:
|
303 |
years.append(year)
|
304 |
|
305 |
if len(years) > 0:
|
306 |
+
df = self._calculate_yearly_index(years)
|
307 |
|
308 |
# Write score table to `_temptable`
|
309 |
dq.write_score_to_temptable(df)
|
|
|
314 |
# UPSERT project record
|
315 |
dq.upsert_project_record()
|
316 |
logging.info("upserted records into motherduck")
|
317 |
+
scores = dq.get_project_scores(self.project_name, start_year, end_year)
|
318 |
return scores
|