CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-geedim

Export and cloud mask Google Earth Engine imagery with automated composite creation and filtering capabilities.

Pending
Overview
Eval results
Files

cloud-masking.mddocs/

Cloud Masking

Specialized cloud and shadow masking for Landsat and Sentinel-2 imagery with configurable algorithms and thresholds. Geedim provides automatic detection of image collection types and applies appropriate masking methods.

Capabilities

Cloud Mask Methods

Enumeration of available cloud masking methods for Sentinel-2 imagery.

class CloudMaskMethod(Enum):
    """Enumeration for Sentinel-2 cloud masking methods."""
    
    cloud_prob = 'cloud-prob'
    """
    Threshold the Sentinel-2 Cloud Probability.
    
    Deprecated since version 1.9.0: Please use the cloud_score method.
    """
    
    qa = 'qa'  
    """
    Bit mask the QA60 quality assessment band.
    
    Deprecated since version 1.9.0: Please use the cloud_score method.
    """
    
    cloud_score = 'cloud-score'
    """
    Threshold the Sentinel-2 Cloud Score+.
    
    Uses the GOOGLE_CLOUD_SCORE_PLUS_V1_S2_HARMONIZED dataset.
    """

Cloud Score Bands

Enumeration for Sentinel-2 Cloud Score+ bands used with the cloud_score method.

class CloudScoreBand(Enum):
    """Enumeration for Sentinel-2 Cloud Score+ bands."""
    
    cs = 'cs'
    """
    Pixel quality score based on spectral distance from a clear reference.
    """
    
    cs_cdf = 'cs_cdf' 
    """
    Cumulative distribution function value of possible cs values.
    """

Masking Parameters

Cloud masking parameters vary by image collection type:

Landsat Parameters

def addMaskBands(
    mask_cirrus: bool = True,
    mask_shadows: bool = True,
    **kwargs
) -> ee.Image:
    """
    Add mask bands for Landsat imagery.
    
    Parameters:
    - mask_cirrus (bool): Whether to mask cirrus clouds (valid for Landsat 8-9)
    - mask_shadows (bool): Whether to mask cloud shadows
    - **kwargs: Additional masking parameters
    """

Sentinel-2 Parameters

def addMaskBands(
    method: CloudMaskMethod = CloudMaskMethod.cloud_score,
    prob: float = 0.6,
    cloud_dist: float = 1000.0,
    band: CloudScoreBand = CloudScoreBand.cs_cdf,
    **kwargs
) -> ee.Image:
    """
    Add mask bands for Sentinel-2 imagery.
    
    Parameters:
    - method (CloudMaskMethod): Cloud masking method
    - prob (float): Cloud probability threshold (0.0-1.0)
    - cloud_dist (float): Maximum cloud distance in meters
    - band (CloudScoreBand): Cloud Score+ band to use
    - **kwargs: Additional masking parameters
    """

Mask Band Types

Different mask bands are automatically added based on image type:

# Common mask bands
FILL_MASK: ee.Image       # Valid data mask
CLOUDLESS_MASK: ee.Image  # Cloud-free pixels mask
CLOUD_DIST: ee.Image      # Distance to nearest cloud (meters)

# Landsat-specific bands  
QA_MASK: ee.Image         # Quality assessment mask
SHADOW_MASK: ee.Image     # Cloud shadow mask
CIRRUS_MASK: ee.Image     # Cirrus cloud mask

# Sentinel-2 specific bands
CLOUD_PROB: ee.Image      # Cloud probability (0-100)
CLOUD_SCORE: ee.Image     # Cloud Score+ value

Coverage Statistics

Calculate cloud coverage and fill statistics for regions:

def set_mask_portions(
    ee_image: ee.Image,
    region: dict | ee.Geometry = None,
    scale: float = None
) -> ee.Image:
    """
    Set FILL_PORTION and CLOUDLESS_PORTION properties.
    
    Parameters:
    - ee_image (ee.Image): Image to analyze
    - region (dict | ee.Geometry, optional): Analysis region
    - scale (float, optional): Analysis scale in meters
    
    Returns:
    ee.Image: Image with portion properties set
    """

Usage Examples

Landsat Cloud Masking

import ee  
import geedim

geedim.Initialize()

# Load Landsat 8 image
image = ee.Image('LANDSAT/LC08/C02/T1_L2/LC08_173083_20200601')

# Add mask bands with custom parameters
masked_image = image.gd.addMaskBands(
    mask_cirrus=True,      # Mask cirrus clouds
    mask_shadows=True      # Mask cloud shadows  
)

# Apply cloud masking
cloud_free = masked_image.maskClouds()

# Check cloud coverage
region = ee.Geometry.Point(-122.4194, 37.7749).buffer(10000)
coverage_image = masked_image.gd.set_mask_portions(region=region, scale=30)

# Get coverage statistics from image properties
fill_portion = coverage_image.get('FILL_PORTION').getInfo()
cloudless_portion = coverage_image.get('CLOUDLESS_PORTION').getInfo()

print(f"Fill portion: {fill_portion}%")
print(f"Cloudless portion: {cloudless_portion}%")

Sentinel-2 Cloud Masking

# Load Sentinel-2 image
s2_image = ee.Image('COPERNICUS/S2_SR_HARMONIZED/20200601T185751_20200601T185931_T10SEG')

# Add mask bands using Cloud Score+ method
masked_s2 = s2_image.gd.addMaskBands(
    method=geedim.CloudMaskMethod.cloud_score,
    prob=0.65,                           # 65% cloud probability threshold
    cloud_dist=2000,                     # 2km maximum cloud distance
    band=geedim.CloudScoreBand.cs_cdf    # Use CDF band
)

# Apply cloud masking  
cloud_free_s2 = masked_s2.maskClouds()

# Alternative: Use legacy methods (deprecated)
legacy_masked = s2_image.gd.addMaskBands(
    method=geedim.CloudMaskMethod.qa,    # QA60 band method
    prob=0.6
)

Collection-Wide Cloud Masking

# Load image collection
collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2') \
    .filterDate('2020-01-01', '2020-12-31') \
    .filterBounds(region)

# Add mask bands to entire collection
masked_collection = collection.gd.addMaskBands(
    mask_cirrus=True,
    mask_shadows=True
)

# Apply cloud masking to all images
cloud_free_collection = masked_collection.maskClouds()

# Filter by cloud coverage
low_cloud_collection = collection.gd.search(
    start_date='2020-06-01',
    end_date='2020-09-30', 
    cloudless_portion=80,    # Minimum 80% cloud-free
    fill_portion=95          # Minimum 95% valid data
)

Advanced Masking Configuration

# Custom cloud distance calculation
def custom_cloud_masking(image):
    # Add mask bands with custom parameters
    masked = image.gd.addMaskBands(
        method=geedim.CloudMaskMethod.cloud_score,
        prob=0.5,               # Lower threshold for more aggressive masking
        cloud_dist=5000,        # 5km cloud buffer
        band=geedim.CloudScoreBand.cs  # Use raw cloud score
    )
    
    # Get cloud distance band for analysis
    cloud_dist = masked.select('CLOUD_DIST')
    
    # Apply masking
    return masked.maskClouds()

# Apply to collection
custom_masked = collection.map(custom_cloud_masking)

Mask Quality Assessment

# Analyze mask quality for a region
def assess_mask_quality(image, region, scale=30):
    # Add mask bands and calculate portions
    masked = image.gd.addMaskBands().gd.set_mask_portions(
        region=region, 
        scale=scale
    )
    
    # Extract mask statistics
    stats = {
        'image_id': image.get('system:id').getInfo(),
        'fill_portion': masked.get('FILL_PORTION').getInfo(),
        'cloudless_portion': masked.get('CLOUDLESS_PORTION').getInfo(),
        'date': image.date().format('YYYY-MM-dd').getInfo()
    }
    
    return stats

# Assess collection
region = ee.Geometry.Rectangle([-122.5, 37.7, -122.3, 37.8])
collection_list = collection.limit(10).getInfo()['features']

quality_stats = []
for img_info in collection_list:
    img = ee.Image(img_info['id'])
    stats = assess_mask_quality(img, region)
    quality_stats.append(stats)

# Print results
for stats in quality_stats:
    print(f"{stats['date']}: {stats['cloudless_portion']:.1f}% cloud-free")

Install with Tessl CLI

npx tessl i tessl/pypi-geedim

docs

cloud-masking.md

collection-operations.md

compositing.md

export-download.md

image-processing.md

index.md

initialization.md

tile.json