dea-tools 0.3.6.dev41__tar.gz → 0.3.6.dev45__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {dea_tools-0.3.6.dev41/dea_tools.egg-info → dea_tools-0.3.6.dev45}/PKG-INFO +1 -1
  2. dea_tools-0.3.6.dev45/dea_tools/app/wetlandsinsighttool.py +635 -0
  3. dea_tools-0.3.6.dev45/dea_tools/landcover.py +1194 -0
  4. dea_tools-0.3.6.dev45/dea_tools/wit_app.py +502 -0
  5. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45/dea_tools.egg-info}/PKG-INFO +1 -1
  6. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools.egg-info/SOURCES.txt +2 -0
  7. dea_tools-0.3.6.dev41/dea_tools/landcover.py +0 -935
  8. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/.gitignore +0 -0
  9. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/LICENSE +0 -0
  10. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/MANIFEST.in +0 -0
  11. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/README.rst +0 -0
  12. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/__init__.py +0 -0
  13. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/__main__.py +0 -0
  14. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/app/__init__.py +0 -0
  15. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/app/animations.py +0 -0
  16. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/app/changefilmstrips.py +0 -0
  17. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/app/crophealth.py +0 -0
  18. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/app/deacoastlines.py +0 -0
  19. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/app/geomedian.py +0 -0
  20. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/app/imageexport.py +0 -0
  21. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/app/miningrehab.py +0 -0
  22. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/app/widgetconstructors.py +0 -0
  23. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/bandindices.py +0 -0
  24. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/bom.py +0 -0
  25. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/classification.py +0 -0
  26. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/climate.py +0 -0
  27. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/coastal.py +0 -0
  28. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/dask.py +0 -0
  29. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/datahandling.py +0 -0
  30. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/maps.py +0 -0
  31. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/plotting.py +0 -0
  32. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/pyfes_model.py +0 -0
  33. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/spatial.py +0 -0
  34. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/temporal.py +0 -0
  35. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/validation.py +0 -0
  36. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/waterbodies.py +0 -0
  37. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools/wetlands.py +0 -0
  38. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools.egg-info/dependency_links.txt +0 -0
  39. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools.egg-info/requires.txt +0 -0
  40. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/dea_tools.egg-info/top_level.txt +0 -0
  41. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/index.rst +0 -0
  42. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/mock_imports.txt +0 -0
  43. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/pyproject.toml +0 -0
  44. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/setup.cfg +0 -0
  45. {dea_tools-0.3.6.dev41 → dea_tools-0.3.6.dev45}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dea-tools
3
- Version: 0.3.6.dev41
3
+ Version: 0.3.6.dev45
4
4
  Summary: Functions and algorithms for analysing Digital Earth Australia data.
5
5
  Home-page: https://github.com/GeoscienceAustralia/dea-notebooks
6
6
  Author: Geoscience Australia
@@ -0,0 +1,635 @@
1
+ """
2
+ Digital Earth Australia Wetlands Insight Tool widget, which can be used to interactively
3
+ extract a stacked line plot using the wetlands insight tool on a wetland polygon.
4
+ """
5
+
6
+ # Import required packages
7
+ import fiona
8
+ import sys
9
+ import datacube
10
+ import warnings
11
+ import matplotlib.pyplot as plt
12
+ from datacube.utils.geometry import CRS
13
+ from ipyleaflet import (
14
+ WMSLayer,
15
+ basemaps,
16
+ basemap_to_tiles,
17
+ Map,
18
+ DrawControl,
19
+ WidgetControl,
20
+ SearchControl,
21
+ Marker,
22
+ LayerGroup,
23
+ LayersControl,
24
+ GeoData,
25
+ )
26
+ from traitlets import Unicode
27
+ from ipywidgets import (
28
+ GridspecLayout,
29
+ Button,
30
+ Layout,
31
+ HBox,
32
+ VBox,
33
+ HTML,
34
+ Output,
35
+ )
36
+ import json
37
+ import geopandas as gpd
38
+ from io import BytesIO
39
+ import ipywidgets as widgets
40
+ import datetime
41
+ import seaborn as sns
42
+
43
+ # from shapely.geometry import box, shape
44
+ import matplotlib.dates as mdates
45
+
46
+ sys.path.insert(1, "../Tools/")
47
+ import dea_tools.app.widgetconstructors as deawidgets
48
+ from dea_tools.dask import create_local_dask_cluster
49
+ from dea_tools.wetlands import generate_low_quality_data_periods
50
+ from dea_tools.wit_app import WIT_drill, spatial_wit
51
+
52
+
53
+ def make_box_layout():
54
+ return Layout(
55
+ # border='solid 1px black',
56
+ margin="0px 10px 10px 0px",
57
+ padding="5px 5px 5px 5px",
58
+ width="100%",
59
+ height="100%",
60
+ )
61
+
62
+
63
+ def create_expanded_button(description, button_style):
64
+ return Button(
65
+ description=description,
66
+ button_style=button_style,
67
+ layout=Layout(width="auto", height="auto"),
68
+ )
69
+
70
+
71
+ class wit_app(HBox):
72
+
73
+ def __init__(self):
74
+ super().__init__()
75
+
76
+ ######################
77
+ # INITIAL ATTRIBUTES #
78
+ ######################
79
+
80
+ enddate = datetime.datetime.today()
81
+ startdate = datetime.datetime(
82
+ year=enddate.year - 1, month=enddate.month, day=enddate.day
83
+ )
84
+ self.startdate = startdate.strftime("%Y-%m-%d")
85
+ self.enddate = enddate.strftime("%Y-%m-%d")
86
+ self.wetland_name = "example WIT"
87
+ # self.out_csv = "example_WIT.csv"
88
+ self.out_plot = False
89
+ self.product_list = [
90
+ ("ESRI World Imagery", "none"),
91
+ ("Open Street Map", "open_street_map"),
92
+ ]
93
+ self.product = self.product_list[0][1]
94
+ self.target = None
95
+ self.action = None
96
+ self.gdf_drawn = None
97
+ self.gdf_uploaded = None
98
+ self.max_size = False
99
+ self.spatial_wit = False
100
+
101
+ ##################
102
+ # HEADER FOR APP #
103
+ ##################
104
+
105
+ # Create the Header widget
106
+ header_title_text = "<h3>Digital Earth Australia Wetlands Insight Tool </h3>"
107
+ instruction_text = "Select parameters and draw a polygon on the map to extract a stacked line plot for a given area. Alternatively, <b>upload a shapefile or a GeoJSON</b> to extract a stacked line plot for your wetland of interest polygon."
108
+ self.header = deawidgets.create_html(
109
+ f"{header_title_text}<p>{instruction_text}</p>"
110
+ )
111
+ self.header.layout = make_box_layout()
112
+
113
+ #####################################
114
+ # HANDLER FUNCTION FOR DRAW CONTROL #
115
+ #####################################
116
+
117
+ # Define the action to take once something is drawn on the map
118
+ def update_geojson(target, action, geo_json):
119
+
120
+ # Remove previously uploaded data if present
121
+ self.gdf_uploaded = None
122
+ fileupload_wetlands._counter = 0
123
+
124
+ # Get data from action
125
+ self.action = action
126
+
127
+ # Convert data to geopandas
128
+ json_data = json.dumps(geo_json)
129
+ binary_data = json_data.encode()
130
+ io = BytesIO(binary_data)
131
+ io.seek(0)
132
+ gdf = gpd.read_file(io)
133
+ gdf.crs = "EPSG:4326"
134
+
135
+ # Convert to Albers and compute area
136
+ gdf_drawn_albers = gdf.copy().to_crs("EPSG:3577")
137
+ m2_per_km2 = 10**6
138
+ area = gdf_drawn_albers.area.values[0] / m2_per_km2
139
+ polyarea_label = "Total polygon area"
140
+ polyarea_text = f"<b>{polyarea_label}</b>: {area:.2f} km<sup>2</sup>"
141
+
142
+ # Test area size
143
+ if self.max_size:
144
+ confirmation_text = '<span style="color: #33cc33"> <b>(Overriding maximum size limit; use with caution as may lead to memory issues)</b></span>'
145
+ self.header.value = (
146
+ header_title_text + polyarea_text + confirmation_text
147
+ )
148
+ self.gdf_drawn = gdf
149
+ elif area <= 2000:
150
+ confirmation_text = '<span style="color: #33cc33"> <b>(Area to extract falls within recommended limit)</b></span>'
151
+ self.header.value = (
152
+ header_title_text + polyarea_text + confirmation_text
153
+ )
154
+ self.gdf_drawn = gdf
155
+ else:
156
+ warning_text = '<span style="color: #ff5050"> <b>(Area to extract is too large, please update your polygon)</b></span>'
157
+ self.header.value = header_title_text + polyarea_text + warning_text
158
+ self.gdf_drawn = None
159
+
160
+ ###########################
161
+ # WIDGETS FOR APP OUTPUTS #
162
+ ###########################
163
+
164
+ self.dask_client = Output(layout=make_box_layout())
165
+ self.progress_bar = Output(layout=make_box_layout())
166
+ self.wit_plot = Output(layout=make_box_layout())
167
+ self.progress_header = deawidgets.create_html("")
168
+
169
+ #########################################
170
+ # MAP WIDGET, DRAWING TOOLS, WMS LAYERS #
171
+ #########################################
172
+
173
+ # Create drawing tools
174
+ desired_drawtools = ["rectangle", "polygon"]
175
+ draw_control = deawidgets.create_drawcontrol(desired_drawtools)
176
+
177
+ # Begin by displaying an empty layer group, and update the group with desired WMS on interaction.
178
+ self.map_layers = LayerGroup(layers=())
179
+ self.map_layers.name = "Map Overlays"
180
+
181
+ # Create map widget
182
+ self.m = deawidgets.create_map(
183
+ map_center=(-28, 135), zoom_level=4, basemap=basemaps.Esri.WorldImagery
184
+ )
185
+ self.m.layout = make_box_layout()
186
+
187
+ # Add tools to map widget
188
+ self.m.add_control(draw_control)
189
+ self.m.add_control(
190
+ SearchControl(
191
+ position="topleft",
192
+ url="https://nominatim.openstreetmap.org/search?format=json&q={s}",
193
+ zoom=13, # 'Village / Suburb' level zoom
194
+ marker=Marker(draggable=False),
195
+ )
196
+ )
197
+ self.m.add_layer(self.map_layers)
198
+
199
+ # Store current basemap for future use
200
+ self.basemap = self.m.basemap
201
+
202
+ ############################
203
+ # WIDGETS FOR APP CONTROLS #
204
+ ############################
205
+
206
+ # Create parameter widgets
207
+ startdate_picker = deawidgets.create_datepicker(
208
+ value=startdate,
209
+ )
210
+ enddate_picker = deawidgets.create_datepicker(
211
+ value=enddate,
212
+ )
213
+ wetland_name = deawidgets.create_inputtext(self.wetland_name, self.wetland_name)
214
+ # output_csv = deawidgets.create_inputtext(self.out_csv, self.out_csv)
215
+ output_plot = deawidgets.create_checkbox(self.out_plot, "Figure (.png)")
216
+ deaoverlay_dropdown = deawidgets.create_dropdown(
217
+ self.product_list, self.product_list[0][1]
218
+ )
219
+ run_button = create_expanded_button("Run", "info")
220
+ fileupload_wetlands = widgets.FileUpload(accept="", multiple=True)
221
+
222
+ # Expandable advanced section
223
+ max_size = deawidgets.create_checkbox(
224
+ self.max_size, "Enable", layout={"width": "95%"}
225
+ )
226
+ output_spatial_wit = deawidgets.create_checkbox(
227
+ self.spatial_wit, "Animation (.gif)"
228
+ )
229
+
230
+ ####################################
231
+ # UPDATE FUNCTIONS FOR EACH WIDGET #
232
+ ####################################
233
+
234
+ # Run update functions whenever various widgets are changed.
235
+ startdate_picker.observe(self.update_startdate, "value")
236
+ enddate_picker.observe(self.update_enddate, "value")
237
+ wetland_name.observe(self.update_wetlandname, "value")
238
+ # output_csv.observe(self.update_outputcsv, "value")
239
+ output_plot.observe(self.update_outputplot, "value")
240
+ deaoverlay_dropdown.observe(self.update_deaoverlay, "value")
241
+ run_button.on_click(self.run_app)
242
+ draw_control.on_draw(update_geojson)
243
+ fileupload_wetlands.observe(self.update_fileupload_wetlands, "value")
244
+ max_size.observe(self.update_maxsize, "value")
245
+ output_spatial_wit.observe(self.update_outputspatialwit, "value")
246
+
247
+ ##################################
248
+ # COLLECTION OF ALL APP CONTROLS #
249
+ ##################################
250
+ expand_box = VBox(
251
+ [
252
+ HTML(
253
+ "<b>Override maximum size limit:</b></br> (use with caution; may cause memory issues/crashes)"
254
+ ),
255
+ max_size,
256
+ HTML("<b>Spatial WIT animation:<b/>"),
257
+ output_spatial_wit,
258
+ ]
259
+ )
260
+
261
+ expand = widgets.Accordion(
262
+ children=[expand_box],
263
+ selected_index=None,
264
+ )
265
+ expand.set_title(0, "Advanced")
266
+
267
+ parameter_selection = VBox(
268
+ [
269
+ HTML("<b>Start Date:</b>"),
270
+ startdate_picker,
271
+ HTML("<b>End Date:</b>"),
272
+ enddate_picker,
273
+ HTML("<b>Wetland Name:</b>"),
274
+ wetland_name,
275
+ # HTML("<b>Output CSV:</b>"),
276
+ # output_csv,
277
+ HTML("<b>Output Plot:</b>"),
278
+ output_plot,
279
+ HTML(
280
+ "</br><i><b>Upload Polygon:</b></br>Upload a GeoJSON or"
281
+ " Shapefile (<5 mb) containing a wetland polygon.</i>"
282
+ ),
283
+ fileupload_wetlands,
284
+ HTML("</br>"),
285
+ expand,
286
+ ]
287
+ )
288
+ map_selection = VBox(
289
+ [
290
+ HTML("</br><b>Map overlay:</b>"),
291
+ deaoverlay_dropdown,
292
+ ]
293
+ )
294
+ parameter_selection.layout = make_box_layout()
295
+ map_selection.layout = make_box_layout()
296
+
297
+ ###############################
298
+ # SPECIFICATION OF APP LAYOUT #
299
+ ###############################
300
+
301
+ # 0 1 2 3 4 5 6 7 8 9
302
+ # ---------------------------------------------
303
+ # 0 | Header | Map sel. |
304
+ # ---------------------------------------------
305
+ # 1 | Params | |
306
+ # 2 | | |
307
+ # 3 | | |
308
+ # 4 | | Map |
309
+ # 5 | | |
310
+ # 6 | | |
311
+ # ---------- |
312
+ # 7 | Run | |
313
+ # ---------------------------------------------
314
+ # 8 | Status info |
315
+ # ---------------------------------------------
316
+ # 9 | |
317
+ # 10 | Output/figure |
318
+ # 11 | |
319
+ # 12 | ------------------------------------------|
320
+
321
+ # Create the layout #[rowspan, colspan]
322
+ grid = GridspecLayout(13, 10, height="1350px", width="auto")
323
+
324
+ # Header and controls
325
+ grid[0, :8] = self.header
326
+ grid[0, 8:] = map_selection
327
+ grid[1:7, 0:2] = parameter_selection
328
+ grid[7, 0:2] = run_button
329
+
330
+ # Status info, map and plot
331
+ grid[1:8, 2:] = self.m # map
332
+ grid[8:9, :] = self.progress_bar
333
+ # grid[7:8, :] = self.dask_client
334
+
335
+ # Plot
336
+ grid[9:, :] = self.wit_plot
337
+
338
+ # Display using HBox children attribute
339
+ self.children = [grid]
340
+
341
+ ######################################
342
+ # DEFINITION OF ALL UPDATE FUNCTIONS #
343
+ ######################################
344
+
345
+ # Set the output csv
346
+ def update_fileupload_wetlands(self, change):
347
+
348
+ # Clear any drawn data if present
349
+ self.gdf_drawn = None
350
+
351
+ # Temporary compatibility fix for ipywidget > 8.0
352
+ # TODO: Update code to use new fileupload API documented here:
353
+ # https://ipywidgets.readthedocs.io/en/latest/user_migration_guides.html#fileupload
354
+ uploaded_data = {
355
+ f["name"]: {"content": f.content.tobytes()} for f in change.new
356
+ }
357
+
358
+ # Save to file
359
+ for uploaded_filename in uploaded_data.keys():
360
+ with open(uploaded_filename, "wb") as output_file:
361
+ content = uploaded_data[uploaded_filename]["content"]
362
+ output_file.write(content)
363
+
364
+ with self.progress_bar:
365
+
366
+ try:
367
+
368
+ print("Loading vector data...", end="\r")
369
+ valid_files = [
370
+ file
371
+ for file in uploaded_data.keys()
372
+ if file.lower().endswith((".shp", ".geojson"))
373
+ ]
374
+ valid_file = valid_files[0]
375
+ wetlands_gdf = (
376
+ gpd.read_file(valid_file)
377
+ .to_crs("EPSG:4326")
378
+ .explode(index_parts=True)
379
+ .reset_index(drop=True)
380
+ )
381
+
382
+ # Use ID column if it exists
383
+ if "id" in wetlands_gdf:
384
+ wetlands_gdf = wetlands_gdf.set_index("id")
385
+ print(f"Uploaded '{valid_file}'; automatically labelling ")
386
+ else:
387
+ print(f"Uploaded '{valid_file}'; no 'id' column detected.")
388
+
389
+ # Create a geodata
390
+ geodata = GeoData(
391
+ geo_dataframe=wetlands_gdf, style={"color": "black", "weight": 3}
392
+ )
393
+
394
+ # Add to map
395
+ xmin, ymin, xmax, ymax = wetlands_gdf.total_bounds
396
+ self.m.fit_bounds([[ymin, xmin], [ymax, xmax]])
397
+ self.m.add_layer(geodata)
398
+
399
+ # If completed, add to attribute
400
+ self.gdf_uploaded = wetlands_gdf
401
+
402
+ except IndexError:
403
+ print(
404
+ "Cannot read uploaded files. Please ensure that data is "
405
+ "in either GeoJSON or Shapefile format.",
406
+ end="\r",
407
+ )
408
+ self.gdf_uploaded = None
409
+
410
+ except fiona.errors.DriverError:
411
+ print(
412
+ "Shapefile is invalid. Please ensure that all shapefile "
413
+ "components (e.g. .shp, .shx, .dbf, .prj) are uploaded.",
414
+ end="\r",
415
+ )
416
+ self.gdf_uploaded = None
417
+
418
+ # Set the start date to the new edited date
419
+ def update_startdate(self, change):
420
+ self.startdate = change.new
421
+
422
+ # Set the end date to the new edited date
423
+ def update_enddate(self, change):
424
+ self.enddate = change.new
425
+
426
+ # Set the wetland name
427
+ def update_wetlandname(self, change):
428
+ self.wetland_name = change.new
429
+
430
+ # Set the output csv
431
+ # def update_outputcsv(self, change):
432
+ # self.out_csv = change.new
433
+
434
+ # Set the output plot
435
+ def update_outputplot(self, change):
436
+ self.out_plot = change.new
437
+
438
+ # Override max size limit
439
+ def update_maxsize(self, change):
440
+ self.max_size = change.new
441
+
442
+ # Select to output spatial WIT
443
+ def update_outputspatialwit(self, change):
444
+ self.spatial_wit = change.new
445
+
446
+ # Update product
447
+ def update_deaoverlay(self, change):
448
+
449
+ self.product = change.new
450
+
451
+ if self.product == "none":
452
+ self.map_layers.clear_layers()
453
+
454
+ elif self.product == "open_street_map":
455
+ self.map_layers.clear_layers()
456
+ layer = basemap_to_tiles(basemaps.OpenStreetMap.Mapnik)
457
+ self.map_layers.add_layer(layer)
458
+
459
+ def run_app(self, change):
460
+
461
+ # Clear progress bar and output areas before running
462
+ self.progress_bar.clear_output()
463
+ self.wit_plot.clear_output()
464
+ self.dask_client.clear_output()
465
+
466
+ # Configure local dask cluster
467
+ with self.dask_client:
468
+ client = create_local_dask_cluster(return_client=True, display_client=True)
469
+
470
+ # Set any defaults
471
+ dask_chunks = dict(x=1000, y=1000, time=1)
472
+
473
+ self.progress_header.value = "<h3>" + ("Progress") + "</h3>"
474
+
475
+ # Run DEA WIT analysis
476
+ with self.progress_bar:
477
+ warnings.filterwarnings("ignore")
478
+
479
+ # Load polygons from either map or uploaded files
480
+ if self.gdf_uploaded is not None:
481
+ wetlands_gdf = self.gdf_uploaded
482
+ run_text = "uploaded file"
483
+ elif self.gdf_drawn is not None:
484
+ wetlands_gdf = self.gdf_drawn
485
+
486
+ # save the drawn polygon as a geojson in the current directory
487
+ try:
488
+ output_geojson_path = f"{self.wetland_name}_drawn_polygon.geojson"
489
+ wetlands_gdf.to_file(output_geojson_path, driver="GeoJSON")
490
+ print(f"Drawn polygon saved to: {output_geojson_path}")
491
+ except Exception as e:
492
+ print(f"Error saving drawn polygon: {e}")
493
+ run_text = "selected polygon"
494
+ else:
495
+ print(
496
+ f"No polygon drawn or uploaded. Please select a polygon on the map, or upload a GeoJSON or Shapefile.",
497
+ end="\r",
498
+ )
499
+ wetlands_gdf = None
500
+
501
+ # Run wetlands polygon drill
502
+ df = None
503
+
504
+ if not self.wetland_name.endswith(".csv"):
505
+ output_csv = self.wetland_name + ".csv"
506
+ else:
507
+ output_csv = self.wetland_name
508
+
509
+ if wetlands_gdf is not None:
510
+ try:
511
+ ds_wit, df = WIT_drill(
512
+ gdf=wetlands_gdf,
513
+ time=(self.startdate, self.enddate),
514
+ export_csv=output_csv,
515
+ dask_chunks=dask_chunks,
516
+ verbose=False,
517
+ verbose_progress=True,
518
+ )
519
+ print("WIT complete")
520
+ except AttributeError:
521
+ print("No polygon selected")
522
+
523
+ else:
524
+ print(
525
+ "No valid polygon to process. Please select or draw a new polygon."
526
+ )
527
+
528
+ # close down the dask client
529
+ client.shutdown()
530
+
531
+ # save the csv
532
+ if df is not None and self.wetland_name:
533
+ df.to_csv(output_csv, index=False)
534
+
535
+ else:
536
+ print("No valid polygon to process. Please select or draw a new polygon.")
537
+ df = None
538
+
539
+ # ---Plotting------------------------------
540
+ if df is not None:
541
+ with self.wit_plot:
542
+
543
+ fontsize = 17
544
+ plt.rcParams.update({"font.size": fontsize})
545
+ # set up color palette
546
+ pal = [
547
+ sns.xkcd_rgb["cobalt blue"],
548
+ sns.xkcd_rgb["neon blue"],
549
+ sns.xkcd_rgb["grass"],
550
+ sns.xkcd_rgb["beige"],
551
+ sns.xkcd_rgb["brown"],
552
+ ]
553
+
554
+ # make a stacked area plot
555
+ plt.close("all")
556
+
557
+ fig, ax = plt.subplots(constrained_layout=True, figsize=(20, 6))
558
+
559
+ ax.stackplot(
560
+ df["date"],
561
+ df["water"] * 100,
562
+ df["wet"] * 100,
563
+ df["norm_pv"] * 100,
564
+ df["norm_npv"] * 100,
565
+ df["norm_bs"] * 100,
566
+ colors=pal,
567
+ alpha=0.7,
568
+ )
569
+
570
+ # manually change the legend display order
571
+ legend = ax.legend(
572
+ ["open water", "wet", "green veg", "dry veg", "bare soil"][::-1],
573
+ loc="lower left",
574
+ )
575
+ handles = legend.legend_handles
576
+
577
+ for i, handle in enumerate(handles):
578
+ handle.set_facecolor(pal[::-1][i])
579
+ handle.set_alpha(0.7)
580
+
581
+ # setup the display ranges
582
+ ax.set_ylim(0, 100)
583
+ ax.set_xlim(df["date"].min(), df["date"].max())
584
+
585
+ # add a new column: 'off_value' based on low quality data setting
586
+ df = generate_low_quality_data_periods(df)
587
+
588
+ ax.fill_between(
589
+ df["date"],
590
+ 0,
591
+ 100,
592
+ where=df["off_value"] == 100,
593
+ color="white",
594
+ alpha=0.5,
595
+ hatch="//",
596
+ )
597
+
598
+ ax.xaxis.set_major_locator(mdates.MonthLocator())
599
+ ax.xaxis.set_major_formatter(mdates.DateFormatter("%b-%Y"))
600
+
601
+ # Rotates and right-aligns the x labels so they don't crowd each other.
602
+ for label in ax.get_xticklabels(which="major"):
603
+ label.set(rotation=30, horizontalalignment="right")
604
+
605
+ x_label_text = "The Fractional Cover algorithm developed by the Joint Remote Sensing Research Program and\n the Water Observations from Space algorithm developed by Geoscience Australia are used in the production of this data"
606
+
607
+ ax.set_xlabel(x_label_text, style="italic")
608
+
609
+ ax.set_ylabel("Percentage of wetland (%)")
610
+
611
+ # add a title
612
+ plt.title(
613
+ f"Percentage of area dominated by WOs, Wetness, Fractional Cover for\n {self.wetland_name}",
614
+ fontsize=16,
615
+ )
616
+ plt.show()
617
+
618
+ if self.out_plot:
619
+ # save the figure
620
+ fig.savefig(f"{self.wetland_name}")
621
+
622
+ else:
623
+ print("No valid polygon to process. Please select or draw a new polygon.")
624
+
625
+ # Export spatial WIT animation if checkbox is selected
626
+ if self.spatial_wit and ds_wit is not None:
627
+
628
+ try:
629
+ ds = spatial_wit(ds=ds_wit, wetland_name=self.wetland_name)
630
+ print("Animation complete")
631
+ except AttributeError:
632
+ print("No polygon selected")
633
+
634
+ else:
635
+ print("No valid polygon to process. Please select or draw a new polygon.")