Skip to main content 首页 创作者 xjtulyc awesome-rosetta-skills satellite-imagery
satellite-imagery Use this Skill for Google Earth Engine satellite analysis: NDVI time series, LULC classification, change detection, and GeoTIFF export via geemap and ee.
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/xjtulyc/awesome-rosetta-skills --skill satellite-imagery命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... name satellite-imagery description Use this Skill for Google Earth Engine satellite analysis: NDVI time series, LULC classification, change detection, and GeoTIFF export via geemap and ee.
tags ["earth-science","remote-sensing","google-earth-engine","geemap","satellite"] version 1.0.0 authors [{"name":"Rosetta Skills Contributors","github":"@xjtulyc"}] license MIT platforms ["claude-code","codex","gemini-cli","cursor"] dependencies {"python":["geemap>=0.29","earthengine-api>=0.1.380","matplotlib>=3.7","numpy>=1.24","pandas>=2.0","geopandas>=0.14"]} last_updated 2026-03-17 status stable
Satellite Imagery Analysis with Google Earth Engine
One-line summary : Analyze satellite imagery at scale using Google Earth Engine Python API and geemap: NDVI, LULC classification, change detection, and GeoTIFF export.
When to Use This Skill
When computing vegetation indices (NDVI, EVI) from Landsat or Sentinel-2
When classifying land use / land cover (LULC) with Random Forest
When detecting land cover change between two time periods
When creating composites and time series at regional/continental scale
When exporting results to GeoTIFF for downstream GIS analysis
When accessing MODIS, Sentinel-1/2, or Landsat data archives
Trigger keywords : Google Earth Engine, GEE, Sentinel-2, Landsat, NDVI, LULC, land cover classification, change detection, satellite imagery, geemap
Background & Key Concepts
Google Earth Engine (GEE)
GEE is a planetary-scale geospatial analysis platform maintained by Google. It provides access to petabytes of satellite imagery and geospatial datasets with cloud-based computation:
No local data download needed for analysis
Scales from local to global automatically
Python API (earthengine-api) + interactive maps (geemap)
NDVI (Normalized Difference Vegetation Index)
$$
\text{NDVI} = \frac{NIR - Red}{NIR + Red} \in [-1, 1]
$$
Values > 0.3 indicate vegetation; > 0.6 indicates dense forest.
Random Forest Classification
Supervised LULC classification: each pixel is a feature vector (spectral bands + indices) classified into land cover categories (forest, urban, water, agriculture).
Change Detection
Bitemporal comparison: compute band differences or spectral indices between two dates. Magnitude of change $= \sqrt{\sum_i (b_{i,t2} - b_{i,t1})^2}$.
Environment Setup
Install Dependencies
pip install geemap>=0.29 earthengine-api>=0.1.380 matplotlib>=3.7 \
numpy>=1.24 pandas>=2.0 geopandas>=0.14
GEE Authentication
earthengine authenticate
export GOOGLE_APPLICATION_CREDENTIALS="<path-to-service-account-key.json>"
import ee
geemap
:
ee.Initialize()
( )
Exception e:
( )
ee.Authenticate()
ee.Initialize()
import
try
print
"GEE initialized successfully"
except
as
print
f"Authentication needed: {e} "
Verify Installation import ee
ee.Initialize()
s2 = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED" )
print (f"Sentinel-2 collection size (sample): {s2.limit(5 ).size().getInfo()} " )
Core Workflow
Step 1: Load and Filter Satellite Imagery import ee
import geemap
import matplotlib.pyplot as plt
import numpy as np
ee.Initialize()
aoi = ee.Geometry.Rectangle([116.0 , 39.7 , 116.8 , 40.2 ])
def get_sentinel2_composite (aoi, start_date, end_date, cloud_pct=20 ):
"""
Create cloud-free Sentinel-2 median composite for a time period.
Parameters
----------
aoi : ee.Geometry
start_date, end_date : str, format 'YYYY-MM-DD'
cloud_pct : int
Maximum cloud percentage filter
Returns
-------
ee.Image — median composite with SR bands
"""
collection = (ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED" )
.filterBounds(aoi)
.filterDate(start_date, end_date)
.filter (ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE" , cloud_pct))
.select(["B2" , "B3" , "B4" , "B8" , "B11" , "B12" ]))
n_images = collection.size().getInfo()
print (f"Images in collection: {n_images} " )
composite = collection.median()
return composite, collection
composite_2023, collection = get_sentinel2_composite(aoi, "2023-06-01" , "2023-09-30" )
print ("Composite created" )
Map = geemap.Map(center=[39.95 , 116.4 ], zoom=10 )
vis_params = {"bands" : ["B4" , "B3" , "B2" ], "min" : 0 , "max" : 3000 , "gamma" : 1.4 }
Map.addLayer(composite_2023, vis_params, "Sentinel-2 RGB 2023" )
Map.addLayer(aoi, {}, "AOI" )
Map
Step 2: Compute Vegetation Indices import ee
import numpy as np
import matplotlib.pyplot as plt
ee.Initialize()
def add_indices (image ):
"""Add NDVI, EVI, NDWI, NDBI to an image."""
ndvi = image.normalizedDifference(["B8" , "B4" ]).rename("NDVI" )
evi = image.expression(
"2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))" ,
{"NIR" : image.select("B8" ), "RED" : image.select("B4" ),
"BLUE" : image.select("B2" )}
).rename("EVI" )
ndwi = image.normalizedDifference(["B3" , "B8" ]).rename("NDWI" )
ndbi = image.normalizedDifference(["B11" , "B8" ]).rename("NDBI" )
return image.addBands([ndvi, evi, ndwi, ndbi])
aoi = ee.Geometry.Rectangle([116.0 , 39.7 , 116.8 , 40.2 ])
def monthly_ndvi (year, month ):
start = f"{year} -{month:02d} -01"
end = ee.Date(start).advance(1 , "month" ).format ("YYYY-MM-dd" ).getInfo()
img = (ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED" )
.filterBounds(aoi)
.filterDate(start, end)
.filter (ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE" , 30 ))
.median())
if img.bandNames().size().getInfo() == 0 :
return None
ndvi = img.normalizedDifference(["B8" , "B4" ])
mean_ndvi = ndvi.reduceRegion(
reducer=ee.Reducer.mean(), geometry=aoi, scale=30 , maxPixels=1e8
).get("nd" ).getInfo()
return mean_ndvi
print ("Computing monthly NDVI for 2023..." )
months = range (1 , 13 )
ndvi_values = []
for m in months:
val = monthly_ndvi(2023 , m)
ndvi_values.append(val if val else np.nan)
print (f" Month {m:02d} : NDVI={val:.4 f} " if val else f" Month {m:02d} : no data" )
fig, ax = plt.subplots(figsize=(10 , 4 ))
ax.plot(list (months), ndvi_values, 'go-' , linewidth=2 , markersize=8 )
ax.set_xlabel("Month (2023)" )
ax.set_ylabel("Mean NDVI" )
ax.set_title("Monthly NDVI Time Series" )
ax.set_xticks(list (months))
ax.set_xticklabels(["Jan" ,"Feb" ,"Mar" ,"Apr" ,"May" ,"Jun" ,
"Jul" ,"Aug" ,"Sep" ,"Oct" ,"Nov" ,"Dec" ])
ax.grid(True , alpha=0.3 )
plt.tight_layout()
plt.savefig("ndvi_time_series.png" , dpi=150 )
plt.show()
Step 3: LULC Classification with Random Forest import ee
import geemap
ee.Initialize()
aoi = ee.Geometry.Rectangle([116.0 , 39.7 , 116.8 , 40.2 ])
def build_feature_image (aoi, year ):
"""Build multi-band feature image for classification."""
composite = (ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED" )
.filterBounds(aoi)
.filterDate(f"{year} -06-01" , f"{year} -09-30" )
.filter (ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE" , 20 ))
.median()
.select(["B2" ,"B3" ,"B4" ,"B8" ,"B11" ,"B12" ]))
ndvi = composite.normalizedDifference(["B8" , "B4" ]).rename("NDVI" )
ndwi = composite.normalizedDifference(["B3" , "B8" ]).rename("NDWI" )
ndbi = composite.normalizedDifference(["B11" , "B8" ]).rename("NDBI" )
return composite.addBands([ndvi, ndwi, ndbi])
feature_image = build_feature_image(aoi, 2023 )
esri_lc = ee.ImageCollection("projects/sat-io/open-datasets/landcover/ESRI_Global-LULC_10m_TS" )
lc_2020 = esri_lc.filterDate("2020-01-01" , "2020-12-31" ).first().clip(aoi)
training_samples = feature_image.addBands(lc_2020.rename("label" )).stratifiedSample(
numPoints=200 ,
classBand="label" ,
region=aoi,
scale=30 ,
seed=42 ,
)
classifier = ee.Classifier.smileRandomForest(100 ).train(
features=training_samples,
classProperty="label" ,
inputProperties=feature_image.bandNames(),
)
classified = feature_image.classify(classifier)
test_samples = feature_image.addBands(lc_2020.rename("label" )).stratifiedSample(
numPoints=50 , classBand="label" , region=aoi, scale=30 , seed=99
)
validated = test_samples.classify(classifier)
confusion_matrix = validated.errorMatrix("label" , "classification" )
print (f"Overall accuracy: {confusion_matrix.accuracy().getInfo():.4 f} " )
print (f"Kappa: {confusion_matrix.kappa().getInfo():.4 f} " )
export_task = ee.batch.Export.image.toDrive(
image=classified,
description="LULC_Classification_2023" ,
folder="GEE_exports" ,
region=aoi,
scale=30 ,
crs="EPSG:4326" ,
)
export_task.start()
print (f"Export task started: {export_task.status()['state' ]} " )
Advanced Usage
Change Detection (Bitemporal) import ee
import geemap
import matplotlib.pyplot as plt
ee.Initialize()
aoi = ee.Geometry.Rectangle([116.0 , 39.7 , 116.8 , 40.2 ])
def get_composite (aoi, year ):
return (ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED" )
.filterBounds(aoi)
.filterDate(f"{year} -06-01" , f"{year} -09-30" )
.filter (ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE" , 20 ))
.median()
.select(["B4" , "B8" , "B11" ]))
comp_2018 = get_composite(aoi, 2018 )
comp_2023 = get_composite(aoi, 2023 )
ndvi_2018 = comp_2018.normalizedDifference(["B8" , "B4" ])
ndvi_2023 = comp_2023.normalizedDifference(["B8" , "B4" ])
ndvi_change = ndvi_2023.subtract(ndvi_2018).rename("NDVI_change" )
change_mask = ndvi_change.abs ().gt(0.15 )
change_type = ee.Image(0 ).where(ndvi_change.lt(-0.15 ), 1 ).where(ndvi_change.gt(0.15 ), 2 )
print ("Change detection complete" )
print ("Use geemap.Map() to visualize change_type layer interactively" )
Troubleshooting
Error: EEException: User memory limit exceeded Cause : Too large an area or too fine resolution.
result = image.reduceRegion(reducer=ee.Reducer.mean(),
geometry=aoi, scale=100 , maxPixels=1e9 )
aoi_small = aoi.buffer(-10000 )
Error: EEException: Computation timed out Fix : Export to Drive/Cloud Storage instead of getInfo():
task = ee.batch.Export.image.toDrive(image=result, ...)
task.start()
print (task.status())
Version Compatibility Package Tested versions Known issues earthengine-api 0.1.380, 0.1.400 API changes frequently; pin version geemap 0.29, 0.32 Interactive maps require Jupyter
External Resources
Official Documentation
Key Papers
Gorelick, N. et al. (2017). Google Earth Engine: Planetary-scale geospatial analysis for everyone . Remote Sensing of Environment.
Examples
Example 1: Urban Heat Island — Night-Time Light Analysis
import ee, geemap, pandas as pd
ee.Initialize()
def get_monthly_ntl (city_geometry, year, month ):
start = f"{year} -{month:02d} -01"
end = ee.Date(start).advance(1 , "month" ).format ("YYYY-MM-dd" ).getInfo()
ntl = (ee.ImageCollection("NOAA/VIIRS/DNB/MONTHLY_V1/VCMCFG" )
.filterDate(start, end)
.first()
.select("avg_rad" ))
mean_val = ntl.reduceRegion(ee.Reducer.mean(), city_geometry, 500 , maxPixels=1e8 )
return mean_val.get("avg_rad" ).getInfo()
cities = {
"Beijing" : ee.Geometry.Rectangle([116.2 , 39.8 , 116.6 , 40.1 ]),
"Shanghai" : ee.Geometry.Rectangle([121.3 , 31.1 , 121.7 , 31.4 ]),
}
records = []
for month in range (1 , 13 ):
for city, geom in cities.items():
val = get_monthly_ntl(geom, 2023 , month)
records.append({"month" : month, "city" : city, "radiance" : val})
df = pd.DataFrame(records)
print (df.pivot(index="month" , columns="city" , values="radiance" ).round (2 ))
Interpreting these results : Higher radiance values indicate more intense light emissions. Seasonal patterns reflect economic activity cycles.
Last updated: 2026-03-17 | Maintainer: @xjtulyc
Issues: GitHub Issues