Updated: August 2025 | Categories: Remote Sensing, NDVI, R, Python, Earth Engine
This tutorial demonstrates how to:
Set up Google Earth Engine (GEE) Python API in RStudio using reticulate
Download monthly Sentinel-2 NDVI GeoTIFFs to Google Drive
Visualize NDVI .tif files using R's raster and viridis packages
Perfect for researchers analyzing vegetation trends or land degradation across time.
We'll use a dedicated Conda environment to manage dependencies.
bash
# Create new environment
conda create --name gee-demo python=3.10
# Activate environment
conda activate gee-demo
# Install GEE Python API and supporting libraries
conda install -c conda-forge earthengine-api pandas numpy
# Authenticate Earth Engine (opens browser window)
earthengine authenticate
In RStudio:
install.packages("reticulate") # Install reticulate if needed
library(reticulate)
# Point R to the correct Conda environment
use_condaenv("gee-demo", conda = "auto", required = TRUE)
Load Python packages and initialize Earth Engine:
ee <- import("ee") # Earth Engine
np <- import("numpy") # Numpy
pd <- import("pandas") # Pandas
time <- import("time") # Time (for delays)
# Initialize Earth Engine (optionally specify a GCP project)
ee$Initialize(project = "smart-caster-359617")
r
CopyEdit
kenya_poly <- ee$Geometry$Polygon(list(
list(
c(33.79791, -4.82957),
c(42.01568, -4.82957),
c(42.01568, 5.04587),
c(33.79791, 5.04587),
c(33.79791, -4.82957)
)
))
# Cloud mask using QA60 band
maskClouds <- function(image) {
cloudMask <- image$select('QA60')$bitwiseAnd(py_eval("1 << 10"))$eq(0L)$
And(image$select('QA60')$bitwiseAnd(py_eval("1 << 11"))$eq(0L))
image$updateMask(cloudMask)
}
# NDVI computation
computeNDVI <- function(image) {
image$normalizedDifference(list('B8', 'B4'))$
rename('NDVI')$
copyProperties(image, list('system:time_start'))
}
for (m in 1:12) {
month_start <- ee$Date$fromYMD(2023, m, 1)
month_end <- month_start$advance(1, 'month')
month_str <- sprintf("%02d", m)
monthly_collection <- ee$ImageCollection('COPERNICUS/S2_SR_HARMONIZED')$
filterDate(month_start, month_end)$
filterBounds(kenya_poly)$
filter(ee$Filter$lt('CLOUDY_PIXEL_PERCENTAGE', 20))$
map(maskClouds)$
map(computeNDVI)
monthly_mean <- monthly_collection$mean()$clip(kenya_poly)$
set('system:time_start', month_start$millis())
count <- monthly_collection$size()$getInfo()
if (count > 0) {
task <- ee$batch$Export$image$toDrive(
image = monthly_mean,
description = paste0('NDVI_2023_', month_str),
folder = 'ndvi',
fileNamePrefix = paste0('NDVI_2023_', month_str),
region = kenya_poly,
scale = 50,
crs = 'EPSG:4326',
maxPixels = 1e13
)
task$start()
cat(sprintf('Export task started for NDVI_2023_%s\n', month_str))
} else {
cat(sprintf('No images for month %s, skipping export.\n', month_str))
}
time$sleep(1)
}
cat('All export tasks submitted. Check your Drive under the "ndvi" folder.\n')
After downloading from Google Drive to your local system:
library(raster)
library(viridis)
# Load and rescale NDVI
ndvi <- raster("C:/Users/your_user/Downloads/NDVI_2022_02.tif")
ndvi <- ndvi / 10000 # Convert to standard NDVI range [-1, 1]
# Plot
plot(ndvi, col = viridis(100), main = "NDVI February 2022")
💡 Use terra::rast() instead of raster() if you prefer the terra package.
☁️ QA60 masking removes clouds and cirrus for clean NDVI
📆 You can automate for any year by changing 2023
📂 Exports are saved as .tif in your Google Drive's ndvi folder
By integrating Google Earth Engine with R using reticulate, you can automate powerful remote sensing pipelines—from cloud-masked NDVI extraction to export and local visualization. This approach scales efficiently, is reproducible, and bridges the power of GEE with the flexibility of R.