Quick Start Guide#

Let’s create your first multi-seasonal NDVI analysis with Ndvi2Gif!

Your First NDVI Analysis#

This example creates a seasonal NDVI composite for a region using Sentinel-2 data.

Step 1: Import and Initialize#

import ee
import geemap
from ndvi2gif import NdviSeasonality

# Initialize Earth Engine
ee.Initialize(project='your-project-id')

Step 2: Define Your Region of Interest (ROI)#

There are multiple ways to define your ROI:

Option A: Draw on a Map#

# Create an interactive map
Map = geemap.Map()
Map.centerObject(ee.Geometry.Point([-3.7, 40.4]), 10)  # Madrid, Spain
Map

# Draw a polygon on the map, then:
roi = Map.user_roi

Option B: Use Coordinates#

# Define a bounding box
roi = ee.Geometry.Rectangle([-3.8, 40.3, -3.6, 40.5])

# Or a point with buffer
roi = ee.Geometry.Point([-3.7, 40.4]).buffer(5000)  # 5km radius

Option C: Use a Shapefile#

# Load from a shapefile
roi = 'path/to/your/area.shp'

Step 3: Create an NDVI Composite#

# Initialize the processor
ndvi_processor = NdviSeasonality(
    roi=roi,
    sat='S2',              # Sentinel-2
    periods=12,            # Monthly composites
    start_year=2023,
    end_year=2024,
    index='ndvi',          # Normalized Difference Vegetation Index
    key='median'           # Use median for cloud-free composites
)

Step 4: Get the Composite#

# get_year_composite() returns an ee.ImageCollection with one image per year.
# Each image has one band per period (january, february, ..., december).
all_composites = ndvi_processor.get_year_composite()

# Select a single year's composite (here, the first year in the range)
composite = all_composites.first()

Step 5: Visualize#

# Visualize on a map
Map = geemap.Map()
vis_params = {
    'min': 0,
    'max': 1,
    'palette': ['red', 'yellow', 'green']
}
# A palette can only be applied to a single band, so select one period (e.g. june)
Map.addLayer(composite.select('june'), vis_params, 'NDVI June')
Map.centerObject(roi, 10)
Map

Step 6: Create an Animated GIF#

# Generate an RGB GIF: one frame per year, using three periods as R, G, B.
# The frame rate (10 fps) and dimensions are set internally.
ndvi_processor.get_gif(
    name='ndvi.gif',
    bands=['march', 'june', 'september']   # [R, G, B] periods
)

Complete Example#

Here’s a complete working example:

import ee
import geemap
from ndvi2gif import NdviSeasonality

# Initialize
ee.Initialize(project='your-project-id')

# Define ROI (small area near Madrid, Spain).
# Kept small on purpose so the direct GeoTIFF download below stays under
# Earth Engine's ~50 MB synchronous limit — see "Export as GeoTIFF".
roi = ee.Geometry.Rectangle([-3.74, 40.38, -3.67, 40.45])

# Create processor
ndvi = NdviSeasonality(
    roi=roi,
    sat='S2',
    periods=12,
    start_year=2023,
    end_year=2024,
    index='ndvi',
    key='median'
)

# Get the yearly composites (ee.ImageCollection, one image per year)
composites = ndvi.get_year_composite()

# Create an RGB GIF (one frame per year)
ndvi.get_gif(name='madrid_ndvi.gif', bands=['march', 'june', 'september'])

print("✓ Analysis complete! Check your GIF file.")

Understanding the Parameters#

sat - Satellite Sensor#

  • 'S2' - Sentinel-2 (10m, 5-day revisit)

  • 'S1' - Sentinel-1 SAR (10m, 6-12 day revisit)

  • 'Landsat' - Landsat 4-9 merged (30m, 16-day revisit)

  • 'MODIS' - MODIS (500m, daily)

  • 'S3' - Sentinel-3 OLCI (300m)

  • 'ERA5' - ERA5-Land climate reanalysis (~11km, daily)

  • 'CHIRPS' - CHIRPS precipitation (~5.5km, daily)

periods - Temporal Resolution#

  • 4 - Seasonal (Winter, Spring, Summer, Fall)

  • 12 - Monthly

  • 24 - Bi-monthly

  • Custom numbers for specific applications

key - Statistical Method#

Central tendency — what the index typically looks like in each period:

  • 'median' - Robust to outliers (recommended for optical)

  • 'max' - Maximum value (good for vegetation indices)

  • 'min' - Minimum value

  • 'mean' - Average value

  • 'sum' - Total sum (accumulation variables such as precipitation)

  • 'percentile' - Custom percentile (specify with percentile parameter)

Dispersion — how much the index varies inside each period, useful to spot phenological change, disturbances or unstable surfaces such as flooded areas:

  • 'std' - Standard deviation

  • 'variance' - Variance

  • 'range' - Maximum minus minimum (amplitude)

  • 'cv' - Coefficient of variation (std / mean)

# Where does NDVI change the most within each season?
variability = NdviSeasonality(
    roi=roi, periods=4, start_year=2020, end_year=2023,
    sat='S2', key='std', index='ndvi'
)
Map.addLayer(variability.get_year_composite().first(), {'min': 0, 'max': 0.2}, 'NDVI std')

Note

'cv' divides by the mean, so it is only meaningful for indices that stay positive. It becomes unstable when the mean approaches zero, and it is negative for Sentinel-1 backscatter in dB — use 'std' or 'range' for SAR instead.

index - Spectral Index#

  • 'ndvi' - Vegetation health

  • 'evi' - Enhanced vegetation index

  • 'ndwi' - Water content

  • 'ndmi' - Moisture stress

  • Many more! See Indices Reference

Export Options#

Export as GeoTIFF#

# Export every year's composite as a GeoTIFF to the current directory.
# Filenames are generated automatically as {sat}_{index}_{key}_{year}.tif
ndvi.get_export(scale=10)

⚠️ Size limit. get_export() downloads directly from Earth Engine, which caps each synchronous request at ~50 MB. A large ROI, many periods (periods=12/24), or a fine scale can easily exceed it and raise Total request size ... must be less than or equal to 50331648 bytes. When that happens, either coarsen the export (ndvi.get_export(scale=30)), shrink the ROI, or export asynchronously to Google Drive (see Export to Google Drive below), which runs as a background task with a far larger budget.

Export to Google Drive#

# Export directly to Google Drive
ndvi.export_to_drive(
    image=composite,
    description='ndvi_madrid_2023',
    folder='earthengine_exports',
    scale=10
)

Common Patterns#

Multi-Year Analysis#

# Analyze multiple years
ndvi = NdviSeasonality(
    roi=roi,
    sat='S2',
    periods=12,
    start_year=2020,
    end_year=2024,
    index='ndvi'
)

# Get the yearly composites (one image per year)
composites = ndvi.get_year_composite()

# Thanks to the band-based design, cross-year seasonal queries are one line.
# For example, the maximum June NDVI across all years:
june_max = composites.select('june').max()

High Temporal Resolution#

# Bi-monthly for detailed monitoring
ndvi = NdviSeasonality(
    roi=roi,
    sat='S2',
    periods=24,  # Every 15 days
    start_year=2024,
    end_year=2024,
    index='ndvi'
)

Using Percentiles#

# Use 85th percentile for cloud contamination
ndvi = NdviSeasonality(
    roi=roi,
    sat='S2',
    periods=12,
    start_year=2023,
    end_year=2024,
    index='ndvi',
    key='percentile',
    percentile=85
)

Next Steps#

Now that you understand the basics, explore:

Tips & Tricks#

  1. Start small: Test with small ROIs and short time periods first

  2. Use median for optical: It’s more robust to clouds

  3. Percentiles are flexible: Try 75th, 85th, or 95th percentiles

  4. SAR uses mean: For Sentinel-1, use key='mean'

  5. Check your projection: ROIs work best in EPSG:4326 (WGS84)