dea-tools 0.3.6.dev34__tar.gz → 0.3.6.dev38__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 (42) hide show
  1. {dea_tools-0.3.6.dev34/dea_tools.egg-info → dea_tools-0.3.6.dev38}/PKG-INFO +1 -1
  2. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/landcover.py +397 -445
  3. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38/dea_tools.egg-info}/PKG-INFO +1 -1
  4. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/.gitignore +0 -0
  5. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/LICENSE +0 -0
  6. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/MANIFEST.in +0 -0
  7. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/README.rst +0 -0
  8. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/__init__.py +0 -0
  9. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/__main__.py +0 -0
  10. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/app/__init__.py +0 -0
  11. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/app/animations.py +0 -0
  12. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/app/changefilmstrips.py +0 -0
  13. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/app/crophealth.py +0 -0
  14. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/app/deacoastlines.py +0 -0
  15. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/app/geomedian.py +0 -0
  16. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/app/imageexport.py +0 -0
  17. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/app/miningrehab.py +0 -0
  18. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/app/widgetconstructors.py +0 -0
  19. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/bandindices.py +0 -0
  20. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/bom.py +0 -0
  21. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/classification.py +0 -0
  22. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/climate.py +0 -0
  23. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/coastal.py +0 -0
  24. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/dask.py +0 -0
  25. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/datahandling.py +0 -0
  26. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/maps.py +0 -0
  27. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/plotting.py +0 -0
  28. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/pyfes_model.py +0 -0
  29. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/spatial.py +0 -0
  30. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/temporal.py +0 -0
  31. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/validation.py +0 -0
  32. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/waterbodies.py +0 -0
  33. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools/wetlands.py +0 -0
  34. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools.egg-info/SOURCES.txt +0 -0
  35. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools.egg-info/dependency_links.txt +0 -0
  36. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools.egg-info/requires.txt +0 -0
  37. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/dea_tools.egg-info/top_level.txt +0 -0
  38. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/index.rst +0 -0
  39. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/mock_imports.txt +0 -0
  40. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/pyproject.toml +0 -0
  41. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/setup.cfg +0 -0
  42. {dea_tools-0.3.6.dev34 → dea_tools-0.3.6.dev38}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dea-tools
3
- Version: 0.3.6.dev34
3
+ Version: 0.3.6.dev38
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
@@ -17,13 +17,13 @@ here: https://gis.stackexchange.com/questions/tagged/open-data-cube).
17
17
  If you would like to report an issue with this script, you can file one
18
18
  on GitHub (https://github.com/GeoscienceAustralia/dea-notebooks/issues/new).
19
19
 
20
- Last modified: January 2022
20
+ Last modified: March 2025
21
21
  """
22
22
 
23
23
  import numpy as np
24
24
  import pandas as pd
25
- import ast
26
- import sys
25
+ # import ast
26
+ # import sys
27
27
 
28
28
  from IPython.display import Image
29
29
 
@@ -32,6 +32,8 @@ from matplotlib import colors as mcolours
32
32
  from matplotlib import patheffects
33
33
  from matplotlib.animation import FuncAnimation
34
34
 
35
+
36
+
35
37
  # Define colour schemes for each land cover measurement
36
38
  lc_colours = {
37
39
  'level3': {111: (172, 188, 45, 255, "Cultivated\nTerrestrial\nVegetation"),
@@ -72,39 +74,6 @@ lc_colours = {
72
74
  220: (77, 159, 220, 255, "Changed to Water"),
73
75
  0: (255, 255, 255, 255, "No Change")},
74
76
 
75
- 'lifeform_veg_cat_l4a': {1: (14, 121, 18, 255, "Woody\nVegetation"),
76
- 2: (172, 188, 45, 255, "Herbaceous\nVegetation"),
77
- 255: (255, 255, 255, 255, "No Data/\nNot vegetated")},
78
-
79
-
80
- 'canopyco_veg_cat_l4d': {10: (14, 121, 18, 255, "> 65 % \ncover"),
81
- 12: (45, 141, 47, 255, "40 to 65 % \ncover"),
82
- 13: (80, 160, 82, 255, "15 to 40 % \ncover"),
83
- 15: (117, 180, 118, 255, "4 to 15 % \ncover"),
84
- 16: (154, 199, 156, 255, "1 to 4 % \ncover"),
85
- 255: (255, 255, 255, 255, "No Data /\n Not vegetated")},
86
-
87
- 'waterstt_wat_cat_l4a': {1: (77, 159, 220, 255, "Water"),
88
- 255: (255, 255, 255, 255, "No Data /\nNot water")},
89
-
90
- 'watersea_veg_cat_l4a_au': {1: (25, 173, 109, 255, "> 3 months"),
91
- 2: (176, 218, 201, 255, "< 3 months"),
92
- 255: (255, 255, 255, 255, "No data/\nNot aquatic\nvegetation")},
93
-
94
- 'inttidal_wat_cat_l4a': {3: (77, 159, 220, 255, "Intertidal"),
95
- 255: (255, 255, 255, 255, "No data /\n Not intertidal")},
96
-
97
- 'waterper_wat_cat_l4d_au': {1: (27, 85, 186, 255, "> 9\nmonths"),
98
- 7: (52, 121, 201, 255, "7 to 9\nmonths"),
99
- 8: (79, 157, 217, 255, "4 to 6\nmonths"),
100
- 9: (113, 202, 253, 255, "1 to 3\nmonths"),
101
- 255: (255, 255, 255, 255, "No data/\nNot water")},
102
-
103
- 'baregrad_phy_cat_l4d_au': {10: (255, 230, 140, 255, "Sparsely\nvegetated\n(<20% bare)"),
104
- 12: (250, 210, 110, 255, "Very sparsely\nvegetated\n(20 to 60% bare)"),
105
- 15: (243, 171, 105, 255, "Bare areas,\nunvegetated\n(>60% bare)"),
106
- 255: (255, 255, 255, 255, "No data/\nNot bare")},
107
-
108
77
  'level4': {
109
78
  1: (151, 187, 26, 255, 'Cultivated Terrestrial\n Vegetated:'),
110
79
  2: (151, 187, 26, 255, 'Cultivated Terrestrial\n Vegetated: Woody'),
@@ -257,38 +226,35 @@ lc_colours = {
257
226
  },
258
227
  }
259
228
 
229
+
230
+
260
231
  # dictionary needed to generate colour schemes of descriptors from the level 4 colour scheme. The structure is as follow:
261
232
  # long_descriptor_name[string]: {keyword_for_finding_classes_in_level4_colourscheme[string] : (RGB_colourscheme[4 integers], label_of_descriptor[string])}
262
233
  lc_colours_mapping = {
263
- 'lifeform_veg_cat_l4a': {'Woody': (14, 121,18, 255, 'Woody Vegetation'),
264
- 'Herbaceous': (172, 188, 45, 255, 'Herbaceous\n Vegetation')},
265
- 'canopyco_veg_cat_l4d': {'> 65 %': (14, 121, 18, 255, '> 65 % cover'),
266
- '40 to 65 %': (45, 141, 47, 255, '40 to 65 % cover'),
267
- '15 to 40 %': (80, 160, 82, 255, '15 to 40 % cover'),
268
- '4 to 15 %': (117, 180, 118, 255, '4 to 15 % cover'),
269
- '1 to 4 %': (154, 199, 156, 255, '1 to 4 % cover')},
234
+ 'lifeform_veg_cat_l4a': {'Woody': (14, 121,18, 255, 'Woody\nVegetation'),
235
+ 'Herbaceous': (172, 188, 45, 255, 'Herbaceous\nVegetation')},
236
+ 'canopyco_veg_cat_l4d': {'> 65 %': (14, 121, 18, 255, '> 65 %\ncover'),
237
+ '40 to 65 %': (45, 141, 47, 255, '40 to 65 %\ncover'),
238
+ '15 to 40 %': (80, 160, 82, 255, '15 to 40 %\ncover'),
239
+ '4 to 15 %': (117, 180, 118, 255, '4 to 15 %\ncover'),
240
+ '1 to 4 %': (154, 199, 156, 255, '1 to 4 %\ncover')},
270
241
  'watersea_veg_cat_l4a_au': {'(semi-) permenant': (25, 173, 109, 255, '> 3 months'),
271
242
  '(temporary or seasonal)': (176, 218, 201, 255, '< 3 months')},
272
243
  'waterstt_wat_cat_l4a': {'Water: (Water)': (77, 159, 220, 255, 'Water')},
273
- 'inttidal_wat_cat_l4a': {'Tidal area': (77, 159, 220, 255, 'Water: (Water) Tidal area')},
274
- 'waterper_wat_cat_l4d_au': {'> 9 months': (27, 85, 186, 255, '> 9 months'),
275
- '7 to 9 months': (52, 121, 201, 255, '7 to 9 months'),
276
- '4 to 6 months': (79, 157, 217, 255, '4 to 6 months'),
277
- '1 to 3 months': (113, 202, 253, 255, '1 to 3 months')},
278
- 'baregrad_phy_cat_l4d_au': {'Sparsely vegetated': (255, 230, 140, 255, 'Sparsely vegetated\n (< 20% bare)'),
279
- 'Very sparesely': (250, 210, 110, 255, 'Very sparsely\n vegetated (20 to 60% bare)'),
280
- 'Bare areas': (243, 171, 105, 255, 'Bare areas,\n unvegetated (> 60% bare)')},
244
+ 'inttidal_wat_cat_l4a': {'Tidal area': (77, 159, 220, 255, 'Tidal area')},
245
+ 'waterper_wat_cat_l4d_au': {'> 9 months': (27, 85, 186, 255, '> 9\nmonths'),
246
+ '7 to 9 months': (52, 121, 201, 255, '7 to 9\nmonths'),
247
+ '4 to 6 months': (79, 157, 217, 255, '4 to 6\nmonths'),
248
+ '1 to 3 months': (113, 202, 253, 255, '1 to 3\nmonths')},
249
+ 'baregrad_phy_cat_l4d_au': {'Sparsely vegetated': (255, 230, 140, 255, 'Sparsely\nvegetated\n(< 20% bare)'),
250
+ 'Very sparesely': (250, 210, 110, 255, 'Very sparsely\nvegetated\n(20 to 60% bare)'),
251
+ 'Bare areas': (243, 171, 105, 255, 'Bare areas,\nunvegetated\n(> 60% bare)')},
281
252
 
282
253
  }
283
254
 
284
255
 
285
256
 
286
-
287
-
288
-
289
-
290
- def get_layer_name(measurement, da):
291
- aliases = {
257
+ aliases = {
292
258
  'lifeform': 'lifeform_veg_cat_l4a',
293
259
  'vegetation_cover': 'canopyco_veg_cat_l4d',
294
260
  'water_seasonality': 'watersea_veg_cat_l4a_au',
@@ -300,98 +266,35 @@ def get_layer_name(measurement, da):
300
266
  'level_4': 'level4'
301
267
  }
302
268
 
303
- # Use provided measurement if able
304
- measurement = measurement.lower() if measurement else da.name
305
- measurement = aliases[measurement] if measurement in aliases.keys(
306
- ) else measurement
307
- return measurement
308
269
 
309
270
 
310
- def make_colorbar(fig, ax, measurement, labelsize=10, horizontal=False, animation=False):
271
+ def _get_layer_name(measurement, da, aliases=aliases):
311
272
  """
312
- Adds a new colorbar with appropriate land cover colours and labels.
313
-
314
- For DEA Land Cover Level 4 data, this function must be used with a double plot.
315
- The 'ax' should be on the left side of the figure, and the colour bar will added
316
- on the right hand side.
317
-
318
- Parameters
319
- ----------
320
- fig : matplotlib figure
321
- Figure to add colourbar to
322
- ax : matplotlib ax
323
- Matplotlib figure ax to add colorbar to.
324
- measurement : str
325
- Land cover measurement to use for colour map and labels.
326
- labelsize : int
327
- size of labels of colourbar
328
-
273
+ Returns detailed name of descriptor given the short alias
329
274
  """
330
275
 
331
- # Create new axis object for colorbar
332
- # parameters for add_axes are [left, bottom, width, height], in
333
- # fractions of total plot
276
+ # Use provided measurement if able
277
+ measurement = measurement.lower() if measurement else da.name
278
+ measurement = aliases[measurement] if measurement in aliases.keys(
279
+ ) else measurement
334
280
 
335
- if measurement == 'level4' and animation == True:
336
-
337
- # special spacing settings for level 4
338
- cax = fig.add_axes([0.62, 0.10, 0.02, 0.80])
339
- orient = 'vertical'
340
-
341
- # get level 4 colour bar colour map ect
342
- cb_cmap, cb_norm, cb_labels, cb_ticks = lc_colourmap_colourbar('level4_colourbar_labels',
343
- colour_bar=True)
344
- elif measurement == 'level4' and animation == False:
345
-
346
- # get level 4 colour bar colour map ect
347
- cb_cmap, cb_norm, cb_labels, cb_ticks = lc_colourmap_colourbar('level4_colourbar_labels',
348
- colour_bar=True)
349
- #move plot over to make room for colourbar
350
- fig.subplots_adjust(right=0.825)
351
-
352
- # Settings for axis positions
353
- cax = fig.add_axes([0.84, 0.15, 0.02, 0.70])
354
- orient = 'vertical'
355
-
356
- else:
357
- #for all other measurements
358
-
359
- #move plot over to make room for colourbar
360
- fig.subplots_adjust(right=0.825)
361
-
362
- # Settings for different axis positions
363
- if horizontal:
364
- cax = fig.add_axes([0.02, 0.05, 0.90, 0.03])
365
- orient = 'horizontal'
366
- else:
367
- cax = fig.add_axes([0.84, 0.15, 0.02, 0.70])
368
- orient = 'vertical'
369
-
370
- # get measurement colour bar colour map ect
371
- cb_cmap, cb_norm, cb_labels, cb_ticks = lc_colourmap_colourbar(measurement,
372
- colour_bar=True)
373
-
374
- img = ax.imshow([cb_ticks], cmap=cb_cmap, norm=cb_norm)
375
- cb = fig.colorbar(img, cax=cax, orientation=orient)
376
-
377
- cb.ax.tick_params(labelsize=labelsize)
378
- cb.set_ticks(cb_ticks + np.diff(cb_ticks, append=cb_ticks[-1]+1) / 2)
379
- cb.set_ticklabels(cb_labels)
281
+ return measurement
380
282
 
381
283
 
382
284
 
383
- def descriptors_colours(lc_colours, lc_colours_mapping, descriptor):
285
+ def _descriptors_colours(lc_colours, lc_colours_mapping, descriptor):
384
286
  """
385
287
  Generates a sorted dictionary of colours based on a given descriptor.
386
288
 
387
289
  This function takes in a dictionary of Land Cover classes, a mapping of descriptors to colours,
388
- and a specific descriptor. It returns a dictionary where the keys are sorted and the values are
389
- the corresponding colours and labels from the descriptor mapping.
290
+ and a specific descriptor. It returns a dictionary where the keys (i.e., classes values) are sorted
291
+ and the values are the corresponding colours and labels from the descriptor mapping.
390
292
 
391
293
  Parameters
392
294
  ----------
393
295
  lc_colours : dict
394
- Dictionary containing colour schemes for all Land Cover classes.
296
+ Dictionary containing colour schemes for all Land Cover classes,
297
+ including Level 4 scheme (needed in this function).
395
298
 
396
299
  lc_colours_mapping : dict
397
300
  Dictionary mapping descriptors (e.g., lifeform) to their corresponding colours and labels.
@@ -412,43 +315,87 @@ def descriptors_colours(lc_colours, lc_colours_mapping, descriptor):
412
315
  descriptor_dict = lc_colours_mapping[descriptor]
413
316
 
414
317
  # create a new colours dictionary with all level 4 values set to white colour
415
- # based on the descriptor, the values of interest will be filled with the pre-defined colours (all the rest will stay white)
318
+ # this dictionary is the foundation of the output returned at the end
416
319
  colours_dict = level4_colours.copy()
417
320
  for key in colours_dict:
418
- colours_dict[key] = (255, 255, 255, 255, "No Data")
321
+ colours_dict[key] = (255, 255, 255, 255, "No Data/\nOther\nClasses")
419
322
 
420
- # update the colours dictionary with the descriptor-specific colours
421
- for class_keyword, colour_n_label in descriptor_dict.items():
323
+ # based on the descriptor, update the colours dictionary with the descriptor-specific colours
324
+ # (all the rest will stay white)
325
+ for class_keyword, colour_n_label in descriptor_dict.items(): # iterate over descriptors mapping keys
422
326
 
423
- for class_value, lvl4_scheme in level4_colours.items():
424
- label_lvl4 = lvl4_scheme[4]
327
+ for class_value, lvl4_scheme in level4_colours.items(): # iterate over Level 4 colour scheme
328
+ # get the label of current Level 4 class colour
329
+ label_lvl4 = lvl4_scheme[4]
425
330
 
426
- if class_keyword in label_lvl4:
427
- colours_dict[class_value] = colour_n_label
331
+ if class_keyword in label_lvl4: # check if the current Level 4 class colour contains the current descriptor mapping key
332
+ # replace white colour with RGB indicated by the descriptor mapping dictionary
333
+ colours_dict[class_value] = colour_n_label
428
334
 
429
- # sort the colours dictionary by keys
335
+ # sort the colours dictionary by keys (i.e., the values of classes)
430
336
  sorted_colours_dict = {key: colours_dict[key] for key in sorted(colours_dict.keys())}
431
337
 
432
338
  return sorted_colours_dict
433
339
 
434
340
 
435
- def lc_colourmap(colour_scheme, colour_bar=False):
341
+
342
+ def get_colour_scheme(measurement):
343
+ """
344
+ Gets colour scheme dictionary given the measurement of interest
436
345
  """
437
- Returns colour map and normalisation for the provided DEA Land Cover
438
- measurement, for use in plotting with Matplotlib library
439
346
 
440
- Parameters
441
- ----------
442
- colour_scheme : string
443
- Name of land cover colour scheme to use
444
- Valid options: 'level3', 'level4', 'lifeform_veg_cat_l4a',
445
- 'canopyco_veg_cat_l4d', 'watersea_veg_cat_l4a_au',
446
- 'waterstt_wat_cat_l4a', 'inttidal_wat_cat_l4a',
447
- 'waterper_wat_cat_l4d_au', 'baregrad_phy_cat_l4d_au'.
448
- colour_bar : bool, optional
449
- Controls if colour bar labels are returned as a list for
450
- plotting a colour bar. Default: False.
347
+ # ensure a valid colour scheme was requested
348
+ assert (
349
+ (measurement in lc_colours.keys()) # either in main colour scheme dictionary
350
+ or (measurement in lc_colours_mapping.keys()) # or in mapping dictionary for descriptors
351
+ or (measurement in aliases.keys()) # or short aliases of descriptors
352
+ ), f'colour scheme must be one of {lc_colours.keys()} {lc_colours_mapping.keys()} {aliases.keys()} (got "{measurement}")'
353
+
354
+ # if a descriptor colour scheme is required, use the _descriptors_colours function
355
+ if measurement in lc_colours_mapping:
356
+ colour_scheme=_descriptors_colours(lc_colours,lc_colours_mapping, measurement)
451
357
 
358
+ else: # else, use standard colours scheme
359
+ colour_scheme = lc_colours[measurement]
360
+
361
+ return colour_scheme
362
+
363
+
364
+
365
+ def _reduce_colour_scheme(colour_scheme):
366
+ """
367
+ Takes a colour scheme dictionary and returns the dictionary without duplicate values.
368
+ This also replaces classes values with subsequent integers, useful for placing ticks of colourbar on the side
369
+ """
370
+
371
+ # foundation of the output dictionary
372
+ reduced_scheme = {}
373
+
374
+ # empty list to be filled with names of classes added to the output dictionary
375
+ classes_added = []
376
+
377
+ new_key = 2 # key 1 was added earlier and corresponds with "no data"
378
+
379
+ for key, value in colour_scheme.items():
380
+ # get string with class name
381
+ class_name = value[4]
382
+
383
+ if (class_name not in classes_added): # check if already added in list
384
+ classes_added.append(class_name)
385
+
386
+ # assign the colour scheme and label to a new key in reduced_scheme
387
+ reduced_scheme[new_key] = value
388
+ # increase value of new_key for next iteration
389
+ new_key += 1
390
+
391
+ return reduced_scheme
392
+
393
+
394
+
395
+ def lc_colourmap(colour_scheme):
396
+ """
397
+ Takes a colour scheme dictionary and returns colormap for matplotlib
398
+
452
399
  Returns
453
400
  ---------
454
401
  cmap : matplotlib colormap
@@ -458,135 +405,144 @@ def lc_colourmap(colour_scheme, colour_bar=False):
458
405
  Matplotlib colormap index based on the discrete intervals of the
459
406
  classes in the specified DEA Land Cover measurement. Ensures the
460
407
  colormap maps the colours to the class numbers correctly.
461
- cblables : array
462
- A two dimentional array containing the numerical class values
463
- (first dim) and string labels (second dim) of the classes found
464
- in the chosen DEA Land Cover measurement.
465
408
  """
466
-
467
-
468
- colour_scheme = colour_scheme.lower()
469
-
470
- # if a descriptor colour scheme is required, use the descriptors_colours function
471
- if colour_scheme in lc_colours_mapping:
472
- lc_colour_scheme=descriptors_colours(lc_colours,lc_colours_mapping, colour_scheme)
473
-
474
- else: # standard colours scheme
475
- lc_colour_scheme = lc_colours[colour_scheme]
476
- # Ensure a valid colour scheme was requested
477
- assert (colour_scheme in lc_colours.keys(
478
- )), f'colour scheme must be one of [{lc_colours.keys()}] (got "{colour_scheme}")'
479
-
480
-
481
- # Create colour map
482
- colour_arr = []
483
- for key, value in lc_colour_scheme.items():
484
- colour_arr.append(np.array(value[:-2]) / 255)
485
-
486
- cmap = mcolours.ListedColormap(colour_arr)
487
- bounds = list(lc_colour_scheme)
488
409
 
489
- if colour_bar == True:
490
- if colour_scheme == 'level4':
491
- # Set colour labels to shortened level 4 list
492
- lc_colour_scheme = lc_colours['level4_colourbar_labels']
493
- cb_ticks = list(lc_colour_scheme)
494
- #print(f'CB_TICKS {cb_ticks}')
495
- cb_labels = []
496
- for x in cb_ticks:
497
- cb_labels.append(lc_colour_scheme[x][4])
410
+ colour_arr = [] # empty list to be populated with colours
411
+ for key, value in colour_scheme.items():
412
+ colour_arr.append(np.array(value[:-2]) / 255) # add colour to list
413
+
414
+ # create a colour map from the list of colours
415
+ cmap = mcolours.ListedColormap(colour_arr)
498
416
 
417
+ # create boundaries of colours by using the exact class values and adding a larger value at the end
418
+ bounds = list(colour_scheme)
499
419
  bounds.append(bounds[-1]+1)
500
420
 
501
- # shift all back by 0.5 to make sure level4 values are within bounds and not mathcing exactly one bound
421
+ # shift all boundaries back by 0.5 to make sure level4 values are within bounds
422
+ # this is a robust method to make sure each value is within a colour bin
502
423
  bounds = [i-0.5 for i in bounds]
503
-
504
- norm = mcolours.BoundaryNorm(np.array(bounds), cmap.N)
505
-
506
- if colour_bar == False:
507
- return (cmap, norm)
508
- else:
509
- return (cmap, norm, cb_labels, cb_ticks)
510
424
 
425
+ # normalisation for colourmap
426
+ norm = mcolours.BoundaryNorm(np.array(bounds), cmap.N)
427
+
428
+ return (cmap, norm)
511
429
 
430
+
512
431
 
513
- def lc_colourmap_colourbar(colour_scheme, colour_bar=False):
432
+ def _legend_colourmap(colour_scheme):
514
433
  """
515
- Returns colour map and normalisation for the provided DEA Land Cover
516
- measurement, for use in plotting with Matplotlib library
517
-
434
+ Returns colour map and normalisation specifcially for the colourbar
435
+ of the provided DEA Land Cover measurement, for use in plotting with Matplotlib library
436
+
518
437
  Parameters
519
438
  ----------
520
- colour_scheme : string
521
- Name of land cover colour scheme to use
522
- Valid options: 'level3', 'level4', 'lifeform_veg_cat_l4a',
523
- 'canopyco_veg_cat_l4d', 'watersea_veg_cat_l4a_au',
524
- 'waterstt_wat_cat_l4a', 'inttidal_wat_cat_l4a',
525
- 'waterper_wat_cat_l4d_au', 'baregrad_phy_cat_l4d_au'.
526
- colour_bar : bool, optional
527
- Controls if colour bar labels are returned as a list for
528
- plotting a colour bar. Default: False.
529
-
439
+ colour_scheme : dictionary with colour scheme
440
+
530
441
  Returns
531
442
  ---------
532
- cmap : matplotlib colormap
443
+ cb_cmap : matplotlib colormap
533
444
  Matplotlib colormap containing the colour scheme for the
534
445
  specified DEA Land Cover measurement.
535
- norm : matplotlib colormap index
446
+ cb_norm : matplotlib colormap index
536
447
  Matplotlib colormap index based on the discrete intervals of the
537
448
  classes in the specified DEA Land Cover measurement. Ensures the
538
- colormap maps the colours to the class numbers correctly.
539
- cblables : array
540
- A two dimentional array containing the numerical class values
541
- (first dim) and string labels (second dim) of the classes found
449
+ colormap maps the colours to the class numbers correctly.
450
+ cb_labels : list
451
+ string labels of the classes found
542
452
  in the chosen DEA Land Cover measurement.
453
+ cb_ticks : list
454
+ position of ticks in colour bar
455
+
543
456
  """
457
+
458
+ # delete duplicates to create colour bar (this effectively applies only with descriptors),
459
+ # and fix values for correct colourbar label positioning
460
+ colour_scheme = _reduce_colour_scheme(colour_scheme)
544
461
 
462
+ cb_cmap, cb_norm = lc_colourmap(colour_scheme)
545
463
 
546
- colour_scheme = colour_scheme.lower()
547
- # Ensure a valid colour scheme was requested
548
- # try:
549
- assert (colour_scheme in lc_colours.keys(
550
- )), f'colour scheme must be one of [{lc_colours.keys()}] (got "{colour_scheme}")'
464
+ cb_ticks = list(colour_scheme)
465
+ cb_labels = []
466
+ for x in cb_ticks:
467
+ cb_labels.append(colour_scheme[x][4])
468
+
469
+ return (cb_cmap, cb_norm, cb_labels, cb_ticks)
470
+
551
471
 
552
- # ('The dataset provided does not have a valid '
553
- # 'name. Please specify which DEA Landcover measurement is being plotted '
554
- # 'by providing the name using the "measurement" variable. For example (measurement = "full_classification")')
555
472
 
556
- # Get colour definitions
557
- lc_colour_scheme = lc_colours[colour_scheme]
473
+ def make_colourbar(fig, ax, measurement, labelsize=10, horizontal=False, animation=False): # in practice, horizontal arg in never used, currently
474
+ """
475
+ Adds a new colorbar with appropriate land cover colours and labels.
558
476
 
559
- # Create colour map
560
- colour_arr = []
561
- for key, value in lc_colour_scheme.items():
562
- colour_arr.append(np.array(value[:-2]) / 255)
477
+ For DEA Land Cover Level 4 data, this function must be used with a double plot.
478
+ The 'ax' should be on the left side of the figure, and the colour bar will added
479
+ on the right hand side.
480
+
481
+ Parameters
482
+ ----------
483
+ fig : matplotlib figure
484
+ Figure to add colourbar to
485
+ ax : matplotlib ax
486
+ Matplotlib figure ax to add colorbar to.
487
+ measurement : string
488
+ name of layer or descriptor of interest
489
+ labelsize : int
490
+ size of labels of colourbar
491
+
492
+ Returns
493
+ ----------
494
+ matplotlib colorbar in its own colour axis
495
+ """
563
496
 
564
- cmap = mcolours.ListedColormap(colour_arr)
565
- bounds = list(lc_colour_scheme)
497
+ if measurement == 'level4':
498
+ colour_scheme = lc_colours['level4_colourbar_labels'] # use shorten labels dictionary
499
+
500
+ if animation == True:
501
+ # special spacing settings for level 4
502
+ cax = fig.add_axes([0.62, 0.05, 0.02, 0.90]) # parameters for add_axes are [left, bottom, width, height], in fractions of total plot
503
+ orient = 'vertical'
504
+ # get level 4 colour bar colour map ect
505
+ cb_cmap, cb_norm, cb_labels, cb_ticks = _legend_colourmap(colour_scheme)
506
+
507
+ elif animation == False:
508
+ #move plot over to make room for colourbar
509
+ fig.subplots_adjust(right=0.825)
510
+ # Settings for axis positions
511
+ cax = fig.add_axes([0.84, 0.145, 0.02, 0.70])
512
+ orient = 'vertical'
513
+ # get level 4 colour bar colour map ect
514
+ cb_cmap, cb_norm, cb_labels, cb_ticks = _legend_colourmap(colour_scheme)
515
+
516
+ else: #for all other measurements
517
+ colour_scheme = get_colour_scheme(measurement) # use standard colour scheme
518
+
519
+ #move plot over to make room for colourbar
520
+ fig.subplots_adjust(right=0.825)
566
521
 
567
- if colour_bar == True:
568
- if colour_scheme == 'level4':
569
- # Set colour labels to shortened level 4 list
570
- lc_colour_scheme = lc_colours['level4_colourbar_labels']
571
- cb_ticks = list(lc_colour_scheme)
572
- #print(f'CB_TICKS {cb_ticks}')
573
- cb_labels = []
574
- for x in cb_ticks:
575
- cb_labels.append(lc_colour_scheme[x][4])
522
+ # settings for different axis positions
523
+ if horizontal:
524
+ cax = fig.add_axes([0.02, 0.05, 0.90, 0.03])
525
+ orient = 'horizontal'
526
+ else:
527
+ cax = fig.add_axes([0.84, 0.145, 0.02, 0.70])
528
+ orient = 'vertical'
529
+
530
+ # get measurement colour bar colour map ect
531
+ cb_cmap, cb_norm, cb_labels, cb_ticks = _legend_colourmap(colour_scheme)
576
532
 
577
- bounds.append(bounds[-1]+1)
578
- norm = mcolours.BoundaryNorm(np.array(bounds), cmap.N)
533
+ img = ax.imshow([cb_ticks], cmap=cb_cmap, norm=cb_norm)
534
+ cb = fig.colorbar(img, cax=cax, orientation=orient)
579
535
 
580
- if colour_bar == False:
581
- return (cmap, norm)
582
- else:
583
- return (cmap, norm, cb_labels, cb_ticks)
536
+ cb.ax.tick_params(labelsize=labelsize)
537
+ cb.set_ticks(cb_ticks)
538
+ cb.set_ticklabels(cb_labels)
584
539
 
585
540
 
586
541
 
587
- def plot_land_cover(data, year=None, measurement=None, out_width=15, cols=4,):
542
+ def plot_land_cover(data, labelsize=10, year=None, measurement=None, out_width=15, cols=4,):
588
543
  """
589
544
  Plot a single land cover measurement with appropriate colour scheme.
545
+
590
546
  Parameters
591
547
  ----------
592
548
  data : xarray.DataArray
@@ -596,22 +552,25 @@ def plot_land_cover(data, year=None, measurement=None, out_width=15, cols=4,):
596
552
  all time slices are plotted.
597
553
  measurement : string, optional
598
554
  Name of the DEA land cover classification to be plotted. Passed to
599
- lc_colourmap to specify which colour scheme will be used. If non
555
+ _legend_colourmap to specify which colour scheme will be used. If non
600
556
  provided, reads data array name from `da` to determine.
557
+ out_width : integer, optional
558
+ It controls the size of the output
559
+ cols: integer, optional
560
+ Sets number of columns if multiple time steps are visualised
561
+
562
+ Returns
563
+ ---------
564
+ Matplotlib image
565
+
601
566
  """
567
+
602
568
  # get measurement name
603
- measurement = get_layer_name(measurement, data)
569
+ measurement = _get_layer_name(measurement, data)
604
570
 
605
- # get colour map, normalisation
606
- try:
607
- cmap, norm = lc_colourmap(measurement)
608
- except AssertionError:
571
+ colour_scheme = get_colour_scheme(measurement)
609
572
 
610
- raise KeyError('Could not automatically determine colour scheme from'
611
- f'DataArray name {measurement}. Please specify which '
612
- 'DEA Landcover measurement is being plotted by providing'
613
- 'the name using the "measurement" variable For example'
614
- '(measurement = "level4")')
573
+ cmap, norm = lc_colourmap(colour_scheme)
615
574
 
616
575
  height, width = data.geobox.shape
617
576
  scale = out_width / width
@@ -626,16 +585,16 @@ def plot_land_cover(data, year=None, measurement=None, out_width=15, cols=4,):
626
585
 
627
586
  fig, ax = plt.subplots()
628
587
  fig.set_size_inches(width * scale, height * scale)
629
- make_colorbar(fig, ax, measurement)
588
+ make_colourbar(fig, ax, measurement, labelsize)
630
589
  im = ax.imshow(data.values, cmap=cmap, norm=norm, interpolation="nearest")
631
590
 
632
-
633
591
  elif len(data.time) == 1:
634
592
  #plotting protocol if only one timestep is passed and not a year variable
635
593
  fig, ax = plt.subplots()
636
594
  fig.set_size_inches(width * scale, height * scale)
637
- make_colorbar(fig, ax, measurement)
595
+ make_colourbar(fig, ax, measurement, labelsize)
638
596
  im = ax.imshow(data.isel(time=0), cmap=cmap, norm=norm, interpolation="nearest")
597
+
639
598
  else:
640
599
  #plotting protocol if multible time steps are passed to plot
641
600
  if cols > len(data.time):
@@ -646,7 +605,7 @@ def plot_land_cover(data, year=None, measurement=None, out_width=15, cols=4,):
646
605
  fig.set_size_inches(
647
606
  width * scale, (height * scale / cols) * (len(data.time) / cols))
648
607
 
649
- make_colorbar(fig, ax.flat[0], measurement)
608
+ make_colourbar(fig, ax.flat[0], measurement, labelsize)
650
609
 
651
610
  for a, b in enumerate(ax.flat):
652
611
  if a < data.shape[0]:
@@ -656,6 +615,60 @@ def plot_land_cover(data, year=None, measurement=None, out_width=15, cols=4,):
656
615
  return im
657
616
 
658
617
 
618
+
619
+ def _calc_class_ratio(da, measurement):
620
+ """
621
+ Creates a table listing year by year what percentage of the
622
+ total area is taken up by each class.
623
+ Parameters
624
+ ----------
625
+ da : xarray.DataArray with time dimension
626
+ measurement: string with name of descriptor/measurement
627
+
628
+ Returns
629
+ -------
630
+ Pandas Dataframe : containing class percentages per year
631
+ """
632
+
633
+ # list all class codes in dataset
634
+ list_classes = (np.unique(da, return_counts=False)).tolist()
635
+
636
+ # if a descriptor colour scheme is required, list_classes need to be chnaged to contain only classes of that descriptor
637
+ # the following code uses the _descriptors_colours function to get the colours scheme and then the values of the descriptor of interest
638
+ if measurement in lc_colours_mapping:
639
+ lc_colour_scheme=_descriptors_colours(lc_colours,lc_colours_mapping, measurement)
640
+ # sort based on RGB colour, so stack plot will show same colours next to each other
641
+ lc_colour_scheme= dict(sorted(lc_colour_scheme.items(), key=lambda item: item[1][0:3]))
642
+ # create list of values
643
+ all_classes_descriptor = list(lc_colour_scheme.keys())
644
+ # out of all possible classes of that descriptor, keep only the ones actually in the data array
645
+ list_classes = [i for i in all_classes_descriptor if i in list_classes] # the order of all_classes_descriptor and list_classes is important: the correct sorting order is the one of all_classes_descriptor
646
+
647
+ # create empty dataframe & dictionary
648
+ ratio_table = pd.DataFrame(data=None, columns=list_classes)
649
+ date_line = {}
650
+
651
+ # count all pixels, should be consistent
652
+ total_pix = da.isel(time=1).size
653
+
654
+ # iterate through each year in dataset
655
+ for i in range(0, len(da.time)):
656
+ date = str(da.time[i].data)[0:10]
657
+
658
+ # for each year iterate though each present class number
659
+ # and count pixels
660
+ for n in list_classes:
661
+ number_of_pixles = int(np.sum(da.isel(time=i) == n))
662
+ percentage = number_of_pixles / total_pix * 100
663
+ date_line[n] = percentage
664
+
665
+ # add each year's counts to dataframe
666
+ ratio_table.loc[date] = date_line
667
+
668
+ return ratio_table
669
+
670
+
671
+
659
672
  def lc_animation(
660
673
  da,
661
674
  file_name="default_animation",
@@ -666,6 +679,7 @@ def lc_animation(
666
679
  width_pixels=10,
667
680
  dpi=150,
668
681
  font_size=15,
682
+ label_size = 15,
669
683
  label_ax=True,
670
684
  ):
671
685
  """
@@ -683,7 +697,7 @@ def lc_animation(
683
697
  Default: "default_animation" code adds .gif suffix.
684
698
  measurement : string, optional
685
699
  Name of the DEA land cover classification to be plotted. Passed to
686
- lc_colourmap to specify which colour scheme will ve used. If non
700
+ _legend_colourmap to specify which colour scheme will be used. If non
687
701
  provided, reads data array name from `da` to determine.
688
702
  stacked_plot: boolean, optional
689
703
  Determines if a stacked plot showing the percentage of area
@@ -704,6 +718,8 @@ def lc_animation(
704
718
  produce a higher resolution image but a larger file size and
705
719
  slower processing. Default: 150.
706
720
  font_size : int, optional.
721
+ Controls the size of the text on the axes and colour bar. Default: 15.
722
+ label_size : int, optional.
707
723
  Controls the size of the text which indicates the year
708
724
  displayed. Default: 15.
709
725
  label_ax : boolean, optional
@@ -715,87 +731,25 @@ def lc_animation(
715
731
  -------
716
732
  A GIF (.gif) animation file.
717
733
  """
718
-
719
- def calc_class_ratio(da, measurement):
720
- """
721
- Creates a table listing year by year what percentage of the
722
- total area is taken up by each class.
723
- Parameters
724
- ----------
725
- da : xarray.DataArray with time dimension
726
- measurement: string with name of descriptor/measurement
727
- Returns
728
- -------
729
- Pandas Dataframe : containing class percentages per year
730
- """
731
-
732
- # list all class codes in dataset
733
- list_classes = (np.unique(da, return_counts=False)).tolist()
734
-
735
- # if a descriptor colour scheme is required, list_classes need to be chnaged to contain only classes of that descriptor
736
- # the following code uses the descriptors_colours function to get the colours scheme and then the values of the descriptor of interest
737
- if measurement in lc_colours_mapping:
738
- lc_colour_scheme=descriptors_colours(lc_colours,lc_colours_mapping, measurement)
739
- # sort based on first RGB colour, so stack plot will show same colours next to each other
740
- lc_colour_scheme= dict(sorted(lc_colour_scheme.items(), key=lambda item: item[1][0]))
741
- # create list of values
742
- all_classes_descriptor = list(lc_colour_scheme.keys())
743
- # out of all possible classes of that descriptor, keep only the ones actually in the data array
744
- list_classes = [i for i in all_classes_descriptor if i in list_classes] # the order of all_classes_descriptor and list_classes is important: the correct sorting order is the one of all_classes_descriptor
745
-
746
- # create empty dataframe & dictionary
747
- ratio_table = pd.DataFrame(data=None, columns=list_classes)
748
- date_line = {}
749
-
750
- # count all pixels, should be consistent
751
- total_pix = da.isel(time=1).size
752
-
753
- # iterate through each year in dataset
754
- for i in range(0, len(da.time)):
755
- date = str(da.time[i].data)[0:10]
756
-
757
- # for each year iterate though each present class number
758
- # and count pixels
759
- for n in list_classes:
760
- number_of_pixles = int(np.sum(da.isel(time=i) == n))
761
- percentage = number_of_pixles / total_pix * 100
762
- date_line[n] = percentage
763
-
764
- # add each year's counts to dataframe
765
- ratio_table.loc[date] = date_line
766
- return ratio_table
767
-
768
- def rgb_to_hex(r, g, b):
769
- hex = "#%x%x%x" % (r, g, b)
770
- if len(hex) < 7:
771
- hex = "#0" + hex[1:]
772
- return hex
773
-
774
- measurement = get_layer_name(measurement, da)
775
-
776
734
  # Add gif to end of filename
777
735
  file_name = file_name + ".gif"
778
-
779
- # Create colour map and normalisation for specified lc measurement
780
- try:
781
- layer_cmap, layer_norm, cb_labels, cb_ticks = lc_colourmap(
782
- measurement, colour_bar=True)
783
- except AssertionError:
784
-
785
- raise KeyError(f'Could not automatically determine colour scheme from '
786
- f'DataArray name {measurement}. Please specify which '
787
- 'DEA Landcover measurement is being plotted by providing '
788
- 'the name using the "measurement" variable For example '
789
- '(measurement = "level4")')
790
736
 
791
- # Prepare variables needed
792
- # Get info on dataset dimensions
737
+ # get long name of measurement/variable
738
+ measurement = _get_layer_name(measurement, da)
739
+
740
+ # get colour scheme
741
+ colour_scheme = get_colour_scheme(measurement)
742
+
743
+ # Create colour map and normalisation for specified lc measurement
744
+ layer_cmap, layer_norm = lc_colourmap(colour_scheme)
745
+
746
+ # Get info on dataset dimensions and define size of output
793
747
  height, width = da.geobox.shape
794
748
  scale = width_pixels / width
795
749
  left, bottom, right, top = da.geobox.extent.boundingbox
796
750
  extent = [left, right, bottom, top]
797
751
 
798
- outline = [patheffects.withStroke(linewidth=2.5, foreground="black")]
752
+ # settings for the label showed on top of the images
799
753
  annotation_defaults = {
800
754
  "xy": (1, 1),
801
755
  "xycoords": "axes fraction",
@@ -803,96 +757,54 @@ def lc_animation(
803
757
  "textcoords": "offset points",
804
758
  "horizontalalignment": "right",
805
759
  "verticalalignment": "top",
806
- "fontsize": font_size,
760
+ "fontsize": label_size,
807
761
  "color": "white",
808
- "path_effects": outline,
762
+ "path_effects": [patheffects.withStroke(linewidth=1,
763
+ foreground="black")],
809
764
  }
810
765
 
766
+
811
767
  # Get information needed to display the year in the top corner
812
768
  times_list = da.time.dt.strftime("%Y").values
813
769
  text_list = [False] * len(times_list)
814
770
  annotation_list = ["\n".join([str(i) for i in (a, b) if i])
815
771
  for a, b in zip(times_list, text_list)]
816
772
 
817
- if stacked_plot == True:
773
+
774
+ if stacked_plot == True: # if need to add stacked line plot on the right
818
775
 
819
776
  # Create table for stacked plot
820
- stacked_plot_table = calc_class_ratio(da, measurement)
777
+ stacked_plot_table = _calc_class_ratio(da, measurement)
821
778
 
822
779
  # Build colour list of hex vals for stacked plot
823
- hex_colour_list = []
824
-
825
- if measurement in lc_colours_mapping: # if descriptor
826
- colour_def=descriptors_colours(lc_colours,lc_colours_mapping, measurement)
827
- else: # if level 3 or 4
828
- colour_def = lc_colours[measurement]
780
+ def _rgb_to_hex(r, g, b):
781
+ hex = "#%x%x%x" % (r, g, b)
782
+ if len(hex) < 7:
783
+ hex = "#0" + hex[1:]
784
+ return hex
829
785
 
830
- # Custom error message to help if user puts incorrect measurement name
786
+ hex_colour_list = []
831
787
  for val in list(stacked_plot_table):
832
- try:
833
- r, g, b = colour_def[val][0:3]
834
- except KeyError:
835
- raise KeyError(
836
- "class number not found in colour definition. "
837
- "Ensure measurement name provided matches the dataset being used")
838
- hex_val = rgb_to_hex(r, g, b)
788
+ r, g, b = colour_scheme[val][0:3]
789
+ hex_val = _rgb_to_hex(r, g, b)
839
790
  hex_colour_list.append(hex_val)
840
791
 
841
- # Define & set up figure
792
+ # Define & set up figure (two axes: the LC array and the stacked line plot)
842
793
  fig, (ax1, ax2) = plt.subplots(1, 2, dpi=dpi, constrained_layout=True)
843
794
  fig.set_size_inches(width * scale * 2, height * scale, forward=True)
844
- fig.set_constrained_layout_pads(
845
- w_pad=0.2, h_pad=0.2, hspace=0, wspace=0)
795
+ fig.set_constrained_layout_pads(w_pad=0.2, h_pad=0.2, hspace=0, wspace=0)
796
+
797
+ # set the size of the ticks labels using font_size
846
798
  ax1.tick_params(axis='both', which='major', labelsize=font_size)
847
799
  ax2.tick_params(axis='both', which='major', labelsize=font_size)
848
- ax1.yaxis.get_offset_text().set_fontsize(font_size)
849
-
850
- # This function is called at regular intervals with changing i
851
- # values for each frame
852
- def _update_frames(i, ax1, ax2, extent, annotation_text,
853
- annotation_defaults, cmap, norm):
854
- # Clear previous frame to optimise render speed and plot imagery
855
- ax1.clear()
856
- ax2.clear()
857
800
 
858
- ax1.imshow(da[i, ...], cmap=cmap, norm=norm,
859
- extent=extent, interpolation="nearest")
860
- if(not label_ax):
861
- ax1.set_axis_off()
862
-
863
- clipped_table = stacked_plot_table.iloc[: int(i + 1)]
864
- data = clipped_table.to_dict(orient="list")
865
- date = clipped_table.index
801
+ # define list of axes to use in anim_fargs and, in turn, in _update_frames
802
+ axes = [ax1,ax2]
803
+
866
804
 
867
- ax2.stackplot(date, data.values(), colors=hex_colour_list)
868
- ax2.tick_params(axis="x", labelrotation=-90)
869
- ax2.margins(x=0, y=0)
870
-
871
- # Add annotation text
872
- ax1.annotate(annotation_text[i], **annotation_defaults)
873
- ax2.annotate(annotation_text[i], **annotation_defaults)
874
-
875
- ax1.yaxis.get_offset_text().set_fontsize(font_size)
876
-
877
-
878
- # anim_fargs contains all the values we send to our
879
- # _update_frames function.
880
- # Note the layer_cmap and layer_norm which were calculated
881
- # earlier being passed through
882
- anim_fargs = (
883
- ax1,
884
- ax2, # axis to plot into
885
- [left, right, bottom, top], # imshow extent
886
- annotation_list,
887
- annotation_defaults,
888
- layer_cmap,
889
- layer_norm,
890
- )
891
-
892
- else: # stacked_plot = False
805
+ else: # i.e., stacked_plot == False
893
806
 
894
807
  # if plotting level 4 with colourbar
895
-
896
808
  if measurement == 'level4' and colour_bar == True:
897
809
 
898
810
  # specific setting to fit level 4 colour bar beside the plot
@@ -902,69 +814,106 @@ def lc_animation(
902
814
  # Define & set up figure, two subplots so colour bar fits :)
903
815
  fig, (ax1, ax2) = plt.subplots(1, 2, dpi=dpi,
904
816
  constrained_layout=True, gridspec_kw={'width_ratios': [3, 1]})
905
- fig.set_size_inches(width * scale * 2,
906
- height * scale, forward=True)
907
- fig.set_constrained_layout_pads(
908
- w_pad=0.2, h_pad=0.2, hspace=0, wspace=0)
909
- ax1.tick_params(axis='both', which='major', labelsize=font_size)
910
- ax2.tick_params(axis='both', which='major', labelsize=font_size)
911
- ax1.yaxis.get_offset_text().set_fontsize(font_size)
912
-
817
+ fig.set_size_inches(width * scale * 2, height * scale, forward=True)
818
+ fig.set_constrained_layout_pads(w_pad=0.2, h_pad=0.2, hspace=0, wspace=0)
819
+
913
820
  # make colour bar
914
821
  # provide left hand canvas to colour bar fuction which is where the image will go
915
822
  # colourbar will plot on right side beside it
916
-
917
- make_colorbar(fig, ax1, measurement, labelsize=font_size, animation=True)
823
+ make_colourbar(fig, ax1, measurement, labelsize=font_size, animation=True)
918
824
 
919
825
  # turn off lines for second plot so it's not ontop of colourbar
920
826
  ax2.set_axis_off()
921
827
 
922
828
  # plotting any other measurement with or with-out colour bar or level 4 without
923
829
  else:
924
-
925
830
  # Define & set up figure
926
831
  fig, ax1 = plt.subplots(1, 1, dpi=dpi)
927
- fig.set_size_inches(width * scale * 1.3, height * scale, forward=True)
928
- ax1.tick_params(axis='both', which='major', labelsize=font_size)
929
- ax1.yaxis.get_offset_text().set_fontsize(font_size)
832
+ fig.set_size_inches(width * scale, height * scale, forward=True)
930
833
 
931
834
  if(not label_ax):
932
835
  fig.subplots_adjust(left=0, bottom=0, right=1,
933
836
  top=1, wspace=None, hspace=None)
934
- # Add colourbar here
837
+ # make colourbar if required
935
838
  if colour_bar:
936
- make_colorbar(fig, ax1, measurement, labelsize=font_size)
839
+ make_colourbar(fig, ax1, measurement, labelsize=font_size)
840
+
841
+ # set the size of the ticks labels using font_size
842
+ ax1.tick_params(axis='both', which='major', labelsize=font_size)
843
+
844
+ # define list of axes to use in anim_fargs and, in turn, in _update_frames
845
+ axes = [ax1]
937
846
 
847
+
938
848
 
939
- # This function is called at regular intervals with changing i
940
- # values for each frame
941
- def _update_frames(i, ax1, extent, annotation_text,
942
- annotation_defaults, cmap, norm):
943
- # Clear previous frame to optimise render speed and plot imagery
944
- ax1.clear()
945
- ax1.imshow(da[i, ...], cmap=cmap, norm=norm,
946
- extent=extent, interpolation="nearest")
947
- if(not label_ax):
948
- ax1.set_axis_off()
849
+ #################################################################
850
+ #### This function is called at the end at regular intervals ####
851
+ #### with changing i values for each frame ####
852
+ #################################################################
853
+ def _update_frames(i, axes, extent, annotation_text,
854
+ annotation_defaults, cmap, norm):
855
+
856
+ ax1 = axes[0] # at least one axis is always present
857
+
858
+ # Clear previous frame to optimise render speed and plot imagery
859
+ ax1.clear()
860
+
861
+ # Add annotation text
862
+ ax1.annotate(annotation_text[i], **annotation_defaults)
863
+
864
+ # Generate image
865
+ ax1.imshow(da[i, ...], cmap=cmap, norm=norm,
866
+ extent=extent, interpolation="nearest")
867
+
868
+ # set size of 1e6 using font_size
869
+ ax1.yaxis.get_offset_text().set_fontsize(font_size)
870
+ ax1.xaxis.get_offset_text().set_fontsize(font_size)
871
+
872
+ # if asked that axes have no labels, remove them
873
+ if(not label_ax):
874
+ ax1.set_axis_off()
875
+
876
+ try: # this will fail and be skipped if a second axes (i.e. stacked line plot) does not exist
877
+ ax2 = axes[1]
878
+ ax2.clear()
949
879
 
880
+ # get the classes ratio up to the current time step i
881
+ clipped_table = stacked_plot_table.iloc[: int(i + 1)]
882
+ data = clipped_table.to_dict(orient="list")
883
+ date = clipped_table.index
884
+
885
+ # add stacked line plot to axes 2
886
+ ax2.stackplot(date, data.values(), colors=hex_colour_list)
887
+ ax2.tick_params(axis="x", labelrotation=-90)
888
+ ax2.margins(x=0, y=0)
889
+
950
890
  # Add annotation text
951
- ax1.annotate(annotation_text[i], **annotation_defaults)
952
- ax1.yaxis.get_offset_text().set_fontsize(font_size)
953
-
954
- # anim_fargs contains all the values we send to our
955
- # _update_frames function.
956
- # Note the layer_cmap and layer_norm which were calculated
957
- # earlier being passed through
958
- anim_fargs = (
959
- ax1,
960
- [left, right, bottom, top], # imshow extent
961
- annotation_list,
962
- annotation_defaults,
963
- layer_cmap,
964
- layer_norm,
891
+ ax2.annotate(annotation_text[i], **annotation_defaults)
892
+
893
+ # set size of 1e6 using font_size
894
+ ax2.yaxis.get_offset_text().set_fontsize(font_size)
895
+ ax2.xaxis.get_offset_text().set_fontsize(font_size)
896
+
897
+ except:
898
+ pass
899
+ #################################################################
900
+ #################################################################
901
+
902
+
903
+
904
+ # anim_fargs contains all the values we send to our
905
+ # _update_frames function.
906
+ anim_fargs = (
907
+ axes,
908
+ [left, right, bottom, top], # imshow extent
909
+ annotation_list,
910
+ annotation_defaults,
911
+ layer_cmap,
912
+ layer_norm,
965
913
  )
966
914
 
967
- # Animate
915
+
916
+ # create animation
968
917
  anim = FuncAnimation(
969
918
  fig=fig,
970
919
  func=_update_frames,
@@ -974,6 +923,9 @@ def lc_animation(
974
923
  repeat=False,
975
924
  )
976
925
 
926
+ # save animation
977
927
  anim.save(file_name, writer="pillow", dpi=dpi)
928
+
978
929
  plt.close()
930
+
979
931
  return Image(filename=file_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dea-tools
3
- Version: 0.3.6.dev34
3
+ Version: 0.3.6.dev38
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
File without changes