# Install Earth Engine Python API!pip install earthengine-api -qprint("Earth Engine API installed successfully!")# Import librariesimport eeimport geemap.core as geemapfrom IPython.display import Image # For displaying thumbnail images# Authenticate and Initialize Earth Engineee.Authenticate()ee.Initialize(project='YOUR-PROJECT-ID')Day 1, Session 4: Google Earth Engine Python API
CoPhil 4-Day Advanced Online Training on AI/ML for Earth Observation
Learning Objectives
By the end of this session, you will be able to:
- Authenticate and initialize Google Earth Engine (GEE) in Python
- Understand GEE core concepts (Image, ImageCollection, Feature, FeatureCollection)
- Access and filter Sentinel-1 and Sentinel-2 data collections
- Apply cloud masking and create temporal composites
- Calculate spectral indices (NDVI) at scale
- Export processed data for use in AI/ML workflows
- Apply GEE best practices for computational efficiency
Why Google Earth Engine?
Google Earth Engine provides:
- Petabyte-scale data catalog: Sentinel-1, Sentinel-2, Landsat, MODIS, and more
- Cloud computing: Process data without downloading
- Planetary-scale analysis: Analyze entire countries or continents
- Free access: For research and education
Perfect for preparing training data for AI/ML models!
Prerequisites
- Completion of Session 3 (Python geospatial basics)
- Google Earth Engine account (sign up at https://earthengine.google.com/)
- Basic understanding of Sentinel-1 and Sentinel-2 missions
1. Setup and Authentication
We’ll start by installing and configuring the Google Earth Engine Python API.
The code above will:1. Install the Earth Engine Python API2. Import required libraries (ee and geemap)3. Authenticate your Google account4. Initialize Earth Engine with your project ID
When you run the cell, you’ll be prompted to authenticate. Follow the link, sign in with your Google account, and paste the authorization code back into the notebook.
Important: Project ID Setup
Earth Engine requires a Google Cloud Project for initialization. In the code above, you need to replace 'YOUR-PROJECT-ID' with your actual project ID.
If you don’t have a project yet or encounter the error "ee.Initialize: no project found", follow the troubleshooting steps below.
2.3 Feature and FeatureCollection
Features are vector data (points, lines, polygons with attributes).
# Create a Feature (point with properties)
manila_feature = ee.Feature(
manila_point,
{'name': 'Metro Manila', 'population': 13000000, 'type': 'capital'}
)
print("Manila Feature properties:", manila_feature.getInfo()['properties'])
# Create a FeatureCollection
cities = ee.FeatureCollection([
ee.Feature(ee.Geometry.Point([121.0, 14.6]), {'name': 'Manila', 'pop': 13000000}),
ee.Feature(ee.Geometry.Point([125.6, 7.1]), {'name': 'Davao', 'pop': 1800000}),
ee.Feature(ee.Geometry.Point([123.9, 10.3]), {'name': 'Cebu', 'pop': 3000000})
])
print(f"\nNumber of cities: {cities.size().getInfo()}")2.4 Filters and ReducersFilters select subsets of collections.Reducers aggregate or summarize data.
# This cell demonstrates filtering concepts# Note: This is example code showing how filters work# We'll apply these concepts to real data in Section 3print("Filter and Reducer Concepts:")print("\nFilters select subsets of collections by:")print(" - Geographic bounds: .filterBounds(geometry)")print(" - Date range: .filterDate('2024-06-01', '2024-08-31')")print(" - Metadata: .filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 20)")print("\nReducers aggregate or summarize data:")print(" - Temporal: mean(), median(), max(), min() across time")print(" - Spatial: reduceRegion() for statistics over an area")print("\nWe'll apply these concepts to Sentinel-2 in Section 3!")—## 3. Working with Sentinel-2### 3.1 Define Area of Interest (AOI)We’ll focus on Palawan Province - important for Natural Resource Management.
# Define Palawan AOI
aoi = ee.Geometry.Rectangle([117.8, 9.0, 119.2, 10.8])
# Calculate AOI area
aoi_area_km2 = aoi.area().divide(1e6).getInfo()
print(f"AOI Area: {aoi_area_km2:.2f} km²")
# Visualize AOI bounds
bounds = aoi.bounds().getInfo()['coordinates'][0]
print(f"AOI Bounds: {bounds}")3.2 Access Sentinel-2 Collection
# Define date range (2024 dry season - less clouds)
start_date = '2024-01-01'
end_date = '2024-03-31'
# Access Sentinel-2 Surface Reflectance
s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED') \
.filterBounds(aoi) \
.filterDate(start_date, end_date) \
.filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 30)
print(f"Sentinel-2 images found: {s2.size().getInfo()}")
# List image dates and cloud cover
def get_image_info(image):
date = ee.Date(image.get('system:time_start')).format('YYYY-MM-dd')
clouds = image.get('CLOUDY_PIXEL_PERCENTAGE')
return ee.Feature(None, {'date': date, 'clouds': clouds})
image_info = s2.map(get_image_info).getInfo()['features']
print("\nAvailable images:")
print(f"{'Date':<15} {'Cloud %':>10}")
print("-" * 25)
for info in image_info[:10]: # Show first 10
props = info['properties']
print(f"{props['date']:<15} {props['clouds']:>10.1f}")
if len(image_info) > 10:
print(f"... and {len(image_info) - 10} more images")3.3 Cloud Masking Function
Sentinel-2 Level-2A includes quality bands for cloud masking.
def maskS2clouds(image):
"""
Mask clouds and cirrus in Sentinel-2 imagery using QA60 band.
QA60 is a bitmask band:
- Bit 10: Opaque clouds
- Bit 11: Cirrus clouds
Parameters:
-----------
image : ee.Image
Sentinel-2 Level-2A image
Returns:
--------
ee.Image : Cloud-masked image
"""
qa = image.select('QA60')
# Bits 10 and 11 are clouds and cirrus, respectively
cloudBitMask = 1 << 10
cirrusBitMask = 1 << 11
# Both flags should be set to zero, indicating clear conditions
mask = qa.bitwiseAnd(cloudBitMask).eq(0).And(
qa.bitwiseAnd(cirrusBitMask).eq(0))
return image.updateMask(mask).copyProperties(image, ['system:time_start'])
print("Cloud masking function defined!")
print("This function will:")
print(" 1. Read the QA60 quality band")
print(" 2. Check bits 10 (clouds) and 11 (cirrus)")
print(" 3. Mask pixels where either bit is set")
print(" 4. Preserve image metadata")3.4 Apply Cloud Masking and Create Composite
# Apply cloud mask to all images
s2_masked = s2.map(maskS2clouds)
print(f"Cloud masking applied to {s2_masked.size().getInfo()} images")
# Create median composite
composite = s2_masked.median().clip(aoi)
print("\nMedian composite created!")
print("Why median?")
print(" - Robust to outliers (remaining clouds, shadows)")
print(" - Better than mean for temporal composites")
print(" - Produces clean, cloud-free images")3.5 Visualize with Thumbnail
Earth Engine can generate quick preview images.
# Define visualization parameters for True Color (RGB)
vis_params_rgb = {
'bands': ['B4', 'B3', 'B2'], # Red, Green, Blue
'min': 0,
'max': 3000,
'gamma': 1.4 # Enhance contrast
}
# Get thumbnail URL
thumbnail_url = composite.getThumbURL({
'region': aoi,
'dimensions': 512,
**vis_params_rgb
})
print("True Color Composite (Sentinel-2 RGB):")
display(Image(url=thumbnail_url))# False Color Composite (NIR, Red, Green) - highlights vegetation
vis_params_false = {
'bands': ['B8', 'B4', 'B3'], # NIR, Red, Green
'min': 0,
'max': 4000,
'gamma': 1.4
}
thumbnail_url_false = composite.getThumbURL({
'region': aoi,
'dimensions': 512,
**vis_params_false
})
print("False Color Composite (NIR-R-G) - Vegetation appears RED:")
display(Image(url=thumbnail_url_false))3.6 Calculate NDVI
NDVI = (NIR - Red) / (NIR + Red)
# Calculate NDVI using normalized difference
ndvi = composite.normalizedDifference(['B8', 'B4']).rename('NDVI')
print("NDVI calculated!")
# Get NDVI statistics over AOI
ndvi_stats = ndvi.reduceRegion(
reducer=ee.Reducer.mean().combine(
reducer2=ee.Reducer.minMax(),
sharedInputs=True
),
geometry=aoi,
scale=10, # 10m resolution
maxPixels=1e9
).getInfo()
print("\nNDVI Statistics:")
print(f" Mean: {ndvi_stats['NDVI_mean']:.3f}")
print(f" Min: {ndvi_stats['NDVI_min']:.3f}")
print(f" Max: {ndvi_stats['NDVI_max']:.3f}")# Visualize NDVI
vis_params_ndvi = {
'bands': ['NDVI'],
'min': -0.2,
'max': 0.8,
'palette': ['blue', 'white', 'yellow', 'green', 'darkgreen']
}
thumbnail_url_ndvi = ndvi.getThumbURL({
'region': aoi,
'dimensions': 512,
**vis_params_ndvi
})
print("NDVI (Normalized Difference Vegetation Index):")
print("Blue/White: Water/Bare soil")
print("Yellow: Sparse vegetation")
print("Green: Moderate vegetation")
print("Dark Green: Dense vegetation\n")
display(Image(url=thumbnail_url_ndvi))Exercise 1: Change Location and DatesTask: Modify the code to analyze a different Philippine location and time period.Suggestions:- Metro Manila: [120.9, 14.4, 121.1, 14.7]- Mindanao (Davao): [125.3, 6.9, 125.7, 7.3]- Cebu: [123.7, 10.2, 124.0, 10.5]Try different seasons:- Dry season: January-May- Wet season: June-November
# Your code here
# Example: Metro Manila during wet season
# Define new AOI
manila_aoi = ee.Geometry.Rectangle([120.9, 14.4, 121.1, 14.7])
# New date range (wet season)
new_start = '2024-07-01'
new_end = '2024-09-30'
# Query Sentinel-2
manila_s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED') \
.filterBounds(manila_aoi) \
.filterDate(new_start, new_end) \
.filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 30) \
.map(maskS2clouds)
print(f"Images found: {manila_s2.size().getInfo()}")
# Create composite
manila_composite = manila_s2.median().clip(manila_aoi)
# Visualize
manila_thumb = manila_composite.getThumbURL({
'region': manila_aoi,
'dimensions': 512,
**vis_params_rgb
})
print("\nMetro Manila True Color Composite:")
display(Image(url=manila_thumb))—## 4. Working with Sentinel-1 SARSentinel-1 provides all-weather, day-night radar imagery - essential for the Philippines’ cloudy tropical climate!### 4.1 Access Sentinel-1 Collection
# Define parameters
sar_aoi = palawan_bbox
sar_start = '2024-01-01'
sar_end = '2024-03-31'
# Access Sentinel-1 GRD (Ground Range Detected)
s1 = ee.ImageCollection('COPERNICUS/S1_GRD') \
.filterBounds(sar_aoi) \
.filterDate(sar_start, sar_end) \
.filter(ee.Filter.eq('instrumentMode', 'IW')) \
.filter(ee.Filter.eq('orbitProperties_pass', 'DESCENDING')) \
.filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV')) \
.filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VH'))
print(f"Sentinel-1 images found: {s1.size().getInfo()}")
print("\nFilters applied:")
print(" - Instrument Mode: IW (Interferometric Wide swath)")
print(" - Orbit: Descending (evening pass)")
print(" - Polarization: VV and VH (dual-pol)")
print("\nWhy these filters?")
print(" - IW mode: Standard for land monitoring (250km swath)")
print(" - Descending: Consistent geometry")
print(" - VV/VH: Sensitive to different surface properties")4.2 Create SAR CompositeFor SAR, we use mean to reduce speckle noise.
# Select VV and VH bands
s1_composite = s1.select(['VV', 'VH']).mean().clip(sar_aoi)
print("SAR composite created using mean (reduces speckle)")
# Calculate VV/VH ratio (useful for land cover)
vv_vh_ratio = s1_composite.select('VV').divide(s1_composite.select('VH')).rename('VV_VH_ratio')
print("VV/VH ratio calculated (useful for classification)")4.3 Visualize SAR Data
# VV polarization visualization
vis_params_vv = {
'bands': ['VV'],
'min': -25,
'max': 0
}
sar_thumb_vv = s1_composite.getThumbURL({
'region': sar_aoi,
'dimensions': 512,
**vis_params_vv
})
print("Sentinel-1 VV Polarization (dB):")
print("Dark areas: Water, calm surfaces (low backscatter)")
print("Bright areas: Urban, rough surfaces (high backscatter)\n")
display(Image(url=sar_thumb_vv))# False color SAR (VV, VH, VV/VH ratio)
sar_false_color = ee.Image.cat([
s1_composite.select('VV'),
s1_composite.select('VH'),
vv_vh_ratio
])
vis_params_sar_false = {
'min': [-25, -25, 0],
'max': [0, 0, 2]
}
sar_thumb_false = sar_false_color.getThumbURL({
'region': sar_aoi,
'dimensions': 512,
**vis_params_sar_false
})
print("Sentinel-1 False Color (VV-VH-Ratio):")
display(Image(url=sar_thumb_false))Exercise 2: Compare VV and VH PolarizationsTask: Create side-by-side visualizations of VV and VH polarizations.Hint: VH is often more sensitive to vegetation volume.
# Your code here
# Solution:
# VH polarization
vis_params_vh = {
'bands': ['VH'],
'min': -25,
'max': 0
}
sar_thumb_vh = s1_composite.getThumbURL({
'region': sar_aoi,
'dimensions': 512,
**vis_params_vh
})
print("VV Polarization:")
display(Image(url=sar_thumb_vv))
print("\nVH Polarization (more sensitive to vegetation structure):")
display(Image(url=sar_thumb_vh))
print("\nKey Differences:")
print(" - VV: Better for water detection, urban areas")
print(" - VH: Better for vegetation, forest structure")
print(" - Using both improves classification accuracy!")5. Data Export for AI/ML Workflows
To train custom ML models, we need to export data from Earth Engine.
5.1 Export Image to Google Drive
# Export Sentinel-2 composite
export_task_s2 = ee.batch.Export.image.toDrive(
image=composite.select(['B2', 'B3', 'B4', 'B8']), # Select bands to export
description='Palawan_S2_Composite_Q1_2024',
folder='EarthEngine_Exports',
fileNamePrefix='palawan_s2_composite',
region=aoi,
scale=10, # 10m resolution
crs='EPSG:4326',
maxPixels=1e9
)
# Start the export task
export_task_s2.start()
print("Export task started!")
print(f"Task ID: {export_task_s2.id}")
print("\nExport details:")
print(f" - Destination: Google Drive/EarthEngine_Exports/")
print(f" - Filename: palawan_s2_composite.tif")
print(f" - Bands: B2, B3, B4, B8 (Blue, Green, Red, NIR)")
print(f" - Resolution: 10m")
print(f" - Format: GeoTIFF")
print("\nMonitor status at: https://code.earthengine.google.com/tasks")5.2 Check Export Status
# Check task status
task_status = export_task_s2.status()
print(f"Task Status: {task_status['state']}")
if task_status['state'] == 'RUNNING':
print("Task is running... Check back in a few minutes.")
elif task_status['state'] == 'COMPLETED':
print("Task completed! Check your Google Drive.")
elif task_status['state'] == 'FAILED':
print(f"Task failed: {task_status.get('error_message', 'Unknown error')}")
else:
print(f"Task state: {task_status['state']}")5.3 Export NDVI
# Export NDVI layer
export_task_ndvi = ee.batch.Export.image.toDrive(
image=ndvi,
description='Palawan_NDVI_Q1_2024',
folder='EarthEngine_Exports',
fileNamePrefix='palawan_ndvi',
region=aoi,
scale=10,
crs='EPSG:4326',
maxPixels=1e9
)
export_task_ndvi.start()
print(f"NDVI export started!")
print(f"Task ID: {export_task_ndvi.id}")5.4 Export Training Samples (for ML)
For ML model training, we often need to export training samples as vectors.
# Create sample points for different land cover types
# In practice, you would digitize these in GEE Code Editor or use existing data
# Example: Random sample points
sample_points = composite.sample(
region=aoi,
scale=30, # Sample every 30m
numPixels=1000, # Number of samples
seed=42 # For reproducibility
)
print(f"Generated {sample_points.size().getInfo()} sample points")
# Export samples to Drive as CSV
export_task_samples = ee.batch.Export.table.toDrive(
collection=sample_points,
description='Palawan_Training_Samples',
folder='EarthEngine_Exports',
fileNamePrefix='palawan_samples',
fileFormat='CSV'
)
export_task_samples.start()
print(f"\nSample points export started!")
print(f"Task ID: {export_task_samples.id}")
print("\nThese samples can be used for:")
print(" - Training ML classifiers")
print(" - Validating model predictions")
print(" - Feature engineering")Exercise 3: Export Custom AOI CompositeTask: Export a composite for your chosen location from Exercise 1.Requirements:- Use your custom AOI- Export RGB bands (B2, B3, B4)- 10m resolution- Give it a meaningful filename
# Your code here
# Solution template:
my_export_task = ee.batch.Export.image.toDrive(
image=manila_composite.select(['B2', 'B3', 'B4']),
description='My_Custom_Export',
folder='EarthEngine_Exports',
fileNamePrefix='my_custom_composite',
region=manila_aoi,
scale=10,
crs='EPSG:4326',
maxPixels=1e9
)
my_export_task.start()
print(f"Custom export started! Task ID: {my_export_task.id}")—## 6. Integration with AI/ML Workflows### 6.1 Preparing Training DataEarth Engine excels at preparing analysis-ready data for ML.
# Create a multi-band image stack for ML
ml_stack = composite.select(['B2', 'B3', 'B4', 'B8', 'B11', 'B12']) \
.addBands(ndvi) \
.addBands(s1_composite.select(['VV', 'VH']))
print("ML-ready image stack created!")
print("\nBands included:")
band_names = ml_stack.bandNames().getInfo()
for i, band in enumerate(band_names, 1):
print(f" {i}. {band}")
print("\nWhy this combination?")
print(" - Optical bands (B2-B12): Spectral information")
print(" - NDVI: Vegetation index")
print(" - SAR (VV, VH): All-weather information")
print(" - Multi-sensor fusion improves classification!")6.2 Sampling for Training
Extract feature vectors for ML model training.
# Define training regions (in practice, digitize or load from shapefile)
# For demonstration, we'll create simple point collections
# Forest training points
forest_points = ee.FeatureCollection([
ee.Feature(ee.Geometry.Point([118.5, 10.2]), {'landcover': 0, 'class_name': 'Forest'}),
ee.Feature(ee.Geometry.Point([118.6, 10.3]), {'landcover': 0, 'class_name': 'Forest'}),
ee.Feature(ee.Geometry.Point([118.4, 10.1]), {'landcover': 0, 'class_name': 'Forest'})
])
# Water training points
water_points = ee.FeatureCollection([
ee.Feature(ee.Geometry.Point([118.2, 9.5]), {'landcover': 1, 'class_name': 'Water'}),
ee.Feature(ee.Geometry.Point([118.3, 9.6]), {'landcover': 1, 'class_name': 'Water'})
])
# Merge training points
training_points = forest_points.merge(water_points)
# Sample image at training points
training_data = ml_stack.sampleRegions(
collection=training_points,
properties=['landcover', 'class_name'],
scale=10
)
print(f"Training samples created: {training_data.size().getInfo()}")
print("\nSample features:")
sample = training_data.first().getInfo()
print(f"Properties: {list(sample['properties'].keys())}")
print("\nNext steps (Day 2):")
print(" 1. Export training data")
print(" 2. Train Random Forest classifier")
print(" 3. Apply classifier to image")
print(" 4. Validate results")6.3 Earth Engine Built-in ML (Preview)
GEE has built-in classifiers for quick prototyping.
# Train a simple classifier (Random Forest)
# Note: This is a preview - we'll cover this in detail on Day 2
classifier = ee.Classifier.smileRandomForest(
numberOfTrees=10
).train(
features=training_data,
classProperty='landcover',
inputProperties=ml_stack.bandNames()
)
# Classify the image
classified = ml_stack.classify(classifier)
print("Simple classification performed!")
print("\nNote: This is a minimal example.")
print("On Day 2, we'll learn:")
print(" - Proper training data collection")
print(" - Feature selection")
print(" - Model validation")
print(" - Accuracy assessment")
# Visualize classification
vis_params_class = {
'min': 0,
'max': 1,
'palette': ['green', 'blue'] # Forest, Water
}
class_thumb = classified.getThumbURL({
'region': aoi,
'dimensions': 512,
**vis_params_class
})
print("\nSimple Classification Result:")
display(Image(url=class_thumb))7. Best Practices and Tips
7.1 Memory Management
Earth Engine Best Practices:1. MEMORY MANAGEMENT:- Avoid .getInfo() on large objects (use for small metadata only)- Use .limit() to restrict collection size during testing- Export large results instead of downloadingExample: Limit collection size for testingpythonlimited_collection = s2.limit(5)print(f"Limited collection size: {limited_collection.size().getInfo()}")2. COMPUTATIONAL QUOTAS:- Free tier: 250GB Cloud Storage, 10k+ compute hours/month- Set maxPixels appropriately (default: 1e8)- Use appropriate scale (don’t oversample)3. EFFICIENCY:- Filter early: bounds → date → metadata- Select only needed bands- Clip to AOI before intensive operations
7.2 When to Use GEE vs Local Processing
USE GOOGLE EARTH ENGINE FOR:- ✓ Data access and pre-processing- ✓ Large-scale spatial analysis- ✓ Time series analysis- ✓ Cloud masking and compositing- ✓ Simple ML (Random Forest, CART)- ✓ Zonal statistics- ✓ Rapid prototypingUSE LOCAL PROCESSING (Python/Colab) FOR:- ✓ Deep learning (CNN, U-Net, LSTM)- ✓ Custom model architectures- ✓ Fine-grained control over training- ✓ Integration with TensorFlow/PyTorch- ✓ Advanced data augmentation- ✓ Transfer learningBEST WORKFLOW:1. Use GEE for data preparation2. Export training data3. Train models locally (Colab GPU)4. Deploy models on new data
7.3 Troubleshooting Common Errors
COMMON ERRORS AND SOLUTIONS:1. ‘User memory limit exceeded’- → Reduce AOI size or increase scale- → Use .limit() on collections- → Export instead of .getInfo()2. ‘Computation timed out’- → Simplify operations- → Filter collections more aggressively- → Break into smaller exports3. ‘EEException: Collection.first: No matching elements’- → Check date range (no images available)- → Verify AOI (outside coverage?)- → Relax filters (clouds, etc.)4. Export task fails- → Check maxPixels limit- → Verify Google Drive space- → Check region coordinates5. ‘Image.select: Pattern X did not match any bands’- → Check band names: .bandNames().getInfo()- → Verify dataset (S2 vs S2_SR bands differ)
—## 8. Key TakeawaysWhat You’ve Learned:1. GEE Authentication & Setup - One-time authentication process - Initialize for each session - Python API basics2. Core GEE Concepts - Geometry: Points, Rectangles, Polygons - Image & ImageCollection: Raster data - Feature & FeatureCollection: Vector data - Filters: Subset data by space, time, metadata - Reducers: Aggregate/summarize data3. Sentinel-2 Workflows - Access surface reflectance data - Cloud masking with QA60 - Create median composites - Calculate NDVI - Visualize with thumbnails4. Sentinel-1 SAR - All-weather imaging capability - VV and VH polarizations - Speckle reduction with mean - Complementary to optical data5. Data Export - Export to Google Drive - Images (GeoTIFF) and tables (CSV) - Monitor tasks - Prepare data for ML6. ML Integration - Prepare multi-band stacks - Sample training data - Built-in classifiers (preview) - GEE ↔︎ Local workflow—## 9. Next StepsDay 2: We’ll apply these skills to build Machine Learning classification models:- Random Forest for land cover classification- Feature engineering and selection- Training data collection strategies- Model validation and accuracy assessment- Philippine case study: Palawan land cover mappingDay 3-4: Advanced deep learning:- CNNs for image classification- U-Net for semantic segmentation- Object detection- Time series analysis with LSTMs—## 10. Additional Resources### Official Documentation- Earth Engine Guide: https://developers.google.com/earth-engine/- Python API Intro: https://developers.google.com/earth-engine/tutorials/community/intro-to-python-api- Data Catalog: https://developers.google.com/earth-engine/datasets/### Tutorials- End-to-End GEE Course: https://courses.spatialthoughts.com/end-to-end-gee.html- GEE Community Tutorials: https://github.com/google/earthengine-community- Awesome Earth Engine: https://github.com/giswqs/Awesome-GEE### Philippine Context- PhilSA: https://philsa.gov.ph/- CoPhil Mirror Site: (Coming 2025)- DOST-ASTI: https://asti.dost.gov.ph/### Books- Cloud-Based Remote Sensing with Google Earth Engine (Cardille et al.)- Earth Observation Using Python (Parente & Pepe)—## 11. Practice ExercisesTo reinforce your learning, try these exercises:### Exercise A: Multi-temporal AnalysisCreate composites for different seasons (dry vs wet) and compare NDVI changes.### Exercise B: Multi-location ComparisonCompare NDVI between different Philippine regions (urban vs forest vs agriculture).### Exercise C: Sentinel-1 Flood DetectionUse SAR data to identify potential flood areas (low VV backscatter).### Exercise D: Data FusionCombine Sentinel-1 and Sentinel-2 to create a comprehensive dataset for classification.### Exercise E: Time SeriesPlot NDVI time series for a specific location over an entire year.—## Congratulations!You’ve completed Day 1 of the CoPhil AI/ML Training!You now have:- ✓ Python geospatial skills (GeoPandas, Rasterio)- ✓ Google Earth Engine proficiency- ✓ Access to petabytes of satellite data- ✓ Ability to prepare data for AI/MLTomorrow: We build our first machine learning models!—Generated with Claude Code for CoPhil Digital Space CampusEU-Philippines Copernicus Capacity Support ProgrammeData-Centric AI for Earth Observation