RadGEEToolbox 1.7.2__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.
@@ -2,6 +2,7 @@ import ee
2
2
  import math
3
3
  import pandas as pd
4
4
  import numpy as np
5
+ import warnings
5
6
 
6
7
 
7
8
  class Sentinel1Collection:
@@ -218,6 +219,11 @@ class Sentinel1Collection:
218
219
  self._monthly_max = None
219
220
  self._monthly_min = None
220
221
  self._monthly_sum = None
222
+ self._yearly_median = None
223
+ self._yearly_mean = None
224
+ self._yearly_max = None
225
+ self._yearly_min = None
226
+ self._yearly_sum = None
221
227
  self._MosaicByDate = None
222
228
  self._PixelAreaSumCollection = None
223
229
  self._speckle_filter = None
@@ -225,6 +231,14 @@ class Sentinel1Collection:
225
231
  self._DbFromSigma0 = None
226
232
  self._multilook = None
227
233
 
234
+ def __call__(self):
235
+ """
236
+ Allows the object to be called as a function, returning itself.
237
+ This enables property-like methods to be accessed with or without parentheses
238
+ (e.g., .mosaicByDate or .mosaicByDate()).
239
+ """
240
+ return self
241
+
228
242
  @staticmethod
229
243
  def image_dater(image):
230
244
  """
@@ -240,7 +254,7 @@ class Sentinel1Collection:
240
254
  return image.set({"Date_Filter": date})
241
255
 
242
256
  @staticmethod
243
- def PixelAreaSum(
257
+ def pixelAreaSum(
244
258
  image, band_name, geometry, threshold=-1, scale=10, maxPixels=1e12
245
259
  ):
246
260
  """
@@ -299,8 +313,26 @@ class Sentinel1Collection:
299
313
  # Call to iterate the calculate_and_set_area function over the list of bands, starting with the original image
300
314
  final_image = ee.Image(bands.iterate(calculate_and_set_area, image))
301
315
  return final_image
316
+
317
+ @staticmethod
318
+ def PixelAreaSum(
319
+ image, band_name, geometry, threshold=-1, scale=10, maxPixels=1e12
320
+ ):
321
+ warnings.warn(
322
+ "The 'PixelAreaSum' method is deprecated. Please use 'pixelAreaSum' instead.",
323
+ DeprecationWarning,
324
+ stacklevel=2
325
+ )
326
+ return Sentinel1Collection.pixelAreaSum(
327
+ image=image,
328
+ band_name=band_name,
329
+ geometry=geometry,
330
+ threshold=threshold,
331
+ scale=scale,
332
+ maxPixels=maxPixels,
333
+ )
302
334
 
303
- def PixelAreaSumCollection(
335
+ def pixelAreaSumCollection(
304
336
  self, band_name, geometry, threshold=-1, scale=10, maxPixels=1e12, output_type='ImageCollection', area_data_export_path=None
305
337
  ):
306
338
  """
@@ -327,7 +359,7 @@ class Sentinel1Collection:
327
359
  collection = self.collection
328
360
  # Area calculation for each image in the collection, using the PixelAreaSum function
329
361
  AreaCollection = collection.map(
330
- lambda image: Sentinel1Collection.PixelAreaSum(
362
+ lambda image: Sentinel1Collection.pixelAreaSum(
331
363
  image,
332
364
  band_name=band_name,
333
365
  geometry=geometry,
@@ -343,17 +375,35 @@ class Sentinel1Collection:
343
375
 
344
376
  # If an export path is provided, the area data will be exported to a CSV file
345
377
  if area_data_export_path:
346
- Sentinel1Collection(collection=self._PixelAreaSumCollection).ExportProperties(property_names=prop_names, file_path=area_data_export_path+'.csv')
378
+ Sentinel1Collection(collection=self._PixelAreaSumCollection).exportProperties(property_names=prop_names, file_path=area_data_export_path+'.csv')
347
379
  # Returning the result in the desired format based on output_type argument or raising an error for invalid input
348
380
  if output_type == 'ImageCollection' or output_type == 'ee.ImageCollection':
349
381
  return self._PixelAreaSumCollection
350
382
  elif output_type == 'Sentinel1Collection':
351
383
  return Sentinel1Collection(collection=self._PixelAreaSumCollection)
352
384
  elif output_type == 'DataFrame' or output_type == 'Pandas' or output_type == 'pd' or output_type == 'dataframe' or output_type == 'df':
353
- return Sentinel1Collection(collection=self._PixelAreaSumCollection).ExportProperties(property_names=prop_names)
385
+ return Sentinel1Collection(collection=self._PixelAreaSumCollection).exportProperties(property_names=prop_names)
354
386
  else:
355
387
  raise ValueError("Incorrect `output_type`. The `output_type` argument must be one of the following: 'ImageCollection', 'ee.ImageCollection', 'Sentinel1Collection', 'DataFrame', 'Pandas', 'pd', 'dataframe', or 'df'.")
356
388
 
389
+ def PixelAreaSumCollection(
390
+ self, band_name, geometry, threshold=-1, scale=10, maxPixels=1e12, output_type='ImageCollection', area_data_export_path=None
391
+ ):
392
+ warnings.warn(
393
+ "The 'PixelAreaSumCollection' method is deprecated. Please use 'pixelAreaSumCollection' instead.",
394
+ DeprecationWarning,
395
+ stacklevel=2
396
+ )
397
+ return self.pixelAreaSumCollection(
398
+ band_name=band_name,
399
+ geometry=geometry,
400
+ threshold=threshold,
401
+ scale=scale,
402
+ maxPixels=maxPixels,
403
+ output_type=output_type,
404
+ area_data_export_path=area_data_export_path
405
+ )
406
+
357
407
  @staticmethod
358
408
  def add_month_property_fn(image):
359
409
  """
@@ -427,7 +477,7 @@ class Sentinel1Collection:
427
477
  # Overwrite on name collision
428
478
  merged = a.addBands(b, None, True)
429
479
  # Keep parent props + date key
430
- merged = merged.copyProperties(a, a.propertyNames())
480
+ merged = merged.copyProperties(a, a.propertyNames()).set('system:time_start', a.get('system:time_start'))
431
481
  merged = merged.set(date_key, a.get(date_key))
432
482
  return ee.Image(merged)
433
483
 
@@ -455,7 +505,7 @@ class Sentinel1Collection:
455
505
  # Add the single band; overwrite if the name already exists in parent
456
506
  merged = parent.addBands(sb.select([bname]).rename([bname]), None, True)
457
507
  # Preserve parent props + date key
458
- merged = merged.copyProperties(parent, parent.propertyNames())
508
+ merged = merged.copyProperties(parent, parent.propertyNames()).set('system:time_start', parent.get('system:time_start'))
459
509
  merged = merged.set(date_key, parent.get(date_key))
460
510
  return ee.Image(merged)
461
511
 
@@ -670,7 +720,7 @@ class Sentinel1Collection:
670
720
  xHat = image.select(bandNames).updateMask(retainPixel).unmask(xHat)
671
721
  output = ee.Image(xHat).rename(bandNames)
672
722
  # return image.addBands(output, None, True)
673
- return output.copyProperties(image)
723
+ return output.copyProperties(image).set('system:time_start', image.get('system:time_start'))
674
724
 
675
725
  def speckle_filter(self, KERNEL_SIZE, geometry=None, Tk=7, sigma=0.9, looks=1):
676
726
  """
@@ -702,7 +752,7 @@ class Sentinel1Collection:
702
752
  return Sentinel1Collection(collection=self._speckle_filter)
703
753
 
704
754
  @property
705
- def Sigma0FromDb(self):
755
+ def sigma0FromDb(self):
706
756
  """
707
757
  Property attribute function to convert image collection from decibels to sigma0. Results are calculated once per class object then cached for future use.
708
758
 
@@ -718,6 +768,7 @@ class Sentinel1Collection:
718
768
  .pow(image.divide(ee.Image(10)))
719
769
  .rename(band_names)
720
770
  .copyProperties(image)
771
+ .set('system:time_start', image.get('system:time_start'))
721
772
  )
722
773
  return sigma_nought
723
774
 
@@ -726,9 +777,18 @@ class Sentinel1Collection:
726
777
  sigma0_collection = collection.map(conversion)
727
778
  self._Sigma0FromDb = sigma0_collection
728
779
  return Sentinel1Collection(collection=self._Sigma0FromDb)
780
+
781
+ @property
782
+ def Sigma0FromDb(self):
783
+ warnings.warn(
784
+ "The 'Sigma0FromDb' property is deprecated. Please use 'sigma0FromDb' instead.",
785
+ DeprecationWarning,
786
+ stacklevel=2
787
+ )
788
+ return self.sigma0FromDb
729
789
 
730
790
  @property
731
- def DbFromSigma0(self):
791
+ def dbFromSigma0(self):
732
792
  """
733
793
  Property attribute function to convert image collection from decibels to sigma0. Results are calculated once per class object then cached for future use.
734
794
 
@@ -744,6 +804,7 @@ class Sentinel1Collection:
744
804
  .multiply(image.log10())
745
805
  .rename(band_names)
746
806
  .copyProperties(image)
807
+ .set('system:time_start', image.get('system:time_start'))
747
808
  )
748
809
  return dB
749
810
 
@@ -753,6 +814,15 @@ class Sentinel1Collection:
753
814
  self._DbFromSigma0 = dB_collection
754
815
  return Sentinel1Collection(collection=self._DbFromSigma0)
755
816
 
817
+ @property
818
+ def DbFromSigma0(self):
819
+ warnings.warn(
820
+ "The 'DbFromSigma0' property is deprecated. Please use 'dbFromSigma0' instead.",
821
+ DeprecationWarning,
822
+ stacklevel=2
823
+ )
824
+ return self.dbFromSigma0
825
+
756
826
  @staticmethod
757
827
  def anomaly_fn(image, geometry, band_name=None, anomaly_band_name=None, replace=True, scale=10):
758
828
  """
@@ -809,7 +879,7 @@ class Sentinel1Collection:
809
879
  if replace:
810
880
  return anomaly_image.copyProperties(image).set('system:time_start', image.get('system:time_start'))
811
881
  else:
812
- return image.addBands(anomaly_image, overwrite=True)
882
+ return image.addBands(anomaly_image, overwrite=True).copyProperties(image).set('system:time_start', image.get('system:time_start'))
813
883
 
814
884
  @property
815
885
  def dates_list(self):
@@ -840,7 +910,7 @@ class Sentinel1Collection:
840
910
  self._dates = dates
841
911
  return self._dates
842
912
 
843
- def ExportProperties(self, property_names, file_path=None):
913
+ def exportProperties(self, property_names, file_path=None):
844
914
  """
845
915
  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.
846
916
 
@@ -895,6 +965,14 @@ class Sentinel1Collection:
895
965
  print(f"Properties saved to {file_path}")
896
966
 
897
967
  return df
968
+
969
+ def ExportProperties(self, property_names, file_path=None):
970
+ warnings.warn(
971
+ "The 'ExportProperties' method is deprecated. Please use 'exportProperties' instead.",
972
+ DeprecationWarning,
973
+ stacklevel=2
974
+ )
975
+ return self.exportProperties(property_names=property_names, file_path=file_path)
898
976
 
899
977
  def get_filtered_collection(self):
900
978
  """
@@ -1463,6 +1541,391 @@ class Sentinel1Collection:
1463
1541
 
1464
1542
  return self._monthly_min
1465
1543
 
1544
+ def yearly_mean_collection(self, start_month=1, end_month=12):
1545
+ """
1546
+ Creates a yearly mean composite from the collection, with optional monthly filtering.
1547
+
1548
+ This function computes the mean for each year within the collection's date range.
1549
+ You can specify a range of months (e.g., start_month=6, end_month=10 for June-October)
1550
+ to calculate the mean only using imagery from that specific season for each year.
1551
+
1552
+ The resulting images have 'system:time_start', 'year', 'image_count', 'season_start',
1553
+ 'season_end', and 'Date_Filter' properties. Years with no images (after filtering) are excluded.
1554
+
1555
+ Args:
1556
+ start_month (int): The starting month (1-12) for the filter. Defaults to 1 (January).
1557
+ end_month (int): The ending month (1-12) for the filter. Defaults to 12 (December).
1558
+
1559
+ Returns:
1560
+ Object: A new instance of the same class (e.g., Sentinel1Collection) containing the yearly mean composites.
1561
+ """
1562
+ if self._yearly_mean is None:
1563
+
1564
+ date_range = self.collection.reduceColumns(ee.Reducer.minMax(), ["system:time_start"])
1565
+ start_date_full = ee.Date(date_range.get('min'))
1566
+ end_date_full = ee.Date(date_range.get('max'))
1567
+
1568
+ start_year = start_date_full.get('year')
1569
+ end_year = end_date_full.get('year')
1570
+
1571
+ if start_month != 1 or end_month != 12:
1572
+ processing_collection = self.collection.filter(ee.Filter.calendarRange(start_month, end_month, 'month'))
1573
+ else:
1574
+ processing_collection = self.collection
1575
+
1576
+ # Capture projection from the first image to restore it after reduction
1577
+ target_proj = self.collection.first().projection()
1578
+
1579
+ years = ee.List.sequence(start_year, end_year)
1580
+
1581
+ def create_yearly_composite(year):
1582
+ year = ee.Number(year)
1583
+ # Define the full calendar year range
1584
+ start_of_year = ee.Date.fromYMD(year, 1, 1)
1585
+ end_of_year = start_of_year.advance(1, 'year')
1586
+
1587
+ yearly_subset = processing_collection.filterDate(start_of_year, end_of_year)
1588
+
1589
+ # Calculate stats
1590
+ image_count = yearly_subset.size()
1591
+ yearly_reduction = yearly_subset.mean()
1592
+
1593
+ # Define the timestamp for the composite.
1594
+ # We use the start_month of that year to accurately reflect the data start time.
1595
+ composite_date = ee.Date.fromYMD(year, start_month, 1)
1596
+
1597
+ return yearly_reduction.set({
1598
+ 'system:time_start': composite_date.millis(),
1599
+ 'year': year,
1600
+ 'month': start_month,
1601
+ 'Date_Filter': composite_date.format('YYYY-MM-dd'),
1602
+ 'image_count': image_count,
1603
+ 'season_start': start_month,
1604
+ 'season_end': end_month
1605
+ }).reproject(target_proj)
1606
+
1607
+ # Map the function over the years list
1608
+ yearly_composites_list = years.map(create_yearly_composite)
1609
+
1610
+ # Convert to Collection
1611
+ yearly_collection = ee.ImageCollection.fromImages(yearly_composites_list)
1612
+
1613
+ # Filter out any composites that were created from zero images.
1614
+ final_collection = yearly_collection.filter(ee.Filter.gt('image_count', 0))
1615
+
1616
+ self._yearly_mean = Sentinel1Collection(collection=final_collection)
1617
+ else:
1618
+ pass
1619
+ return self._yearly_mean
1620
+
1621
+ def yearly_median_collection(self, start_month=1, end_month=12):
1622
+ """
1623
+ Creates a yearly median composite from the collection, with optional monthly filtering.
1624
+
1625
+ This function computes the median for each year within the collection's date range.
1626
+ You can specify a range of months (e.g., start_month=6, end_month=10 for June-October)
1627
+ to calculate the median only using imagery from that specific season for each year.
1628
+ The resulting images have 'system:time_start', 'year', 'image_count', 'season_start',
1629
+ 'season_end', and 'Date_Filter' properties. Years with no images (after filtering) are excluded.
1630
+
1631
+ Args:
1632
+ start_month (int): The starting month (1-12) for the filter. Defaults to 1 (January).
1633
+ end_month (int): The ending month (1-12) for the filter. Defaults to 12 (December).
1634
+
1635
+ Returns:
1636
+ Object: A new instance of the same class (e.g., Sentinel1Collection) containing the yearly median composites.
1637
+ """
1638
+ if self._yearly_median is None:
1639
+
1640
+ date_range = self.collection.reduceColumns(ee.Reducer.minMax(), ["system:time_start"])
1641
+ start_date_full = ee.Date(date_range.get('min'))
1642
+ end_date_full = ee.Date(date_range.get('max'))
1643
+
1644
+ start_year = start_date_full.get('year')
1645
+ end_year = end_date_full.get('year')
1646
+
1647
+ if start_month != 1 or end_month != 12:
1648
+ processing_collection = self.collection.filter(ee.Filter.calendarRange(start_month, end_month, 'month'))
1649
+ else:
1650
+ processing_collection = self.collection
1651
+
1652
+ # Capture projection from the first image to restore it after reduction
1653
+ target_proj = self.collection.first().projection()
1654
+
1655
+ years = ee.List.sequence(start_year, end_year)
1656
+
1657
+ def create_yearly_composite(year):
1658
+ year = ee.Number(year)
1659
+ # Define the full calendar year range
1660
+ start_of_year = ee.Date.fromYMD(year, 1, 1)
1661
+ end_of_year = start_of_year.advance(1, 'year')
1662
+
1663
+ # Filter to the specific year using the PRE-FILTERED seasonal collection
1664
+ yearly_subset = processing_collection.filterDate(start_of_year, end_of_year)
1665
+
1666
+ # Calculate stats
1667
+ image_count = yearly_subset.size()
1668
+ yearly_reduction = yearly_subset.median()
1669
+
1670
+ # Define the timestamp for the composite.
1671
+ # We use the start_month of that year to accurately reflect the data start time.
1672
+ composite_date = ee.Date.fromYMD(year, start_month, 1)
1673
+
1674
+ return yearly_reduction.set({
1675
+ 'system:time_start': composite_date.millis(),
1676
+ 'year': year,
1677
+ 'month': start_month,
1678
+ 'Date_Filter': composite_date.format('YYYY-MM-dd'),
1679
+ 'image_count': image_count,
1680
+ 'season_start': start_month,
1681
+ 'season_end': end_month
1682
+ }).reproject(target_proj)
1683
+
1684
+ # Map the function over the years list
1685
+ yearly_composites_list = years.map(create_yearly_composite)
1686
+
1687
+ # Convert to Collection
1688
+ yearly_collection = ee.ImageCollection.fromImages(yearly_composites_list)
1689
+
1690
+ # Filter out any composites that were created from zero images.
1691
+ final_collection = yearly_collection.filter(ee.Filter.gt('image_count', 0))
1692
+
1693
+ self._yearly_median = Sentinel1Collection(collection=final_collection)
1694
+ else:
1695
+ pass
1696
+ return self._yearly_median
1697
+
1698
+ def yearly_max_collection(self, start_month=1, end_month=12):
1699
+ """
1700
+ Creates a yearly max composite from the collection, with optional monthly filtering.
1701
+
1702
+ This function computes the max for each year within the collection's date range.
1703
+ You can specify a range of months (e.g., start_month=6, end_month=10 for June-October)
1704
+ to calculate the max only using imagery from that specific season for each year.
1705
+ The resulting images have 'system:time_start', 'year', 'image_count', 'season_start',
1706
+ 'season_end', and 'Date_Filter' properties. Years with no images (after filtering) are excluded.
1707
+
1708
+ Args:
1709
+ start_month (int): The starting month (1-12) for the filter. Defaults to 1 (January).
1710
+ end_month (int): The ending month (1-12) for the filter. Defaults to 12 (December).
1711
+
1712
+ Returns:
1713
+ Object: A new instance of the same class (e.g., Sentinel1Collection) containing the yearly max composites.
1714
+ """
1715
+ if self._yearly_max is None:
1716
+
1717
+ date_range = self.collection.reduceColumns(ee.Reducer.minMax(), ["system:time_start"])
1718
+ start_date_full = ee.Date(date_range.get('min'))
1719
+ end_date_full = ee.Date(date_range.get('max'))
1720
+
1721
+ start_year = start_date_full.get('year')
1722
+ end_year = end_date_full.get('year')
1723
+
1724
+ if start_month != 1 or end_month != 12:
1725
+ processing_collection = self.collection.filter(ee.Filter.calendarRange(start_month, end_month, 'month'))
1726
+ else:
1727
+ processing_collection = self.collection
1728
+
1729
+ # Capture projection from the first image to restore it after reduction
1730
+ target_proj = self.collection.first().projection()
1731
+
1732
+ years = ee.List.sequence(start_year, end_year)
1733
+
1734
+ def create_yearly_composite(year):
1735
+ year = ee.Number(year)
1736
+ # Define the full calendar year range
1737
+ start_of_year = ee.Date.fromYMD(year, 1, 1)
1738
+ end_of_year = start_of_year.advance(1, 'year')
1739
+
1740
+ # Filter to the specific year using the PRE-FILTERED seasonal collection
1741
+ yearly_subset = processing_collection.filterDate(start_of_year, end_of_year)
1742
+
1743
+ # Calculate stats
1744
+ image_count = yearly_subset.size()
1745
+ yearly_reduction = yearly_subset.max()
1746
+
1747
+ # Define the timestamp for the composite.
1748
+ # We use the start_month of that year to accurately reflect the data start time.
1749
+ composite_date = ee.Date.fromYMD(year, start_month, 1)
1750
+
1751
+ return yearly_reduction.set({
1752
+ 'system:time_start': composite_date.millis(),
1753
+ 'year': year,
1754
+ 'month': start_month,
1755
+ 'Date_Filter': composite_date.format('YYYY-MM-dd'),
1756
+ 'image_count': image_count,
1757
+ 'season_start': start_month,
1758
+ 'season_end': end_month
1759
+ }).reproject(target_proj)
1760
+
1761
+ # Map the function over the years list
1762
+ yearly_composites_list = years.map(create_yearly_composite)
1763
+
1764
+ # Convert to Collection
1765
+ yearly_collection = ee.ImageCollection.fromImages(yearly_composites_list)
1766
+
1767
+ # Filter out any composites that were created from zero images.
1768
+ final_collection = yearly_collection.filter(ee.Filter.gt('image_count', 0))
1769
+
1770
+ self._yearly_max = Sentinel1Collection(collection=final_collection)
1771
+ else:
1772
+ pass
1773
+ return self._yearly_max
1774
+
1775
+ def yearly_min_collection(self, start_month=1, end_month=12):
1776
+ """
1777
+ Creates a yearly min composite from the collection, with optional monthly filtering.
1778
+
1779
+ This function computes the min for each year within the collection's date range.
1780
+ You can specify a range of months (e.g., start_month=6, end_month=10 for June-October)
1781
+ to calculate the min only using imagery from that specific season for each year.
1782
+ The resulting images have 'system:time_start', 'year', 'image_count', 'season_start',
1783
+ 'season_end', and 'Date_Filter' properties. Years with no images (after filtering) are excluded.
1784
+
1785
+ Args:
1786
+ start_month (int): The starting month (1-12) for the filter. Defaults to 1 (January).
1787
+ end_month (int): The ending month (1-12) for the filter. Defaults to 12 (December).
1788
+
1789
+ Returns:
1790
+ Object: A new instance of the same class (e.g., Sentinel1Collection) containing the yearly min composites.
1791
+ """
1792
+ if self._yearly_min is None:
1793
+
1794
+ date_range = self.collection.reduceColumns(ee.Reducer.minMax(), ["system:time_start"])
1795
+ start_date_full = ee.Date(date_range.get('min'))
1796
+ end_date_full = ee.Date(date_range.get('max'))
1797
+
1798
+ start_year = start_date_full.get('year')
1799
+ end_year = end_date_full.get('year')
1800
+
1801
+ if start_month != 1 or end_month != 12:
1802
+ processing_collection = self.collection.filter(ee.Filter.calendarRange(start_month, end_month, 'month'))
1803
+ else:
1804
+ processing_collection = self.collection
1805
+
1806
+ # Capture projection from the first image to restore it after reduction
1807
+ target_proj = self.collection.first().projection()
1808
+
1809
+ years = ee.List.sequence(start_year, end_year)
1810
+
1811
+ def create_yearly_composite(year):
1812
+ year = ee.Number(year)
1813
+ # Define the full calendar year range
1814
+ start_of_year = ee.Date.fromYMD(year, 1, 1)
1815
+ end_of_year = start_of_year.advance(1, 'year')
1816
+
1817
+ # Filter to the specific year using the PRE-FILTERED seasonal collection
1818
+ yearly_subset = processing_collection.filterDate(start_of_year, end_of_year)
1819
+
1820
+ # Calculate stats
1821
+ image_count = yearly_subset.size()
1822
+ yearly_reduction = yearly_subset.min()
1823
+
1824
+ # Define the timestamp for the composite.
1825
+ # We use the start_month of that year to accurately reflect the data start time.
1826
+ composite_date = ee.Date.fromYMD(year, start_month, 1)
1827
+
1828
+ return yearly_reduction.set({
1829
+ 'system:time_start': composite_date.millis(),
1830
+ 'year': year,
1831
+ 'month': start_month,
1832
+ 'Date_Filter': composite_date.format('YYYY-MM-dd'),
1833
+ 'image_count': image_count,
1834
+ 'season_start': start_month,
1835
+ 'season_end': end_month
1836
+ }).reproject(target_proj)
1837
+
1838
+ # Map the function over the years list
1839
+ yearly_composites_list = years.map(create_yearly_composite)
1840
+
1841
+ # Convert to Collection
1842
+ yearly_collection = ee.ImageCollection.fromImages(yearly_composites_list)
1843
+
1844
+ # Filter out any composites that were created from zero images.
1845
+ final_collection = yearly_collection.filter(ee.Filter.gt('image_count', 0))
1846
+
1847
+ self._yearly_min = Sentinel1Collection(collection=final_collection)
1848
+ else:
1849
+ pass
1850
+ return self._yearly_min
1851
+
1852
+ def yearly_sum_collection(self, start_month=1, end_month=12):
1853
+ """
1854
+ Creates a yearly sum composite from the collection, with optional monthly filtering.
1855
+
1856
+ This function computes the sum for each year within the collection's date range.
1857
+ You can specify a range of months (e.g., start_month=6, end_month=10 for June-October)
1858
+ to calculate the sum only using imagery from that specific season for each year.
1859
+ The resulting images have 'system:time_start', 'year', 'image_count', 'season_start',
1860
+ 'season_end', and 'Date_Filter' properties. Years with no images (after filtering) are excluded.
1861
+
1862
+ Args:
1863
+ start_month (int): The starting month (1-12) for the filter. Defaults to 1 (January).
1864
+ end_month (int): The ending month (1-12) for the filter. Defaults to 12 (December).
1865
+
1866
+ Returns:
1867
+ Object: A new instance of the same class (e.g., Sentinel1Collection) containing the yearly sum composites.
1868
+ """
1869
+ if self._yearly_sum is None:
1870
+
1871
+ date_range = self.collection.reduceColumns(ee.Reducer.minMax(), ["system:time_start"])
1872
+ start_date_full = ee.Date(date_range.get('min'))
1873
+ end_date_full = ee.Date(date_range.get('max'))
1874
+
1875
+ start_year = start_date_full.get('year')
1876
+ end_year = end_date_full.get('year')
1877
+
1878
+ if start_month != 1 or end_month != 12:
1879
+ processing_collection = self.collection.filter(ee.Filter.calendarRange(start_month, end_month, 'month'))
1880
+ else:
1881
+ processing_collection = self.collection
1882
+
1883
+ # Capture projection from the first image to restore it after reduction
1884
+ target_proj = self.collection.first().projection()
1885
+
1886
+ years = ee.List.sequence(start_year, end_year)
1887
+
1888
+ def create_yearly_composite(year):
1889
+ year = ee.Number(year)
1890
+ # Define the full calendar year range
1891
+ start_of_year = ee.Date.fromYMD(year, 1, 1)
1892
+ end_of_year = start_of_year.advance(1, 'year')
1893
+
1894
+ # Filter to the specific year using the PRE-FILTERED seasonal collection
1895
+ yearly_subset = processing_collection.filterDate(start_of_year, end_of_year)
1896
+
1897
+ # Calculate stats
1898
+ image_count = yearly_subset.size()
1899
+ yearly_reduction = yearly_subset.sum()
1900
+
1901
+ # Define the timestamp for the composite.
1902
+ # We use the start_month of that year to accurately reflect the data start time.
1903
+ composite_date = ee.Date.fromYMD(year, start_month, 1)
1904
+
1905
+ return yearly_reduction.set({
1906
+ 'system:time_start': composite_date.millis(),
1907
+ 'year': year,
1908
+ 'month': start_month,
1909
+ 'Date_Filter': composite_date.format('YYYY-MM-dd'),
1910
+ 'image_count': image_count,
1911
+ 'season_start': start_month,
1912
+ 'season_end': end_month
1913
+ }).reproject(target_proj)
1914
+
1915
+ # Map the function over the years list
1916
+ yearly_composites_list = years.map(create_yearly_composite)
1917
+
1918
+ # Convert to Collection
1919
+ yearly_collection = ee.ImageCollection.fromImages(yearly_composites_list)
1920
+
1921
+ # Filter out any composites that were created from zero images.
1922
+ final_collection = yearly_collection.filter(ee.Filter.gt('image_count', 0))
1923
+
1924
+ self._yearly_sum = Sentinel1Collection(collection=final_collection)
1925
+ else:
1926
+ pass
1927
+ return self._yearly_sum
1928
+
1466
1929
  def anomaly(self, geometry, band_name=None, anomaly_band_name=None, replace=True, scale=10):
1467
1930
  """
1468
1931
  Calculates the anomaly of each image in a collection compared to the mean of each image.
@@ -1526,9 +1989,242 @@ class Sentinel1Collection:
1526
1989
  raise ValueError("Threshold must be specified for binary masking.")
1527
1990
 
1528
1991
  col = self.collection.map(
1529
- lambda image: image.select(band_name).gte(threshold).rename(band_name)
1992
+ lambda image: image.select(band_name).gte(threshold).rename(band_name).copyProperties(image).set('system:time_start', image.get('system:time_start'))
1530
1993
  )
1531
1994
  return Sentinel1Collection(collection=col)
1995
+
1996
+ def mann_kendall_trend(self, target_band=None, join_method='system:time_start', geometry=None):
1997
+ """
1998
+ Calculates the Mann-Kendall S-value, Variance, Z-Score, and Confidence Level for each pixel in the image collection, in addition to calculating
1999
+ the Sen's slope for each pixel in the image collection. The output is an image with the following bands: 's_statistic', 'variance', 'z_score', 'confidence', and 'slope'.
2000
+
2001
+ This function can be used to identify trends in the image collection over time, such as increasing or decreasing values in the target band, and can be used to assess the significance of these trends.
2002
+ Note that this function is computationally intensive and may take a long time to run for large image collections or high-resolution images.
2003
+
2004
+ The 's_statistic' band represents the Mann-Kendall S-value, which is a measure of the strength and direction of the trend.
2005
+ The 'variance' band represents the variance of the S-value, which is a measure of the variability of the S-value.
2006
+ The 'z_score' band represents the Z-Score, which is a measure of the significance of the trend.
2007
+ The 'confidence' band represents the confidence level of the trend based on the z_score, which is a probabilistic measure of the confidence in the trend (percentage).
2008
+ The 'slope' band represents the Sen's slope, which is a measure of the rate of change in the target band over time. This value can be small as multispectral indices commonly range from -1 to 1, so a slope may have values of <0.2 for most cases.
2009
+
2010
+ Be sure to select the correct band for the `target_band` parameter, as this will be used to calculate the trend statistics.
2011
+ You may optionally provide an ee.Geometry object for the `geometry` parameter to limit the area over which the trend statistics are calculated.
2012
+ The `geometry` parameter is optional and defaults to None, which means that the trend statistics will be calculated over the entire footprint of the image collection.
2013
+
2014
+ Args:
2015
+ image_collection (Sentinel1Collection or ee.ImageCollection): The input image collection for which the Mann-Kendall and Sen's slope trend statistics will be calculated.
2016
+ target_band (str): The band name to be used for the output anomaly image. e.g. 'ndvi'
2017
+ join_method (str, optional): The method used to join images in the collection. Options are 'system:time_start' or 'Date_Filter'. Default is 'system:time_start'.
2018
+ geometry (ee.Geometry, optional): An ee.Geometry object to limit the area over which the trend statistics are calculated and mask the output image. Default is None.
2019
+
2020
+ Returns:
2021
+ ee.Image: An image with the following bands: 's_statistic', 'variance', 'z_score', 'confidence', and 'slope'.
2022
+ """
2023
+ ########## PART 1 - S-VALUE CALCULATION ##########
2024
+ ##### https://vsp.pnnl.gov/help/vsample/design_trend_mann_kendall.htm #####
2025
+ image_collection = self
2026
+ if isinstance(image_collection, Sentinel1Collection):
2027
+ image_collection = image_collection.collection
2028
+ elif isinstance(image_collection, ee.ImageCollection):
2029
+ pass
2030
+ else:
2031
+ raise ValueError(f'The chosen `image_collection`: {image_collection} is not a valid Sentinel1Collection or ee.ImageCollection object.')
2032
+
2033
+ if target_band is None:
2034
+ raise ValueError('The `target_band` parameter must be specified.')
2035
+ if not isinstance(target_band, str):
2036
+ raise ValueError(f'The chosen `target_band`: {target_band} is not a valid string.')
2037
+
2038
+ if geometry is not None and not isinstance(geometry, ee.Geometry):
2039
+ raise ValueError(f'The chosen `geometry`: {geometry} is not a valid ee.Geometry object.')
2040
+
2041
+ native_projection = image_collection.first().select(target_band).projection()
2042
+
2043
+ # define the join, which will join all images newer than the current image
2044
+ # use system:time_start if the image does not have a Date_Filter property
2045
+ if join_method == 'system:time_start':
2046
+ # get all images where the leftField value is less than (before) the rightField value
2047
+ time_filter = ee.Filter.lessThan(leftField='system:time_start',
2048
+ rightField='system:time_start')
2049
+ elif join_method == 'Date_Filter':
2050
+ # get all images where the leftField value is less than (before) the rightField value
2051
+ time_filter = ee.Filter.lessThan(leftField='Date_Filter',
2052
+ rightField='Date_Filter')
2053
+ else:
2054
+ raise ValueError(f'The chosen `join_method`: {join_method} does not match the options of "system:time_start" or "Date_Filter".')
2055
+
2056
+ # for any matches during a join, set image as a property key called 'future_image'
2057
+ join = ee.Join.saveAll(matchesKey='future_image')
2058
+
2059
+ # apply the join on the input collection
2060
+ # joining all images newer than the current image with the current image
2061
+ joined_collection = ee.ImageCollection(join.apply(primary=image_collection,
2062
+ secondary=image_collection, condition=time_filter))
2063
+
2064
+ # defining a collection to calculate the partial S value for each match in the join
2065
+ # e.g. t4-t1, t3-t1, t2-1 if there are 4 images
2066
+ def calculate_partial_s(current_image):
2067
+ # select the target band for arithmetic
2068
+ current_val = current_image.select(target_band)
2069
+ # get the joined images from the current image properties and cast the joined images as a list
2070
+ future_image_list = ee.List(current_image.get('future_image'))
2071
+ # convert the joined list to an image collection
2072
+ future_image_collection = ee.ImageCollection(future_image_list)
2073
+
2074
+ # define a function that will calculate the difference between the joined images and the current image,
2075
+ # then calculate the partial S sign based on the value of the difference calculation
2076
+ def get_sign(future_image):
2077
+ # select the target band for arithmetic from the future image
2078
+ future_val = future_image.select(target_band)
2079
+ # calculate the difference, i.e. t2-t1
2080
+ difference = future_val.subtract(current_val)
2081
+ # determine the sign of the difference value (1 if diff > 0, 0 if 0, and -1 if diff < 0)
2082
+ # use .unmask(0) to set any masked pixels as 0 to avoid
2083
+
2084
+ sign = difference.signum().unmask(0)
2085
+
2086
+ return sign
2087
+
2088
+ # map the get_sign() function along the future image col
2089
+ # then sum the values for each pixel to get the partial S value
2090
+ return future_image_collection.map(get_sign).sum()
2091
+
2092
+ # calculate the partial s value for each image in the joined/input image collection
2093
+ partial_s_col = joined_collection.map(calculate_partial_s)
2094
+
2095
+ # convert the image collection to an image of s_statistic values per pixel
2096
+ # where the s_statistic is the sum of partial s values
2097
+ # renaming the band as 's_statistic' for later usage
2098
+ final_s_image = partial_s_col.sum().rename('s_statistic').setDefaultProjection(native_projection)
2099
+
2100
+
2101
+ ########## PART 2 - VARIANCE and Z-SCORE ##########
2102
+ # to calculate variance we need to know how many pixels were involved in the partial_s calculations per pixel
2103
+ # we do this by using count() and turn the value to a float for later arithmetic
2104
+ n = image_collection.select(target_band).count().toFloat()
2105
+
2106
+ ##### VARIANCE CALCULATION #####
2107
+ # as we are using floating point values with high precision, it is HIGHLY
2108
+ # unlikely that there will be multiple pixel values with the same value.
2109
+ # Thus, we opt to use the simplified variance calculation approach as the
2110
+ # impacts to the output value are negligible and the processing benefits are HUGE
2111
+ # variance = (n * (n - 1) * (2n + 5)) / 18
2112
+ var_s = n.multiply(n.subtract(1))\
2113
+ .multiply(n.multiply(2).add(5))\
2114
+ .divide(18).rename('variance')
2115
+
2116
+ z_score = ee.Image().expression(
2117
+ """
2118
+ (s > 0) ? (s - 1) / sqrt(var) :
2119
+ (s < 0) ? (s + 1) / sqrt(var) :
2120
+ 0
2121
+ """,
2122
+ {'s': final_s_image, 'var': var_s}
2123
+ ).rename('z_score')
2124
+
2125
+ confidence = z_score.abs().divide(ee.Number(2).sqrt()).erf().rename('confidence')
2126
+
2127
+ stat_bands = ee.Image([var_s, z_score, confidence])
2128
+
2129
+ mk_stats_image = final_s_image.addBands(stat_bands)
2130
+
2131
+ ########## PART 3 - Sen's Slope ##########
2132
+ def add_year_band(image):
2133
+ if join_method == 'Date_Filter':
2134
+ # Get the string 'YYYY-MM-DD'
2135
+ date_string = image.get('Date_Filter')
2136
+ # Parse it into an ee.Date object (handles the conversion to time math)
2137
+ date = ee.Date.parse('YYYY-MM-dd', date_string)
2138
+ else:
2139
+ # Standard way: assumes system:time_start exists
2140
+ date = image.date()
2141
+ years = date.difference(ee.Date('1970-01-01'), 'year')
2142
+ return image.addBands(ee.Image(years).float().rename('year'))
2143
+
2144
+ slope_input = image_collection.map(add_year_band).select(['year', target_band])
2145
+
2146
+ sens_slope = slope_input.reduce(ee.Reducer.sensSlope())
2147
+
2148
+ slope_band = sens_slope.select('slope')
2149
+
2150
+ # add a mask to the final image to remove pixels with less than min_observations
2151
+ # mainly an effort to mask pixels outside of the boundary of the input image collection
2152
+ min_observations = 1
2153
+ valid_mask = n.gte(min_observations)
2154
+
2155
+ final_image = mk_stats_image.addBands(slope_band).updateMask(valid_mask)
2156
+
2157
+ if geometry is not None:
2158
+ mask = ee.Image(1).clip(geometry)
2159
+ final_image = final_image.updateMask(mask)
2160
+
2161
+ return final_image.setDefaultProjection(native_projection)
2162
+
2163
+ def sens_slope_trend(self, target_band=None, join_method='system:time_start', geometry=None):
2164
+ """
2165
+ Calculates Sen's Slope (trend magnitude) for the collection.
2166
+ This is a lighter-weight alternative to the full `mann_kendall_trend` function if only
2167
+ the direction and magnitude of the trend are needed.
2168
+
2169
+ Be sure to select the correct band for the `target_band` parameter, as this will be used to calculate the trend statistics.
2170
+ You may optionally provide an ee.Geometry object for the `geometry` parameter to limit the area over which the trend statistics are calculated.
2171
+ The `geometry` parameter is optional and defaults to None, which means that the trend statistics will be calculated over the entire footprint of the image collection.
2172
+
2173
+ Args:
2174
+ target_band (str): The name of the band to analyze. Defaults to 'ndvi'.
2175
+ join_method (str): Property to use for time sorting ('system:time_start' or 'Date_Filter').
2176
+ geometry (ee.Geometry, optional): Geometry to mask the final output.
2177
+
2178
+ Returns:
2179
+ ee.Image: An image containing the 'slope' band.
2180
+ """
2181
+ image_collection = self
2182
+ if isinstance(image_collection, Sentinel1Collection):
2183
+ image_collection = image_collection.collection
2184
+ elif isinstance(image_collection, ee.ImageCollection):
2185
+ pass
2186
+ else:
2187
+ raise ValueError(f'The chosen `image_collection`: {image_collection} is not a valid Sentinel1Collection or ee.ImageCollection object.')
2188
+
2189
+ if target_band is None:
2190
+ raise ValueError('The `target_band` parameter must be specified.')
2191
+ if not isinstance(target_band, str):
2192
+ raise ValueError(f'The chosen `target_band`: {target_band} is not a valid string.')
2193
+
2194
+ if geometry is not None and not isinstance(geometry, ee.Geometry):
2195
+ raise ValueError(f'The chosen `geometry`: {geometry} is not a valid ee.Geometry object.')
2196
+
2197
+ native_projection = image_collection.first().select(target_band).projection()
2198
+
2199
+ # Add Year Band (Time X-Axis)
2200
+ def add_year_band(image):
2201
+ # Handle user-defined date strings vs system time
2202
+ if join_method == 'Date_Filter':
2203
+ date_string = image.get('Date_Filter')
2204
+ date = ee.Date.parse('YYYY-MM-dd', date_string)
2205
+ else:
2206
+ date = image.date()
2207
+
2208
+ # Convert to fractional years relative to epoch
2209
+ years = date.difference(ee.Date('1970-01-01'), 'year')
2210
+ return image.addBands(ee.Image(years).float().rename('year'))
2211
+
2212
+ # Prepare Collection: Select ONLY [Year, Target]
2213
+ # sensSlope expects Band 0 = Independent (X), Band 1 = Dependent (Y)
2214
+ slope_input = self.collection.map(add_year_band).select(['year', target_band])
2215
+
2216
+ # Run the Native Reducer
2217
+ sens_result = slope_input.reduce(ee.Reducer.sensSlope())
2218
+
2219
+ # Extract and Mask
2220
+ slope_band = sens_result.select('slope')
2221
+
2222
+ if geometry is not None:
2223
+ mask = ee.Image(1).clip(geometry)
2224
+ slope_band = slope_band.updateMask(mask)
2225
+
2226
+ return slope_band.setDefaultProjection(native_projection)
2227
+
1532
2228
 
1533
2229
  def mask_to_polygon(self, polygon):
1534
2230
  """
@@ -1541,20 +2237,15 @@ class Sentinel1Collection:
1541
2237
  Sentinel1Collection: masked Sentinel1Collection image collection
1542
2238
 
1543
2239
  """
1544
- if self._geometry_masked_collection is None:
1545
- # Convert the polygon to a mask
1546
- mask = ee.Image.constant(1).clip(polygon)
2240
+ # Convert the polygon to a mask
2241
+ mask = ee.Image.constant(1).clip(polygon)
1547
2242
 
1548
- # Update the mask of each image in the collection
1549
- masked_collection = self.collection.map(lambda img: img.updateMask(mask))
1550
-
1551
- # Update the internal collection state
1552
- self._geometry_masked_collection = Sentinel1Collection(
1553
- collection=masked_collection
1554
- )
2243
+ # Update the mask of each image in the collection
2244
+ masked_collection = self.collection.map(lambda img: img.updateMask(mask)\
2245
+ .copyProperties(img).set('system:time_start', img.get('system:time_start')))
1555
2246
 
1556
2247
  # Return the updated object
1557
- return self._geometry_masked_collection
2248
+ return Sentinel1Collection(collection=masked_collection)
1558
2249
 
1559
2250
  def mask_out_polygon(self, polygon):
1560
2251
  """
@@ -1567,23 +2258,18 @@ class Sentinel1Collection:
1567
2258
  Sentinel1Collection: masked Sentinel1Collection image collection
1568
2259
 
1569
2260
  """
1570
- if self._geometry_masked_out_collection is None:
1571
- # Convert the polygon to a mask
1572
- full_mask = ee.Image.constant(1)
2261
+ # Convert the polygon to a mask
2262
+ full_mask = ee.Image.constant(1)
1573
2263
 
1574
- # Use paint to set pixels inside polygon as 0
1575
- area = full_mask.paint(polygon, 0)
2264
+ # Use paint to set pixels inside polygon as 0
2265
+ area = full_mask.paint(polygon, 0)
1576
2266
 
1577
- # Update the mask of each image in the collection
1578
- masked_collection = self.collection.map(lambda img: img.updateMask(area))
1579
-
1580
- # Update the internal collection state
1581
- self._geometry_masked_out_collection = Sentinel1Collection(
1582
- collection=masked_collection
1583
- )
2267
+ # Update the mask of each image in the collection
2268
+ masked_collection = self.collection.map(lambda img: img.updateMask(area)\
2269
+ .copyProperties(img).set('system:time_start', img.get('system:time_start')))
1584
2270
 
1585
2271
  # Return the updated object
1586
- return self._geometry_masked_out_collection
2272
+ return Sentinel1Collection(collection=masked_collection)
1587
2273
 
1588
2274
  def image_grab(self, img_selector):
1589
2275
  """
@@ -1635,7 +2321,7 @@ class Sentinel1Collection:
1635
2321
  new_col = self.collection.filter(ee.Filter.eq("Date_Filter", img_date))
1636
2322
  return new_col.first()
1637
2323
 
1638
- def CollectionStitch(self, img_col2):
2324
+ def collectionStitch(self, img_col2):
1639
2325
  """
1640
2326
  Function to mosaic two Sentinel1Collection objects which share image dates.
1641
2327
  Mosaics are only formed for dates where both image collections have images.
@@ -1687,9 +2373,17 @@ class Sentinel1Collection:
1687
2373
 
1688
2374
  # Return a Sentinel1Collection instance
1689
2375
  return Sentinel1Collection(collection=new_col)
2376
+
2377
+ def CollectionStitch(self, img_col2):
2378
+ warnings.warn(
2379
+ "The 'CollectionStitch' method is deprecated. Please use 'collectionStitch' instead.",
2380
+ DeprecationWarning,
2381
+ stacklevel=2
2382
+ )
2383
+ return self.collectionStitch(img_col2)
1690
2384
 
1691
2385
  @property
1692
- def MosaicByDate(self):
2386
+ def mosaicByDateDepr(self):
1693
2387
  """
1694
2388
  Property attribute function to mosaic collection images that share the same date.
1695
2389
  The property CLOUD_COVER for each image is used to calculate an overall mean,
@@ -1750,6 +2444,74 @@ class Sentinel1Collection:
1750
2444
 
1751
2445
  # Convert the list of mosaics to an ImageCollection
1752
2446
  return self._MosaicByDate
2447
+
2448
+ @property
2449
+ def mosaicByDate(self):
2450
+ """
2451
+ Property attribute function to mosaic collection images that share the same date.
2452
+
2453
+ The property CLOUD_COVER for each image is used to calculate an overall mean,
2454
+ which replaces the CLOUD_COVER property for each mosaiced image.
2455
+ Server-side friendly.
2456
+
2457
+ NOTE: if images are removed from the collection from cloud filtering, you may have mosaics composed of only one image.
2458
+
2459
+ Returns:
2460
+ LandsatCollection: LandsatCollection image collection with mosaiced imagery and mean CLOUD_COVER as a property
2461
+ """
2462
+ if self._MosaicByDate is None:
2463
+ distinct_dates = self.collection.distinct("Date_Filter")
2464
+
2465
+ # Define a join to link images by Date_Filter
2466
+ filter_date = ee.Filter.equals(leftField="Date_Filter", rightField="Date_Filter")
2467
+ join = ee.Join.saveAll(matchesKey="date_matches")
2468
+
2469
+ # Apply the join
2470
+ # Primary: Distinct dates collection
2471
+ # Secondary: The full original collection
2472
+ joined_col = ee.ImageCollection(join.apply(distinct_dates, self.collection, filter_date))
2473
+
2474
+ # Define the mosaicking function
2475
+ def _mosaic_day(img):
2476
+ # Recover the list of images for this day
2477
+ daily_list = ee.List(img.get("date_matches"))
2478
+ daily_col = ee.ImageCollection.fromImages(daily_list)
2479
+
2480
+ # Create the mosaic
2481
+ mosaic = daily_col.mosaic().setDefaultProjection(img.projection())
2482
+
2483
+ # Properties to preserve from the representative image
2484
+ props_of_interest = [
2485
+ "platform_number",
2486
+ "instrument",
2487
+ "instrumentMode",
2488
+ "orbitNumber_start",
2489
+ "orbitNumber_stop",
2490
+ "orbitProperties_pass",
2491
+ "resolution_meters",
2492
+ "transmitterReceiverPolarisation",
2493
+ "system:time_start",
2494
+ "crs"
2495
+ ]
2496
+
2497
+ # Return mosaic with properties set
2498
+ return mosaic.copyProperties(img, props_of_interest)
2499
+
2500
+ # 5. Map the function and wrap the result
2501
+ mosaiced_col = joined_col.map(_mosaic_day)
2502
+ self._MosaicByDate = Sentinel1Collection(collection=mosaiced_col)
2503
+
2504
+ # Convert the list of mosaics to an ImageCollection
2505
+ return self._MosaicByDate
2506
+
2507
+ @property
2508
+ def MosaicByDate(self):
2509
+ warnings.warn(
2510
+ "The 'MosaicByDate' property is deprecated. Please use 'mosaicByDate' instead.",
2511
+ DeprecationWarning,
2512
+ stacklevel=2
2513
+ )
2514
+ return self.mosaicByDate
1753
2515
 
1754
2516
  @staticmethod
1755
2517
  def ee_to_df(