The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
This vignette demonstrates comprehensive vegetation analysis using GeoSpatialSuite’s 60+ vegetation indices. Learn to monitor plant health, detect stress, and analyze agricultural productivity using satellite imagery.
GeoSpatialSuite supports over 60 vegetation indices across multiple categories:
library(geospatialsuite)
# See all available vegetation indices
all_indices <- list_vegetation_indices()
print(all_indices)
# View detailed information with formulas
detailed_indices <- list_vegetation_indices(detailed = TRUE)
head(detailed_indices)
# Filter by category
basic_indices <- list_vegetation_indices(category = "basic")
stress_indices <- list_vegetation_indices(category = "stress")Basic Vegetation Indices (10): - NDVI, SAVI, MSAVI, OSAVI, EVI, EVI2, DVI, RVI, GNDVI, WDVI
Enhanced/Improved Indices (12): - ARVI, RDVI, PVI, IPVI, TNDVI, GEMI, VARI, TSAVI, ATSAVI, GESAVI, MTVI, CTVI
Red Edge and Advanced Indices (10): - NDRE, MTCI, IRECI, S2REP, PSRI, CRI1, CRI2, ARI1, ARI2, MCARI
Stress and Chlorophyll Indices (12): - PRI, SIPI, CCI, NDNI, CARI, TCARI, MTVI1, MTVI2, TVI, NPCI, RARS, NPQI
All 62 vegetation indices with formulas, ranges, and references:
| Index | Category | Formula | Range | Reference |
|---|---|---|---|---|
| NDVI | basic | (NIR - Red) / (NIR + Red) | [-1, 1] | Rouse et al. (1974) |
| SAVI | basic | ((NIR - Red) / (NIR + Red + L)) * (1 + L), where L=0.5 | [-1, 1.5] | Huete (1988) |
| MSAVI | basic | 0.5 * (2NIR + 1 - sqrt((2NIR + 1)^2 - 8*(NIR - Red))) | [0, 2] | Qi et al. (1994) |
| OSAVI | basic | (NIR - Red) / (NIR + Red + 0.16) | [-1, 1] | Rondeaux et al. (1996) |
| EVI | basic | 2.5 * ((NIR - Red) / (NIR + 6Red - 7.5Blue + 1)) | [-1, 3] | Huete et al. (1997) |
| EVI2 | basic | 2.5 * ((NIR - Red) / (NIR + 2.4*Red + 1)) | [-1, 3] | Jiang et al. (2008) |
| DVI | basic | NIR - Red | [-2, 2] | Richardson & Wiegand (1977) |
| RVI | basic | NIR / Red | [0, 30] | Birth & McVey (1968) |
| GNDVI | basic | (NIR - Green) / (NIR + Green) | [-1, 1] | Gitelson et al. (1996) |
| WDVI | basic | NIR - 0.5 * Red | [-2, 2] | Clevers (1988) |
| ARVI | enhanced | (NIR - (2Red - Blue)) / (NIR + (2Red - Blue)) | [-1, 1] | Kaufman & Tanre (1992) |
| RDVI | enhanced | (NIR - Red) / sqrt(NIR + Red) | [-2, 2] | Roujean & Breon (1995) |
| PVI | enhanced | (NIR - a*Red - b) / sqrt(1 + a^2), where a=1.5, b=10 | [-2, 2] | Richardson & Wiegand (1977) |
| IPVI | enhanced | NIR / (NIR + Red) | [0, 1] | Crippen (1990) |
| TNDVI | enhanced | sqrt(((NIR - Red) / (NIR + Red)) + 0.5) | [0, 1.2] | Deering et al. (1975) |
| GEMI | enhanced | eta * (1 - 0.25eta) - (Red - 0.125) / (1 - Red), where eta = (2(NIR^2 - Red^2) + 1.5NIR + 0.5Red) / (NIR + Red + 0.5) | [-1, 1] | Pinty & Verstraete (1992) |
| VARI | enhanced | (Green - Red) / (Green + Red - Blue) | [-1, 1] | Gitelson et al. (2002) |
| TSAVI | enhanced | (a * (NIR - aRed - b)) / (Red + aNIR - ab + X(1 + a^2)), where a=1.5, b=10, X=0.08 | [-1, 1.5] | Baret & Guyot (1991) |
| ATSAVI | enhanced | (a * (NIR - aRed - b)) / (aNIR + Red - ab + X(1 + a^2)), where a=1.22, b=0.03, X=0.08 | [-1, 1.5] | Baret et al. (1992) |
| GESAVI | enhanced | ((NIR - Red) / (NIR + Red + Z)) * (1 + Z), where Z=0.5 | [-1, 1.5] | Gilabert et al. (2002) |
| MTVI | enhanced | 1.2 * (1.2(NIR - Green) - 2.5(Red - Green)) | [-2, 2] | Haboudane et al. (2004) |
| CTVI | enhanced | ((NDVI + 0.5) / |NDVI + 0.5|) * sqrt(|NDVI + 0.5|), where NDVI = (NIR - Red) / (NIR + Red) | [-1, 1.5] | Perry & Lautenschlager (1984) |
| NDRE | advanced | (NIR - RedEdge) / (NIR + RedEdge) | [-1, 1] | Gitelson & Merzlyak (1994) |
| MTCI | advanced | (RedEdge - Red) / (NIR - Red) | [0, 8] | Dash & Curran (2004) |
| IRECI | advanced | (RedEdge - Red) / (RedEdge / NIR) | [-2, 5] | Frampton et al. (2013) |
| S2REP | advanced | 705 + 35 * ((Red + RedEdge)/2 - Red) / (RedEdge - Red) | [680, 780] | Frampton et al. (2013) |
| PSRI | advanced | (Red - Green) / RedEdge | [-1, 1] | Merzlyak et al. (1999) |
| CRI1 | advanced | (1 / Green) - (1 / Red) | [-10, 10] | Gitelson et al. (2002) |
| CRI2 | advanced | (1 / Green) - (1 / RedEdge) | [-10, 10] | Gitelson et al. (2002) |
| ARI1 | advanced | (1 / Green) - (1 / RedEdge) | [-10, 10] | Gitelson et al. (2001) |
| ARI2 | advanced | NIR * ((1 / Green) - (1 / RedEdge)) | [-10, 10] | Gitelson et al. (2001) |
| MCARI | advanced | ((RedEdge - Red) - 0.2(RedEdge - Green)) (RedEdge / Red) | [-2, 2] | Daughtry et al. (2000) |
| PRI | stress | (Green - NIR) / (Green + NIR) | [-1, 1] | Gamon et al. (1992) |
| SIPI | stress | (NIR - Red) / (NIR - Green) | [0, 2] | Penuelas et al. (1995) |
| CCI | stress | (RedEdge - Red) / (RedEdge + Red) | [-1, 1] | Barnes et al. (2000) |
| NDNI | stress | (log(1/NIR) - log(1/SWIR1)) / (log(1/NIR) + log(1/SWIR1)) | [-1, 1] | Serrano et al. (2002) |
| CARI | stress | RedEdge * (Red / Green) | [0, 5] | Kim et al. (1994) |
| TCARI | stress | 3 * ((RedEdge - Red) - 0.2(RedEdge - Green) (RedEdge / Red)) | [-2, 5] | Haboudane et al. (2002) |
| MTVI1 | stress | 1.2 * (1.2(NIR - Green) - 2.5(Red - Green)) | [-2, 2] | Haboudane et al. (2004) |
| MTVI2 | stress | 1.5 * (1.2(NIR - Green) - 2.5(Red - Green)) / sqrt((2NIR + 1)^2 - (6NIR - 5*sqrt(Red)) - 0.5) | [-2, 5] | Haboudane et al. (2004) |
| TVI | stress | 0.5 * (120(NIR - Green) - 200(Red - Green)) | [-100, 100] | Broge & Leblanc (2001) |
| NPCI | stress | (Red - Blue) / (Red + Blue) | [-1, 1] | Penuelas et al. (1994) |
| RARS | stress | Red / NIR | [0, 5] | Chappelle et al. (1992) |
| NPQI | stress | (Red - Blue) / (Red + Blue) | [-1, 1] | Barnes et al. (1992) |
| NDWI | water | (Green - NIR) / (Green + NIR) | [-1, 1] | McFeeters (1996) |
| MNDWI | water | (Green - SWIR1) / (Green + SWIR1) | [-1, 1] | Xu (2006) |
| NDMI | water | (NIR - SWIR1) / (NIR + SWIR1) | [-1, 1] | Gao (1996) |
| MSI | water | SWIR1 / NIR | [0, 5] | Hunt & Rock (1989) |
| NDII | water | (NIR - SWIR1) / (NIR + SWIR1) | [-1, 1] | Hardisky et al. (1983) |
| WI | water | NIR / SWIR1 | [0, 10] | Gao (1996) |
| SRWI | water | NIR / SWIR1 | [0, 10] | Zarco-Tejada et al. (2003) |
| LSWI | water | (NIR - SWIR1) / (NIR + SWIR1) | [-1, 1] | Xiao et al. (2002) |
| LAI | specialized | 3.618 * EVI - 0.118, where EVI = 2.5 * ((NIR - Red) / (NIR + 6Red - 7.5Blue + 1)) | [0, 15] | Baret & Guyot (1991) |
| FAPAR | specialized | -0.161 + 1.257 * NDVI, where NDVI = (NIR - Red) / (NIR + Red) | [0, 1] | Myneni & Williams (1994) |
| FCOVER | specialized | -2.274 + 4.336NDVI - 1.33NDVI^2, where NDVI = (NIR - Red) / (NIR + Red) | [0, 1] | Baret et al. (2007) |
| NBR | specialized | (NIR - SWIR2) / (NIR + SWIR2) | [-1, 1] | Lopez Garcia & Caselles (1991) |
| BAI | specialized | 1 / ((0.1 - Red)^2 + (0.06 - NIR)^2) | [0, 1000] | Chuvieco et al. (2002) |
| NDSI | specialized | (Green - SWIR1) / (Green + SWIR1) | [-1, 1] | Hall et al. (1995) |
| GRVI | specialized | (Green - Red) / (Green + Red) | [-1, 1] | Tucker (1979) |
| VIG | specialized | (Green - Red) / (Green + Red) | [-1, 1] | Gitelson et al. (2002) |
| CI | specialized | (Red - Green) / Red | [-1, 1] | Escadafal & Huete (1991) |
| GBNDVI | specialized | (NIR - (Green + Blue)) / (NIR + Green + Blue) | [-1, 1] | Wang et al. (2007) |
# Get all indices with formulas
all_indices <- list_vegetation_indices(detailed = TRUE)
View(all_indices)
# Filter by category
stress <- list_vegetation_indices(category = "stress", detailed = TRUE)
water <- list_vegetation_indices(application = "water", detailed = TRUE)
# Get specific index information
ndvi_info <- all_indices[all_indices$Index == "NDVI", ]
cat(sprintf("NDVI Formula: %s\n", ndvi_info$Formula))
cat(sprintf("NDVI Range: %s\n", ndvi_info$Range))
cat(sprintf("Reference: %s\n", ndvi_info$Reference))The most common vegetation analysis:
# Simple NDVI from individual bands
ndvi <- calculate_vegetation_index(
red = "landsat_red.tif",
nir = "landsat_nir.tif",
index_type = "NDVI",
verbose = TRUE
)
# View results
terra::plot(ndvi, main = "NDVI Analysis",
col = colorRampPalette(c("brown", "yellow", "green"))(100))
# Check value range
summary(terra::values(ndvi, mat = FALSE))Calculate several indices at once for comprehensive analysis:
# Calculate multiple vegetation indices
vegetation_stack <- calculate_multiple_indices(
red = red_band,
nir = nir_band,
blue = blue_band,
green = green_band,
indices = c("NDVI", "EVI", "SAVI", "GNDVI", "DVI", "RVI"),
output_stack = TRUE,
region_boundary = "Ohio",
verbose = TRUE
)
# Access individual indices
ndvi_layer <- vegetation_stack$NDVI
evi_layer <- vegetation_stack$EVI
# Create comparison visualization
terra::plot(vegetation_stack, main = names(vegetation_stack))Work with multi-band satellite imagery:
# From multi-band raster with auto-detection
evi <- calculate_vegetation_index(
spectral_data = "sentinel2_image.tif",
index_type = "EVI",
auto_detect_bands = TRUE,
verbose = TRUE
)
# From directory with band detection
savi <- calculate_vegetation_index(
spectral_data = "/path/to/sentinel/bands/",
index_type = "SAVI",
auto_detect_bands = TRUE
)
# Custom band names for non-standard data
ndvi_custom <- calculate_vegetation_index(
spectral_data = custom_satellite_data,
band_names = c("B4", "B3", "B2", "B8"), # Red, Green, Blue, NIR
index_type = "NDVI"
)Use calculate_ndvi_enhanced() for time series:
# Time series NDVI with quality control
ndvi_series <- calculate_ndvi_enhanced(
red_data = "/path/to/red/time_series/",
nir_data = "/path/to/nir/time_series/",
match_by_date = TRUE, # Automatically match files by date
quality_filter = TRUE, # Remove outliers
temporal_smoothing = TRUE, # Smooth time series
clamp_range = c(-0.2, 1),
verbose = TRUE
)
# Plot time series layers
terra::plot(ndvi_series, main = paste("NDVI", names(ndvi_series)))
# Calculate temporal statistics
temporal_mean <- terra::app(ndvi_series, mean, na.rm = TRUE)
temporal_cv <- terra::app(ndvi_series, function(x) sd(x, na.rm = TRUE) / mean(x, na.rm = TRUE))Analyze vegetation changes over time:
# Temporal analysis workflow
temporal_results <- analyze_temporal_changes(
data_list = c("ndvi_2020.tif", "ndvi_2021.tif", "ndvi_2022.tif", "ndvi_2023.tif"),
dates = c("2020", "2021", "2022", "2023"),
region_boundary = "Iowa",
analysis_type = "trend",
output_folder = "temporal_analysis/"
)
# Access trend results
slope_raster <- temporal_results$trend_rasters$slope
r_squared_raster <- temporal_results$trend_rasters$r_squared
# Interpret trends
# Positive slope = increasing vegetation
# Negative slope = declining vegetation
# High R² = strong temporal trendAnalyze vegetation for specific crops:
# Comprehensive crop vegetation analysis
corn_analysis <- analyze_crop_vegetation(
spectral_data = "sentinel2_data.tif",
crop_type = "corn",
growth_stage = "mid",
analysis_type = "comprehensive",
cdl_mask = corn_mask,
verbose = TRUE
)
# Access results
vegetation_indices <- corn_analysis$vegetation_indices
stress_analysis <- corn_analysis$analysis_results$stress_analysis
yield_analysis <- corn_analysis$analysis_results$yield_analysis
# View stress detection results
print(stress_analysis$NDVI)Monitor crop development through the season:
# Early season analysis (emergence to vegetative)
early_season <- analyze_crop_vegetation(
spectral_data = "may_sentinel2.tif",
crop_type = "soybeans",
growth_stage = "early",
analysis_type = "growth"
)
# Mid-season analysis (reproductive stage)
mid_season <- analyze_crop_vegetation(
spectral_data = "july_sentinel2.tif",
crop_type = "soybeans",
growth_stage = "mid",
analysis_type = "stress"
)
# Compare growth stages
early_ndvi <- early_season$vegetation_indices$NDVI
mid_ndvi <- mid_season$vegetation_indices$NDVI
growth_change <- mid_ndvi - early_ndviUse specialized indices for stress detection:
# Calculate stress-sensitive indices
stress_indices <- calculate_multiple_indices(
red = red_band,
nir = nir_band,
green = green_band,
red_edge = red_edge_band, # If available
indices = c("NDVI", "PRI", "SIPI", "NDRE"),
output_stack = TRUE
)
# Stress analysis thresholds
ndvi_values <- terra::values(stress_indices$NDVI, mat = FALSE)
healthy_threshold <- 0.6
stress_threshold <- 0.4
# Classify stress levels
stress_mask <- terra::classify(stress_indices$NDVI,
matrix(c(-1, stress_threshold, 1, # Severe stress
stress_threshold, healthy_threshold, 2, # Moderate stress
healthy_threshold, 1, 3), ncol = 3, byrow = TRUE))
# Visualization
stress_colors <- c("red", "orange", "green")
terra::plot(stress_mask, main = "Vegetation Stress Classification",
col = stress_colors, legend = c("Severe", "Moderate", "Healthy"))Monitor vegetation water content:
# Water content indices
water_stress <- calculate_multiple_indices(
nir = nir_band,
swir1 = swir1_band,
indices = c("NDMI", "MSI", "NDII"),
output_stack = TRUE
)
# NDMI interpretation:
# High NDMI (>0.4) = High water content
# Low NDMI (<0.1) = Water stress
# MSI interpretation (opposite of NDMI):
# Low MSI (<1.0) = High moisture
# High MSI (>1.6) = Water stress
# Create water stress map
water_stress_binary <- terra::classify(water_stress$NDMI,
matrix(c(-1, 0.1, 1, # Water stress
0.1, 0.4, 2, # Moderate
0.4, 1, 3), ncol = 3, byrow = TRUE))Apply quality controls to vegetation data:
# Enhanced NDVI with quality filtering
ndvi_quality <- calculate_ndvi_enhanced(
red_data = red_raster,
nir_data = nir_raster,
quality_filter = TRUE, # Remove outliers
clamp_range = c(-0.2, 1), # Reasonable NDVI range
temporal_smoothing = FALSE, # For single date
verbose = TRUE
)
# Custom quality filtering
vegetation_filtered <- calculate_vegetation_index(
red = red_band,
nir = nir_band,
index_type = "NDVI",
mask_invalid = TRUE, # Remove invalid values
clamp_range = c(-1, 1) # Theoretical NDVI range
)# Compare with field measurements
field_ndvi_validation <- universal_spatial_join(
source_data = "field_measurements.csv", # Ground truth points
target_data = ndvi_raster, # Satellite NDVI
method = "extract",
buffer_distance = 30, # 30m buffer around points
summary_function = "mean"
)
# Calculate correlation
observed <- field_ndvi_validation$field_ndvi
predicted <- field_ndvi_validation$extracted_NDVI
correlation <- cor(observed, predicted, use = "complete.obs")
rmse <- sqrt(mean((observed - predicted)^2, na.rm = TRUE))
print(paste("Validation R =", round(correlation, 3)))
print(paste("RMSE =", round(rmse, 4)))Track vegetation phenology over time:
# Create NDVI time series for phenology
phenology_analysis <- analyze_temporal_changes(
data_list = list.files("/ndvi/2023/", pattern = "*.tif", full.names = TRUE),
dates = extract_dates_universal(list.files("/ndvi/2023/", pattern = "*.tif")),
region_boundary = "Iowa",
analysis_type = "seasonal",
output_folder = "phenology_results/"
)
# Access seasonal patterns
spring_ndvi <- phenology_analysis$seasonal_rasters$Spring
summer_ndvi <- phenology_analysis$seasonal_rasters$Summer
fall_ndvi <- phenology_analysis$seasonal_rasters$Fall
# Calculate growing season metrics
peak_season <- terra::app(c(spring_ndvi, summer_ndvi), max, na.rm = TRUE)
growing_season_range <- summer_ndvi - spring_ndviCombine data from different sensors:
# Landsat NDVI (30m resolution)
landsat_ndvi <- calculate_vegetation_index(
red = "landsat8_red.tif",
nir = "landsat8_nir.tif",
index_type = "NDVI"
)
# MODIS NDVI (250m resolution)
modis_ndvi <- calculate_vegetation_index(
red = "modis_red.tif",
nir = "modis_nir.tif",
index_type = "NDVI"
)
# Resample MODIS to Landsat resolution for comparison
modis_resampled <- universal_spatial_join(
source_data = modis_ndvi,
target_data = landsat_ndvi,
method = "resample",
summary_function = "bilinear"
)
# Compare sensors
sensor_difference <- landsat_ndvi - modis_resampled
correlation <- calculate_spatial_correlation(landsat_ndvi, modis_resampled)# Forest-specific indices
forest_indices <- calculate_multiple_indices(
red = red_band,
nir = nir_band,
swir1 = swir1_band,
swir2 = swir2_band,
indices = c("NDVI", "EVI", "NBR", "NDMI"), # Normalized Burn Ratio, moisture
region_boundary = "forest_boundary.shp",
output_stack = TRUE
)
# Burn severity assessment using NBR
pre_fire_nbr <- forest_indices$NBR # Before fire
post_fire_nbr <- calculate_vegetation_index(
red = "post_fire_red.tif",
nir = "post_fire_nir.tif",
swir2 = "post_fire_swir2.tif",
index_type = "NBR"
)
# Calculate burn severity (dNBR)
burn_severity <- pre_fire_nbr - post_fire_nbr
# Classify burn severity
severity_classes <- terra::classify(burn_severity,
matrix(c(-Inf, 0.1, 1, # Unburned
0.1, 0.27, 2, # Low severity
0.27, 0.44, 3, # Moderate-low
0.44, 0.66, 4, # Moderate-high
0.66, Inf, 5), ncol = 3, byrow = TRUE))# Grassland-specific analysis
grassland_health <- calculate_multiple_indices(
red = red_band,
nir = nir_band,
green = green_band,
indices = c("NDVI", "SAVI", "MSAVI", "GNDVI"), # Soil-adjusted indices
output_stack = TRUE
)
# Analyze grazing impact
grazed_area_ndvi <- terra::mask(grassland_health$NDVI, grazing_areas)
ungrazed_area_ndvi <- terra::mask(grassland_health$NDVI, ungrazed_areas)
# Compare means
grazed_mean <- terra::global(grazed_area_ndvi, "mean", na.rm = TRUE)
ungrazed_mean <- terra::global(ungrazed_area_ndvi, "mean", na.rm = TRUE)
grazing_impact <- ungrazed_mean - grazed_mean# Urban vegetation analysis with atmospheric correction
urban_vegetation <- calculate_vegetation_index(
red = urban_red,
nir = urban_nir,
blue = urban_blue,
index_type = "ARVI", # Atmospherically Resistant VI
region_boundary = "city_boundary.shp"
)
# Green space analysis
green_space_threshold <- 0.3
urban_greenness <- terra::classify(urban_vegetation,
matrix(c(-1, green_space_threshold, 0, # Non-vegetated
green_space_threshold, 1, 1), ncol = 3, byrow = TRUE)) # Vegetated
# Calculate green space statistics
total_urban_area <- terra::ncell(urban_greenness) * prod(terra::res(urban_greenness))
green_area <- terra::global(urban_greenness, "sum", na.rm = TRUE) * prod(terra::res(urban_greenness))
green_space_percentage <- (green_area / total_urban_area) * 100NDVI (Normalized Difference Vegetation Index): - Best for: General vegetation monitoring, biomass estimation - Range: -1 to 1 (vegetation typically 0.3-0.9) - Limitations: Saturates in dense vegetation
EVI (Enhanced Vegetation Index): - Best for: Dense vegetation, reducing atmospheric effects - Range: -1 to 3 (vegetation typically 0.2-1.0) - Advantages: Less saturation than NDVI
SAVI (Soil Adjusted Vegetation Index): - Best for: Areas with exposed soil, early season crops - Range: -1 to 1.5 - Advantages: Reduces soil background effects
PRI (Photochemical Reflectance Index): - Best for: Plant stress detection, photosynthetic efficiency - Range: -1 to 1 - Applications: Early stress detection before visible symptoms
Crop Health Monitoring:
crop_health <- calculate_multiple_indices(
red = red, nir = nir, green = green,
indices = c("NDVI", "GNDVI", "SIPI"), # Structure Insensitive Pigment Index
output_stack = TRUE
)Drought Monitoring:
drought_indices <- calculate_multiple_indices(
nir = nir, swir1 = swir1,
indices = c("NDMI", "MSI"), # Moisture indices
output_stack = TRUE
)Precision Agriculture:
# NDVI-specific visualization
create_spatial_map(
spatial_data = ndvi_raster,
color_scheme = "ndvi", # NDVI-specific colors
region_boundary = "study_area.shp",
title = "NDVI Analysis - Growing Season Peak",
output_file = "ndvi_analysis.png"
)
# Multi-index comparison
create_comparison_map(
data1 = ndvi_raster,
data2 = evi_raster,
comparison_type = "side_by_side",
titles = c("NDVI", "EVI"),
color_scheme = "viridis"
)# Interactive vegetation analysis
interactive_veg_map <- create_interactive_map(
spatial_data = vegetation_points,
fill_variable = "NDVI",
popup_vars = c("NDVI", "EVI", "collection_date"),
basemap = "satellite",
title = "Interactive Vegetation Analysis"
)
# Save interactive map
htmlwidgets::saveWidget(interactive_veg_map, "vegetation_interactive.html")# Calculate comprehensive statistics
vegetation_stats <- terra::global(vegetation_stack,
c("mean", "sd", "min", "max"),
na.rm = TRUE)
print(vegetation_stats)
# Zonal statistics by land cover
landcover_stats <- universal_spatial_join(
source_data = vegetation_stack,
target_data = "landcover_polygons.shp",
method = "zonal",
summary_function = "mean"
)
# Statistics by vegetation class
healthy_vegetation <- vegetation_stack$NDVI > 0.6
moderate_vegetation <- vegetation_stack$NDVI > 0.3 & vegetation_stack$NDVI <= 0.6
poor_vegetation <- vegetation_stack$NDVI <= 0.3
# Calculate percentages
total_pixels <- terra::ncell(vegetation_stack$NDVI)
healthy_pct <- terra::global(healthy_vegetation, "sum", na.rm = TRUE) / total_pixels * 100
moderate_pct <- terra::global(moderate_vegetation, "sum", na.rm = TRUE) / total_pixels * 100
poor_pct <- terra::global(poor_vegetation, "sum", na.rm = TRUE) / total_pixels * 100# Analyze relationships between indices
index_correlations <- analyze_variable_correlations(
variable_list = list(
NDVI = vegetation_stack$NDVI,
EVI = vegetation_stack$EVI,
SAVI = vegetation_stack$SAVI,
GNDVI = vegetation_stack$GNDVI
),
method = "pearson",
create_plots = TRUE,
output_folder = "correlation_analysis/"
)
# View correlation matrix
print(index_correlations$correlation_matrix)Complete workflow for monitoring corn fields:
# 1. Define study area
study_area <- get_region_boundary("Iowa:Story") # Story County, Iowa
# 2. Create corn mask
corn_mask <- create_crop_mask(
cdl_data = "cdl_iowa_2023.tif",
crop_codes = get_comprehensive_cdl_codes("corn"),
region_boundary = study_area
)
# 3. Calculate vegetation indices for corn areas
corn_vegetation <- calculate_multiple_indices(
spectral_data = "sentinel2_iowa_july.tif",
indices = c("NDVI", "EVI", "GNDVI", "SAVI"),
auto_detect_bands = TRUE,
output_stack = TRUE
)
# 4. Apply corn mask
corn_vegetation_masked <- terra::mask(corn_vegetation, corn_mask)
# 5. Analyze corn health
corn_stats <- terra::global(corn_vegetation_masked,
c("mean", "sd", "min", "max"),
na.rm = TRUE)
# 6. Create visualization
quick_map(corn_vegetation_masked$NDVI,
title = "Story County Corn NDVI - July 2023")
# 7. Save results
terra::writeRaster(corn_vegetation_masked, "story_county_corn_indices.tif")Detect and map vegetation stress:
# 1. Load multi-temporal data
april_data <- calculate_ndvi_enhanced("april_sentinel2.tif")
july_data <- calculate_ndvi_enhanced("july_sentinel2.tif")
# 2. Calculate stress indices
stress_indices <- calculate_multiple_indices(
spectral_data = "july_sentinel2.tif",
indices = c("NDVI", "PRI", "SIPI", "NDMI"),
auto_detect_bands = TRUE,
output_stack = TRUE
)
# 3. Identify stressed areas
# NDVI decline from April to July
ndvi_change <- july_data - april_data
stress_areas <- ndvi_change < -0.2 # Significant decline
# Water stress (low NDMI)
water_stress <- stress_indices$NDMI < 0.2
# Combined stress map
combined_stress <- stress_areas | water_stress
# 4. Quantify stress
total_area <- terra::ncell(combined_stress) * prod(terra::res(combined_stress)) / 10000 # hectares
stressed_pixels <- terra::global(combined_stress, "sum", na.rm = TRUE)
stressed_area <- stressed_pixels * prod(terra::res(combined_stress)) / 10000 # hectares
stress_percentage <- (stressed_area / total_area) * 100
print(paste("Stressed area:", round(stressed_area, 1), "hectares"))
print(paste("Stress percentage:", round(stress_percentage, 1), "%"))Large-scale vegetation analysis:
# 1. Multi-state analysis
great_lakes_states <- c("Michigan", "Ohio", "Indiana", "Illinois", "Wisconsin")
regional_ndvi <- list()
for (state in great_lakes_states) {
state_ndvi <- calculate_vegetation_index(
spectral_data = paste0("/data/", tolower(state), "_modis.tif"),
index_type = "NDVI",
region_boundary = state,
auto_detect_bands = TRUE
)
regional_ndvi[[state]] <- state_ndvi
}
# 2. Create regional mosaic
great_lakes_mosaic <- create_raster_mosaic(
input_data = regional_ndvi,
method = "merge",
region_boundary = "Great Lakes Region"
)
# 3. Regional statistics
regional_stats <- terra::global(great_lakes_mosaic,
c("mean", "sd", "min", "max"),
na.rm = TRUE)
# 4. State-by-state comparison
state_means <- sapply(regional_ndvi, function(x) {
terra::global(x, "mean", na.rm = TRUE)[[1]]
})
names(state_means) <- great_lakes_states
print(sort(state_means, decreasing = TRUE))# If auto-detection fails, specify band names manually
custom_ndvi <- calculate_vegetation_index(
spectral_data = "unusual_satellite_data.tif",
band_names = c("Band_4_Red", "Band_5_NIR"), # Custom names
index_type = "NDVI",
auto_detect_bands = FALSE
)
# Or use individual band approach
manual_ndvi <- calculate_vegetation_index(
red = satellite_data$Band_4_Red,
nir = satellite_data$Band_5_NIR,
index_type = "NDVI"
)# Process large areas efficiently
# 1. Use chunked processing
large_area_ndvi <- calculate_vegetation_index(
spectral_data = "very_large_raster.tif",
index_type = "NDVI",
chunk_size = 500000, # Smaller chunks
auto_detect_bands = TRUE
)
# 2. Process by regions
us_states <- c("Ohio", "Michigan", "Indiana")
state_results <- list()
for (state in us_states) {
state_results[[state]] <- calculate_vegetation_index(
spectral_data = "continental_satellite_data.tif",
index_type = "NDVI",
region_boundary = state, # Process each state separately
auto_detect_bands = TRUE
)
}
# 3. Combine results
combined_results <- create_raster_mosaic(state_results, method = "merge")# Efficient workflow
optimized_workflow <- function(satellite_data, study_region) {
# 1. Crop to region first (reduces data size)
cropped_data <- universal_spatial_join(
source_data = satellite_data,
target_data = get_region_boundary(study_region),
method = "crop"
)
# 2. Calculate multiple indices in one call
vegetation_indices <- calculate_multiple_indices(
spectral_data = cropped_data,
indices = c("NDVI", "EVI", "SAVI", "GNDVI"),
auto_detect_bands = TRUE,
output_stack = TRUE,
parallel = FALSE # Use FALSE for stability
)
# 3. Cache results
terra::writeRaster(vegetation_indices, "cached_vegetation_indices.tif")
return(vegetation_indices)
}Cropland: - 0.2-0.4: Bare soil/early growth -
0.4-0.6: Developing vegetation
- 0.6-0.8: Healthy, mature crops - 0.8-0.9: Peak vegetation (dense
crops)
Forest: - 0.4-0.6: Sparse forest/stressed trees - 0.6-0.8: Healthy forest - 0.8-0.9: Dense, healthy forest
Grassland: - 0.2-0.4: Sparse grass/dormant season - 0.4-0.6: Moderate grass cover - 0.6-0.8: Dense, healthy grassland
Temperate Crops (Corn/Soybeans): - April-May: 0.2-0.4 (emergence) - June-July: 0.4-0.7 (vegetative growth) - July-August: 0.6-0.9 (peak season) - September-October: 0.4-0.6 (senescence)
Create your own vegetation indices:
# Custom ratio index
custom_ratio <- nir_band / red_band
names(custom_ratio) <- "Custom_Ratio"
# Custom normalized difference
custom_nd <- (nir_band - green_band) / (nir_band + green_band)
names(custom_nd) <- "Custom_ND"
# Combine with standard indices
all_indices <- c(
calculate_vegetation_index(red = red, nir = nir, index_type = "NDVI"),
custom_ratio,
custom_nd
)# Combine vegetation indices with environmental data
environmental_analysis <- universal_spatial_join(
source_data = vegetation_indices,
target_data = list(
precipitation = "annual_precip.tif",
temperature = "mean_temp.tif",
elevation = "dem.tif"
),
method = "extract"
)
# Analyze relationships
vegetation_env_correlation <- analyze_variable_correlations(
variable_list = list(
NDVI = vegetation_indices$NDVI,
precipitation = environmental_data$precipitation,
temperature = environmental_data$temperature
),
create_plots = TRUE
)This vignette covered comprehensive vegetation analysis with GeoSpatialSuite:
The calculate_vegetation_index() and
analyze_crop_vegetation() functions support automatic band
detection from multiple satellite platforms. This document explains how
band naming works and what formats are supported.
All band names are case-insensitive.
✅ These are all equivalent: - "red",
"RED", "Red", "rEd" -
"nir", "NIR", "Nir",
"nIR" - "B4", "b4",
"B04", "b04"
Recognized patterns: - Generic: red, RED,
Red - Landsat 8/9: B4, b4,
band4, Band_4, Band4 -
Sentinel-2: B04, b04 - Generic numbered:
band_4, sr_band4
Example:
# All of these will be recognized as the red band
calculate_vegetation_index(red = red_band, nir = nir_band, index_type = "NDVI")
calculate_vegetation_index(spectral_data = raster_with_band_named_"B4", auto_detect_bands = TRUE)
calculate_vegetation_index(spectral_data = raster_with_band_named_"RED", auto_detect_bands = TRUE)Recognized patterns: - Generic: nir, NIR,
Nir, near_infrared - Landsat 8/9:
B5, b5, band5,
Band_5, Band5 - Sentinel-2: B08,
b08, B8, b8, band8,
Band_8, Band8
Recognized patterns: - Generic: blue, BLUE,
Blue - Landsat 8/9: B2, b2,
band2, Band_2, Band2 -
Sentinel-2: B02, b02
Recognized patterns: - Generic: green,
GREEN, Green - Landsat 8/9: B3,
b3, band3, Band_3,
Band3 - Sentinel-2: B03, b03
Recognized patterns: - Generic: swir1,
SWIR1, Swir1,
shortwave_infrared_1, SWIR_1 - Landsat 8/9:
B6, b6 - Sentinel-2: B11,
b11
Recognized patterns: - Generic: swir2,
SWIR2, Swir2,
shortwave_infrared_2, SWIR_2 - Landsat 8/9:
B7, b7 - Sentinel-2: B12,
b12
Recognized patterns: - Generic: rededge,
RedEdge, red_edge, RED_EDGE,
RE, re - Sentinel-2: B05,
b05, B5, b5 (note: conflicts with
Landsat NIR)
Recognized patterns: - Generic: coastal,
COASTAL, Coastal, aerosol -
Landsat 8/9: B1, b1 - Sentinel-2:
B01, b01
Band Mapping: | Band | Number | Wavelength |
geospatialsuite Name | |——|——–|————|———————| | Coastal/Aerosol | B1 |
0.43-0.45 µm | coastal, B1 | | Blue | B2 |
0.45-0.51 µm | blue, B2 | | Green | B3 |
0.53-0.59 µm | green, B3 | | Red | B4 |
0.64-0.67 µm | red, B4 | | NIR | B5 |
0.85-0.88 µm | nir, B5 | | SWIR1 | B6 |
1.57-1.65 µm | swir1, B6 | | SWIR2 | B7 |
2.11-2.29 µm | swir2, B7 |
Example - Landsat Scene:
library(geospatialsuite)
library(terra)
# If your Landsat file has band names: B1, B2, B3, B4, B5, B6, B7
landsat <- rast("LC08_L2SP_029030_20230615_B1-7.tif")
# Auto-detection will work automatically
ndvi <- calculate_vegetation_index(
spectral_data = landsat,
index_type = "NDVI",
auto_detect_bands = TRUE
)
# Or specify bands explicitly
ndvi <- calculate_vegetation_index(
red = landsat[["B4"]],
nir = landsat[["B5"]],
index_type = "NDVI"
)Band Mapping: | Band | Number | Wavelength |
Resolution | geospatialsuite Name | |——|——–|————|———–|———————| | Coastal
| B01 | 0.433-0.453 µm | 60m | coastal, B01,
B1 | | Blue | B02 | 0.458-0.523 µm | 10m |
blue, B02, B2 | | Green | B03 |
0.543-0.578 µm | 10m | green, B03,
B3 | | Red | B04 | 0.650-0.680 µm | 10m | red,
B04, B4 | | Red Edge 1 | B05 | 0.698-0.713 µm
| 20m | red_edge, B05, B5 | | Red
Edge 2 | B06 | 0.733-0.748 µm | 20m | N/A | | Red Edge 3 | B07 |
0.765-0.785 µm | 20m | N/A | | NIR | B08 | 0.785-0.900 µm | 10m |
nir, B08, B8 | | SWIR1 | B11 |
1.565-1.655 µm | 20m | swir1, B11 | | SWIR2 |
B12 | 2.100-2.280 µm | 20m | swir2, B12 |
Example - Sentinel-2 Scene:
# If your Sentinel-2 file has standard band names
sentinel <- rast("S2A_MSIL2A_20230615_B02-B12.tif")
# Auto-detection works
ndvi <- calculate_vegetation_index(
spectral_data = sentinel,
index_type = "NDVI",
auto_detect_bands = TRUE
)
# Calculate red edge index (requires Sentinel-2)
ndre <- calculate_vegetation_index(
spectral_data = sentinel,
index_type = "NDRE",
auto_detect_bands = TRUE
)
# Or use specific band names
evi <- calculate_vegetation_index(
red = sentinel[["B04"]],
nir = sentinel[["B08"]],
blue = sentinel[["B02"]],
index_type = "EVI"
)Band Mapping: | Band | Number | Wavelength |
geospatialsuite Name | |——|——–|————|———————| | Red | 1 | 0.620-0.670 µm
| red, band1 | | NIR | 2 | 0.841-0.876 µm |
nir, band2 | | Blue | 3 | 0.459-0.479 µm |
blue, band3 | | Green | 4 | 0.545-0.565 µm |
green, band4 | | SWIR1 | 6 | 1.628-1.652 µm |
swir1, band6 | | SWIR2 | 7 | 2.105-2.155 µm |
swir2, band7 |
Example - MODIS:
modis <- rast("MOD13Q1_NDVI_2023165.tif")
# If bands are named generically
ndvi <- calculate_vegetation_index(
spectral_data = modis,
index_type = "NDVI",
auto_detect_bands = TRUE
)If your raster has non-standard band names, you have three options:
# Your raster has bands: "band_A", "band_B", "band_C", "band_D"
my_raster <- rast("custom_satellite.tif")
# Rename to standard names
names(my_raster) <- c("red", "green", "blue", "nir")
# Now auto-detection works
ndvi <- calculate_vegetation_index(
spectral_data = my_raster,
index_type = "NDVI",
auto_detect_bands = TRUE
)Problem: Your band names don’t match any recognized patterns
Solution:
Problem: Auto-detection picked the wrong bands (e.g., Sentinel-2 B05 detected as NIR instead of Red Edge)
Solution:
Problem: Your bands are in separate files
Solution A - List of files:
# Create file list
band_files <- c(
red = "LC08_B4.tif",
nir = "LC08_B5.tif",
blue = "LC08_B2.tif"
)
# Let the function load them
evi <- calculate_vegetation_index(
spectral_data = band_files,
index_type = "EVI"
)Solution B - Directory:
# If all bands are in one directory with recognizable names
ndvi <- calculate_vegetation_index(
spectral_data = "/path/to/landsat/bands/",
index_type = "NDVI",
auto_detect_bands = TRUE
)When using auto-detection, the function searches in this order:
"red" matches "red", "RED",
"Red""B4" for Landsat red"B04" for Sentinel-2 red"band4", "Band_4", etc.To see which bands were detected:
# Turn on verbose mode
result <- calculate_vegetation_index(
spectral_data = my_raster,
index_type = "NDVI",
auto_detect_bands = TRUE,
verbose = TRUE # This will print which bands were detected
)Expected output:
Processing spectral_data input...
Extracting bands from multi-band raster...
Exact match detected red band: B4
Exact match detected nir band: B8
Starting NDVI calculation with comprehensive input handling...
red, nir, blue, etc.)| Your Data | Band Detection Method | Example |
|---|---|---|
| Landsat with standard names | Auto-detect | auto_detect_bands = TRUE |
| Sentinel-2 with standard names | Auto-detect | auto_detect_bands = TRUE |
| Generic names (red, nir, blue) | Auto-detect | auto_detect_bands = TRUE |
| Custom names | Rename first | names(raster) <- c("red", "nir", ...) |
| Separate files | Pass list | spectral_data = c(red="file1.tif", ...) |
| Non-standard structure | Extract & pass | red = raster[[1]], nir = raster[[4]] |
This work was developed by the GeoSpatialSuite team with contributions from: Olatunde D. Akanbi, Erika I. Barcelos, and Roger H. French.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.