RadGEEToolbox 1.7.3__py3-none-any.whl → 1.7.4__py3-none-any.whl

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.
@@ -1,6 +1,7 @@
1
1
  import ee
2
2
  import pandas as pd
3
3
  import numpy as np
4
+ import warnings
4
5
 
5
6
 
6
7
  # ---- Reflectance scaling for Landsat Collection 2 SR ----
@@ -186,6 +187,14 @@ class LandsatCollection:
186
187
  self._PixelAreaSumCollection = None
187
188
  self._Reflectance = None
188
189
 
190
+ def __call__(self):
191
+ """
192
+ Allows the object to be called as a function, returning itself.
193
+ This enables property-like methods to be accessed with or without parentheses
194
+ (e.g., .mosaicByDate or .mosaicByDate()).
195
+ """
196
+ return self
197
+
189
198
  @staticmethod
190
199
  def image_dater(image):
191
200
  """
@@ -772,7 +781,7 @@ class LandsatCollection:
772
781
  return image.addBands(anomaly_image, overwrite=True).copyProperties(image)
773
782
 
774
783
  @staticmethod
775
- def MaskWaterLandsat(image):
784
+ def maskWater(image):
776
785
  """
777
786
  Masks water pixels based on Landsat image QA band.
778
787
 
@@ -787,9 +796,19 @@ class LandsatCollection:
787
796
  water_extract = qa.bitwiseAnd(WaterBitMask).eq(0)
788
797
  masked_image = image.updateMask(water_extract).copyProperties(image).set('system:time_start', image.get('system:time_start'))
789
798
  return masked_image
799
+
800
+ @staticmethod
801
+ def MaskWaterLandsat(image):
802
+ warnings.warn(
803
+ "MaskWaterLandsat is deprecated and will be removed in a future release. "
804
+ "Please use maskWater instead.",
805
+ DeprecationWarning,
806
+ stacklevel=2
807
+ )
808
+ return LandsatCollection.maskWater(image)
790
809
 
791
810
  @staticmethod
792
- def MaskWaterLandsatByNDWI(image, threshold, ng_threshold=None):
811
+ def maskWaterByNDWI(image, threshold, ng_threshold=None):
793
812
  """
794
813
  Masks water pixels (mask land and cloud pixels) for all bands based on NDWI and a set threshold where
795
814
  all pixels less than NDWI threshold are masked out. Can specify separate thresholds for Landsat 5 vs 8&9 images, where the threshold
@@ -825,9 +844,19 @@ class LandsatCollection:
825
844
  "threshold", threshold, 'system:time_start', image.get('system:time_start')
826
845
  )
827
846
  return water
828
-
847
+
829
848
  @staticmethod
830
- def MaskToWaterLandsat(image):
849
+ def MaskWaterLandsatByNDWI(image, threshold, ng_threshold=None):
850
+ warnings.warn(
851
+ "MaskWaterLandsatByNDWI is deprecated and will be removed in a future release. "
852
+ "Please use maskWaterByNDWI instead.",
853
+ DeprecationWarning,
854
+ stacklevel=2
855
+ )
856
+ return LandsatCollection.maskWaterByNDWI(image, threshold, ng_threshold=ng_threshold)
857
+
858
+ @staticmethod
859
+ def maskToWater(image):
831
860
  """
832
861
  Masks image to water pixels by masking land and cloud pixels based on Landsat image QA band.
833
862
 
@@ -842,9 +871,19 @@ class LandsatCollection:
842
871
  water_extract = qa.bitwiseAnd(WaterBitMask).neq(0)
843
872
  masked_image = image.updateMask(water_extract).copyProperties(image).set('system:time_start', image.get('system:time_start'))
844
873
  return masked_image
874
+
875
+ @staticmethod
876
+ def MaskToWaterLandsat(image):
877
+ warnings.warn(
878
+ "MaskToWaterLandsat is deprecated and will be removed in a future release. "
879
+ "Please use maskToWater instead.",
880
+ DeprecationWarning,
881
+ stacklevel=2
882
+ )
883
+ return LandsatCollection.maskToWater(image)
845
884
 
846
885
  @staticmethod
847
- def MaskToWaterLandsatByNDWI(image, threshold, ng_threshold=None):
886
+ def maskToWaterByNDWI(image, threshold, ng_threshold=None):
848
887
  """
849
888
  Masks water pixels using NDWI based on threshold. Can specify separate thresholds for Landsat 5 vs 8&9 images, where the threshold
850
889
  argument applies to Landsat 5 and the ng_threshold argument applies to Landsat 8&9
@@ -879,6 +918,50 @@ class LandsatCollection:
879
918
  "threshold", threshold, 'system:time_start', image.get('system:time_start')
880
919
  )
881
920
  return water
921
+
922
+ @staticmethod
923
+ def MaskToWaterLandsatByNDWI(image, threshold, ng_threshold=None):
924
+ warnings.warn(
925
+ "MaskToWaterLandsatNDWI is deprecated and will be removed in a future release. "
926
+ "Please use maskToWaterByNDWI instead.",
927
+ DeprecationWarning,
928
+ stacklevel=2
929
+ )
930
+ return LandsatCollection.maskToWaterByNDWI(image, threshold, ng_threshold=ng_threshold)
931
+
932
+ # @staticmethod
933
+ # def maskClouds(image):
934
+ # """
935
+ # Masks clouds pixels based on Landsat image QA band.
936
+
937
+ # Args:
938
+ # image (ee.Image): input ee.Image
939
+
940
+ # Returns:
941
+ # ee.Image: ee.Image with cloud pixels masked.
942
+ # """
943
+ # CloudBitMask = ee.Number(2).pow(3).int()
944
+ # qa = image.select("QA_PIXEL")
945
+ # cloud_extract = qa.bitwiseAnd(CloudBitMask).eq(0)
946
+ # masked_image = image.updateMask(cloud_extract).copyProperties(image).set('system:time_start', image.get('system:time_start'))
947
+ # return masked_image
948
+
949
+ # @staticmethod
950
+ # def maskShadows(image):
951
+ # """
952
+ # Masks shadows pixels based on Landsat image QA band.
953
+
954
+ # Args:
955
+ # image (ee.Image): input ee.Image
956
+
957
+ # Returns:
958
+ # ee.Image: ee.Image with cloud pixels masked.
959
+ # """
960
+ # ShadowBitMask = ee.Number(2).pow(4).int()
961
+ # qa = image.select("QA_PIXEL")
962
+ # shadow_extract = qa.bitwiseAnd(ShadowBitMask).eq(0)
963
+ # masked_image = image.updateMask(shadow_extract).copyProperties(image).set('system:time_start', image.get('system:time_start'))
964
+ # return masked_image
882
965
 
883
966
  @staticmethod
884
967
  def mask_via_band_fn(image, band_to_mask, band_for_mask, threshold, mask_above=False, add_band_to_original_image=False):
@@ -1026,7 +1109,7 @@ class LandsatCollection:
1026
1109
  return mask
1027
1110
 
1028
1111
  @staticmethod
1029
- def maskL8clouds(image):
1112
+ def maskClouds(image):
1030
1113
  """
1031
1114
  Masks clouds baseed on Landsat 8 QA band.
1032
1115
 
@@ -1042,9 +1125,17 @@ class LandsatCollection:
1042
1125
  cloud_mask = qa.bitwiseAnd(cloudBitMask).eq(0)
1043
1126
  cirrus_mask = qa.bitwiseAnd(CirrusBitMask).eq(0)
1044
1127
  return image.updateMask(cloud_mask).updateMask(cirrus_mask)
1128
+
1129
+ @staticmethod
1130
+ def maskL8clouds(image):
1131
+ warnings.warn(
1132
+ "maskL8clouds is deprecated and will be removed in a future release. Please use maskClouds instead.",
1133
+ DeprecationWarning,
1134
+ stacklevel=2)
1135
+ return LandsatCollection.maskClouds(image)
1045
1136
 
1046
1137
  @staticmethod
1047
- def maskL8shadows(image):
1138
+ def maskShadows(image):
1048
1139
  """
1049
1140
  Masks cloud shadows based on Landsat 8 QA band.
1050
1141
 
@@ -1058,6 +1149,14 @@ class LandsatCollection:
1058
1149
  qa = image.select("QA_PIXEL")
1059
1150
  shadow_mask = qa.bitwiseAnd(shadowBitMask).eq(0)
1060
1151
  return image.updateMask(shadow_mask)
1152
+
1153
+ @staticmethod
1154
+ def maskL8shadows(image):
1155
+ warnings.warn(
1156
+ "maskL8shadows is deprecated and will be removed in a future release. Please use maskShadows instead.",
1157
+ DeprecationWarning,
1158
+ stacklevel=2)
1159
+ return LandsatCollection.maskShadows(image)
1061
1160
 
1062
1161
  @staticmethod
1063
1162
  def temperature_bands(img):
@@ -1172,7 +1271,7 @@ class LandsatCollection:
1172
1271
  return out.copyProperties(img)
1173
1272
 
1174
1273
  @staticmethod
1175
- def PixelAreaSum(
1274
+ def pixelAreaSum(
1176
1275
  image, band_name, geometry, threshold=-1, scale=30, maxPixels=1e12
1177
1276
  ):
1178
1277
  """
@@ -1231,8 +1330,18 @@ class LandsatCollection:
1231
1330
  # Call to iterate the calculate_and_set_area function over the list of bands, starting with the original image
1232
1331
  final_image = ee.Image(bands.iterate(calculate_and_set_area, image))
1233
1332
  return final_image
1333
+
1334
+ @staticmethod
1335
+ def PixelAreaSum(image, band_name, geometry, threshold=-1, scale=30, maxPixels=1e12):
1336
+ warnings.warn(
1337
+ "PixelAreaSum is deprecated and will be removed in a future release. "
1338
+ "Please use pixelAreaSum instead.",
1339
+ DeprecationWarning,
1340
+ stacklevel=2
1341
+ )
1342
+ return LandsatCollection.pixelAreaSum(image, band_name, geometry, threshold, scale, maxPixels)
1234
1343
 
1235
- def PixelAreaSumCollection(
1344
+ def pixelAreaSumCollection(
1236
1345
  self, band_name, geometry, threshold=-1, scale=30, maxPixels=1e12, output_type='ImageCollection', area_data_export_path=None):
1237
1346
  """
1238
1347
  Calculates the geodesic summation of area for pixels of interest (above a specific threshold)
@@ -1258,7 +1367,7 @@ class LandsatCollection:
1258
1367
  collection = self.collection
1259
1368
  # Area calculation for each image in the collection, using the PixelAreaSum function
1260
1369
  AreaCollection = collection.map(
1261
- lambda image: LandsatCollection.PixelAreaSum(
1370
+ lambda image: LandsatCollection.pixelAreaSum(
1262
1371
  image,
1263
1372
  band_name=band_name,
1264
1373
  geometry=geometry,
@@ -1274,17 +1383,27 @@ class LandsatCollection:
1274
1383
 
1275
1384
  # If an export path is provided, the area data will be exported to a CSV file
1276
1385
  if area_data_export_path:
1277
- LandsatCollection(collection=self._PixelAreaSumCollection).ExportProperties(property_names=prop_names, file_path=area_data_export_path+'.csv')
1386
+ LandsatCollection(collection=self._PixelAreaSumCollection).exportProperties(property_names=prop_names, file_path=area_data_export_path+'.csv')
1278
1387
  # Returning the result in the desired format based on output_type argument or raising an error for invalid input
1279
1388
  if output_type == 'ImageCollection' or output_type == 'ee.ImageCollection':
1280
1389
  return self._PixelAreaSumCollection
1281
1390
  elif output_type == 'LandsatCollection':
1282
1391
  return LandsatCollection(collection=self._PixelAreaSumCollection)
1283
1392
  elif output_type == 'DataFrame' or output_type == 'Pandas' or output_type == 'pd' or output_type == 'dataframe' or output_type == 'df':
1284
- return LandsatCollection(collection=self._PixelAreaSumCollection).ExportProperties(property_names=prop_names)
1393
+ return LandsatCollection(collection=self._PixelAreaSumCollection).exportProperties(property_names=prop_names)
1285
1394
  else:
1286
1395
  raise ValueError("Incorrect `output_type`. The `output_type` argument must be one of the following: 'ImageCollection', 'ee.ImageCollection', 'LandsatCollection', 'DataFrame', 'Pandas', 'pd', 'dataframe', or 'df'.")
1287
1396
 
1397
+ def PixelAreaSumCollection(
1398
+ self, band_name, geometry, threshold=-1, scale=30, maxPixels=1e12, output_type='ImageCollection', area_data_export_path=None):
1399
+ warnings.warn(
1400
+ "PixelAreaSumCollection is deprecated and will be removed in a future release. "
1401
+ "Please use pixelAreaSumCollection instead.",
1402
+ DeprecationWarning,
1403
+ stacklevel=2
1404
+ )
1405
+ return self.pixelAreaSumCollection(band_name, geometry, threshold, scale, maxPixels, output_type, area_data_export_path)
1406
+
1288
1407
  @staticmethod
1289
1408
  def add_month_property_fn(image):
1290
1409
  """
@@ -1388,7 +1507,12 @@ class LandsatCollection:
1388
1507
  return LandsatCollection(collection=ee.ImageCollection(paired.map(_pair_two)))
1389
1508
 
1390
1509
  # Preferred path: merge many singleband products into the parent
1391
- if not isinstance(collections, list) or len(collections) == 0:
1510
+ # if not isinstance(collections, list) or len(collections) == 0:
1511
+ # raise ValueError("Provide a non-empty list of LandsatCollection objects in `collections`.")
1512
+ if not isinstance(collections, list):
1513
+ collections = [collections]
1514
+
1515
+ if len(collections) == 0:
1392
1516
  raise ValueError("Provide a non-empty list of LandsatCollection objects in `collections`.")
1393
1517
 
1394
1518
  result = self.collection
@@ -1515,7 +1639,7 @@ class LandsatCollection:
1515
1639
  self._dates = dates
1516
1640
  return self._dates
1517
1641
 
1518
- def ExportProperties(self, property_names, file_path=None):
1642
+ def exportProperties(self, property_names, file_path=None):
1519
1643
  """
1520
1644
  Fetches and returns specified properties from each image in the collection as a list, and returns a pandas DataFrame and optionally saves the results to a csv file.
1521
1645
 
@@ -1570,6 +1694,15 @@ class LandsatCollection:
1570
1694
  print(f"Properties saved to {file_path}")
1571
1695
 
1572
1696
  return df
1697
+
1698
+ def ExportProperties(self, property_names, file_path=None):
1699
+ warnings.warn(
1700
+ "ExportProperties is deprecated and will be removed in a future release. "
1701
+ "Please use exportProperties instead.",
1702
+ DeprecationWarning,
1703
+ stacklevel=2
1704
+ )
1705
+ return self.exportProperties(property_names, file_path)
1573
1706
 
1574
1707
  def get_filtered_collection(self):
1575
1708
  """
@@ -3089,7 +3222,7 @@ class LandsatCollection:
3089
3222
  LandsatCollection: LandsatCollection image collection
3090
3223
  """
3091
3224
  if self._masked_water_collection is None:
3092
- col = self.collection.map(LandsatCollection.MaskWaterLandsat)
3225
+ col = self.collection.map(LandsatCollection.maskWater)
3093
3226
  self._masked_water_collection = LandsatCollection(collection=col)
3094
3227
  return self._masked_water_collection
3095
3228
 
@@ -3104,7 +3237,7 @@ class LandsatCollection:
3104
3237
  LandsatCollection: LandsatCollection image collection
3105
3238
  """
3106
3239
  col = self.collection.map(
3107
- lambda image: LandsatCollection.MaskWaterLandsatByNDWI(
3240
+ lambda image: LandsatCollection.maskWaterByNDWI(
3108
3241
  image, threshold=threshold
3109
3242
  )
3110
3243
  )
@@ -3119,7 +3252,7 @@ class LandsatCollection:
3119
3252
  LandsatCollection: LandsatCollection image collection
3120
3253
  """
3121
3254
  if self._masked_to_water_collection is None:
3122
- col = self.collection.map(LandsatCollection.MaskToWaterLandsat)
3255
+ col = self.collection.map(LandsatCollection.maskToWater)
3123
3256
  self._masked_to_water_collection = LandsatCollection(collection=col)
3124
3257
  return self._masked_to_water_collection
3125
3258
 
@@ -3134,7 +3267,7 @@ class LandsatCollection:
3134
3267
  LandsatCollection: LandsatCollection image collection
3135
3268
  """
3136
3269
  col = self.collection.map(
3137
- lambda image: LandsatCollection.MaskToWaterLandsatByNDWI(
3270
+ lambda image: LandsatCollection.maskToWaterByNDWI(
3138
3271
  image, threshold=threshold
3139
3272
  )
3140
3273
  )
@@ -3149,7 +3282,7 @@ class LandsatCollection:
3149
3282
  LandsatCollection: LandsatCollection image collection
3150
3283
  """
3151
3284
  if self._masked_clouds_collection is None:
3152
- col = self.collection.map(LandsatCollection.maskL8clouds)
3285
+ col = self.collection.map(LandsatCollection.maskClouds)
3153
3286
  self._masked_clouds_collection = LandsatCollection(collection=col)
3154
3287
  return self._masked_clouds_collection
3155
3288
 
@@ -3162,7 +3295,7 @@ class LandsatCollection:
3162
3295
  LandsatCollection: LandsatCollection image collection
3163
3296
  """
3164
3297
  if self._masked_shadows_collection is None:
3165
- col = self.collection.map(LandsatCollection.maskL8shadows)
3298
+ col = self.collection.map(LandsatCollection.maskShadows)
3166
3299
  self._masked_shadows_collection = LandsatCollection(collection=col)
3167
3300
  return self._masked_shadows_collection
3168
3301
 
@@ -3231,20 +3364,14 @@ class LandsatCollection:
3231
3364
  LandsatCollection: masked LandsatCollection image collection
3232
3365
 
3233
3366
  """
3234
- if self._geometry_masked_collection is None:
3235
- # Convert the polygon to a mask
3236
- mask = ee.Image.constant(1).clip(polygon)
3367
+ # Convert the polygon to a mask
3368
+ mask = ee.Image.constant(1).clip(polygon)
3237
3369
 
3238
- # Update the mask of each image in the collection
3239
- masked_collection = self.collection.map(lambda img: img.updateMask(mask))
3370
+ # Update the mask of each image in the collection
3371
+ masked_collection = self.collection.map(lambda img: img.updateMask(mask)\
3372
+ .copyProperties(img).set('system:time_start', img.get('system:time_start')))
3240
3373
 
3241
- # Update the internal collection state
3242
- self._geometry_masked_collection = LandsatCollection(
3243
- collection=masked_collection
3244
- )
3245
-
3246
- # Return the updated object
3247
- return self._geometry_masked_collection
3374
+ return LandsatCollection(collection=masked_collection)
3248
3375
 
3249
3376
  def mask_out_polygon(self, polygon):
3250
3377
  """
@@ -3257,23 +3384,18 @@ class LandsatCollection:
3257
3384
  LandsatCollection: masked LandsatCollection image collection
3258
3385
 
3259
3386
  """
3260
- if self._geometry_masked_out_collection is None:
3261
- # Convert the polygon to a mask
3262
- full_mask = ee.Image.constant(1)
3387
+ # Convert the polygon to a mask
3388
+ full_mask = ee.Image.constant(1)
3263
3389
 
3264
- # Use paint to set pixels inside polygon as 0
3265
- area = full_mask.paint(polygon, 0)
3390
+ # Use paint to set pixels inside polygon as 0
3391
+ area = full_mask.paint(polygon, 0)
3266
3392
 
3267
- # Update the mask of each image in the collection
3268
- masked_collection = self.collection.map(lambda img: img.updateMask(area))
3269
-
3270
- # Update the internal collection state
3271
- self._geometry_masked_out_collection = LandsatCollection(
3272
- collection=masked_collection
3273
- )
3393
+ # Update the mask of each image in the collection
3394
+ masked_collection = self.collection.map(lambda img: img.updateMask(area)\
3395
+ .copyProperties(img).set('system:time_start', img.get('system:time_start')))
3274
3396
 
3275
3397
  # Return the updated object
3276
- return self._geometry_masked_out_collection
3398
+ return LandsatCollection(collection=masked_collection)
3277
3399
 
3278
3400
  def mask_halite(self, threshold, ng_threshold=None):
3279
3401
  """
@@ -3473,6 +3595,8 @@ class LandsatCollection:
3473
3595
  rightField='Date_Filter')
3474
3596
  else:
3475
3597
  raise ValueError(f'The chosen `join_method`: {join_method} does not match the options of "system:time_start" or "Date_Filter".')
3598
+
3599
+ native_projection = image_collection.first().select(target_band).projection()
3476
3600
 
3477
3601
  # for any matches during a join, set image as a property key called 'future_image'
3478
3602
  join = ee.Join.saveAll(matchesKey='future_image')
@@ -3516,7 +3640,7 @@ class LandsatCollection:
3516
3640
  # convert the image collection to an image of s_statistic values per pixel
3517
3641
  # where the s_statistic is the sum of partial s values
3518
3642
  # renaming the band as 's_statistic' for later usage
3519
- final_s_image = partial_s_col.sum().rename('s_statistic')
3643
+ final_s_image = partial_s_col.sum().rename('s_statistic').setDefaultProjection(native_projection)
3520
3644
 
3521
3645
 
3522
3646
  ########## PART 2 - VARIANCE and Z-SCORE ##########
@@ -3579,7 +3703,7 @@ class LandsatCollection:
3579
3703
  mask = ee.Image(1).clip(geometry)
3580
3704
  final_image = final_image.updateMask(mask)
3581
3705
 
3582
- return final_image
3706
+ return final_image.setDefaultProjection(native_projection)
3583
3707
 
3584
3708
  def sens_slope_trend(self, target_band=None, join_method='system:time_start', geometry=None):
3585
3709
  """
@@ -3615,6 +3739,8 @@ class LandsatCollection:
3615
3739
  if geometry is not None and not isinstance(geometry, ee.Geometry):
3616
3740
  raise ValueError(f'The chosen `geometry`: {geometry} is not a valid ee.Geometry object.')
3617
3741
 
3742
+ native_projection = image_collection.first().select(target_band).projection()
3743
+
3618
3744
  # Add Year Band (Time X-Axis)
3619
3745
  def add_year_band(image):
3620
3746
  # Handle user-defined date strings vs system time
@@ -3642,7 +3768,7 @@ class LandsatCollection:
3642
3768
  mask = ee.Image(1).clip(geometry)
3643
3769
  slope_band = slope_band.updateMask(mask)
3644
3770
 
3645
- return slope_band
3771
+ return slope_band.setDefaultProjection(native_projection)
3646
3772
 
3647
3773
  def mask_via_band(self, band_to_mask, band_for_mask, threshold=-1, mask_above=True, add_band_to_original_image=False):
3648
3774
  """
@@ -3809,7 +3935,7 @@ class LandsatCollection:
3809
3935
  new_col = self.collection.filter(ee.Filter.eq("Date_Filter", img_date))
3810
3936
  return new_col.first()
3811
3937
 
3812
- def CollectionStitch(self, img_col2):
3938
+ def collectionStitch(self, img_col2):
3813
3939
  """
3814
3940
  Function to mosaic two LandsatCollection objects which share image dates.
3815
3941
  Mosaics are only formed for dates where both image collections have images.
@@ -3861,9 +3987,15 @@ class LandsatCollection:
3861
3987
 
3862
3988
  # Return a LandsatCollection instance
3863
3989
  return LandsatCollection(collection=new_col)
3990
+
3991
+ def CollectionStitch(self, img_col2):
3992
+ warnings.warn(
3993
+ "CollectionStitch is deprecated and will be removed in future versions. Please use the 'collectionStitch' property instead.",
3994
+ DeprecationWarning, stacklevel=2)
3995
+ return self.collectionStitch(img_col2)
3864
3996
 
3865
3997
  @property
3866
- def MosaicByDate(self):
3998
+ def mosaicByDateDepr(self):
3867
3999
  """
3868
4000
  Property attribute function to mosaic collection images that share the same date.
3869
4001
 
@@ -3928,6 +4060,73 @@ class LandsatCollection:
3928
4060
 
3929
4061
  # Convert the list of mosaics to an ImageCollection
3930
4062
  return self._MosaicByDate
4063
+
4064
+ @property
4065
+ def mosaicByDate(self):
4066
+ """
4067
+ Property attribute function to mosaic collection images that share the same date.
4068
+
4069
+ The property CLOUD_COVER for each image is used to calculate an overall mean,
4070
+ which replaces the CLOUD_COVER property for each mosaiced image.
4071
+ Server-side friendly.
4072
+
4073
+ NOTE: if images are removed from the collection from cloud filtering, you may have mosaics composed of only one image.
4074
+
4075
+ Returns:
4076
+ LandsatCollection: LandsatCollection image collection with mosaiced imagery and mean CLOUD_COVER as a property
4077
+ """
4078
+ if self._MosaicByDate is None:
4079
+ distinct_dates = self.collection.distinct("Date_Filter")
4080
+
4081
+ # Define a join to link images by Date_Filter
4082
+ filter_date = ee.Filter.equals(leftField="Date_Filter", rightField="Date_Filter")
4083
+ join = ee.Join.saveAll(matchesKey="date_matches")
4084
+
4085
+ # Apply the join
4086
+ # Primary: Distinct dates collection
4087
+ # Secondary: The full original collection
4088
+ joined_col = ee.ImageCollection(join.apply(distinct_dates, self.collection, filter_date))
4089
+
4090
+ # Define the mosaicking function
4091
+ def _mosaic_day(img):
4092
+ # Recover the list of images for this day
4093
+ daily_list = ee.List(img.get("date_matches"))
4094
+ daily_col = ee.ImageCollection.fromImages(daily_list)
4095
+
4096
+ # Create the mosaic
4097
+ mosaic = daily_col.mosaic().setDefaultProjection(img.projection())
4098
+
4099
+ # Calculate mean metadata properties
4100
+ cloud_percentage = daily_col.aggregate_mean("CLOUD_COVER")
4101
+
4102
+ # Properties to preserve from the representative image
4103
+ props_of_interest = [
4104
+ "SPACECRAFT_ID",
4105
+ "SENSOR_ID",
4106
+ "PROCESSING_LEVEL",
4107
+ "ACQUISITION_DATE",
4108
+ "system:time_start",
4109
+ "Date_Filter"
4110
+ ]
4111
+
4112
+ # Return mosaic with properties set
4113
+ return mosaic.copyProperties(img, props_of_interest).set({
4114
+ "CLOUD_COVER": cloud_percentage
4115
+ })
4116
+
4117
+ # 5. Map the function and wrap the result
4118
+ mosaiced_col = joined_col.map(_mosaic_day)
4119
+ self._MosaicByDate = LandsatCollection(collection=mosaiced_col)
4120
+
4121
+ # Convert the list of mosaics to an ImageCollection
4122
+ return self._MosaicByDate
4123
+
4124
+ @property
4125
+ def MosaicByDate(self):
4126
+ warnings.warn(
4127
+ "MosaicByDate is deprecated and will be removed in future versions. Please use the 'mosaicByDate' property instead.",
4128
+ DeprecationWarning, stacklevel=2)
4129
+ return self.mosaicByDate
3931
4130
 
3932
4131
  @staticmethod
3933
4132
  def ee_to_df(