dea-tools 0.3.6.dev1__tar.gz → 0.3.6.dev31__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.dev1/dea_tools.egg-info → dea_tools-0.3.6.dev31}/PKG-INFO +14 -3
  2. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/datahandling.py +29 -11
  3. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/landcover.py +260 -69
  4. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31/dea_tools.egg-info}/PKG-INFO +14 -3
  5. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools.egg-info/requires.txt +1 -1
  6. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/setup.py +1 -1
  7. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/.gitignore +0 -0
  8. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/LICENSE +0 -0
  9. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/MANIFEST.in +0 -0
  10. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/README.rst +0 -0
  11. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/__init__.py +0 -0
  12. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/__main__.py +0 -0
  13. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/app/__init__.py +0 -0
  14. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/app/animations.py +0 -0
  15. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/app/changefilmstrips.py +0 -0
  16. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/app/crophealth.py +0 -0
  17. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/app/deacoastlines.py +0 -0
  18. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/app/geomedian.py +0 -0
  19. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/app/imageexport.py +0 -0
  20. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/app/miningrehab.py +0 -0
  21. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/app/widgetconstructors.py +0 -0
  22. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/bandindices.py +0 -0
  23. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/bom.py +0 -0
  24. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/classification.py +0 -0
  25. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/climate.py +0 -0
  26. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/coastal.py +0 -0
  27. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/dask.py +0 -0
  28. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/maps.py +0 -0
  29. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/plotting.py +0 -0
  30. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/pyfes_model.py +0 -0
  31. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/spatial.py +0 -0
  32. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/temporal.py +0 -0
  33. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/validation.py +0 -0
  34. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/waterbodies.py +0 -0
  35. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools/wetlands.py +0 -0
  36. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools.egg-info/SOURCES.txt +0 -0
  37. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools.egg-info/dependency_links.txt +0 -0
  38. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/dea_tools.egg-info/top_level.txt +0 -0
  39. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/index.rst +0 -0
  40. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/mock_imports.txt +0 -0
  41. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/pyproject.toml +0 -0
  42. {dea_tools-0.3.6.dev1 → dea_tools-0.3.6.dev31}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: dea-tools
3
- Version: 0.3.6.dev1
3
+ Version: 0.3.6.dev31
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
@@ -44,7 +44,7 @@ Requires-Dist: pyproj
44
44
  Requires-Dist: pystac-client
45
45
  Requires-Dist: planetary-computer
46
46
  Requires-Dist: python-dateutil
47
- Requires-Dist: pyTMD<=2.1.6
47
+ Requires-Dist: pyTMD!=2.1.7
48
48
  Requires-Dist: pytz
49
49
  Requires-Dist: rasterio
50
50
  Requires-Dist: rasterstats
@@ -65,6 +65,17 @@ Provides-Extra: dask-gateway
65
65
  Requires-Dist: dask_gateway; extra == "dask-gateway"
66
66
  Provides-Extra: otps
67
67
  Requires-Dist: otps; extra == "otps"
68
+ Dynamic: author
69
+ Dynamic: author-email
70
+ Dynamic: classifier
71
+ Dynamic: description
72
+ Dynamic: description-content-type
73
+ Dynamic: home-page
74
+ Dynamic: license
75
+ Dynamic: provides-extra
76
+ Dynamic: requires-dist
77
+ Dynamic: requires-python
78
+ Dynamic: summary
68
79
 
69
80
 
70
81
  dea-tools package
@@ -17,7 +17,7 @@ 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: Feb 2024
20
+ Last modified: February 2025
21
21
  """
22
22
 
23
23
  import datetime
@@ -112,7 +112,7 @@ def load_ard(
112
112
  ):
113
113
  """
114
114
  Load multiple Geoscience Australia Landsat or Sentinel 2
115
- Collection 3 products (e.g. Landsat 5, 7, 8, 9; Sentinel 2A and 2B),
115
+ Collection 3 Analysis Ready Data products (e.g. Landsat 5, 7, 8, 9; Sentinel 2A, 2B, 2C),
116
116
  optionally apply pixel quality/cloud masking and contiguity masks,
117
117
  and drop time steps that contain greater than a minimum proportion
118
118
  of good quality (e.g. non-cloudy or shadowed) pixels.
@@ -126,13 +126,14 @@ def load_ard(
126
126
  And Sentinel-2 products:
127
127
  * ga_s2am_ard_3
128
128
  * ga_s2bm_ard_3
129
+ * ga_s2cm_ard_3
129
130
 
130
131
  Cloud masking can be performed using the Fmask (Function of Mask)
131
132
  cloud mask for Landsat and Sentinel-2, and the s2cloudless
132
133
  (Sentinel Hub cloud detector for Sentinel-2 imagery) cloud mask for
133
134
  Sentinel-2.
134
135
 
135
- Last modified: June 2023
136
+ Last modified: February 2025
136
137
 
137
138
  Parameters
138
139
  ----------
@@ -142,7 +143,8 @@ def load_ard(
142
143
  products : list
143
144
  A list of product names to load. Valid options are
144
145
  ['ga_ls5t_ard_3', 'ga_ls7e_ard_3', 'ga_ls8c_ard_3', 'ga_ls9c_ard_3']
145
- for Landsat, ['ga_s2am_ard_3', 'ga_s2bm_ard_3'] for Sentinel 2.
146
+ for Landsat, ['ga_s2am_ard_3', 'ga_s2bm_ard_3', 'ga_s2cm_ard_3']
147
+ for Sentinel 2.
146
148
  cloud_mask : string, optional
147
149
  The cloud mask used by the function. This is used for both
148
150
  masking out poor quality pixels (e.g. clouds) if
@@ -256,21 +258,27 @@ def load_ard(
256
258
  # Setup #
257
259
  #########
258
260
 
261
+ # Convert products to a list if it is passed as a string
262
+ products = [products] if isinstance(products, str) else products
263
+
264
+ # Valid Landsat products
265
+ valid_ls = ["ga_ls5t_ard_3", "ga_ls7e_ard_3", "ga_ls8c_ard_3", "ga_ls9c_ard_3"]
266
+ valid_s2 = ["ga_s2am_ard_3", "ga_s2bm_ard_3", "ga_s2cm_ard_3"]
267
+
259
268
  # Verify that products were provided
260
269
  if not products:
261
270
  raise ValueError(
262
- "Please provide a list of product names to load data from. "
263
- "Valid options are: ['ga_ls5t_ard_3', 'ga_ls7e_ard_3', "
264
- "'ga_ls8c_ard_3', 'ga_ls9c_ard_3'] for Landsat, and "
265
- "['ga_s2am_ard_3', 'ga_s2bm_ard_3'] for Sentinel 2."
271
+ f"Please provide a list of Landsat or Sentinel-2 Analysis Ready Data "
272
+ f"product names to load data from. Valid options are: "
273
+ f"{valid_ls + valid_s2}."
266
274
  )
267
275
 
268
276
  # Determine whether products are all Landsat, all S2, or mixed
269
- elif all(["ls" in product for product in products]):
277
+ elif all([product in valid_ls for product in products]):
270
278
  product_type = "ls"
271
- elif all(["s2" in product for product in products]):
279
+ elif all([product in valid_s2 for product in products]):
272
280
  product_type = "s2"
273
- else:
281
+ elif all([product in valid_s2 + valid_ls for product in products]):
274
282
  product_type = "mixed"
275
283
 
276
284
  warnings.warn(
@@ -280,6 +288,16 @@ def load_ard(
280
288
  "(e.g. Landsat and Sentinel-2's 'nbart_swir_2'); use with "
281
289
  "caution."
282
290
  )
291
+ else:
292
+ # If an invalid product is passed, raise error
293
+ invalid_products = [
294
+ product for product in products if product not in valid_s2 + valid_ls
295
+ ]
296
+ raise ValueError(
297
+ f"The `load_ard` function only supports Landsat and "
298
+ f"Sentinel-2 Analysis Ready Data products; {invalid_products} is not supported. "
299
+ f"Valid options are: {valid_ls + valid_s2}."
300
+ )
283
301
 
284
302
  # Set contiguity band depending on `mask_contiguity`;
285
303
  # "oa_nbart_contiguity" if True, False or "nbart",
@@ -34,13 +34,13 @@ from matplotlib.animation import FuncAnimation
34
34
 
35
35
  # Define colour schemes for each land cover measurement
36
36
  lc_colours = {
37
- 'level3': {0: (255, 255, 255, 255, "No Data"),
38
- 111: (172, 188, 45, 255, "Cultivated Terrestrial\n Vegetation"),
37
+ 'level3': {111: (172, 188, 45, 255, "Cultivated Terrestrial\n Vegetation"),
39
38
  112: (14, 121, 18, 255, "Natural Terrestrial\n Vegetation"),
40
39
  124: (30, 191, 121, 255, "Natural Aquatic\n Vegetation"),
41
40
  215: (218, 92, 105, 255, "Artificial Surface"),
42
41
  216: (243, 171, 105, 255, "Natural Bare\n Surface"),
43
- 220: (77, 159, 220, 255, "Water")},
42
+ 220: (77, 159, 220, 255, "Water"),
43
+ 255: (255, 255, 255, 255, "No Data")},
44
44
 
45
45
  'level3_change_colour_scheme': {0: (255, 255, 255, 255, "No Change"),
46
46
  111112: (14, 121, 18, 255, "CTV -> NTV"),
@@ -61,49 +61,51 @@ lc_colours = {
61
61
  216215: (218, 92, 105, 255, "BS -> AS"),
62
62
  216220: (77, 159, 220, 255, "BS -> Water"),
63
63
  220112: (14, 121, 18, 255, "Water -> NTV"),
64
- 220216: (243, 171, 105, 255, "Water -> BS")},
64
+ 220216: (243, 171, 105, 255, "Water -> BS"),
65
+ },
65
66
 
66
- 'level3_change_colour_bar': {0: (255, 255, 255, 255, "No change"),
67
- 111: (172, 188, 45, 255, "Changed to Cultivated\n Terrestrial Vegetation"),
67
+ 'level3_change_colour_bar': {111: (172, 188, 45, 255, "Changed to Cultivated\n Terrestrial Vegetation"),
68
68
  112: (14, 121, 18, 255, "Changed to Natural\n Terrestrial Vegetation"),
69
69
  124: (30, 191, 121, 255, "Changed to Natural\n Aquatic Vegetation"),
70
70
  215: (218, 92, 105, 255, "Changed to Artificial\n Surface"),
71
71
  216: (243, 171, 105, 255, "Changed to Natural\n Bare Surface"),
72
- 220: (77, 159, 220, 255, "Changed to Water")},
72
+ 220: (77, 159, 220, 255, "Changed to Water"),
73
+ 0: (255, 255, 255, 255, "No Change")},
73
74
 
74
- 'lifeform_veg_cat_l4a': {0: (255, 255, 255, 255, "No Data /\n Not vegetated"),
75
- 1: (14, 121, 18, 255, "Woody Vegetation"),
76
- 2: (172, 188, 45, 255, "Herbaceous\n Vegetation")},
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")},
77
78
 
78
- 'canopyco_veg_cat_l4d': {0: (255, 255, 255, 255, "No Data /\n Not vegetated"),
79
- 10: (14, 121, 18, 255, "> 65 % cover"),
79
+
80
+ 'canopyco_veg_cat_l4d': {10: (14, 121, 18, 255, "> 65 % cover"),
80
81
  12: (45, 141, 47, 255, "40 to 65 % cover"),
81
82
  13: (80, 160, 82, 255, "15 to 40 % cover"),
82
83
  15: (117, 180, 118, 255, "4 to 15 % cover"),
83
- 16: (154, 199, 156, 255, "1 to 4 % cover")},
84
+ 16: (154, 199, 156, 255, "1 to 4 % cover"),
85
+ 255: (255, 255, 255, 255, "No Data /\n Not vegetated")},
84
86
 
85
- 'waterstt_wat_cat_l4a': {0: (255, 255, 255, 255, "No Data /\n Not water"),
86
- 1: (77, 159, 220, 255, "Water")},
87
+ 'waterstt_wat_cat_l4a': {1: (77, 159, 220, 255, "Water"),
88
+ 255: (255, 255, 255, 255, "No Data /\n Not water")},
87
89
 
88
- 'watersea_veg_cat_l4a_au': {0: (255, 255, 255, 255, "No data /\n Not aquatic vegetation"),
89
- 1: (25, 173, 109, 255, "> 3 months"),
90
- 2: (176, 218, 201, 255, "< 3 months")},
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")},
91
93
 
92
- 'inttidal_wat_cat_l4a': {0: (255, 255, 255, 255, "No data /\n Not intertidal"),
93
- 3: (77, 159, 220, 255, "Intertidal")},
94
+ 'inttidal_wat_cat_l4a': {3: (77, 159, 220, 255, "Intertidal"),
95
+ 255: (255, 255, 255, 255, "No data /\n Not intertidal")},
94
96
 
95
- 'waterper_wat_cat_l4d_au': {0: (255, 255, 255, 255, "No data /\n Not water"),
96
- 1: (27, 85, 186, 255, "> 9 months"),
97
+ 'waterper_wat_cat_l4d_au': {1: (27, 85, 186, 255, "> 9 months"),
97
98
  7: (52, 121, 201, 255, "7 to 9 months"),
98
99
  8: (79, 157, 217, 255, "4 to 6 months"),
99
- 9: (113, 202, 253, 255, "1 to 3 months")},
100
+ 9: (113, 202, 253, 255, "1 to 3 months"),
101
+ 255: (255, 255, 255, 255, "No data /\n Not water")},
100
102
 
101
- 'baregrad_phy_cat_l4d_au': {0: (255, 255, 255, 255, "No data /\n Not bare"),
102
- 10: (255, 230, 140, 255, "Sparsely vegetated\n (< 20% bare)"),
103
+ 'baregrad_phy_cat_l4d_au': {10: (255, 230, 140, 255, "Sparsely vegetated\n (< 20% bare)"),
103
104
  12: (250, 210, 110, 255, "Very sparsely\n vegetated (20 to 60% bare)"),
104
- 15: (243, 171, 105, 255, "Bare areas,\n unvegetated (> 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")},
105
107
 
106
- 'level4': {0: (255, 255, 255, 255, "No Data"),
108
+ 'level4': {
107
109
  1: (151, 187, 26, 255, 'Cultivated Terrestrial\n Vegetated:'),
108
110
  2: (151, 187, 26, 255, 'Cultivated Terrestrial\n Vegetated: Woody'),
109
111
  3: (209, 224, 51, 255, 'Cultivated Terrestrial\n Vegetated: Herbaceous'),
@@ -112,11 +114,11 @@ lc_colours = {
112
114
  6: (213, 193, 79, 255, 'Cultivated Terrestrial\n Vegetated: Open\n (15 to 40 %)'),
113
115
  7: (228, 210, 108, 255, 'Cultivated Terrestrial\n Vegetated: Sparse\n (4 to 15 %)'),
114
116
  8: (242, 227, 138, 255, 'Cultivated Terrestrial\n Vegetated: Scattered\n (1 to 4 %)'),
115
- # 9: (197, 168, 71, 255, 'Cultivated Terrestrial\n Vegetated: Woody Closed\n (> 65 %)'),
116
- # 10: (205, 181, 75, 255, 'Cultivated Terrestrial\n Vegetated: Woody Open\n (40 to 65 %)'),
117
- # 11: (213, 193, 79, 255, 'Cultivated Terrestrial\n Vegetated: Woody Open\n (15 to 40 %)'),
118
- # 12: (228, 210, 108, 255, 'Cultivated Terrestrial\n Vegetated: Woody Sparse\n (4 to 15 %)'),
119
- # 13: (242, 227, 138, 255, 'Cultivated Terrestrial\n Vegetated: Woody Scattered\n (1 to 4 %)'),
117
+ 9: (197, 168, 71, 255, 'Cultivated Terrestrial\n Vegetated: Woody Closed\n (> 65 %)'),
118
+ 10: (205, 181, 75, 255, 'Cultivated Terrestrial\n Vegetated: Woody Open\n (40 to 65 %)'),
119
+ 11: (213, 193, 79, 255, 'Cultivated Terrestrial\n Vegetated: Woody Open\n (15 to 40 %)'),
120
+ 12: (228, 210, 108, 255, 'Cultivated Terrestrial\n Vegetated: Woody Sparse\n (4 to 15 %)'),
121
+ 13: (242, 227, 138, 255, 'Cultivated Terrestrial\n Vegetated: Woody Scattered\n (1 to 4 %)'),
120
122
  14: (228, 224, 52, 255, 'Cultivated Terrestrial\n Vegetated: Herbaceous Closed\n (> 65 %)'),
121
123
  15: (235, 232, 84, 255, 'Cultivated Terrestrial\n Vegetated: Herbaceous Open\n (40 to 65 %)'),
122
124
  16: (242, 240, 127, 255, 'Cultivated Terrestrial\n Vegetated: Herbaceous Open\n (15 to 40 %)'),
@@ -140,24 +142,24 @@ lc_colours = {
140
142
  34: (153, 196, 80, 255, 'Natural Terrestrial Vegetated: Herbaceous Open (15 to 40 %)'),
141
143
  35: (170, 212, 113, 255, 'Natural Terrestrial Vegetated: Herbaceous Sparse (4 to 15 %)'),
142
144
  36: (186, 226, 146, 255, 'Natural Terrestrial Vegetated: Herbaceous Scattered (1 to 4 %)'),
143
- # 37: (86, 236, 231, 255, 'Cultivated Aquatic Vegetated:'),
144
- # 38: (61, 170, 140, 255, 'Cultivated Aquatic Vegetated: Woody'),
145
- # 39: (82, 231, 172, 255, 'Cultivated Aquatic Vegetated: Herbaceous'),
146
- # 40: (43, 210, 203, 255, 'Cultivated Aquatic Vegetated: Closed (> 65 %)'),
147
- # 41: (73, 222, 216, 255, 'Cultivated Aquatic Vegetated: Open (40 to 65 %)'),
148
- # 42: (110, 233, 228, 255, 'Cultivated Aquatic Vegetated: Open (15 to 40 %)'),
149
- # 43: (149, 244, 240, 255, 'Cultivated Aquatic Vegetated: Sparse (4 to 15 %)'),
150
- # 44: (187, 255, 252, 255, 'Cultivated Aquatic Vegetated: Scattered (1 to 4 %)'),
145
+ 37: (86, 236, 231, 255, 'Cultivated Aquatic Vegetated:'),
146
+ 38: (61, 170, 140, 255, 'Cultivated Aquatic Vegetated: Woody'),
147
+ 39: (82, 231, 172, 255, 'Cultivated Aquatic Vegetated: Herbaceous'),
148
+ 40: (43, 210, 203, 255, 'Cultivated Aquatic Vegetated: Closed (> 65 %)'),
149
+ 41: (73, 222, 216, 255, 'Cultivated Aquatic Vegetated: Open (40 to 65 %)'),
150
+ 42: (110, 233, 228, 255, 'Cultivated Aquatic Vegetated: Open (15 to 40 %)'),
151
+ 43: (149, 244, 240, 255, 'Cultivated Aquatic Vegetated: Sparse (4 to 15 %)'),
152
+ 44: (187, 255, 252, 255, 'Cultivated Aquatic Vegetated: Scattered (1 to 4 %)'),
151
153
  # 45: (43, 210, 203, 255, 'Cultivated Aquatic Vegetated: Woody Closed (> 65 %)'),
152
154
  # 46: (73, 222, 216, 255, 'Cultivated Aquatic Vegetated: Woody Open (40 to 65 %)'),
153
155
  # 47: (110, 233, 228, 255, 'Cultivated Aquatic Vegetated: Woody Open (15 to 40 %)'),
154
156
  # 48: (149, 244, 240, 255, 'Cultivated Aquatic Vegetated: Woody Sparse (4 to 15 %)'),
155
157
  # 49: (187, 255, 252, 255, 'Cultivated Aquatic Vegetated: Woody Scattered (1 to 4 %)'),
156
- # 50: (82, 231, 196, 255, 'Cultivated Aquatic Vegetated: Herbaceous Closed (> 65 %)'),
157
- # 51: (113, 237, 208, 255, 'Cultivated Aquatic Vegetated: Herbaceous Open (40 to 65 %)'),
158
- # 52: (144, 243, 220, 255, 'Cultivated Aquatic Vegetated: Herbaceous Open (15 to 40 %)'),
159
- # 53: (175, 249, 232, 255, 'Cultivated Aquatic Vegetated: Herbaceous Sparse (4 to 15 %)'),
160
- # 54: (207, 255, 244, 255, 'Cultivated Aquatic Vegetated: Herbaceous Scattered (1 to 4 %)'),
158
+ 50: (82, 231, 196, 255, 'Cultivated Aquatic Vegetated: Herbaceous Closed (> 65 %)'),
159
+ 51: (113, 237, 208, 255, 'Cultivated Aquatic Vegetated: Herbaceous Open (40 to 65 %)'),
160
+ 52: (144, 243, 220, 255, 'Cultivated Aquatic Vegetated: Herbaceous Open (15 to 40 %)'),
161
+ 53: (175, 249, 232, 255, 'Cultivated Aquatic Vegetated: Herbaceous Sparse (4 to 15 %)'),
162
+ 54: (207, 255, 244, 255, 'Cultivated Aquatic Vegetated: Herbaceous Scattered (1 to 4 %)'),
161
163
  55: (30, 191, 121, 255, 'Natural Aquatic Vegetated:'),
162
164
  56: (18, 142, 148, 255, 'Natural Aquatic Vegetated: Woody'),
163
165
  57: (112, 234, 134, 255, 'Natural Aquatic Vegetated: Herbaceous'),
@@ -209,9 +211,14 @@ lc_colours = {
209
211
  103: (79, 157, 217, 255, 'Water: (Water) Non-perennial (4 to 6 months)'),
210
212
  104: (133, 202, 253, 255, 'Water: (Water) Non-perennial (1 to 3 months)'),
211
213
  # 105: (250, 250, 250, 255, 'Water: (Snow)')
214
+ 255: (255, 255, 255, 255, "No Data")
212
215
  },
213
216
 
214
- 'level4_colourbar_labels': {0: (255, 255, 255, 255, "No Data"),
217
+ 'level4_colourbar_labels': {9: (197, 168, 71, 255, 'Cultivated Terrestrial Vegetated: Woody Closed (> 65 %)'),
218
+ 10: (205, 181, 75, 255, 'Cultivated Terrestrial Vegetated: Woody Open (40 to 65 %)'),
219
+ 11: (213, 193, 79, 255, 'Cultivated Terrestrial Vegetated: Woody Open (15 to 40 %)'),
220
+ 12: (228, 210, 108, 255, 'Cultivated Terrestrial Vegetated: Woody Sparse (4 to 15 %)'),
221
+ 13: (242, 227, 138, 255, 'Cultivated Terrestrial Vegetated: Woody Scattered (1 to 4 %)'),
215
222
  14: (228, 224, 52, 255, 'Cultivated Terrestrial Vegetated: Herbaceous Closed (> 65 %)'),
216
223
  15: (235, 232, 84, 255, 'Cultivated Terrestrial Vegetated: Herbaceous Open (40 to 65 %)'),
217
224
  16: (242, 240, 127, 255, 'Cultivated Terrestrial Vegetated: Herbaceous Open (15 to 40 %)'),
@@ -245,10 +252,41 @@ lc_colours = {
245
252
  101: (27, 85, 186, 255, 'Water: (Water) Perennial (> 9 months)'),
246
253
  102: (52, 121, 201, 255, 'Water: (Water) Non-perennial (7 to 9 months)'),
247
254
  103: (79, 157, 217, 255, 'Water: (Water) Non-perennial (4 to 6 months)'),
248
- 104: (133, 202, 253, 255, 'Water: (Water) Non-perennial (1 to 3 months)')},
255
+ 104: (133, 202, 253, 255, 'Water: (Water) Non-perennial (1 to 3 months)'),
256
+ 255: (255, 255, 255, 255, "No Data")
257
+ },
258
+ }
259
+
260
+ # dictionary needed to generate colour schemes of descriptors from the level 4 colour scheme. The structure is as follow:
261
+ # long_descriptor_name[string]: {keyword_for_finding_classes_in_level4_colourscheme[string] : (RGB_colourscheme[4 integers], label_of_descriptor[string])}
262
+ 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')},
270
+ 'watersea_veg_cat_l4a_au': {'(semi-) permenant': (25, 173, 109, 255, '> 3 months'),
271
+ '(temporary or seasonal)': (176, 218, 201, 255, '< 3 months')},
272
+ '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)')},
281
+
249
282
  }
250
283
 
251
284
 
285
+
286
+
287
+
288
+
289
+
252
290
  def get_layer_name(measurement, da):
253
291
  aliases = {
254
292
  'lifeform': 'lifeform_veg_cat_l4a',
@@ -287,6 +325,7 @@ def make_colorbar(fig, ax, measurement, horizontal=False, animation=False):
287
325
  Land cover measurement to use for colour map and labels.
288
326
 
289
327
  """
328
+
290
329
  # Create new axis object for colorbar
291
330
  # parameters for add_axes are [left, bottom, width, height], in
292
331
  # fractions of total plot
@@ -298,12 +337,12 @@ def make_colorbar(fig, ax, measurement, horizontal=False, animation=False):
298
337
  orient = 'vertical'
299
338
 
300
339
  # get level 4 colour bar colour map ect
301
- cb_cmap, cb_norm, cb_labels, cb_ticks = lc_colourmap('level4_colourbar_labels',
340
+ cb_cmap, cb_norm, cb_labels, cb_ticks = lc_colourmap_colourbar('level4_colourbar_labels',
302
341
  colour_bar=True)
303
342
  elif measurement == 'level4' and animation == False:
304
343
 
305
344
  # get level 4 colour bar colour map ect
306
- cb_cmap, cb_norm, cb_labels, cb_ticks = lc_colourmap('level4_colourbar_labels',
345
+ cb_cmap, cb_norm, cb_labels, cb_ticks = lc_colourmap_colourbar('level4_colourbar_labels',
307
346
  colour_bar=True)
308
347
  #move plot over to make room for colourbar
309
348
  fig.subplots_adjust(right=0.825)
@@ -327,7 +366,7 @@ def make_colorbar(fig, ax, measurement, horizontal=False, animation=False):
327
366
  orient = 'vertical'
328
367
 
329
368
  # get measurement colour bar colour map ect
330
- cb_cmap, cb_norm, cb_labels, cb_ticks = lc_colourmap(measurement,
369
+ cb_cmap, cb_norm, cb_labels, cb_ticks = lc_colourmap_colourbar(measurement,
331
370
  colour_bar=True)
332
371
 
333
372
  img = ax.imshow([cb_ticks], cmap=cb_cmap, norm=cb_norm)
@@ -339,6 +378,59 @@ def make_colorbar(fig, ax, measurement, horizontal=False, animation=False):
339
378
 
340
379
 
341
380
 
381
+
382
+ def descriptors_colours(lc_colours, lc_colours_mapping, descriptor):
383
+ """
384
+ Generates a sorted dictionary of colours based on a given descriptor.
385
+
386
+ 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.
389
+
390
+ Parameters
391
+ ----------
392
+ lc_colours : dict
393
+ Dictionary containing colour schemes for all Land Cover classes.
394
+
395
+ lc_colours_mapping : dict
396
+ Dictionary mapping descriptors (e.g., lifeform) to their corresponding colours and labels.
397
+
398
+ descriptor : str
399
+ The descriptor to be used for mapping colours.
400
+
401
+ Returns
402
+ -------
403
+ sorted_colours_dict : dict
404
+ Sorted dictionary with class values as keys and colour tuples as values.
405
+ """
406
+
407
+ # get the level 4 colour scheme from the lc_colours dictionary
408
+ level4_colours = lc_colours['level4']
409
+
410
+ # get the descriptor dictionary from the lc_colours_mapping
411
+ descriptor_dict = lc_colours_mapping[descriptor]
412
+
413
+ # 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)
415
+ colours_dict = level4_colours.copy()
416
+ for key in colours_dict:
417
+ colours_dict[key] = (255, 255, 255, 255, "No Data")
418
+
419
+ # update the colours dictionary with the descriptor-specific colours
420
+ for class_keyword, colour_n_label in descriptor_dict.items():
421
+
422
+ for class_value, lvl4_scheme in level4_colours.items():
423
+ label_lvl4 = lvl4_scheme[4]
424
+
425
+ if class_keyword in label_lvl4:
426
+ colours_dict[class_value] = colour_n_label
427
+
428
+ # sort the colours dictionary by keys
429
+ sorted_colours_dict = {key: colours_dict[key] for key in sorted(colours_dict.keys())}
430
+
431
+ return sorted_colours_dict
432
+
433
+
342
434
  def lc_colourmap(colour_scheme, colour_bar=False):
343
435
  """
344
436
  Returns colour map and normalisation for the provided DEA Land Cover
@@ -371,6 +463,85 @@ def lc_colourmap(colour_scheme, colour_bar=False):
371
463
  in the chosen DEA Land Cover measurement.
372
464
  """
373
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
+
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])
497
+
498
+ bounds.append(bounds[-1]+1)
499
+
500
+ # shift all back by 0.5 to make sure level4 values are within bounds and not mathcing exactly one bound
501
+ 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
+
510
+
511
+
512
+ def lc_colourmap_colourbar(colour_scheme, colour_bar=False):
513
+ """
514
+ Returns colour map and normalisation for the provided DEA Land Cover
515
+ measurement, for use in plotting with Matplotlib library
516
+
517
+ Parameters
518
+ ----------
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
+
529
+ Returns
530
+ ---------
531
+ cmap : matplotlib colormap
532
+ Matplotlib colormap containing the colour scheme for the
533
+ specified DEA Land Cover measurement.
534
+ norm : matplotlib colormap index
535
+ Matplotlib colormap index based on the discrete intervals of the
536
+ 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
541
+ in the chosen DEA Land Cover measurement.
542
+ """
543
+
544
+
374
545
  colour_scheme = colour_scheme.lower()
375
546
  # Ensure a valid colour scheme was requested
376
547
  # try:
@@ -380,9 +551,9 @@ def lc_colourmap(colour_scheme, colour_bar=False):
380
551
  # ('The dataset provided does not have a valid '
381
552
  # 'name. Please specify which DEA Landcover measurement is being plotted '
382
553
  # 'by providing the name using the "measurement" variable. For example (measurement = "full_classification")')
383
-
554
+
384
555
  # Get colour definitions
385
- lc_colour_scheme = lc_colours[colour_scheme]
556
+ lc_colour_scheme = lc_colours[colour_scheme]
386
557
 
387
558
  # Create colour map
388
559
  colour_arr = []
@@ -397,6 +568,7 @@ def lc_colourmap(colour_scheme, colour_bar=False):
397
568
  # Set colour labels to shortened level 4 list
398
569
  lc_colour_scheme = lc_colours['level4_colourbar_labels']
399
570
  cb_ticks = list(lc_colour_scheme)
571
+ #print(f'CB_TICKS {cb_ticks}')
400
572
  cb_labels = []
401
573
  for x in cb_ticks:
402
574
  cb_labels.append(lc_colour_scheme[x][4])
@@ -410,6 +582,7 @@ def lc_colourmap(colour_scheme, colour_bar=False):
410
582
  return (cmap, norm, cb_labels, cb_ticks)
411
583
 
412
584
 
585
+
413
586
  def plot_land_cover(data, year=None, measurement=None, out_width=15, cols=4,):
414
587
  """
415
588
  Plot a single land cover measurement with appropriate colour scheme.
@@ -437,30 +610,33 @@ def plot_land_cover(data, year=None, measurement=None, out_width=15, cols=4,):
437
610
  f'DataArray name {measurement}. Please specify which '
438
611
  'DEA Landcover measurement is being plotted by providing'
439
612
  'the name using the "measurement" variable For example'
440
- '(measurement = "full_classification")')
613
+ '(measurement = "level4")')
441
614
 
442
615
  height, width = data.geobox.shape
443
616
  scale = out_width / width
444
617
 
445
618
  if year:
446
- #plotting protocall if 'year' variable is passed
447
- year_string = f"{year}-01-01"
448
- data = data.sel(time=year_string, method="nearest")
619
+ #plotting protocol if 'year' variable is passed
620
+ if int(year) not in pd.to_datetime(data.time.values).year: # check if year selecte is in the datacube
621
+ raise ValueError(f'Year {year} is not in the data array.')
622
+
623
+ year_string = f"{year}-07-01" # LC collection 3 dates are in July
624
+ data = data.sel(time=year_string, method='nearest')
449
625
 
450
626
  fig, ax = plt.subplots()
451
627
  fig.set_size_inches(width * scale, height * scale)
452
628
  make_colorbar(fig, ax, measurement)
453
- im = ax.imshow(data, cmap=cmap, norm=norm, interpolation="nearest")
629
+ im = ax.imshow(data.values, cmap=cmap, norm=norm, interpolation="nearest")
454
630
 
455
631
 
456
632
  elif len(data.time) == 1:
457
- #plotting protocall if only one timestep is passed and not a year variable
633
+ #plotting protocol if only one timestep is passed and not a year variable
458
634
  fig, ax = plt.subplots()
459
635
  fig.set_size_inches(width * scale, height * scale)
460
636
  make_colorbar(fig, ax, measurement)
461
637
  im = ax.imshow(data.isel(time=0), cmap=cmap, norm=norm, interpolation="nearest")
462
638
  else:
463
- #plotting protocall if multible time steps are passed to plot
639
+ #plotting protocol if multible time steps are passed to plot
464
640
  if cols > len(data.time):
465
641
  cols = len(data.time)
466
642
  rows = int((len(data.time) + cols-1)/cols)
@@ -489,7 +665,8 @@ def lc_animation(
489
665
  width_pixels=10,
490
666
  dpi=150,
491
667
  font_size=15,
492
- label_ax=True):
668
+ label_ax=True,
669
+ ):
493
670
  """
494
671
  Creates an animation of DEA Landcover though time beside
495
672
  corresponding stacked plots of the landcover classes. Saves the
@@ -531,19 +708,21 @@ def lc_animation(
531
708
  label_ax : boolean, optional
532
709
  Determines if animation plot should have tick marks and numbers
533
710
  on axes. Also removes white space around plot. default: True
711
+
534
712
 
535
713
  Returns
536
714
  -------
537
715
  A GIF (.gif) animation file.
538
716
  """
539
717
 
540
- def calc_class_ratio(da):
718
+ def calc_class_ratio(da, measurement):
541
719
  """
542
720
  Creates a table listing year by year what percentage of the
543
721
  total area is taken up by each class.
544
722
  Parameters
545
723
  ----------
546
724
  da : xarray.DataArray with time dimension
725
+ measurement: string with name of descriptor/measurement
547
726
  Returns
548
727
  -------
549
728
  Pandas Dataframe : containing class percentages per year
@@ -551,13 +730,24 @@ def lc_animation(
551
730
 
552
731
  # list all class codes in dataset
553
732
  list_classes = (np.unique(da, return_counts=False)).tolist()
554
-
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
+
555
745
  # create empty dataframe & dictionary
556
746
  ratio_table = pd.DataFrame(data=None, columns=list_classes)
557
747
  date_line = {}
558
748
 
559
749
  # count all pixels, should be consistent
560
- total_pix = int(np.sum(da.isel(time=1)))
750
+ total_pix = da.isel(time=1).size
561
751
 
562
752
  # iterate through each year in dataset
563
753
  for i in range(0, len(da.time)):
@@ -572,7 +762,6 @@ def lc_animation(
572
762
 
573
763
  # add each year's counts to dataframe
574
764
  ratio_table.loc[date] = date_line
575
-
576
765
  return ratio_table
577
766
 
578
767
  def rgb_to_hex(r, g, b):
@@ -596,7 +785,7 @@ def lc_animation(
596
785
  f'DataArray name {measurement}. Please specify which '
597
786
  'DEA Landcover measurement is being plotted by providing '
598
787
  'the name using the "measurement" variable For example '
599
- '(measurement = "full_classification")')
788
+ '(measurement = "level4")')
600
789
 
601
790
  # Prepare variables needed
602
791
  # Get info on dataset dimensions
@@ -626,14 +815,16 @@ def lc_animation(
626
815
 
627
816
  if stacked_plot == True:
628
817
 
629
-
630
-
631
818
  # Create table for stacked plot
632
- stacked_plot_table = calc_class_ratio(da)
819
+ stacked_plot_table = calc_class_ratio(da, measurement)
633
820
 
634
821
  # Build colour list of hex vals for stacked plot
635
822
  hex_colour_list = []
636
- colour_def = lc_colours[measurement]
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]
637
828
 
638
829
  # Custom error message to help if user puts incorrect measurement name
639
830
  for val in list(stacked_plot_table):
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: dea-tools
3
- Version: 0.3.6.dev1
3
+ Version: 0.3.6.dev31
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
@@ -44,7 +44,7 @@ Requires-Dist: pyproj
44
44
  Requires-Dist: pystac-client
45
45
  Requires-Dist: planetary-computer
46
46
  Requires-Dist: python-dateutil
47
- Requires-Dist: pyTMD<=2.1.6
47
+ Requires-Dist: pyTMD!=2.1.7
48
48
  Requires-Dist: pytz
49
49
  Requires-Dist: rasterio
50
50
  Requires-Dist: rasterstats
@@ -65,6 +65,17 @@ Provides-Extra: dask-gateway
65
65
  Requires-Dist: dask_gateway; extra == "dask-gateway"
66
66
  Provides-Extra: otps
67
67
  Requires-Dist: otps; extra == "otps"
68
+ Dynamic: author
69
+ Dynamic: author-email
70
+ Dynamic: classifier
71
+ Dynamic: description
72
+ Dynamic: description-content-type
73
+ Dynamic: home-page
74
+ Dynamic: license
75
+ Dynamic: provides-extra
76
+ Dynamic: requires-dist
77
+ Dynamic: requires-python
78
+ Dynamic: summary
68
79
 
69
80
 
70
81
  dea-tools package
@@ -26,7 +26,7 @@ pyproj
26
26
  pystac-client
27
27
  planetary-computer
28
28
  python-dateutil
29
- pyTMD<=2.1.6
29
+ pyTMD!=2.1.7
30
30
  pytz
31
31
  rasterio
32
32
  rasterstats
@@ -56,7 +56,7 @@ REQUIRED = [
56
56
  'pystac-client',
57
57
  'planetary-computer',
58
58
  'python-dateutil',
59
- 'pyTMD<=2.1.6',
59
+ 'pyTMD!=2.1.7',
60
60
  'pytz',
61
61
  'rasterio',
62
62
  'rasterstats',
File without changes