mbuuck commited on
Commit
b3a5aeb
2 Parent(s): ee85f9b 43078fa

Merge pull request #13 from buuck/default_projects

Browse files
Files changed (3) hide show
  1. app.py +13 -6
  2. utils/duckdb_queries.py +1 -1
  3. 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="Biodiversity scores by year",
25
  )
26
  calc_btn.click(
27
- indexgenerator.calculate_biodiversity_score,
28
- inputs=[start_year, end_year, project_name],
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
- return gr.Dropdown.update(choices=projects["name"].tolist())
 
 
 
 
 
 
 
 
 
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 != 'null'",
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
- def _load_indices(self, indices_file):
 
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, project_geometry, indices=[]):
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, project_name):
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
- def _latlon_to_config(self, longitudes=None, latitudes=None):
 
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, project_name):
253
- project_geometry = dq.get_project_geometry(project_name)
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 calculate_biodiversity_score(self, start_year, end_year, project_name):
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, project_name)
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