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