From 239b8af024a8c9de5aa8b5b1d5997089e2869f50 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Fri, 10 Nov 2023 16:34:36 +0000 Subject: [PATCH] feat: integrate the best ee_extra methods --- geetools/Image/__init__.py | 620 +++++++++------ geetools/ImageCollection/__init__.py | 385 ++++++++++ geetools/__init__.py | 1 + geetools/cloud_mask.py | 708 ------------------ test.ipynb | 33 + tests/conftest.py | 49 ++ tests/test_Image.py | 123 ++- tests/test_Image/test_get_scale_params.yml | 12 + tests/test_Image/test_get_stac.yml | 619 +++++++++++++++ tests/test_Image/test_histogram_match.yml | 3 + tests/test_Image/test_mask_S2_clouds.yml | 16 + tests/test_Image/test_pan_sharpen.yml | 6 + tests/test_Image/test_preprocess.yml | 16 + tests/test_Image/test_scale_and_offset.yml | 16 + tests/test_Image/test_tasseled_cap.yml | 25 +- tests/test_ImageCollection.py | 108 +++ .../test_closest_s2_sr.yml | 2 + .../test_get_citation.yml | 2 + tests/test_ImageCollection/test_get_doi.yml | 2 + .../test_get_offset_params.yml | 23 + .../test_get_scale_params.yml | 23 + tests/test_ImageCollection/test_get_stac.yml | 619 +++++++++++++++ tests/test_ImageCollection/test_mask_s2.yml | 27 + .../test_ImageCollection/test_mask_s2_sr.yml | 27 + .../test_ImageCollection/test_pan_sharpen.yml | 6 + .../test_ImageCollection/test_preprocess.yml | 23 + .../test_scale_and_offset.yml | 23 + .../test_spectral_indices.yml | 25 + .../test_tasseled_cap.yml | 16 + 29 files changed, 2607 insertions(+), 951 deletions(-) create mode 100644 geetools/ImageCollection/__init__.py delete mode 100644 geetools/cloud_mask.py create mode 100644 tests/test_Image/test_get_scale_params.yml create mode 100644 tests/test_Image/test_get_stac.yml create mode 100644 tests/test_Image/test_histogram_match.yml create mode 100644 tests/test_Image/test_mask_S2_clouds.yml create mode 100644 tests/test_Image/test_pan_sharpen.yml create mode 100644 tests/test_Image/test_preprocess.yml create mode 100644 tests/test_Image/test_scale_and_offset.yml create mode 100644 tests/test_ImageCollection.py create mode 100644 tests/test_ImageCollection/test_closest_s2_sr.yml create mode 100644 tests/test_ImageCollection/test_get_citation.yml create mode 100644 tests/test_ImageCollection/test_get_doi.yml create mode 100644 tests/test_ImageCollection/test_get_offset_params.yml create mode 100644 tests/test_ImageCollection/test_get_scale_params.yml create mode 100644 tests/test_ImageCollection/test_get_stac.yml create mode 100644 tests/test_ImageCollection/test_mask_s2.yml create mode 100644 tests/test_ImageCollection/test_mask_s2_sr.yml create mode 100644 tests/test_ImageCollection/test_pan_sharpen.yml create mode 100644 tests/test_ImageCollection/test_preprocess.yml create mode 100644 tests/test_ImageCollection/test_scale_and_offset.yml create mode 100644 tests/test_ImageCollection/test_spectral_indices.yml create mode 100644 tests/test_ImageCollection/test_tasseled_cap.yml diff --git a/geetools/Image/__init__.py b/geetools/Image/__init__.py index c902ecfa..0623c9bd 100644 --- a/geetools/Image/__init__.py +++ b/geetools/Image/__init__.py @@ -5,6 +5,7 @@ import ee import ee_extra +import ee_extra.Algorithms.core from geetools.accessors import geetools_accessor @@ -17,147 +18,6 @@ def __init__(self, obj: ee.Image): """Initialize the Image class.""" self._obj = obj - # -- image Indices manipulation -------------------------------------------- - def index_list(cls) -> dict: - """Return the list of indices implemented in this module. - - Returns: - List of indices implemented in this module - - Examples: - .. jupyter-execute:: - - import ee, geetools - - ind = ee.Image.geetools.indices()["BAIS2"] - print(ind["long_name"]) - print(ind["formula"]) - print(ind["reference"]) - """ - return ee_extra.Spectral.core.indices() - - def spectralIndices( - self, - index: str = "NDVI", - G: Union[float, int] = 2.5, - C1: Union[float, int] = 6.0, - C2: Union[float, int] = 7.5, - L: Union[float, int] = 1.0, - cexp: Union[float, int] = 1.16, - nexp: Union[float, int] = 2.0, - alpha: Union[float, int] = 0.1, - slope: Union[float, int] = 1.0, - intercept: Union[float, int] = 0.0, - gamma: Union[float, int] = 1.0, - omega: Union[float, int] = 2.0, - beta: Union[float, int] = 0.05, - k: Union[float, int] = 0.0, - fdelta: Union[float, int] = 0.581, - kernel: str = "RBF", - sigma: str = "0.5 * (a + b)", - p: Union[float, int] = 2.0, - c: Union[float, int] = 1.0, - lambdaN: Union[float, int] = 858.5, - lambdaR: Union[float, int] = 645.0, - lambdaG: Union[float, int] = 555.0, - online: Union[float, int] = False, - ): - """Computes one or more spectral indices (indices are added as bands) for an image from the Awesome List of Spectral Indices. - - Parameters: - self: Image to compute indices on. Must be scaled to [0,1]. - index: Index or list of indices to compute, default = 'NDVI' - Available options: - - 'vegetation' : Compute all vegetation indices. - - 'burn' : Compute all burn indices. - - 'water' : Compute all water indices. - - 'snow' : Compute all snow indices. - - 'urban' : Compute all urban (built-up) indices. - - 'kernel' : Compute all kernel indices. - - 'all' : Compute all indices listed below. - - Awesome Spectral Indices for GEE: Check the complete list of indices `here `_. - G: Gain factor. Used just for index = 'EVI', default = 2.5 - C1: Coefficient 1 for the aerosol resistance term. Used just for index = 'EVI', default = 6.0 - C2: Coefficient 2 for the aerosol resistance term. Used just for index = 'EVI', default = 7.5 - L: Canopy background adjustment. Used just for index = ['EVI','SAVI'], default = 1.0 - cexp: Exponent used for OCVI, default = 1.16 - nexp: Exponent used for GDVI, default = 2.0 - alpha: Weighting coefficient used for WDRVI, default = 0.1 - slope: Soil line slope, default = 1.0 - intercept: Soil line intercept, default = 0.0 - gamma: Weighting coefficient used for ARVI, default = 1.0 - omega: Weighting coefficient used for MBWI, default = 2.0 - beta: Calibration parameter used for NDSIns, default = 0.05 - k: Slope parameter by soil used for NIRvH2, default = 0.0 - fdelta: Adjustment factor used for SEVI, default = 0.581 - kernel: Kernel used for kernel indices, default = 'RBF' - Available options: - - 'linear' : Linear Kernel. - - 'RBF' : Radial Basis Function (RBF) Kernel. - - 'poly' : Polynomial Kernel. - sigma: Length-scale parameter. Used for kernel = 'RBF', default = '0.5 * (a + b)'. If str, this must be an expression including 'a' and 'b'. If numeric, this must be positive. - p: Kernel degree. Used for kernel = 'poly', default = 2.0 - c: Free parameter that trades off the influence of higher-order versus lower-order terms in the polynomial kernel. Used for kernel = 'poly', default = 1.0. This must be greater than or equal to 0. - lambdaN: NIR wavelength used for NIRvH2 and NDGI, default = 858.5 - lambdaR: Red wavelength used for NIRvH2 and NDGI, default = 645.0 - lambdaG: Green wavelength used for NDGI, default = 555.0 - drop: Whether to drop all bands except the new spectral indices, default = False - - Returns: - Image with the computed spectral index, or indices, as new bands. - - Examples: - .. jupyter-execute:: - - import ee, geetools - - ee.Initialize() - image = ee.Image('COPERNICUS/S2_SR/20190828T151811_20190828T151809_T18GYT') - image = image.specralIndices(["NDVI", "NDFI"]) - """ - # fmt: off - return ee_extra.Spectral.core.spectralIndices( - self._obj, index, G, C1, C2, L, cexp, nexp, alpha, slope, intercept, gamma, omega, - beta, k, fdelta, kernel, sigma, p, c, lambdaN, lambdaR, lambdaG, online, - drop=False, - ) - # fmt: on - - def tasseledCap(self): - """Calculates tasseled cap brightness, wetness, and greenness components. - - Tasseled cap transformations are applied using coefficients published for these - supported platforms: - - * Sentinel-2 MSI Level 1C - * Landsat 9 OLI-2 SR - * Landsat 9 OLI-2 TOA - * Landsat 8 OLI SR - * Landsat 8 OLI TOA - * Landsat 7 ETM+ TOA - * Landsat 5 TM Raw DN - * Landsat 4 TM Raw DN - * Landsat 4 TM Surface Reflectance - * MODIS NBAR - - Parameters: - self: ee.Image to calculate tasseled cap components for. Must belong to a supported platform. - - Returns: - Image with the tasseled cap components as new bands. - - Examples: - .. jupyter-execute:: - - import ee, geetools - - ee.Initialize() - - image = ee.Image('COPERNICUS/S2_SR/20190828T151811_20190828T151809_T18GYT') - img = img.tasseledCap() - """ - return ee_extra.Spectral.core.tasseledCap(self._obj) - # -- band manipulation ----------------------------------------------------- def addDate(self) -> ee.Image: """Add a band with the date of the image in the provided format. @@ -824,84 +684,6 @@ def addBand(n, i): return ee.Image(sequence.iterate(addBand, self._obj)) - def histogramMatch(self, target): - """Match the histogram of the image to the target image. - - The target images must use the same band names as the source one. - See the following article for more details: https://medium.com/google-earth/histogram-matching-c7153c85066d - - Args: - target: The target image to match the histogram to - - Returns: - The image with the histogram matched to the target image - - Examples: - .. jupyter-execute:: - - import ee, geetools - - ee.Initialize() - - vatican = ee.Geometry.Point([12.4534, 41.9033]).buffer(1) - image = ( - ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") - .filterBounds(vatican) - .filterDate("2023-06-01", "2023-06-30") - .first() - .select("B4", "B3", "B2") - .rename("R", "G", "B") - ) - target = ( - ee.ImageCollection("LANDSAT/LC08/C02/T1_L2") - .filterBounds(vatican) - .filterDate("2023-06-01", "2023-06-30") - .first() - .select("SR_B4", "SR_B3", "SR_B2") - .rename("R", "G", "B") - ) - image = image.geetools.histogramMatch(target) - print(image.bandNames().getInfo()) - """ - bands = self._obj.bandNames() - - # get the histogram of the source and target images - kwargs = { - "reducer": ee.Reducer.autoHistogram(maxBuckets=256, cumulative=True), - "geometry": self._obj.geometry(), - "bestEffort": True, - } - sourceHistogram = self._obj.reduceRegion(**kwargs) - targetHistogram = target.updateMask(self._obj.mask()).reduceRegion(**kwargs) - - # Create a lookup table to make sourceHist match targetHist. - def lookup(sourceHist, targetHist): - # Split the histograms by column and normalize the counts. - sourceValues = sourceHist.slice(1, 0, 1).project([0]) - sourceCounts = sourceHist.slice(1, 1, 2).project([0]) - sourceCounts = sourceCounts.divide(sourceCounts.get([-1])) - - targetValues = targetHist.slice(1, 0, 1).project([0]) - targetCounts = targetHist.slice(1, 1, 2).project([0]) - targetCounts = targetCounts.divide(targetCounts.get([-1])) - - # Find first position in target where targetCount >= srcCount[i], for each i. - lookup = sourceCounts.toList().map( - lambda n: targetValues.get(targetCounts.gte(n).argmax()) - ) - - return {"x": sourceValues.toList(), "y": lookup} - - matchedList = bands.map( - lambda b: ( - self._obj.select(ee.String(b)).interpolate( - **lookup(sourceHistogram.getArray(b), targetHistogram.getArray(b)) - ) - ) - ) - - return ee.ImageCollection(matchedList).toBands().rename(bands) - def removeZeros(self) -> ee.Image: """Return an image array with non-zero values extracted from each band. @@ -1008,3 +790,403 @@ def isletMask(self, offset: Union[ee.Number, float, int]) -> ee.Image: .multiply(area) ) return isletArea.lt(offset).rename("mask").selfMask() + + # -- ee-extra wrapper ------------------------------------------------------ + def index_list(cls) -> dict: + """Return the list of indices implemented in this module. + + Returns: + List of indices implemented in this module + + Examples: + .. jupyter-execute:: + + import ee, geetools + + ind = ee.Image.geetools.indices()["BAIS2"] + print(ind["long_name"]) + print(ind["formula"]) + print(ind["reference"]) + """ + return ee_extra.Spectral.core.indices() + + def spectralIndices( + self, + index: str = "NDVI", + G: Union[float, int] = 2.5, + C1: Union[float, int] = 6.0, + C2: Union[float, int] = 7.5, + L: Union[float, int] = 1.0, + cexp: Union[float, int] = 1.16, + nexp: Union[float, int] = 2.0, + alpha: Union[float, int] = 0.1, + slope: Union[float, int] = 1.0, + intercept: Union[float, int] = 0.0, + gamma: Union[float, int] = 1.0, + omega: Union[float, int] = 2.0, + beta: Union[float, int] = 0.05, + k: Union[float, int] = 0.0, + fdelta: Union[float, int] = 0.581, + kernel: str = "RBF", + sigma: str = "0.5 * (a + b)", + p: Union[float, int] = 2.0, + c: Union[float, int] = 1.0, + lambdaN: Union[float, int] = 858.5, + lambdaR: Union[float, int] = 645.0, + lambdaG: Union[float, int] = 555.0, + online: Union[float, int] = False, + ) -> ee.Image: + """Computes one or more spectral indices (indices are added as bands) for an image from the Awesome List of Spectral Indices. + + Parameters: + self: Image to compute indices on. Must be scaled to [0,1]. + index: Index or list of indices to compute, default = 'NDVI' + Available options: + - 'vegetation' : Compute all vegetation indices. + - 'burn' : Compute all burn indices. + - 'water' : Compute all water indices. + - 'snow' : Compute all snow indices. + - 'urban' : Compute all urban (built-up) indices. + - 'kernel' : Compute all kernel indices. + - 'all' : Compute all indices listed below. + - Awesome Spectral Indices for GEE: Check the complete list of indices `here `_. + G: Gain factor. Used just for index = 'EVI', default = 2.5 + C1: Coefficient 1 for the aerosol resistance term. Used just for index = 'EVI', default = 6.0 + C2: Coefficient 2 for the aerosol resistance term. Used just for index = 'EVI', default = 7.5 + L: Canopy background adjustment. Used just for index = ['EVI','SAVI'], default = 1.0 + cexp: Exponent used for OCVI, default = 1.16 + nexp: Exponent used for GDVI, default = 2.0 + alpha: Weighting coefficient used for WDRVI, default = 0.1 + slope: Soil line slope, default = 1.0 + intercept: Soil line intercept, default = 0.0 + gamma: Weighting coefficient used for ARVI, default = 1.0 + omega: Weighting coefficient used for MBWI, default = 2.0 + beta: Calibration parameter used for NDSIns, default = 0.05 + k: Slope parameter by soil used for NIRvH2, default = 0.0 + fdelta: Adjustment factor used for SEVI, default = 0.581 + kernel: Kernel used for kernel indices, default = 'RBF' + Available options: + - 'linear' : Linear Kernel. + - 'RBF' : Radial Basis Function (RBF) Kernel. + - 'poly' : Polynomial Kernel. + sigma: Length-scale parameter. Used for kernel = 'RBF', default = '0.5 * (a + b)'. If str, this must be an expression including 'a' and 'b'. If numeric, this must be positive. + p: Kernel degree. Used for kernel = 'poly', default = 2.0 + c: Free parameter that trades off the influence of higher-order versus lower-order terms in the polynomial kernel. Used for kernel = 'poly', default = 1.0. This must be greater than or equal to 0. + lambdaN: NIR wavelength used for NIRvH2 and NDGI, default = 858.5 + lambdaR: Red wavelength used for NIRvH2 and NDGI, default = 645.0 + lambdaG: Green wavelength used for NDGI, default = 555.0 + drop: Whether to drop all bands except the new spectral indices, default = False + + Returns: + Image with the computed spectral index, or indices, as new bands. + + Examples: + .. jupyter-execute:: + + import ee, geetools + + ee.Initialize() + image = ee.Image('COPERNICUS/S2_SR/20190828T151811_20190828T151809_T18GYT') + image = image.specralIndices(["NDVI", "NDFI"]) + """ + # fmt: off + return ee_extra.Spectral.core.spectralIndices( + self._obj, index, G, C1, C2, L, cexp, nexp, alpha, slope, intercept, gamma, omega, + beta, k, fdelta, kernel, sigma, p, c, lambdaN, lambdaR, lambdaG, online, + drop=False, + ) + # fmt: on + + def getScaleParams(self) -> dict: + """Gets the scale parameters for each band of the image. + + Returns: + Dictionary with the scale parameters for each band. + + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('MODIS/006/MOD11A2').first().geetools.getScaleParams() + """ + return ee_extra.STAC.core.getScaleParams(self._obj) + + def getOffsetParams(self) -> dict: + """Gets the offset parameters for each band of the image. + + Returns: + Dictionary with the offset parameters for each band. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('MODIS/006/MOD11A2').first().getOffsetParams() + """ + return ee_extra.STAC.core.getOffsetParams(self._obj) + + def scaleAndOffset(self) -> ee.Image: + """Scales bands on an image according to their scale and offset parameters. + + Returns: + Scaled image. + + Examples: + .. jupyter_execute:: + + import ee, geetools + + ee.Initialize() + + S2 = ee.ImageCollection('COPERNICUS/S2_SR').first().scaleAndOffset() + """ + return ee_extra.STAC.core.scaleAndOffset(self._obj) + + def preprocess(self, **kwargs) -> ee.Image: + """Pre-processes the image: masks clouds and shadows, and scales and offsets the image. + + Parameters: + **kwargs: Keywords arguments for ``maskClouds`` method. + + Returns: + Pre-processed image. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + S2 = ee.ImageCollection('COPERNICUS/S2_SR').first().preprocess() + """ + return ee_extra.QA.pipelines.preprocess(self._obj, **kwargs) + + def getSTAC(self) -> dict: + """Gets the STAC of the image. + + Returns: + STAC of the image. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('COPERNICUS/S2_SR').first().getSTAC() + """ + return ee_extra.STAC.core.getSTAC(self._obj) + + def getDOI(self) -> str: + """Gets the DOI of the image, if available. + + Returns: + DOI of the ee.Image dataset. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('NASA/GPM_L3/IMERG_V06').first().getDOI() + """ + return ee_extra.STAC.core.getDOI(self._obj) + + def getCitation(self) -> str: + """Gets the citation of the image, if available. + + Returns: + Citation of the ee.Image dataset. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('NASA/GPM_L3/IMERG_V06').first().getCitation() + """ + return ee_extra.STAC.core.getCitation(self._obj) + + def panSharpen(self, method: str = "SFIM", qa: str = "", **kwargs) -> ee.Image: + """Apply panchromatic sharpening to the Image. + + Optionally, run quality assessments between the original and sharpened Image to + measure spectral distortion and set results as properties of the sharpened Image. + + Parameters: + method: The sharpening algorithm to apply. Current options are "SFIM" (Smoothing Filter-based Intensity Modulation), "HPFA" (High Pass Filter Addition), "PCS" (Principal Component Substitution), and "SM" (simple mean). Different sharpening methods will produce different quality sharpening results in different scenarios. + qa: One or more optional quality assessment names to apply after sharpening. Results will be stored as image properties with the pattern `geetools:metric`, e.g. `geetools:RMSE`. + **kwargs: Keyword arguments passed to ee.Image.reduceRegion() such as "geometry", "maxPixels", "bestEffort", etc. These arguments are only used for PCS sharpening and quality assessments. + + Returns: + The Image with all sharpenable bands sharpened to the panchromatic resolution and quality assessments run and set as properties. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + source = ee.Image("LANDSAT/LC08/C01/T1_TOA/LC08_047027_20160819") + sharp = source.panSharpen(method="HPFA", qa=["MSE", "RMSE"], maxPixels=1e13) + """ + return ee_extra.Algorithms.core.panSharpen( + img=self._obj, method=method, qa=qa, prefix="geetools", **kwargs + ) + + def tasseledCap(self) -> ee.Image: + """Calculates tasseled cap brightness, wetness, and greenness components. + + Tasseled cap transformations are applied using coefficients published for these + supported platforms: + + * Sentinel-2 MSI Level 1C + * Landsat 9 OLI-2 SR + * Landsat 9 OLI-2 TOA + * Landsat 8 OLI SR + * Landsat 8 OLI TOA + * Landsat 7 ETM+ TOA + * Landsat 5 TM Raw DN + * Landsat 4 TM Raw DN + * Landsat 4 TM Surface Reflectance + * MODIS NBAR + + Parameters: + self: ee.Image to calculate tasseled cap components for. Must belong to a supported platform. + + Returns: + Image with the tasseled cap components as new bands. + + Examples: + .. jupyter-execute:: + + import ee, geetools + + ee.Initialize() + + image = ee.Image('COPERNICUS/S2_SR/20190828T151811_20190828T151809_T18GYT') + img = img.tasseledCap() + """ + return ee_extra.Spectral.core.tasseledCap(self._obj) + + def matchHistogram( + self, + target: ee.Image, + bands: dict, + geometry: Optional[ee.Geometry] = None, + maxBuckets: int = 256, + ) -> ee.Image: + """Adjust the image's histogram to match a target image. + + Parameters: + target: Image to match. + bands: A dictionary of band names to match, with source bands as keys and target bands as values. + geometry: The region to match histograms in that overlaps both images. If none is provided, the geometry of the source image will be used. + maxBuckets: The maximum number of buckets to use when building histograms. Will be rounded to the nearest power of 2. + + Returns: + The adjusted image containing the matched source bands. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + source = ee.Image("LANDSAT/LC08/C01/T1_TOA/LC08_047027_20160819") + target = ee.Image("LANDSAT/LE07/C01/T1_TOA/LE07_046027_20150701") + bands = { + "B4": "B3", + "B3": "B2", + "B2": "B1" + } + matched = source.matchHistogram(target, bands) + """ + return ee_extra.Spectral.core.matchHistogram( + source=self._obj, + target=target, + bands=bands, + geometry=geometry, + maxBuckets=maxBuckets, + ) + + def maskClouds( + self, + method: str = "cloud_prob", + prob: int = 60, + maskCirrus: bool = True, + maskShadows: bool = True, + scaledImage: bool = False, + dark: float = 0.15, + cloudDist: int = 1000, + buffer: int = 250, + cdi: Optional[int] = None, + ): + """Masks clouds and shadows in an image (valid just for Surface Reflectance products). + + Parameters: + self: Image to mask. + method: Method used to mask clouds. This parameter is ignored for Landsat products. + Available options: + - 'cloud_prob' : Use cloud probability. + - 'qa' : Use Quality Assessment band. + prob: Cloud probability threshold. Valid just for method = 'cloud_prob'. This parameter is ignored for Landsat products. + maskCirrus: Whether to mask cirrus clouds. Default to ``True``. Valid just for method = 'qa'. This parameter is ignored for Landsat products. + maskShadows: Whether to mask cloud shadows. Default to ``True`` This parameter is ignored for Landsat products. + scaledImage: Whether the pixel values are scaled to the range [0,1] (reflectance values). This parameter is ignored for Landsat products. + dark: NIR threshold. NIR values below this threshold are potential cloud shadows. This parameter is ignored for Landsat products. + cloudDist: Maximum distance in meters (m) to look for cloud shadows from cloud edges. This parameter is ignored for Landsat products. + buffer: Distance in meters (m) to dilate cloud and cloud shadows objects. This parameter is ignored for Landsat products. + cdi: Cloud Displacement Index threshold. Values below this threshold are considered potential clouds. A cdi = None means that the index is not used. This parameter is ignored for Landsat products. + + Returns: + Cloud-shadow masked image. + + Notes: + This method may mask water as well as clouds for the Sentinel-3 Radiance product. + + Examples: + .. jupyter-execute:: + + import ee, geetools + + ee.Initialize() + S2 = ( + ee.ImageCollection('COPERNICUS/S2_SR') + .first() + .maskClouds(prob = 75,buffer = 300,cdi = -0.5)) + """ + return ee_extra.QA.clouds.maskClouds( + self._obj, + method, + prob, + maskCirrus, + maskShadows, + scaledImage, + dark, + cloudDist, + buffer, + cdi, + ) diff --git a/geetools/ImageCollection/__init__.py b/geetools/ImageCollection/__init__.py new file mode 100644 index 00000000..22eef906 --- /dev/null +++ b/geetools/ImageCollection/__init__.py @@ -0,0 +1,385 @@ +"""Toolbox for the ``ee.ImageCollection`` class.""" +from __future__ import annotations + +from typing import Optional, Union + +import ee +import ee_extra + +from geetools.accessors import geetools_accessor + + +@geetools_accessor(ee.ImageCollection) +class ImageCollection: + """Toolbox for the ``ee.ImageCollection`` class.""" + + def __init__(self, obj: ee.ImageCollection): + """Instantiate the class.""" + self._obj = obj + + # -- ee-extra wrapper ------------------------------------------------------ + def maskClouds( + self, + method: str = "cloud_prob", + prob: int = 60, + maskCirrus: bool = True, + maskShadows: bool = True, + scaledImage: bool = False, + dark: float = 0.15, + cloudDist: int = 1000, + buffer: int = 250, + cdi: Optional[int] = None, + ) -> ee.ImageCollection: + """Masks clouds and shadows in each image of an ImageCollection (valid just for Surface Reflectance products). + + Parameters: + self: ImageCollection to mask. + method: Method used to mask clouds. This parameter is ignored for Landsat products. + Available options: + - 'cloud_prob' : Use cloud probability. + - 'qa' : Use Quality Assessment band. + prob: Cloud probability threshold. Valid just for method = 'cloud_prob'. This parameter is ignored for Landsat products. + maskCirrus: Whether to mask cirrus clouds. Default to ``True``. Valid just for method = 'qa'. This parameter is ignored for Landsat products. + maskShadows: Whether to mask cloud shadows. Default to ``True`` This parameter is ignored for Landsat products. + scaledImage: Whether the pixel values are scaled to the range [0,1] (reflectance values). This parameter is ignored for Landsat products. + dark: NIR threshold. NIR values below this threshold are potential cloud shadows. This parameter is ignored for Landsat products. + cloudDist: Maximum distance in meters (m) to look for cloud shadows from cloud edges. This parameter is ignored for Landsat products. + buffer: Distance in meters (m) to dilate cloud and cloud shadows objects. This parameter is ignored for Landsat products. + cdi: Cloud Displacement Index threshold. Values below this threshold are considered potential clouds. A cdi = None means that the index is not used. This parameter is ignored for Landsat products. + + Returns: + Cloud-shadow masked image. + + Notes: + This method may mask water as well as clouds for the Sentinel-3 Radiance product. + + Examples: + .. jupyter-execute:: + + import ee, geetools + + ee.Initialize() + S2 = ( + ee.ImageCollection('COPERNICUS/S2_SR') + .maskClouds(prob = 75,buffer = 300,cdi = -0.5) + .first() + ) + + """ + return ee_extra.QA.clouds.maskClouds( + self._obj, + method, + prob, + maskCirrus, + maskShadows, + scaledImage, + dark, + cloudDist, + buffer, + cdi, + ) + + def closest( + self, date: Union[ee.Date, str], tolerance: int = 1, unit: str = "month" + ) -> ee.ImageCollection: + """Gets the closest image (or set of images if the collection intersects a region that requires multiple scenes) to the specified date. + + Parameters: + date: Date of interest. The method will look for images closest to this date. + tolerance: Filter the collection to [date - tolerance, date + tolerance) before searching the closest image. This speeds up the searching process for collections with a high temporal resolution. + unit: Units for tolerance. Available units: 'year', 'month', 'week', 'day', 'hour', 'minute' or 'second'. + + Returns: + Closest images to the specified date. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + s2 = ee.ImageCollection('COPERNICUS/S2_SR').closest('2020-10-15') + s2.size().getInfo() + """ + return ee_extra.ImageCollection.core.closest(self._obj, date, tolerance, unit) + + def spectralIndices( + self, + index: str = "NDVI", + G: Union[float, int] = 2.5, + C1: Union[float, int] = 6.0, + C2: Union[float, int] = 7.5, + L: Union[float, int] = 1.0, + cexp: Union[float, int] = 1.16, + nexp: Union[float, int] = 2.0, + alpha: Union[float, int] = 0.1, + slope: Union[float, int] = 1.0, + intercept: Union[float, int] = 0.0, + gamma: Union[float, int] = 1.0, + omega: Union[float, int] = 2.0, + beta: Union[float, int] = 0.05, + k: Union[float, int] = 0.0, + fdelta: Union[float, int] = 0.581, + kernel: str = "RBF", + sigma: str = "0.5 * (a + b)", + p: Union[float, int] = 2.0, + c: Union[float, int] = 1.0, + lambdaN: Union[float, int] = 858.5, + lambdaR: Union[float, int] = 645.0, + lambdaG: Union[float, int] = 555.0, + online: Union[float, int] = False, + ) -> ee.ImageCollection: + """Computes one or more spectral indices (indices are added as bands) for an image from the Awesome List of Spectral Indices. + + Parameters: + self: Image to compute indices on. Must be scaled to [0,1]. + index: Index or list of indices to compute, default = 'NDVI' + Available options: + - 'vegetation' : Compute all vegetation indices. + - 'burn' : Compute all burn indices. + - 'water' : Compute all water indices. + - 'snow' : Compute all snow indices. + - 'urban' : Compute all urban (built-up) indices. + - 'kernel' : Compute all kernel indices. + - 'all' : Compute all indices listed below. + - Awesome Spectral Indices for GEE: Check the complete list of indices `here `_. + G: Gain factor. Used just for index = 'EVI', default = 2.5 + C1: Coefficient 1 for the aerosol resistance term. Used just for index = 'EVI', default = 6.0 + C2: Coefficient 2 for the aerosol resistance term. Used just for index = 'EVI', default = 7.5 + L: Canopy background adjustment. Used just for index = ['EVI','SAVI'], default = 1.0 + cexp: Exponent used for OCVI, default = 1.16 + nexp: Exponent used for GDVI, default = 2.0 + alpha: Weighting coefficient used for WDRVI, default = 0.1 + slope: Soil line slope, default = 1.0 + intercept: Soil line intercept, default = 0.0 + gamma: Weighting coefficient used for ARVI, default = 1.0 + omega: Weighting coefficient used for MBWI, default = 2.0 + beta: Calibration parameter used for NDSIns, default = 0.05 + k: Slope parameter by soil used for NIRvH2, default = 0.0 + fdelta: Adjustment factor used for SEVI, default = 0.581 + kernel: Kernel used for kernel indices, default = 'RBF' + Available options: + - 'linear' : Linear Kernel. + - 'RBF' : Radial Basis Function (RBF) Kernel. + - 'poly' : Polynomial Kernel. + sigma: Length-scale parameter. Used for kernel = 'RBF', default = '0.5 * (a + b)'. If str, this must be an expression including 'a' and 'b'. If numeric, this must be positive. + p: Kernel degree. Used for kernel = 'poly', default = 2.0 + c: Free parameter that trades off the influence of higher-order versus lower-order terms in the polynomial kernel. Used for kernel = 'poly', default = 1.0. This must be greater than or equal to 0. + lambdaN: NIR wavelength used for NIRvH2 and NDGI, default = 858.5 + lambdaR: Red wavelength used for NIRvH2 and NDGI, default = 645.0 + lambdaG: Green wavelength used for NDGI, default = 555.0 + drop: Whether to drop all bands except the new spectral indices, default = False + + Returns: + Image with the computed spectral index, or indices, as new bands. + + Examples: + .. jupyter-execute:: + + import ee, geetools + + ee.Initialize() + image = ee.Image('COPERNICUS/S2_SR/20190828T151811_20190828T151809_T18GYT') + image = image.geetools.specralIndices(["NDVI", "NDFI"]) + """ + # fmt: off + return ee_extra.Spectral.core.spectralIndices( + self._obj, index, G, C1, C2, L, cexp, nexp, alpha, slope, intercept, gamma, omega, + beta, k, fdelta, kernel, sigma, p, c, lambdaN, lambdaR, lambdaG, online, + drop=False, + ) + # fmt: on + + def getScaleParams(self) -> dict: + """Gets the scale parameters for each band of the image. + + Returns: + Dictionary with the scale parameters for each band. + + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('MODIS/006/MOD11A2').geetools.getScaleParams() + """ + return ee_extra.STAC.core.getScaleParams(self._obj) + + def getOffsetParams(self) -> dict: + """Gets the offset parameters for each band of the image. + + Returns: + Dictionary with the offset parameters for each band. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('MODIS/006/MOD11A2').getOffsetParams() + """ + return ee_extra.STAC.core.getOffsetParams(self._obj) + + def scaleAndOffset(self) -> ee.ImageCollection: + """Scales bands on an image according to their scale and offset parameters. + + Returns: + Scaled image. + + Examples: + .. jupyter_execute:: + + import ee, geetools + + ee.Initialize() + + S2 = ee.ImageCollection('COPERNICUS/S2_SR').scaleAndOffset() + """ + return ee_extra.STAC.core.scaleAndOffset(self._obj) + + def preprocess(self, **kwargs) -> ee.ImageCollection: + """Pre-processes the image: masks clouds and shadows, and scales and offsets the image. + + Parameters: + **kwargs: Keywords arguments for ``maskClouds`` method. + + Returns: + Pre-processed image. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + S2 = ee.ImageCollection('COPERNICUS/S2_SR').preprocess() + """ + return ee_extra.QA.pipelines.preprocess(self._obj, **kwargs) + + def getSTAC(self) -> dict: + """Gets the STAC of the image. + + Returns: + STAC of the image. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('COPERNICUS/S2_SR').getSTAC() + """ + return ee_extra.STAC.core.getSTAC(self._obj) + + def getDOI(self) -> str: + """Gets the DOI of the image, if available. + + Returns: + DOI of the ee.Image dataset. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('NASA/GPM_L3/IMERG_V06').getDOI() + """ + return ee_extra.STAC.core.getDOI(self._obj) + + def getCitation(self) -> str: + """Gets the citation of the image, if available. + + Returns: + Citation of the ee.Image dataset. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + ee.ImageCollection('NASA/GPM_L3/IMERG_V06').getCitation() + """ + return ee_extra.STAC.core.getCitation(self._obj) + + def panSharpen( + self, method: str = "SFIM", qa: str = "", **kwargs + ) -> ee.ImageCollection: + """Apply panchromatic sharpening to the ImageCollection images. + + Optionally, run quality assessments between the original and sharpened Image to + measure spectral distortion and set results as properties of the sharpened Image. + + Parameters: + method: The sharpening algorithm to apply. Current options are "SFIM" (Smoothing Filter-based Intensity Modulation), "HPFA" (High Pass Filter Addition), "PCS" (Principal Component Substitution), and "SM" (simple mean). Different sharpening methods will produce different quality sharpening results in different scenarios. + qa: One or more optional quality assessment names to apply after sharpening. Results will be stored as image properties with the pattern `geetools:metric`, e.g. `geetools:RMSE`. + **kwargs: Keyword arguments passed to ee.Image.reduceRegion() such as "geometry", "maxPixels", "bestEffort", etc. These arguments are only used for PCS sharpening and quality assessments. + + Returns: + The ImageCollections with all sharpenable bands sharpened to the panchromatic resolution and quality assessments run and set as properties. + + Examples: + .. jupyter-execute:: + + import ee + import geetools + + ee.Initialize() + + source = ee.Image("LANDSAT/LC08/C01/T1_TOA/LC08_047027_20160819") + sharp = source.panSharpen(method="HPFA", qa=["MSE", "RMSE"], maxPixels=1e13) + """ + return ee_extra.Algorithms.core.panSharpen( + img=self._obj, method=method, qa=qa or None, prefix="geetools", **kwargs + ) + + def tasseledCap(self) -> ee.ImageCollection: + """Calculates tasseled cap brightness, wetness, and greenness components. + + Tasseled cap transformations are applied using coefficients published for these + supported platforms: + + * Sentinel-2 MSI Level 1C + * Landsat 9 OLI-2 SR + * Landsat 9 OLI-2 TOA + * Landsat 8 OLI SR + * Landsat 8 OLI TOA + * Landsat 7 ETM+ TOA + * Landsat 5 TM Raw DN + * Landsat 4 TM Raw DN + * Landsat 4 TM Surface Reflectance + * MODIS NBAR + + Parameters: + self: ee.ImageCollection to calculate tasseled cap components for. Must belong to a supported platform. + + Returns: + ImageCollections with the tasseled cap components as new bands. + + Examples: + .. jupyter-execute:: + + import ee, geetools + + ee.Initialize() + + image = ee.Image('COPERNICUS/S2_SR') + img = img.tasseledCap() + """ + return ee_extra.Spectral.core.tasseledCap(self._obj) diff --git a/geetools/__init__.py b/geetools/__init__.py index e3f5bb0f..e50e5ac8 100644 --- a/geetools/__init__.py +++ b/geetools/__init__.py @@ -42,6 +42,7 @@ from .Number import Number from .String import String from .User import User +from .ImageCollection import ImageCollection __title__ = "geetools" diff --git a/geetools/cloud_mask.py b/geetools/cloud_mask.py deleted file mode 100644 index f6eb037b..00000000 --- a/geetools/cloud_mask.py +++ /dev/null @@ -1,708 +0,0 @@ -# !/usr/bin/env python -# coding=utf-8 - -from __future__ import print_function - -import ee - -from . import __version__, decision_tree, tools -from .bitreader import BitReader - -# options for BitReaders for known collections - -# 16 bits -BITS_MODIS09GA = { - "0-1": {0: "clear", 1: "cloud", 2: "mix"}, - "2": {1: "shadow"}, - "8-9": {1: "small_cirrus", 2: "average_cirrus", 3: "high_cirrus"}, - "13": {1: "adjacent"}, - "15": {1: "snow"}, -} - -# 16 bits -BITS_MODIS13Q1 = { - "0-1": {0: "good_qa"}, - "2-5": {0: "highest_qa"}, - "8": {1: "adjacent"}, - "10": {1: "cloud"}, - "14": {1: "snow"}, - "15": {1: "shadow"}, -} - -# USGS SURFACE REFLECTANCE -# 8 bits -BITS_LANDSAT_CLOUD_QA = { - "0": {1: "ddv"}, - "1": {1: "cloud"}, - "2": {1: "shadow"}, - "3": {1: "adjacent"}, - "4": {1: "snow"}, - "5": {1: "water"}, -} - -# USGS SURFACE REFLECTANCE -# 16 bits -BITS_LANDSAT_PIXEL_QA = { - "1": {1: "clear"}, - "2": {1: "water"}, - "3": {1: "shadow"}, - "4": {1: "snow"}, - "5": {1: "cloud"}, - "6-7": {3: "high_confidence_cloud"}, -} - -# USGS SURFACE REFLECTANCE L8 -BITS_LANDSAT_PIXEL_QA_L8 = { - "1": {1: "clear"}, - "2": {1: "water"}, - "3": {1: "shadow"}, - "4": {1: "snow"}, - "5": {1: "cloud"}, - "6-7": {3: "high_confidence_cloud"}, - "8-9": {3: "cirrus"}, - "10": {1: "occlusion"}, -} - -# USGS TOA -BITS_LANDSAT_BQA = { - "4": {1: "cloud"}, - "5-6": {3: "high_confidence_cloud"}, - "7-8": {3: "shadow"}, - "9-10": {3: "snow"}, -} - -# USGS TOA L8 -BITS_LANDSAT_BQA_L8 = { - "4": {1: "cloud"}, - "5-6": {3: "high_confidence_cloud"}, - "7-8": {3: "shadow"}, - "9-10": {3: "snow"}, - "11-12": {3: "cirrus"}, -} - -# SENTINEL 2 -BITS_SENTINEL2 = {"10": {1: "cloud"}, "11": {1: "cirrus"}} - - -def decodeBitsEE(bit_reader, qa_band): - """ - :param bit_reader: the bit reader - :type bit_reader: BitReader - :param qa_band: name of the band that holds the bit information - :type qa_band: str - :return: a function to map over a collection. The function adds all - categories masks as new bands - """ - options = ee.Dictionary(bit_reader.info) - categories = ee.List(bit_reader.all_categories) - - def wrap(image): - def eachcat(cat, ini): - ini = ee.Image(ini) - qa = ini.select(qa_band) - # get data for category - data = ee.Dictionary(options.get(cat)) - lshift = ee.Number(data.get("lshift")) - length = ee.Number(data.get("bit_length")) - decoded = ee.Number(data.get("shifted")) - # move = places to move bits right and left back - move = lshift.add(length) - # move bits right and left - rest = qa.rightShift(move).leftShift(move) - # subtract the rest - norest = qa.subtract(rest) - # right shift to compare with decoded data - to_compare = norest.rightShift(lshift) ## Image - # compare if is equal, return 0 if not equal, 1 if equal - mask = to_compare.eq(decoded) - # rename to the name of the category - qa_mask = mask.select([0], [cat]) - - return ini.addBands(qa_mask) - - return ee.Image(categories.iterate(eachcat, image)) - - return wrap - - -def generalMask( - options, - reader, - qa_band, - update_mask=True, - add_mask_band=True, - add_every_mask=False, - all_masks_name="mask", -): - """General function to get a bit mask band given a set of options - a bit reader and the name of the qa_band. - - :param options: options to decode - :param reader: the bit reader - :param qa_band: the name of the qa band - :param updateMask: whether to update the mask for all options or not - :param addBands: whether to add the mask band for all options or not - :return: a function to map over a collection - """ - encoder = decodeBitsEE(reader, qa_band) - opt = ee.List(options) - clases = ("'{}', " * len(options))[:-2].format(*options) - - # Property when adding every band - msg_eb = ( - "Band called 'mask' is for {} and was computed by geetools" - " version {} (https://github.com/gee-community/gee_tools)" - ) - prop_eb = ee.String(msg_eb.format(clases, __version__)) - prop_name_eb = ee.String("{}_band".format(all_masks_name)) - - def add_every_bandF(image, encoded): - return image.addBands(encoded).set(prop_name_eb, prop_eb) - - def get_all_mask(encoded): - # TODO: put this function in tools - initial = encoded.select([ee.String(opt.get(0))]) - rest = ee.List(opt.slice(1)) - - def func(cat, ini): - ini = ee.Image(ini) - new = encoded.select([cat]) - return ee.Image(ini.Or(new)) - - all_masks = ee.Image(rest.iterate(func, initial)).select([0], [all_masks_name]) - mask = all_masks.Not() - return mask - - # 0 0 1 - if not add_every_mask and not update_mask and add_mask_band: - - def wrap(image): - encoded = encoder(image).select(opt) - mask = get_all_mask(encoded) - return image.addBands(mask) - - # 0 1 0 - elif not add_every_mask and update_mask and not add_mask_band: - - def wrap(image): - encoded = encoder(image).select(opt) - mask = get_all_mask(encoded) - return image.updateMask(mask) - - # 0 1 1 - elif not add_every_mask and update_mask and add_mask_band: - - def wrap(image): - encoded = encoder(image).select(opt) - mask = get_all_mask(encoded) - return image.updateMask(mask).addBands(mask) - - # 1 0 0 - elif add_every_mask and not update_mask and not add_mask_band: - - def wrap(image): - encoded = encoder(image).select(opt) - return add_every_bandF(image, encoded) - - # 1 0 1 - elif add_every_mask and not update_mask and add_mask_band: - - def wrap(image): - encoded = encoder(image).select(opt) - mask = get_all_mask(encoded) - return add_every_bandF(image, encoded).addBands(mask) - - # 1 1 0 - elif add_every_mask and update_mask and not add_mask_band: - - def wrap(image): - encoded = encoder(image).select(opt) - mask = get_all_mask(encoded) - updated = image.updateMask(mask) - with_bands = add_every_bandF(updated, encoded) - return with_bands - - # 1 1 1 - elif add_every_mask and update_mask and add_mask_band: - - def wrap(image): - encoded = encoder(image).select(opt) - mask = get_all_mask(encoded) - updated = image.updateMask(mask) - with_bands = add_every_bandF(updated, encoded) - return with_bands.addBands(mask) - - return wrap - - -def modis09ga( - options=("cloud", "mix", "shadow", "snow"), - update_mask=True, - add_mask_band=True, - add_every_mask=False, -): - """Function for masking MOD09GA and MYD09GA collections. - - :return: a function to use in a map function over a collection - """ - reader = BitReader(BITS_MODIS09GA, 16) - return generalMask( - options, - reader, - "state_1km", - update_mask=update_mask, - add_mask_band=add_mask_band, - add_every_mask=add_every_mask, - ) - - -def modis13q1( - options=("cloud", "adjacent", "shadow", "snow"), - update_mask=True, - add_mask_band=True, - add_every_mask=False, -): - """Function for masking MOD13Q1 and MYD13Q1 collections. - - :return: a function to use in a map function over a collection - """ - reader = BitReader(BITS_MODIS13Q1, 16) - return generalMask( - options, - reader, - "DetailedQA", - update_mask=update_mask, - add_mask_band=add_mask_band, - add_every_mask=add_every_mask, - ) - - -def landsat457SRCloudQA( - options=("cloud", "adjacent", "shadow", "snow"), - update_mask=True, - add_mask_band=True, - add_every_mask=False, -): - - reader = BitReader(BITS_LANDSAT_CLOUD_QA, 8) - return generalMask( - options, - reader, - "sr_cloud_qa", - update_mask=update_mask, - add_mask_band=add_mask_band, - add_every_mask=add_every_mask, - ) - - -def landsat457SRPixelQA( - options=("cloud", "shadow", "snow"), - update_mask=True, - add_mask_band=True, - add_every_mask=False, -): - reader = BitReader(BITS_LANDSAT_PIXEL_QA, 16) - return generalMask( - options, - reader, - "pixel_qa", - update_mask=update_mask, - add_mask_band=add_mask_band, - add_every_mask=add_every_mask, - ) - - -def landsat8SRPixelQA( - options=("cloud", "shadow", "snow", "cirrus"), - update_mask=True, - add_mask_band=True, - add_every_mask=False, -): - reader = BitReader(BITS_LANDSAT_PIXEL_QA_L8, 16) - return generalMask( - options, - reader, - "pixel_qa", - update_mask=update_mask, - add_mask_band=add_mask_band, - add_every_mask=add_every_mask, - ) - - -def landsat457ToaBQA( - options=("cloud", "shadow", "snow"), - update_mask=True, - add_mask_band=True, - add_every_mask=False, -): - reader = BitReader(BITS_LANDSAT_BQA, 16) - return generalMask( - options, - reader, - "BQA", - update_mask=update_mask, - add_mask_band=add_mask_band, - add_every_mask=add_every_mask, - ) - - -def landsat8ToaBQA( - options=("cloud", "shadow", "snow", "cirrus"), - update_mask=True, - add_mask_band=True, - add_every_mask=False, -): - reader = BitReader(BITS_LANDSAT_BQA_L8, 16) - return generalMask( - options, - reader, - "BQA", - update_mask=update_mask, - add_mask_band=add_mask_band, - add_every_mask=add_every_mask, - ) - - -def sentinel2( - options=("cloud", "cirrus"), - update_mask=True, - add_mask_band=True, - add_every_mask=False, -): - reader = BitReader(BITS_SENTINEL2, 16) - return generalMask( - options, - reader, - "QA60", - update_mask=update_mask, - add_mask_band=add_mask_band, - add_every_mask=add_every_mask, - ) - - -def compute(image, mask_band, bits, options=None, name_all="all_masks"): - """Compute bits using a specified band, a bit's relation and a list of - options. - - :param image: the image that holds the bit mask band - :type image: ee.Image - :param mask_band: the name of the band that holds the bits mask - :type mask_band: str - :param bits: relation between name and bit - :type bits: dict - :param options: list of 'bits' to compute. Example: ['cloud', 'snow']. If - None, will use all keys of the relation's dict - :type options: list - :param name_all: name for the band that holds the final mask. Default: - 'all_masks' - :type name_all: str - :return: The computed mask - :rtype: ee.Image - """ - # cast params in case they are not EE objects - bits_dict = ee.Dictionary(bits) - opt = ee.List(options) if options else bits_dict.keys() - image = ee.Image(image).select(mask_band) - - first = ee.Image.constant(0).select([0], [name_all]) # init image - - # function for iterate over the options - def for_iterate(option, ini): - i = ee.Image(ini) # cast ini - all = i.select([name_all]) - - # bits relation dict contains the option? - cond = bits_dict.contains(option) - - def for_true(): - """function to execute if condition == True.""" - # get the mask for the option - mask = tools.image.computeBits( - image, bits_dict.get(option), bits_dict.get(option), option - ) - - # name the mask - # mask = ee.Image(mask).select([0], [option]) - newmask = all.Or(mask) - - # return ee.Image(all.Or(mask)).addBands(mask) - return tools.image.replace(i, name_all, newmask).addBands(mask) - - return ee.Image(ee.Algorithms.If(cond, for_true(), i)) - - good_pix = ee.Image(opt.iterate(for_iterate, first)) - - # return good_pix.Not() - return good_pix - - -def hollsteinMask( - image, - options=("cloud", "snow", "shadow", "water", "cirrus"), - aerosol="B1", - blue="B2", - green="B3", - red_edge1="B5", - red_edge2="B6", - red_edge3="B7", - red_edge4="B8A", - water_vapor="B9", - cirrus="B10", - swir="B11", - name="hollstein", -): - """Get Hollstein mask.""" - - def difference(a, b): - def wrap(img): - return img.select(a).subtract(img.select(b)) - - return wrap - - def ratio(a, b): - def wrap(img): - return img.select(a).divide(img.select(b)) - - return wrap - - # 1 - b3 = image.select(green).lt(3190) - - # 2 - b8a = image.select(red_edge4).lt(1660) - r511 = ratio(red_edge1, swir)(image).lt(4.33) - - # 3 - s1110 = difference(swir, cirrus)(image).lt(2550) - b3_3 = image.select(green).lt(5250) - r210 = ratio(blue, cirrus)(image).lt(14.689) - s37 = difference(green, red_edge3)(image).lt(270) - - # 4 - r15 = ratio(aerosol, red_edge1)(image).lt(1.184) - s67 = difference(red_edge2, red_edge3)(image).lt(-160) - b1 = image.select(aerosol).lt(3000) - r29 = ratio(blue, water_vapor)(image).lt(0.788) - s911 = difference(water_vapor, swir)(image).lt(210) - s911_2 = difference(water_vapor, swir)(image).lt(-970) - - snow = {"snow": [["1", 0], ["22", 0], ["34", 0]]} - cloud = { - "cloud-1": [["1", 0], ["22", 1], ["33", 1], ["44", 1]], - "cloud-2": [["1", 0], ["22", 1], ["33", 0], ["45", 0]], - } - cirrus = { - "cirrus-1": [["1", 0], ["22", 1], ["33", 1], ["44", 0]], - "cirrus-2": [["1", 1], ["21", 0], ["32", 1], ["43", 0]], - } - shadow = { - "shadow-1": [["1", 1], ["21", 1], ["31", 1], ["41", 0]], - "shadow-2": [["1", 1], ["21", 1], ["31", 0], ["42", 0]], - "shadow-3": [["1", 0], ["22", 0], ["34", 1], ["46", 0]], - } - water = {"water": [["1", 1], ["21", 1], ["31", 0], ["42", 1]]} - - all = { - "cloud": cloud, - "snow": snow, - "shadow": shadow, - "water": water, - "cirrus": cirrus, - } - - final = {} - - for option in options: - final.update(all[option]) - - dtf = decision_tree.binary( - { - "1": b3, - "21": b8a, - "22": r511, - "31": s37, - "32": r210, - "33": s1110, - "34": b3_3, - "41": s911_2, - "42": s911, - "43": r29, - "44": s67, - "45": b1, - "46": r15, - }, - final, - name, - ) - - return dtf - - -def applyHollstein( - image, - options=("cloud", "snow", "shadow", "water", "cirrus"), - aerosol="B1", - blue="B2", - green="B3", - red_edge1="B5", - red_edge2="B6", - red_edge3="B7", - red_edge4="B8A", - water_vapor="B9", - cirrus="B10", - swir="B11", -): - """Apply Hollstein mask.""" - mask = hollsteinMask( - image, - options, - aerosol, - blue, - green, - red_edge1, - red_edge2, - red_edge3, - red_edge4, - water_vapor, - cirrus, - swir, - ).select("hollstein") - return image.updateMask(mask) - - -def hollsteinS2( - options=("cloud", "snow", "shadow", "water", "cirrus"), - name="hollstein", - addBands=False, - updateMask=True, -): - """Compute Hollstein Decision tree for detecting clouds, clouds shadow, - cirrus, snow and water in Sentinel 2 imagery. - - :param options: masks to apply. Options: 'cloud', 'shadow', 'snow', - 'cirrus', 'water' - :type options: list - :param name: name of the band that will hold the final mask. Default: 'hollstein' - :type name: str - :param addBands: add all bands to the image. Default: False - :type addBands: bool - :param updateMask: update the mask of the Image. Default: True - :type updateMask: bool - :return: a function for applying the mask - :rtype: function - """ - - def compute_dt(img): - - results = hollsteinMask(img, options, name) - - if updateMask and addBands: - return img.addBands(results).updateMask(results.select(name)) - elif addBands: - return img.addBands(results) - elif updateMask: - return img.updateMask(results.select(name)) - - return compute_dt - - -def darkPixels(green, swir2, threshold=0.25): - """Detect dark pixels from green and swir2 band. - - :param green: name of the green band - :type green: str - :param swir2: name of the swir2 band - :type swir2: str - :param threshold: threshold value from which are considered dark pixels - :type threshold: float - :return: a function - """ - - def wrap(img): - return img.normalizedDifference([green, swir2]).gt(threshold) - - return wrap - - -### DEPRECATED FUNCTIONS ### -# GENERIC APPLICATION OF MASKS - -# LEDAPS -def ledaps(image): - """Function to use in Surface Reflectance Collections computed by - LEDAPS. - - Use: - - `masked = collection.map(cloud_mask.ledaps)` - """ - cmask = image.select("QA") - - valid_data_mask = tools.image.computeBits(cmask, 1, 1, "valid_data") - cloud_mask = tools.image.computeBits(cmask, 2, 2, "cloud") - snow_mask = tools.image.computeBits(cmask, 4, 4, "snow") - - good_pix = cloud_mask.eq(0).And(valid_data_mask.eq(0)).And(snow_mask.eq(0)) - result = image.updateMask(good_pix) - - return result - - -def landsatSR( - options=("cloud", "shadow", "adjacent", "snow"), - name="sr_mask", - addBands=False, - updateMask=True, -): - """Function to use in Landsat Surface Reflectance Collections: - LANDSAT/LT04/C01/T1_SR, LANDSAT/LT05/C01/T1_SR, LANDSAT/LE07/C01/T1_SR, - LANDSAT/LC08/C01/T1_SR. - - :param options: masks to apply. Options: 'cloud', 'shadow', 'adjacent', - 'snow' - :type options: list - :param name: name of the band that will hold the final mask. Default: 'toa_mask' - :type name: str - :param addBands: add all bands to the image. Default: False - :type addBands: bool - :param updateMask: update the mask of the Image. Default: True - :type updateMask: bool - :return: a function for applying the mask - :rtype: function - """ - sr = { - "bits": ee.Dictionary({"cloud": 1, "shadow": 2, "adjacent": 3, "snow": 4}), - "band": "sr_cloud_qa", - } - - pix = { - "bits": ee.Dictionary({"cloud": 5, "shadow": 3, "snow": 4}), - "band": "pixel_qa", - } - - # Parameters - options = ee.List(options) - - def wrap(image): - bands = image.bandNames() - contains_sr = bands.contains("sr_cloud_qa") - good_pix = ee.Image( - ee.Algorithms.If( - contains_sr, - compute(image, sr["band"], sr["bits"], options, name_all=name), - compute(image, pix["band"], pix["bits"], options, name_all=name), - ) - ) - - mask = good_pix.select([name]).Not() - - if addBands and updateMask: - return image.updateMask(mask).addBands(good_pix) - elif addBands: - return image.addBands(good_pix) - elif updateMask: - return image.updateMask(mask) - else: - return image - - return wrap diff --git a/test.ipynb b/test.ipynb index 2a530781..4c88b434 100644 --- a/test.ipynb +++ b/test.ipynb @@ -32,6 +32,39 @@ "image.id().getInfo()" ] }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "module 'ee' has no attribute 'initialize'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/mnt/e/WSL/Users/rambap/github/gee_tools/test.ipynb Cell 2\u001b[0m line \u001b[0;36m3\n\u001b[1;32m 1\u001b[0m \u001b[39mimport\u001b[39;00m \u001b[39mee\u001b[39;00m \n\u001b[0;32m----> 3\u001b[0m ee\u001b[39m.\u001b[39;49minitialize() \n", + "\u001b[0;31mAttributeError\u001b[0m: module 'ee' has no attribute 'initialize'" + ] + } + ], + "source": [ + "import ee\n", + "\n", + "ee.initialize()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "collection = ee.ImageCollection(\"COPERNICUS/S2_SR\")\n", + "first_100 = collection.toList(100)" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/tests/conftest.py b/tests/conftest.py index cb12e541..f63d0b47 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ import ee import httplib2 +import pytest def pytest_configure() -> None: @@ -29,3 +30,51 @@ def pytest_configure() -> None: # if the user is in local development the authentication should # already be available ee.Initialize(http_transport=httplib2.Http()) + + +# -- fixtures that will be used throughout the tests --------------------------- +@pytest.fixture(scope="session") +def amazonas() -> ee.FeatureCollection: + """Return the Amazonas state from colombia.""" + level2 = ee.FeatureCollection("FAO/GAUL/2015/level2") + colombia = level2.filter(ee.Filter.eq("ADM0_NAME", "Colombia")) + return colombia.filter(ee.Filter.eq("ADM1_NAME", "Amazonas")) + + +@pytest.fixture(scope="session") +def s2_sr(amazonas) -> ee.ImageCollection: + """Return a copernicus based collection. + + the 100 first images of the Sentinel-2 Surface Reflectance ImageCollection centered on the amazonas state of colombia and from 2021-01-01 to 2021-12-01. + """ + return ( + ee.ImageCollection("COPERNICUS/S2_SR") + .filterBounds(amazonas) + .filterDate("2021-01-01", "2021-12-01") + ) + + +@pytest.fixture(scope="session") +def s2(amazonas) -> ee.ImageCollection: + """Return a copernicus based collection. + + the 100 first images of the Sentinel-2 Surface Reflectance ImageCollection centered on the amazonas state of colombia and from 2021-01-01 to 2021-12-01. + """ + return ( + ee.ImageCollection("COPERNICUS/S2") + .filterBounds(amazonas) + .filterDate("2021-01-01", "2021-12-01") + ) + + +@pytest.fixture(scope="session") +def l8_toa(amazonas) -> ee.ImageCollection: + """Return a landsat based collection. + + the 100 first images of the landast 8 TOA ImageCollection centered on the amazonas state of colombia and from 2021-01-01 to 2021-12-01. + """ + return ( + ee.ImageCollection("LANDSAT/LC08/C01/T1_TOA") + .filterBounds(amazonas) + .filterDate("2021-01-01", "2021-12-01") + ) diff --git a/tests/test_Image.py b/tests/test_Image.py index 10747523..a6f1adea 100644 --- a/tests/test_Image.py +++ b/tests/test_Image.py @@ -562,13 +562,16 @@ def image_instance(self): return ee.Image(src).select(["B1", "B2", "B3"]) -class TestHistogramMatch: +class TestmatchHistogram: """Test the ``histogramMatch`` method.""" - def test_histogram_match(self, image_source, image_target, vatican): - image = image_source.geetools.histogramMatch(image_target) + def test_histogram_match( + self, image_source, image_target, vatican, data_regression + ): + bands = {"R": "R", "G": "G", "B": "B"} + image = image_source.geetools.matchHistogram(image_target, bands) values = image.reduceRegion(ee.Reducer.mean(), vatican, 1) - assert values.getInfo() == {"B": 7680, "G": 8448, "R": 8416} + data_regression.check(values.getInfo()) @pytest.fixture def vatican(self): @@ -739,11 +742,11 @@ def vatican(self): return ee.Geometry.Point([12.4534, 41.9033]).buffer(100) -class TestTasseledCap: - """Test the ``tasseledCap`` method.""" +class TestMaskClouds: + """Test the ``maskClouds`` method.""" - def test_tasseled_cap(self, image_instance, vatican, data_regression): - image = image_instance.geetools.tasseledCap() + def test_mask_S2_clouds(self, image_instance, vatican, data_regression): + image = image_instance.geetools.maskClouds() values = image.reduceRegion(ee.Reducer.mean(), vatican, 1) data_regression.check(values.getInfo()) @@ -755,3 +758,107 @@ def image_instance(self): @pytest.fixture def vatican(self): return ee.Geometry.Point([12.4534, 41.9033]).buffer(100) + + +class TestGetscaleParams: + """Test the ``getScaleParams`` method.""" + + def test_get_scale_params(self, data_regression): + params = ( + ee.ImageCollection("MODIS/006/MOD11A2").first().geetools.getScaleParams() + ) + data_regression.check(params) + + +class TestGetOffsetParams: + """Test the ``getOffsetParams`` method.""" + + def get_offset_params(self, data_regression): + params = ( + ee.ImageCollection("MODIS/006/MOD11A2").first().geetools.getOffsetParams() + ) + data_regression.check(params) + + +class TestScaleAndOffset: + """Test the ``scaleAndOffset`` method.""" + + def test_scale_and_offset(self, vatican, image_instance, data_regression): + image = image_instance.geetools.scaleAndOffset() + values = image.reduceRegion(ee.Reducer.mean(), vatican, 1) + data_regression.check(values.getInfo()) + + @pytest.fixture + def vatican(self): + return ee.Geometry.Point([12.4534, 41.9033]).buffer(100) + + @pytest.fixture + def image_instance(self): + src = "COPERNICUS/S2/20230105T100319_20230105T100317_T32TQM" + return ee.Image(src) + + +class TestPreprocess: + """Test the ``preprocess`` method.""" + + def test_preprocess(self, vatican, image_instance, data_regression): + image = image_instance.geetools.preprocess() + values = image.reduceRegion(ee.Reducer.mean(), vatican, 1) + data_regression.check(values.getInfo()) + + @pytest.fixture + def vatican(self): + return ee.Geometry.Point([12.4534, 41.9033]).buffer(100) + + @pytest.fixture + def image_instance(self): + src = "COPERNICUS/S2/20230105T100319_20230105T100317_T32TQM" + return ee.Image(src) + + +class TestGetSTAC: + """Test the ``getSTAC`` method.""" + + def test_get_stac(self, data_regression): + stac = ee.ImageCollection("COPERNICUS/S2_SR").first().geetools.getSTAC() + data_regression.check(stac) + + +class TestGetDOI: + """Test the ``getDOI`` method.""" + + def get_doi(self, data_regression): + doi = ee.ImageCollection("COPERNICUS/S2_SR").first().geetools.getDOI() + data_regression.check(doi) + + +class TestGetCitation: + """Test the ``getCitation`` method.""" + + def get_citation(self, data_regression): + citation = ee.ImageCollection("COPERNICUS/S2_SR").first().geetools.getCitation() + data_regression.check(citation) + + +class TestPanSharpen: + """Test the panSharpen method.""" + + def test_pan_sharpen(self, data_regression): + source = ee.Image("LANDSAT/LC08/C01/T1_TOA/LC08_047027_20160819") + sharp = source.geetools.panSharpen( + method="HPFA", qa=["MSE", "RMSE"], maxPixels=1e13 + ) + centroid = sharp.geometry().centroid().buffer(100) + values = sharp.reduceRegion(ee.Reducer.mean(), centroid, 1) + data_regression.check(values.getInfo()) + + +class TestTasseledCap: + """Test the tasseledCap method.""" + + def test_tasseled_cap(self, data_regression): + img = ee.Image("LANDSAT/LT05/C01/T1/LT05_044034_20081011") + img = img.geetools.tasseledCap() + centroid = img.geometry().centroid().buffer(100) + values = img.reduceRegion(ee.Reducer.mean(), centroid, 1) + data_regression.check(values.getInfo()) diff --git a/tests/test_Image/test_get_scale_params.yml b/tests/test_Image/test_get_scale_params.yml new file mode 100644 index 00000000..bbbf4cf7 --- /dev/null +++ b/tests/test_Image/test_get_scale_params.yml @@ -0,0 +1,12 @@ +Clear_sky_days: 1.0 +Clear_sky_nights: 1.0 +Day_view_angl: 1.0 +Day_view_time: 0.1 +Emis_31: 0.002 +Emis_32: 0.002 +LST_Day_1km: 0.02 +LST_Night_1km: 0.02 +Night_view_angl: 1.0 +Night_view_time: 0.1 +QC_Day: 1.0 +QC_Night: 1.0 diff --git a/tests/test_Image/test_get_stac.yml b/tests/test_Image/test_get_stac.yml new file mode 100644 index 00000000..34eb79d2 --- /dev/null +++ b/tests/test_Image/test_get_stac.yml @@ -0,0 +1,619 @@ +description: 'See also collection COPERNICUS/S2_SR_HARMONIZED that shifts data with + + PROCESSING_BASELINE ''04.00'' or above (after 2022-01-25) to be in the same range + + as in older scenes. + + + Sentinel-2 is a wide-swath, high-resolution, multi-spectral + + imaging mission supporting Copernicus Land Monitoring studies, + + including the monitoring of vegetation, soil and water cover, + + as well as observation of inland waterways and coastal areas. + + + The Sentinel-2 L2 data are downloaded from scihub. They were + + computed by running sen2cor. WARNING: ESA did not produce L2 data + + for all L1 assets, and earlier L2 coverage is not global. + + + The assets contain + + 12 UINT16 spectral bands representing SR scaled by 10000 (unlike in L1 data, + + there is no B10). There are also several more L2-specific bands (see band + + list for details). See the + + [Sentinel-2 User Handbook](https://sentinel.esa.int/documents/247904/685211/Sentinel-2_User_Handbook) + + for details. In addition, three QA bands are present where one + + (QA60) is a bitmask band with cloud mask information. For more + + details, [see the full explanation of how cloud masks are computed.](https://sentinel.esa.int/web/sentinel/technical-guides/sentinel-2-msi/level-1c/cloud-masks) + + + EE asset ids for Sentinel-2 L2 assets have the following format: + + COPERNICUS/S2_SR/20151128T002653_20151128T102149_T56MNN. Here the + + first numeric part represents the sensing date and time, the + + second numeric part represents the product generation date and + + time, and the final 6-character string is a unique granule identifier + + indicating its UTM grid reference (see [MGRS](https://en.wikipedia.org/wiki/Military_Grid_Reference_System)). + + + For datasets to assist with cloud and/or cloud shadow detection, see [COPERNICUS/S2_CLOUD_PROBABILITY](COPERNICUS_S2_CLOUD_PROBABILITY) + + and [GOOGLE/CLOUD_SCORE_PLUS/V1/S2_HARMONIZED](GOOGLE_CLOUD_SCORE_PLUS_V1_S2_HARMONIZED). + + + For more details on Sentinel-2 radiometric resolution, [see this page](https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi/resolutions/radiometric). + + ' +extent: + spatial: + bbox: + - - -180 + - -56 + - 180 + - 83 + temporal: + interval: + - - '2017-03-28T00:00:00Z' + - '2023-11-09T19:01:18' +gee:interval: + interval: 5 + type: revisit_interval + unit: day +gee:terms_of_use: 'The use of Sentinel data is governed by the [Copernicus + + Sentinel Data Terms and Conditions.](https://scihub.copernicus.eu/twiki/pub/SciHubWebPortal/TermsConditions/Sentinel_Data_Terms_and_Conditions.pdf) + + ' +gee:type: image_collection +id: COPERNICUS/S2_SR +keywords: +- copernicus +- esa +- eu +- msi +- reflectance +- sentinel +- sr +license: proprietary +links: +- href: https://storage.googleapis.com/earthengine-stac/catalog/COPERNICUS/COPERNICUS_S2_SR.json + rel: self + type: application/json +- href: https://storage.googleapis.com/earthengine-stac/catalog/COPERNICUS/catalog.json + rel: parent + type: application/json +- href: https://storage.googleapis.com/earthengine-stac/catalog/catalog.json + rel: root + type: application/json +- code: JavaScript + href: https://code.earthengine.google.com/?scriptPath=Examples:Datasets/COPERNICUS/COPERNICUS_S2_SR + rel: related + title: Run the example for COPERNICUS/S2_SR in the Earth Engine Code Editor + type: text/html +- href: https://developers.google.com/earth-engine/datasets/images/COPERNICUS/COPERNICUS_S2_SR_sample.png + rel: preview + type: image/png +- href: https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR#terms-of-use + rel: license + type: text/html +providers: +- name: European Union/ESA/Copernicus + roles: + - licensor + - producer + url: https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi/processing-levels/level-2 +- name: Google Earth Engine + roles: + - host + url: https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR +stac_extensions: +- https://stac-extensions.github.io/eo/v1.0.0/schema.json +stac_version: 1.0.0 +summaries: + MSK_CLDPRB: + gee:estimated_range: false + maximum: 100 + minimum: 0 + MSK_SNWPRB: + gee:estimated_range: false + maximum: 100 + minimum: 0 + SCL: + gee:estimated_range: false + maximum: 11 + minimum: 1 + eo:bands: + - center_wavelength: 0.4439 + description: Aerosols + gee:scale: 0.0001 + gee:wavelength: 443.9nm (S2A) / 442.3nm (S2B) + gsd: 60 + name: B1 + - center_wavelength: 0.4966 + description: Blue + gee:scale: 0.0001 + gee:wavelength: 496.6nm (S2A) / 492.1nm (S2B) + gsd: 10 + name: B2 + - center_wavelength: 0.56 + description: Green + gee:scale: 0.0001 + gee:wavelength: 560nm (S2A) / 559nm (S2B) + gsd: 10 + name: B3 + - center_wavelength: 0.6645 + description: Red + gee:scale: 0.0001 + gee:wavelength: 664.5nm (S2A) / 665nm (S2B) + gsd: 10 + name: B4 + - center_wavelength: 0.7039 + description: Red Edge 1 + gee:scale: 0.0001 + gee:wavelength: 703.9nm (S2A) / 703.8nm (S2B) + gsd: 20 + name: B5 + - center_wavelength: 0.7402 + description: Red Edge 2 + gee:scale: 0.0001 + gee:wavelength: 740.2nm (S2A) / 739.1nm (S2B) + gsd: 20 + name: B6 + - center_wavelength: 0.7825 + description: Red Edge 3 + gee:scale: 0.0001 + gee:wavelength: 782.5nm (S2A) / 779.7nm (S2B) + gsd: 20 + name: B7 + - center_wavelength: 0.8351 + description: NIR + gee:scale: 0.0001 + gee:wavelength: 835.1nm (S2A) / 833nm (S2B) + gsd: 10 + name: B8 + - center_wavelength: 0.8648 + description: Red Edge 4 + gee:scale: 0.0001 + gee:wavelength: 864.8nm (S2A) / 864nm (S2B) + gsd: 20 + name: B8A + - center_wavelength: 0.945 + description: Water vapor + gee:scale: 0.0001 + gee:wavelength: 945nm (S2A) / 943.2nm (S2B) + gsd: 60 + name: B9 + - center_wavelength: 1.6137 + description: SWIR 1 + gee:scale: 0.0001 + gee:wavelength: 1613.7nm (S2A) / 1610.4nm (S2B) + gsd: 20 + name: B11 + - center_wavelength: 2.2024 + description: SWIR 2 + gee:scale: 0.0001 + gee:wavelength: 2202.4nm (S2A) / 2185.7nm (S2B) + gsd: 20 + name: B12 + - description: Aerosol Optical Thickness + gee:scale: 0.001 + gsd: 10 + name: AOT + - description: 'Water Vapor Pressure. The height the water would occupy if the vapor + were condensed into + + liquid and spread evenly across the column. + + ' + gee:scale: 0.001 + gee:units: cm + gsd: 10 + name: WVP + - description: Scene Classification Map (The "No Data" value of 0 is masked out) + gee:classes: + - color: ff0004 + description: Saturated or defective + value: 1 + - color: '868686' + description: Dark Area Pixels + value: 2 + - color: 774b0a + description: Cloud Shadows + value: 3 + - color: 10d22c + description: Vegetation + value: 4 + - color: ffff52 + description: Bare Soils + value: 5 + - color: 0000ff + description: Water + value: 6 + - color: '818181' + description: Clouds Low Probability / Unclassified + value: 7 + - color: c0c0c0 + description: Clouds Medium Probability + value: 8 + - color: f1f1f1 + description: Clouds High Probability + value: 9 + - color: bac5eb + description: Cirrus + value: 10 + - color: 52fff9 + description: Snow / Ice + value: 11 + gsd: 20 + name: SCL + - description: True Color Image, Red channel + gsd: 10 + name: TCI_R + - description: True Color Image, Green channel + gsd: 10 + name: TCI_G + - description: True Color Image, Blue channel + gsd: 10 + name: TCI_B + - description: Cloud Probability Map (missing in some products) + gsd: 20 + name: MSK_CLDPRB + - description: Snow Probability Map (missing in some products) + gsd: 10 + name: MSK_SNWPRB + - description: Always empty + gsd: 10 + name: QA10 + - description: Always empty + gsd: 20 + name: QA20 + - description: Cloud mask + gee:bitmask: + bitmask_parts: + - bit_count: 10 + description: Unused + first_bit: 0 + - bit_count: 1 + description: Opaque clouds + first_bit: 10 + values: + - description: No opaque clouds + value: 0 + - description: Opaque clouds present + value: 1 + - bit_count: 1 + description: Cirrus clouds + first_bit: 11 + values: + - description: No cirrus clouds + value: 0 + - description: Cirrus clouds present + value: 1 + total_bit_count: 12 + gsd: 60 + name: QA60 + gee:schema: + - description: Accuracy of Aerosol Optical thickness model + name: AOT_RETRIEVAL_ACCURACY + type: DOUBLE + - description: Granule-specific cloudy pixel percentage taken from the original + metadata + name: CLOUDY_PIXEL_PERCENTAGE + type: DOUBLE + - description: 'Cloudy pixel percentage for the whole archive that + + contains this granule. Taken from the original metadata + + ' + name: CLOUD_COVERAGE_ASSESSMENT + type: DOUBLE + - description: Percentage of pixels classified as cloud shadow + name: CLOUDY_SHADOW_PERCENTAGE + type: DOUBLE + - description: Percentage of pixels classified as dark features or shadows + name: DARK_FEATURES_PERCENTAGE + type: DOUBLE + - description: Unique identifier of the datastrip Product Data Item (PDI) + name: DATASTRIP_ID + type: STRING + - description: 'Uniquely identifies a given Datatake. The ID contains + + the Sentinel-2 satellite, start date and time, absolute orbit + + number, and processing baseline. + + ' + name: DATATAKE_IDENTIFIER + type: STRING + - description: MSI operation mode + name: DATATAKE_TYPE + type: STRING + - description: Percentage of degraded MSI and ancillary data + name: DEGRADED_MSI_DATA_PERCENTAGE + type: DOUBLE + - description: 'Synthesis of the On-Line Quality Control (OLQC) checks + + performed at granule (Product_Syntax) and datastrip (Product + + Syntax and DS_Consistency) levels + + ' + name: FORMAT_CORRECTNESS + type: STRING + - description: Synthesis of the OLQC checks performed at the datastrip level (Relative_Orbit_Number) + name: GENERAL_QUALITY + type: STRING + - description: Product generation time + name: GENERATION_TIME + type: DOUBLE + - description: Synthesis of the OLQC checks performed at the datastrip level (Attitude_Quality_Indicator) + name: GEOMETRIC_QUALITY + type: STRING + - description: Unique identifier of the granule PDI (PDI_ID) + name: GRANULE_ID + type: STRING + - description: Percentage of pixels classified as high probability clouds + name: HIGH_PROBA_CLOUDS_PERCENTAGE + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B1 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B1 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B2 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B2 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B3 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B3 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B4 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B4 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B5 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B5 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B6 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B6 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B7 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B7 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B8 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B8 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B8a and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B8A + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B9 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B9 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B10 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B10 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B11 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B11 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B12 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B12 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B1 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B1 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B2 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B2 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B3 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B3 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B4 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B4 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B5 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B5 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B6 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B6 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B7 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B7 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B8 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B8 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B8a and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B8A + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B9 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B9 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B10 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B10 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B11 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B11 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B12 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B12 + type: DOUBLE + - description: Mean value containing sun azimuth angle average for all bands and + detectors + name: MEAN_SOLAR_AZIMUTH_ANGLE + type: DOUBLE + - description: Mean value containing sun zenith angle average for all bands and + detectors + name: MEAN_SOLAR_ZENITH_ANGLE + type: DOUBLE + - description: Percentage of pixels classified as medium probability clouds + name: MEDIUM_PROBA_CLOUDS_PERCENTAGE + type: DOUBLE + - description: US-Military Grid Reference System (MGRS) tile + name: MGRS_TILE + type: STRING + - description: Percentage of No Data pixels + name: NODATA_PIXEL_PERCENTAGE + type: DOUBLE + - description: Percentage of pixels classified as non-vegetated + name: NOT_VEGETATED_PERCENTAGE + type: DOUBLE + - description: 'Configuration baseline used at the time of the product + + generation in terms of processor software version and major Ground + + Image Processing Parameters (GIPP) version + + ' + name: PROCESSING_BASELINE + type: STRING + - description: The full id of the original Sentinel-2 product + name: PRODUCT_ID + type: STRING + - description: Accuracy of radiative transfer model + name: RADIATIVE_TRANSFER_ACCURACY + type: DOUBLE + - description: Based on the OLQC reports contained in the Datastrips/QI_DATA with + RADIOMETRIC_QUALITY checklist name + name: RADIOMETRIC_QUALITY + type: STRING + - description: Earth-Sun distance correction factor + name: REFLECTANCE_CONVERSION_CORRECTION + type: DOUBLE + - description: Percentage of saturated or defective pixels + name: SATURATED_DEFECTIVE_PIXEL_PERCENTAGE + type: DOUBLE + - description: Imaging orbit direction + name: SENSING_ORBIT_DIRECTION + type: STRING + - description: Imaging orbit number + name: SENSING_ORBIT_NUMBER + type: DOUBLE + - description: 'Synthesis of the OLQC checks performed at granule + + (Missing_Lines, Corrupted_ISP, and Sensing_Time) and datastrip + + (Degraded_SAD and Datation_Model) levels + + ' + name: SENSOR_QUALITY + type: STRING + - description: Mean solar exoatmospheric irradiance for band B1 + name: SOLAR_IRRADIANCE_B1 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B2 + name: SOLAR_IRRADIANCE_B2 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B3 + name: SOLAR_IRRADIANCE_B3 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B4 + name: SOLAR_IRRADIANCE_B4 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B5 + name: SOLAR_IRRADIANCE_B5 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B6 + name: SOLAR_IRRADIANCE_B6 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B7 + name: SOLAR_IRRADIANCE_B7 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B8 + name: SOLAR_IRRADIANCE_B8 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B8a + name: SOLAR_IRRADIANCE_B8A + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B9 + name: SOLAR_IRRADIANCE_B9 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B10 + name: SOLAR_IRRADIANCE_B10 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B11 + name: SOLAR_IRRADIANCE_B11 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B12 + name: SOLAR_IRRADIANCE_B12 + type: DOUBLE + - description: Percentage of pixels classified as snow or ice + name: SNOW_ICE_PERCENTAGE + type: DOUBLE + - description: 'Sentinel-2 spacecraft name: Sentinel-2A, Sentinel-2B' + name: SPACECRAFT_NAME + type: STRING + - description: Percentage of pixels classified as thin cirrus clouds + name: THIN_CIRRUS_PERCENTAGE + type: DOUBLE + - description: Percentage of unclassified pixels + name: UNCLASSIFIED_PERCENTAGE + type: DOUBLE + - description: Percentage of pixels classified as vegetation + name: VEGETATION_PERCENTAGE + type: DOUBLE + - description: Percentage of pixels classified as water + name: WATER_PERCENTAGE + type: DOUBLE + - description: Declared accuracy of the Water Vapor model + name: WATER_VAPOUR_RETRIEVAL_ACCURACY + type: DOUBLE + gee:visualizations: + - display_name: RGB + image_visualization: + band_vis: + bands: + - B4 + - B3 + - B2 + max: + - 3000 + min: + - 0 + lookat: + lat: 17.7009 + lon: 83.277 + zoom: 12 + instruments: + - MSI + platform: + - Sentinel-2A + - Sentinel-2B +title: 'Sentinel-2 MSI: MultiSpectral Instrument, Level-2A' +type: Collection diff --git a/tests/test_Image/test_histogram_match.yml b/tests/test_Image/test_histogram_match.yml new file mode 100644 index 00000000..676b2bf3 --- /dev/null +++ b/tests/test_Image/test_histogram_match.yml @@ -0,0 +1,3 @@ +B: 7472 +G: 8448 +R: 8416 diff --git a/tests/test_Image/test_mask_S2_clouds.yml b/tests/test_Image/test_mask_S2_clouds.yml new file mode 100644 index 00000000..599c8920 --- /dev/null +++ b/tests/test_Image/test_mask_S2_clouds.yml @@ -0,0 +1,16 @@ +B1: 4017.0118503135764 +B10: 1018.4309151904954 +B11: 3644.192839337805 +B12: 3393.206849476967 +B2: 3649.35633543797 +B3: 3286.879672566305 +B4: 3326.1979222734963 +B5: 3354.219620696217 +B6: 3519.1575580287777 +B7: 3663.8948510675423 +B8: 3498.90347046247 +B8A: 3744.3591341899078 +B9: 1837.9670847722061 +QA10: 0 +QA20: 0 +QA60: 0 diff --git a/tests/test_Image/test_pan_sharpen.yml b/tests/test_Image/test_pan_sharpen.yml new file mode 100644 index 00000000..9fd4ee33 --- /dev/null +++ b/tests/test_Image/test_pan_sharpen.yml @@ -0,0 +1,6 @@ +B2: 0.07491742089575296 +B3: 0.06227998381551604 +B4: 0.035777563662935274 +B5: 0.2757017661094787 +B6: 0.08511920000989216 +B7: 0.030933266714784642 diff --git a/tests/test_Image/test_preprocess.yml b/tests/test_Image/test_preprocess.yml new file mode 100644 index 00000000..ae45ef72 --- /dev/null +++ b/tests/test_Image/test_preprocess.yml @@ -0,0 +1,16 @@ +B1: 0.40170118503135477 +B10: 0.10184309151904332 +B11: 0.3644192839337238 +B12: 0.3393206849476893 +B2: 0.36493563354380965 +B3: 0.3286879672566436 +B4: 0.33261979222733346 +B5: 0.3354219620696527 +B6: 0.35191575580286294 +B7: 0.3663894851067769 +B8: 0.34989034704626104 +B8A: 0.37443591341895804 +B9: 0.1837967084772179 +QA10: 0 +QA20: 0 +QA60: 0 diff --git a/tests/test_Image/test_scale_and_offset.yml b/tests/test_Image/test_scale_and_offset.yml new file mode 100644 index 00000000..ae45ef72 --- /dev/null +++ b/tests/test_Image/test_scale_and_offset.yml @@ -0,0 +1,16 @@ +B1: 0.40170118503135477 +B10: 0.10184309151904332 +B11: 0.3644192839337238 +B12: 0.3393206849476893 +B2: 0.36493563354380965 +B3: 0.3286879672566436 +B4: 0.33261979222733346 +B5: 0.3354219620696527 +B6: 0.35191575580286294 +B7: 0.3663894851067769 +B8: 0.34989034704626104 +B8A: 0.37443591341895804 +B9: 0.1837967084772179 +QA10: 0 +QA20: 0 +QA60: 0 diff --git a/tests/test_Image/test_tasseled_cap.yml b/tests/test_Image/test_tasseled_cap.yml index d0f97ece..bc4072a8 100644 --- a/tests/test_Image/test_tasseled_cap.yml +++ b/tests/test_Image/test_tasseled_cap.yml @@ -1,16 +1,9 @@ -B1: 4017.0118503135764 -B10: 1018.4309151904954 -B11: 3644.192839337805 -B12: 3393.206849476967 -B2: 3649.35633543797 -B3: 3286.879672566305 -B4: 3326.1979222734963 -B5: 3354.219620696217 -B6: 3519.1575580287777 -B7: 3663.8948510675423 -B8: 3498.90347046247 -B8A: 3744.3591341899078 -B9: 1837.9670847722061 -TCB: 11050.35053743479 -TCG: -2029.8820973441802 -TCW: -3173.9246337681716 +B1: 80.64678609951652 +B2: 36.99609104501302 +B3: 39.20192303804169 +B4: 41.4762897504355 +B5: 60.247799617469894 +B7: 37.87367052061099 +TCB: 107.8169395800415 +TCG: -23.51120101153365 +TCW: -7.983038306874465 diff --git a/tests/test_ImageCollection.py b/tests/test_ImageCollection.py new file mode 100644 index 00000000..9c8cfad1 --- /dev/null +++ b/tests/test_ImageCollection.py @@ -0,0 +1,108 @@ +"""Test the ImageCollection class.""" + +import ee + +import geetools # noqa: F401 + + +def reduce(collection: ee.ImageCollection) -> ee.Dictionary: + """Compute the mean reduction on the first image of the imageCollection.""" + first = collection.first() + geometry = first.geometry().centroid().buffer(100) + return first.reduceRegion(ee.Reducer.mean(), geometry, 1) + + +class TestMaskClouds: + """Test the ``maskClouds`` method.""" + + def test_mask_s2_sr(self, s2_sr, data_regression): + masked = s2_sr.geetools.maskClouds(prob=75, buffer=300, cdi=-0.5) + data_regression.check(reduce(masked).getInfo()) + + +class TestClosest: + """Test the ``closest`` method.""" + + def test_closest_s2_sr(self, s2_sr, data_regression): + closest = s2_sr.geetools.closest("2021-10-01") + data_regression.check(closest.size().getInfo()) + + +class TestSpectralIndices: + """Test the ``spectralIndices`` method.""" + + def test_spectral_indices(self, s2_sr, data_regression): + indices = s2_sr.geetools.spectralIndices(["NDVI", "NDWI"]) + data_regression.check(reduce(indices).getInfo()) + + +class TestGetScaleParams: + """Test the ``getScaleParams`` method.""" + + def test_get_scale_params(self, s2_sr, data_regression): + scale_params = s2_sr.geetools.getScaleParams() + data_regression.check(scale_params) + + +class TestGetOffsetParams: + """Test the ``getOffsetParams`` method.""" + + def test_get_offset_params(self, s2_sr, data_regression): + offset_params = s2_sr.geetools.getOffsetParams() + data_regression.check(offset_params) + + +class TestScaleAndOffset: + """Test the ``scaleAndOffset`` method.""" + + def test_scale_and_offset(self, s2_sr, data_regression): + scaled = s2_sr.geetools.scaleAndOffset() + data_regression.check(reduce(scaled).getInfo()) + + +class TestPreprocess: + """Test the ``preprocess`` method.""" + + def test_preprocess(self, s2_sr, data_regression): + preprocessed = s2_sr.geetools.preprocess() + data_regression.check(reduce(preprocessed).getInfo()) + + +class TestGetSTAC: + """Test the ``getSTAC`` method.""" + + def test_get_stac(self, s2_sr, data_regression): + stac = s2_sr.geetools.getSTAC() + data_regression.check(stac) + + +class TestGetDOI: + """Test the ``getDOI`` method.""" + + def test_get_doi(self, s2_sr, data_regression): + doi = s2_sr.geetools.getDOI() + data_regression.check(doi) + + +class TestGetCitation: + """Test the ``getCitation`` method.""" + + def test_get_citation(self, s2_sr, data_regression): + citation = s2_sr.geetools.getCitation() + data_regression.check(citation) + + +class TestPanSharpen: + """Test the ``panSharpen`` method.""" + + def test_pan_sharpen(self, l8_toa, data_regression): + sharpened = l8_toa.geetools.panSharpen() + data_regression.check(reduce(sharpened).getInfo()) + + +class TestTasseledCap: + """Test the ``tasseledCap`` method.""" + + def test_tasseled_cap(self, s2, data_regression): + tc = s2.geetools.tasseledCap() + data_regression.check(reduce(tc).getInfo()) diff --git a/tests/test_ImageCollection/test_closest_s2_sr.yml b/tests/test_ImageCollection/test_closest_s2_sr.yml new file mode 100644 index 00000000..ece6e09e --- /dev/null +++ b/tests/test_ImageCollection/test_closest_s2_sr.yml @@ -0,0 +1,2 @@ +8 +... diff --git a/tests/test_ImageCollection/test_get_citation.yml b/tests/test_ImageCollection/test_get_citation.yml new file mode 100644 index 00000000..866e45cf --- /dev/null +++ b/tests/test_ImageCollection/test_get_citation.yml @@ -0,0 +1,2 @@ +Citation not available +... diff --git a/tests/test_ImageCollection/test_get_doi.yml b/tests/test_ImageCollection/test_get_doi.yml new file mode 100644 index 00000000..57e63a82 --- /dev/null +++ b/tests/test_ImageCollection/test_get_doi.yml @@ -0,0 +1,2 @@ +DOI not available +... diff --git a/tests/test_ImageCollection/test_get_offset_params.yml b/tests/test_ImageCollection/test_get_offset_params.yml new file mode 100644 index 00000000..a3314864 --- /dev/null +++ b/tests/test_ImageCollection/test_get_offset_params.yml @@ -0,0 +1,23 @@ +AOT: 0.0 +B1: 0.0 +B11: 0.0 +B12: 0.0 +B2: 0.0 +B3: 0.0 +B4: 0.0 +B5: 0.0 +B6: 0.0 +B7: 0.0 +B8: 0.0 +B8A: 0.0 +B9: 0.0 +MSK_CLDPRB: 0.0 +MSK_SNWPRB: 0.0 +QA10: 0.0 +QA20: 0.0 +QA60: 0.0 +SCL: 0.0 +TCI_B: 0.0 +TCI_G: 0.0 +TCI_R: 0.0 +WVP: 0.0 diff --git a/tests/test_ImageCollection/test_get_scale_params.yml b/tests/test_ImageCollection/test_get_scale_params.yml new file mode 100644 index 00000000..46506551 --- /dev/null +++ b/tests/test_ImageCollection/test_get_scale_params.yml @@ -0,0 +1,23 @@ +AOT: 0.001 +B1: 0.0001 +B11: 0.0001 +B12: 0.0001 +B2: 0.0001 +B3: 0.0001 +B4: 0.0001 +B5: 0.0001 +B6: 0.0001 +B7: 0.0001 +B8: 0.0001 +B8A: 0.0001 +B9: 0.0001 +MSK_CLDPRB: 1.0 +MSK_SNWPRB: 1.0 +QA10: 1.0 +QA20: 1.0 +QA60: 1.0 +SCL: 1.0 +TCI_B: 1.0 +TCI_G: 1.0 +TCI_R: 1.0 +WVP: 0.001 diff --git a/tests/test_ImageCollection/test_get_stac.yml b/tests/test_ImageCollection/test_get_stac.yml new file mode 100644 index 00000000..34eb79d2 --- /dev/null +++ b/tests/test_ImageCollection/test_get_stac.yml @@ -0,0 +1,619 @@ +description: 'See also collection COPERNICUS/S2_SR_HARMONIZED that shifts data with + + PROCESSING_BASELINE ''04.00'' or above (after 2022-01-25) to be in the same range + + as in older scenes. + + + Sentinel-2 is a wide-swath, high-resolution, multi-spectral + + imaging mission supporting Copernicus Land Monitoring studies, + + including the monitoring of vegetation, soil and water cover, + + as well as observation of inland waterways and coastal areas. + + + The Sentinel-2 L2 data are downloaded from scihub. They were + + computed by running sen2cor. WARNING: ESA did not produce L2 data + + for all L1 assets, and earlier L2 coverage is not global. + + + The assets contain + + 12 UINT16 spectral bands representing SR scaled by 10000 (unlike in L1 data, + + there is no B10). There are also several more L2-specific bands (see band + + list for details). See the + + [Sentinel-2 User Handbook](https://sentinel.esa.int/documents/247904/685211/Sentinel-2_User_Handbook) + + for details. In addition, three QA bands are present where one + + (QA60) is a bitmask band with cloud mask information. For more + + details, [see the full explanation of how cloud masks are computed.](https://sentinel.esa.int/web/sentinel/technical-guides/sentinel-2-msi/level-1c/cloud-masks) + + + EE asset ids for Sentinel-2 L2 assets have the following format: + + COPERNICUS/S2_SR/20151128T002653_20151128T102149_T56MNN. Here the + + first numeric part represents the sensing date and time, the + + second numeric part represents the product generation date and + + time, and the final 6-character string is a unique granule identifier + + indicating its UTM grid reference (see [MGRS](https://en.wikipedia.org/wiki/Military_Grid_Reference_System)). + + + For datasets to assist with cloud and/or cloud shadow detection, see [COPERNICUS/S2_CLOUD_PROBABILITY](COPERNICUS_S2_CLOUD_PROBABILITY) + + and [GOOGLE/CLOUD_SCORE_PLUS/V1/S2_HARMONIZED](GOOGLE_CLOUD_SCORE_PLUS_V1_S2_HARMONIZED). + + + For more details on Sentinel-2 radiometric resolution, [see this page](https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi/resolutions/radiometric). + + ' +extent: + spatial: + bbox: + - - -180 + - -56 + - 180 + - 83 + temporal: + interval: + - - '2017-03-28T00:00:00Z' + - '2023-11-09T19:01:18' +gee:interval: + interval: 5 + type: revisit_interval + unit: day +gee:terms_of_use: 'The use of Sentinel data is governed by the [Copernicus + + Sentinel Data Terms and Conditions.](https://scihub.copernicus.eu/twiki/pub/SciHubWebPortal/TermsConditions/Sentinel_Data_Terms_and_Conditions.pdf) + + ' +gee:type: image_collection +id: COPERNICUS/S2_SR +keywords: +- copernicus +- esa +- eu +- msi +- reflectance +- sentinel +- sr +license: proprietary +links: +- href: https://storage.googleapis.com/earthengine-stac/catalog/COPERNICUS/COPERNICUS_S2_SR.json + rel: self + type: application/json +- href: https://storage.googleapis.com/earthengine-stac/catalog/COPERNICUS/catalog.json + rel: parent + type: application/json +- href: https://storage.googleapis.com/earthengine-stac/catalog/catalog.json + rel: root + type: application/json +- code: JavaScript + href: https://code.earthengine.google.com/?scriptPath=Examples:Datasets/COPERNICUS/COPERNICUS_S2_SR + rel: related + title: Run the example for COPERNICUS/S2_SR in the Earth Engine Code Editor + type: text/html +- href: https://developers.google.com/earth-engine/datasets/images/COPERNICUS/COPERNICUS_S2_SR_sample.png + rel: preview + type: image/png +- href: https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR#terms-of-use + rel: license + type: text/html +providers: +- name: European Union/ESA/Copernicus + roles: + - licensor + - producer + url: https://sentinel.esa.int/web/sentinel/user-guides/sentinel-2-msi/processing-levels/level-2 +- name: Google Earth Engine + roles: + - host + url: https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR +stac_extensions: +- https://stac-extensions.github.io/eo/v1.0.0/schema.json +stac_version: 1.0.0 +summaries: + MSK_CLDPRB: + gee:estimated_range: false + maximum: 100 + minimum: 0 + MSK_SNWPRB: + gee:estimated_range: false + maximum: 100 + minimum: 0 + SCL: + gee:estimated_range: false + maximum: 11 + minimum: 1 + eo:bands: + - center_wavelength: 0.4439 + description: Aerosols + gee:scale: 0.0001 + gee:wavelength: 443.9nm (S2A) / 442.3nm (S2B) + gsd: 60 + name: B1 + - center_wavelength: 0.4966 + description: Blue + gee:scale: 0.0001 + gee:wavelength: 496.6nm (S2A) / 492.1nm (S2B) + gsd: 10 + name: B2 + - center_wavelength: 0.56 + description: Green + gee:scale: 0.0001 + gee:wavelength: 560nm (S2A) / 559nm (S2B) + gsd: 10 + name: B3 + - center_wavelength: 0.6645 + description: Red + gee:scale: 0.0001 + gee:wavelength: 664.5nm (S2A) / 665nm (S2B) + gsd: 10 + name: B4 + - center_wavelength: 0.7039 + description: Red Edge 1 + gee:scale: 0.0001 + gee:wavelength: 703.9nm (S2A) / 703.8nm (S2B) + gsd: 20 + name: B5 + - center_wavelength: 0.7402 + description: Red Edge 2 + gee:scale: 0.0001 + gee:wavelength: 740.2nm (S2A) / 739.1nm (S2B) + gsd: 20 + name: B6 + - center_wavelength: 0.7825 + description: Red Edge 3 + gee:scale: 0.0001 + gee:wavelength: 782.5nm (S2A) / 779.7nm (S2B) + gsd: 20 + name: B7 + - center_wavelength: 0.8351 + description: NIR + gee:scale: 0.0001 + gee:wavelength: 835.1nm (S2A) / 833nm (S2B) + gsd: 10 + name: B8 + - center_wavelength: 0.8648 + description: Red Edge 4 + gee:scale: 0.0001 + gee:wavelength: 864.8nm (S2A) / 864nm (S2B) + gsd: 20 + name: B8A + - center_wavelength: 0.945 + description: Water vapor + gee:scale: 0.0001 + gee:wavelength: 945nm (S2A) / 943.2nm (S2B) + gsd: 60 + name: B9 + - center_wavelength: 1.6137 + description: SWIR 1 + gee:scale: 0.0001 + gee:wavelength: 1613.7nm (S2A) / 1610.4nm (S2B) + gsd: 20 + name: B11 + - center_wavelength: 2.2024 + description: SWIR 2 + gee:scale: 0.0001 + gee:wavelength: 2202.4nm (S2A) / 2185.7nm (S2B) + gsd: 20 + name: B12 + - description: Aerosol Optical Thickness + gee:scale: 0.001 + gsd: 10 + name: AOT + - description: 'Water Vapor Pressure. The height the water would occupy if the vapor + were condensed into + + liquid and spread evenly across the column. + + ' + gee:scale: 0.001 + gee:units: cm + gsd: 10 + name: WVP + - description: Scene Classification Map (The "No Data" value of 0 is masked out) + gee:classes: + - color: ff0004 + description: Saturated or defective + value: 1 + - color: '868686' + description: Dark Area Pixels + value: 2 + - color: 774b0a + description: Cloud Shadows + value: 3 + - color: 10d22c + description: Vegetation + value: 4 + - color: ffff52 + description: Bare Soils + value: 5 + - color: 0000ff + description: Water + value: 6 + - color: '818181' + description: Clouds Low Probability / Unclassified + value: 7 + - color: c0c0c0 + description: Clouds Medium Probability + value: 8 + - color: f1f1f1 + description: Clouds High Probability + value: 9 + - color: bac5eb + description: Cirrus + value: 10 + - color: 52fff9 + description: Snow / Ice + value: 11 + gsd: 20 + name: SCL + - description: True Color Image, Red channel + gsd: 10 + name: TCI_R + - description: True Color Image, Green channel + gsd: 10 + name: TCI_G + - description: True Color Image, Blue channel + gsd: 10 + name: TCI_B + - description: Cloud Probability Map (missing in some products) + gsd: 20 + name: MSK_CLDPRB + - description: Snow Probability Map (missing in some products) + gsd: 10 + name: MSK_SNWPRB + - description: Always empty + gsd: 10 + name: QA10 + - description: Always empty + gsd: 20 + name: QA20 + - description: Cloud mask + gee:bitmask: + bitmask_parts: + - bit_count: 10 + description: Unused + first_bit: 0 + - bit_count: 1 + description: Opaque clouds + first_bit: 10 + values: + - description: No opaque clouds + value: 0 + - description: Opaque clouds present + value: 1 + - bit_count: 1 + description: Cirrus clouds + first_bit: 11 + values: + - description: No cirrus clouds + value: 0 + - description: Cirrus clouds present + value: 1 + total_bit_count: 12 + gsd: 60 + name: QA60 + gee:schema: + - description: Accuracy of Aerosol Optical thickness model + name: AOT_RETRIEVAL_ACCURACY + type: DOUBLE + - description: Granule-specific cloudy pixel percentage taken from the original + metadata + name: CLOUDY_PIXEL_PERCENTAGE + type: DOUBLE + - description: 'Cloudy pixel percentage for the whole archive that + + contains this granule. Taken from the original metadata + + ' + name: CLOUD_COVERAGE_ASSESSMENT + type: DOUBLE + - description: Percentage of pixels classified as cloud shadow + name: CLOUDY_SHADOW_PERCENTAGE + type: DOUBLE + - description: Percentage of pixels classified as dark features or shadows + name: DARK_FEATURES_PERCENTAGE + type: DOUBLE + - description: Unique identifier of the datastrip Product Data Item (PDI) + name: DATASTRIP_ID + type: STRING + - description: 'Uniquely identifies a given Datatake. The ID contains + + the Sentinel-2 satellite, start date and time, absolute orbit + + number, and processing baseline. + + ' + name: DATATAKE_IDENTIFIER + type: STRING + - description: MSI operation mode + name: DATATAKE_TYPE + type: STRING + - description: Percentage of degraded MSI and ancillary data + name: DEGRADED_MSI_DATA_PERCENTAGE + type: DOUBLE + - description: 'Synthesis of the On-Line Quality Control (OLQC) checks + + performed at granule (Product_Syntax) and datastrip (Product + + Syntax and DS_Consistency) levels + + ' + name: FORMAT_CORRECTNESS + type: STRING + - description: Synthesis of the OLQC checks performed at the datastrip level (Relative_Orbit_Number) + name: GENERAL_QUALITY + type: STRING + - description: Product generation time + name: GENERATION_TIME + type: DOUBLE + - description: Synthesis of the OLQC checks performed at the datastrip level (Attitude_Quality_Indicator) + name: GEOMETRIC_QUALITY + type: STRING + - description: Unique identifier of the granule PDI (PDI_ID) + name: GRANULE_ID + type: STRING + - description: Percentage of pixels classified as high probability clouds + name: HIGH_PROBA_CLOUDS_PERCENTAGE + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B1 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B1 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B2 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B2 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B3 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B3 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B4 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B4 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B5 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B5 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B6 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B6 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B7 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B7 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B8 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B8 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B8a and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B8A + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B9 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B9 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B10 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B10 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B11 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B11 + type: DOUBLE + - description: Mean value containing viewing incidence azimuth angle average for + band B12 and for all detectors + name: MEAN_INCIDENCE_AZIMUTH_ANGLE_B12 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B1 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B1 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B2 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B2 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B3 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B3 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B4 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B4 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B5 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B5 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B6 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B6 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B7 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B7 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B8 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B8 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B8a and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B8A + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B9 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B9 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B10 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B10 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B11 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B11 + type: DOUBLE + - description: Mean value containing viewing incidence zenith angle average for + band B12 and for all detectors + name: MEAN_INCIDENCE_ZENITH_ANGLE_B12 + type: DOUBLE + - description: Mean value containing sun azimuth angle average for all bands and + detectors + name: MEAN_SOLAR_AZIMUTH_ANGLE + type: DOUBLE + - description: Mean value containing sun zenith angle average for all bands and + detectors + name: MEAN_SOLAR_ZENITH_ANGLE + type: DOUBLE + - description: Percentage of pixels classified as medium probability clouds + name: MEDIUM_PROBA_CLOUDS_PERCENTAGE + type: DOUBLE + - description: US-Military Grid Reference System (MGRS) tile + name: MGRS_TILE + type: STRING + - description: Percentage of No Data pixels + name: NODATA_PIXEL_PERCENTAGE + type: DOUBLE + - description: Percentage of pixels classified as non-vegetated + name: NOT_VEGETATED_PERCENTAGE + type: DOUBLE + - description: 'Configuration baseline used at the time of the product + + generation in terms of processor software version and major Ground + + Image Processing Parameters (GIPP) version + + ' + name: PROCESSING_BASELINE + type: STRING + - description: The full id of the original Sentinel-2 product + name: PRODUCT_ID + type: STRING + - description: Accuracy of radiative transfer model + name: RADIATIVE_TRANSFER_ACCURACY + type: DOUBLE + - description: Based on the OLQC reports contained in the Datastrips/QI_DATA with + RADIOMETRIC_QUALITY checklist name + name: RADIOMETRIC_QUALITY + type: STRING + - description: Earth-Sun distance correction factor + name: REFLECTANCE_CONVERSION_CORRECTION + type: DOUBLE + - description: Percentage of saturated or defective pixels + name: SATURATED_DEFECTIVE_PIXEL_PERCENTAGE + type: DOUBLE + - description: Imaging orbit direction + name: SENSING_ORBIT_DIRECTION + type: STRING + - description: Imaging orbit number + name: SENSING_ORBIT_NUMBER + type: DOUBLE + - description: 'Synthesis of the OLQC checks performed at granule + + (Missing_Lines, Corrupted_ISP, and Sensing_Time) and datastrip + + (Degraded_SAD and Datation_Model) levels + + ' + name: SENSOR_QUALITY + type: STRING + - description: Mean solar exoatmospheric irradiance for band B1 + name: SOLAR_IRRADIANCE_B1 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B2 + name: SOLAR_IRRADIANCE_B2 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B3 + name: SOLAR_IRRADIANCE_B3 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B4 + name: SOLAR_IRRADIANCE_B4 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B5 + name: SOLAR_IRRADIANCE_B5 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B6 + name: SOLAR_IRRADIANCE_B6 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B7 + name: SOLAR_IRRADIANCE_B7 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B8 + name: SOLAR_IRRADIANCE_B8 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B8a + name: SOLAR_IRRADIANCE_B8A + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B9 + name: SOLAR_IRRADIANCE_B9 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B10 + name: SOLAR_IRRADIANCE_B10 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B11 + name: SOLAR_IRRADIANCE_B11 + type: DOUBLE + - description: Mean solar exoatmospheric irradiance for band B12 + name: SOLAR_IRRADIANCE_B12 + type: DOUBLE + - description: Percentage of pixels classified as snow or ice + name: SNOW_ICE_PERCENTAGE + type: DOUBLE + - description: 'Sentinel-2 spacecraft name: Sentinel-2A, Sentinel-2B' + name: SPACECRAFT_NAME + type: STRING + - description: Percentage of pixels classified as thin cirrus clouds + name: THIN_CIRRUS_PERCENTAGE + type: DOUBLE + - description: Percentage of unclassified pixels + name: UNCLASSIFIED_PERCENTAGE + type: DOUBLE + - description: Percentage of pixels classified as vegetation + name: VEGETATION_PERCENTAGE + type: DOUBLE + - description: Percentage of pixels classified as water + name: WATER_PERCENTAGE + type: DOUBLE + - description: Declared accuracy of the Water Vapor model + name: WATER_VAPOUR_RETRIEVAL_ACCURACY + type: DOUBLE + gee:visualizations: + - display_name: RGB + image_visualization: + band_vis: + bands: + - B4 + - B3 + - B2 + max: + - 3000 + min: + - 0 + lookat: + lat: 17.7009 + lon: 83.277 + zoom: 12 + instruments: + - MSI + platform: + - Sentinel-2A + - Sentinel-2B +title: 'Sentinel-2 MSI: MultiSpectral Instrument, Level-2A' +type: Collection diff --git a/tests/test_ImageCollection/test_mask_s2.yml b/tests/test_ImageCollection/test_mask_s2.yml new file mode 100644 index 00000000..eb745b9d --- /dev/null +++ b/tests/test_ImageCollection/test_mask_s2.yml @@ -0,0 +1,27 @@ +AOT: 354 +B1: 590.1282626148882 +B11: 1986.408679038172 +B12: 1522.041930986203 +B2: 564.3067665633657 +B3: 784.1374447316587 +B4: 821.3239234989915 +B5: 1217.444006928795 +B6: 2003.3571632505652 +B7: 2236.471328753445 +B8: 2390.463396773381 +B8A: 2407.3933334274057 +B9: 2422.9298342189186 +CLOUD_MASK: 0 +CLOUD_MASK_CDI: 0 +CLOUD_SHADOW_MASK: 0 +MSK_CLDPRB: null +MSK_SNWPRB: null +QA10: 0 +QA20: 0 +QA60: 0 +SCL: 4.598658742666373 +SHADOW_MASK: 0 +TCI_B: 57.67908547074245 +TCI_G: 80.82178790722993 +TCI_R: 83.79020123070153 +WVP: 602.9321499075425 diff --git a/tests/test_ImageCollection/test_mask_s2_sr.yml b/tests/test_ImageCollection/test_mask_s2_sr.yml new file mode 100644 index 00000000..4a2eead1 --- /dev/null +++ b/tests/test_ImageCollection/test_mask_s2_sr.yml @@ -0,0 +1,27 @@ +AOT: 199 +B1: 16640.09480520156 +B11: 1568.290431797825 +B12: 1968.3295548610204 +B2: 15419.765396984198 +B3: 14101.613293902175 +B4: 12931.892357709678 +B5: 14237.406449381348 +B6: 12795.376786673241 +B7: 11806.409740942607 +B8: 12098.870170407052 +B8A: 11006.397712738837 +B9: 15899 +CLOUD_MASK: 1 +CLOUD_MASK_CDI: 0 +CLOUD_SHADOW_MASK: 0 +MSK_CLDPRB: 0 +MSK_SNWPRB: 0 +QA10: 0 +QA20: 0 +QA60: 1024 +SCL: 8 +SHADOW_MASK: 0 +TCI_B: 255 +TCI_G: 255 +TCI_R: 255 +WVP: 3645 diff --git a/tests/test_ImageCollection/test_pan_sharpen.yml b/tests/test_ImageCollection/test_pan_sharpen.yml new file mode 100644 index 00000000..018df447 --- /dev/null +++ b/tests/test_ImageCollection/test_pan_sharpen.yml @@ -0,0 +1,6 @@ +B2: 0.42341190228971326 +B3: 0.39605810178360285 +B4: 0.3992878130436489 +B5: 0.5636123119616493 +B6: 0.4296632179937829 +B7: 0.3645338736877478 diff --git a/tests/test_ImageCollection/test_preprocess.yml b/tests/test_ImageCollection/test_preprocess.yml new file mode 100644 index 00000000..06608d47 --- /dev/null +++ b/tests/test_ImageCollection/test_preprocess.yml @@ -0,0 +1,23 @@ +AOT: null +B1: null +B11: null +B12: null +B2: null +B3: null +B4: null +B5: null +B6: null +B7: null +B8: null +B8A: null +B9: null +MSK_CLDPRB: null +MSK_SNWPRB: null +QA10: null +QA20: null +QA60: null +SCL: null +TCI_B: null +TCI_G: null +TCI_R: null +WVP: null diff --git a/tests/test_ImageCollection/test_scale_and_offset.yml b/tests/test_ImageCollection/test_scale_and_offset.yml new file mode 100644 index 00000000..458060b0 --- /dev/null +++ b/tests/test_ImageCollection/test_scale_and_offset.yml @@ -0,0 +1,23 @@ +AOT: 0.199 +B1: 1.6640094805201937 +B11: 0.1568290431797771 +B12: 0.19683295548610844 +B2: 1.5419765396983116 +B3: 1.4101613293903945 +B4: 1.293189235771166 +B5: 1.4237406449383538 +B6: 1.2795376786673232 +B7: 1.1806409740941775 +B8: 1.2098870170405167 +B8A: 1.1006397712739482 +B9: 1.5899 +MSK_CLDPRB: 0 +MSK_SNWPRB: 0 +QA10: 0 +QA20: 0 +QA60: 1024 +SCL: 8 +TCI_B: 255 +TCI_G: 255 +TCI_R: 255 +WVP: 3.645 diff --git a/tests/test_ImageCollection/test_spectral_indices.yml b/tests/test_ImageCollection/test_spectral_indices.yml new file mode 100644 index 00000000..64df49eb --- /dev/null +++ b/tests/test_ImageCollection/test_spectral_indices.yml @@ -0,0 +1,25 @@ +AOT: 199 +B1: 16640.09480520156 +B11: 1568.290431797825 +B12: 1968.3295548610204 +B2: 15419.765396984198 +B3: 14101.613293902175 +B4: 12931.892357709678 +B5: 14237.406449381348 +B6: 12795.376786673241 +B7: 11806.409740942607 +B8: 12098.870170407052 +B8A: 11006.397712738837 +B9: 15899 +MSK_CLDPRB: 0 +MSK_SNWPRB: 0 +NDVI: -0.033280621230818465 +NDWI: 0.07643880717013143 +QA10: 0 +QA20: 0 +QA60: 1024 +SCL: 8 +TCI_B: 255 +TCI_G: 255 +TCI_R: 255 +WVP: 3645 diff --git a/tests/test_ImageCollection/test_tasseled_cap.yml b/tests/test_ImageCollection/test_tasseled_cap.yml new file mode 100644 index 00000000..002caeaf --- /dev/null +++ b/tests/test_ImageCollection/test_tasseled_cap.yml @@ -0,0 +1,16 @@ +B1: 10066.350413384662 +B10: 1985.183464943073 +B11: 1396.123476099155 +B12: 1472.0991456509985 +B2: 9949.62239642315 +B3: 9518.508209960955 +B4: 9810.827737020161 +B5: 9841.473886568694 +B6: 9820.702220202185 +B7: 9893.375333069614 +B8: 9659.31928839866 +B8A: 9780.89342279154 +B9: 6767.9725350249855 +TCB: 29088.33031388279 +TCG: -2787.9802013251656 +TCW: 1808.6277587458064