Using analyze_crop_vegetation() in GeoSpatialSuite

GeoSpatialSuite Development Team

Complete Documentation: analyze_crop_vegetation() Output Structure

Overview

The analyze_crop_vegetation() function returns a nested list with three main components containing comprehensive crop analysis results.


Output Structure

result <- analyze_crop_vegetation(
  spectral_data = your_data,
  crop_type = "corn",
  analysis_type = "comprehensive"
)

# Structure:
result$vegetation_indices      # SpatRaster with calculated indices
result$analysis_results        # Detailed analysis results
result$metadata                # Processing metadata

1. vegetation_indices (SpatRaster)

Type: terra::SpatRaster object (multi-layer)

Description: A raster stack containing all calculated vegetation indices for the specified crop type.

Contents: - Each layer is a different vegetation index (NDVI, EVI, GNDVI, etc.) - Values are the actual index calculations for each pixel - Layer names correspond to the index abbreviations

Example Access:

# View all calculated indices
names(result$vegetation_indices)
# [1] "NDVI" "EVI" "GNDVI" "DVI" "RVI" "PRI"

# Extract a specific index
ndvi <- result$vegetation_indices[["NDVI"]]

# Plot an index
plot(result$vegetation_indices[["NDVI"]], main = "NDVI")

# Get values for analysis
ndvi_values <- terra::values(result$vegetation_indices[["NDVI"]])

2. analysis_results (List)

Type: Named list with up to 5 components depending on analysis_type

2.1 stress_analysis (if analysis_type includes “stress” or “comprehensive”)

Purpose: Identifies vegetation stress based on index thresholds

Structure:

result$analysis_results$stress_analysis

Contents for each index analyzed (e.g., NDVI, EVI, SIPI, GNDVI):

Field Type Description Example
healthy_percentage numeric % of pixels classified as healthy vegetation 65.3
moderate_stress_percentage numeric % of pixels showing moderate stress 25.1
severe_stress_percentage numeric % of pixels showing severe stress 9.6
mean_value numeric Mean index value across all pixels 0.72
median_value numeric Median index value 0.74
std_dev numeric Standard deviation of index values 0.15
thresholds_used list The threshold values used for classification See below
total_pixels_analyzed integer Total number of valid pixels 125000

Threshold Structure:

result$analysis_results$stress_analysis$NDVI$thresholds_used
# $healthy: c(0.6, 1.0)         # NDVI 0.6-1.0 = healthy
# $moderate_stress: c(0.4, 0.6) # NDVI 0.4-0.6 = moderate stress
# $severe_stress: c(0.0, 0.4)   # NDVI 0.0-0.4 = severe stress

Interpretation: - Healthy: Vegetation is growing normally, adequate water/nutrients - Moderate Stress: Some stress present, may indicate water deficit or nutrient issues - Severe Stress: Significant stress, requires immediate attention

Example Usage:

# Access stress results for NDVI
ndvi_stress <- result$analysis_results$stress_analysis$NDVI

# What percentage of my field is healthy?
cat(sprintf("Healthy vegetation: %.1f%%\n", ndvi_stress$healthy_percentage))

# What's the average NDVI?
cat(sprintf("Mean NDVI: %.3f\n", ndvi_stress$mean_value))

# Check all indices analyzed
names(result$analysis_results$stress_analysis)

2.2 growth_analysis (if analysis_type includes “growth” or “comprehensive”)

Purpose: Estimates crop growth stage and provides growth statistics

Structure:

result$analysis_results$growth_analysis

Contents:

Index-specific statistics (for NDVI, EVI, GNDVI, DVI):

Field Type Description Example
mean numeric Mean index value 0.68
median numeric Median index value 0.70
std_dev numeric Standard deviation 0.12
min numeric Minimum value 0.15
max numeric Maximum value 0.92
range numeric Max - Min 0.77
percentiles numeric vector 10th, 25th, 75th, 90th percentiles c(0.45, 0.58, 0.78, 0.85)
coefficient_of_variation numeric std_dev / mean (measure of variability) 0.18
n_pixels integer Number of pixels analyzed 125000

Growth stage prediction (overall):

Field Type Description Example
predicted_growth_stage character Predicted crop growth stage “reproductive”
stage_confidence numeric Confidence in prediction (0-1) 0.85
crop_type_used character Crop type used for classification “corn”

Growth Stage Classifications:

For Corn: - “emergence”: NDVI < 0.3 - “vegetative”: NDVI 0.3-0.6 - “reproductive”: NDVI 0.6-0.8 - “maturity”: NDVI > 0.8

For Soybeans: - “emergence”: NDVI < 0.4 - “vegetative”: NDVI 0.4-0.65 - “reproductive”: NDVI 0.65-0.8 - “maturity”: NDVI > 0.8

For Wheat: - “tillering”: NDVI < 0.35 - “stem_elongation”: NDVI 0.35-0.7 - “grain_filling”: NDVI 0.7-0.8 - “maturity”: NDVI > 0.8

Example Usage:

# What growth stage is the crop in?
growth <- result$analysis_results$growth_analysis
cat(sprintf("Growth stage: %s (confidence: %.2f)\n", 
            growth$predicted_growth_stage,
            growth$stage_confidence))

# Get detailed NDVI statistics
ndvi_stats <- growth$NDVI
cat(sprintf("NDVI range: %.3f - %.3f\n", ndvi_stats$min, ndvi_stats$max))
cat(sprintf("NDVI variability (CV): %.3f\n", ndvi_stats$coefficient_of_variation))

2.3 yield_analysis (if analysis_type includes “yield” or “comprehensive”)

Purpose: Estimates yield potential using multiple vegetation indices

Structure:

result$analysis_results$yield_analysis

Contents:

Field Type Description Example
composite_yield_index numeric Normalized yield potential score (0-1) 0.72
yield_potential_class character Categorical yield potential “High”
indices_used character vector Which indices contributed c(“NDVI”, “EVI”, “GNDVI”)
n_indices_used integer Number of indices used 3
index_contributions list Individual index contributions See below
crop_type character Crop type used “corn”
classification_confidence numeric Confidence in classification (0-1) 0.44

Index Contributions Structure:

result$analysis_results$yield_analysis$index_contributions$NDVI
# $mean_normalized: 0.75    # Normalized contribution (0-1)
# $raw_mean: 0.68           # Raw mean NDVI value
# $raw_std: 0.12            # Raw standard deviation

Composite Yield Index Calculation: 1. Each index (NDVI, EVI, GNDVI, DVI, RVI) is normalized to 0-1 scale 2. Normalized values are averaged across all available indices 3. Result is a single 0-1 score where: - 0.0 = Very low yield potential - 0.5 = Medium yield potential
- 1.0 = Maximum yield potential

Yield Potential Classifications:

For Corn: - “Low”: composite_index < 0.3 - “Medium”: composite_index 0.3-0.6 - “High”: composite_index 0.6-0.8 - “Very High”: composite_index > 0.8

For Soybeans: - “Low”: composite_index < 0.35 - “Medium”: composite_index 0.35-0.65 - “High”: composite_index 0.65-0.85 - “Very High”: composite_index > 0.85

For Wheat: - “Low”: composite_index < 0.3 - “Medium”: composite_index 0.3-0.6 - “High”: composite_index 0.6-0.8 - “Very High”: composite_index > 0.8

Interpretation: - Composite Yield Index (0.0-1.0): Higher values indicate better yield potential - Yield Potential Class: Categorical assessment for easier interpretation - Index Contributions: Shows which indices contributed and their individual scores - Classification Confidence: Higher when composite_index is far from class boundaries (e.g., 0.2 or 0.9 are more confident than 0.5)

Example Usage:

# What's the yield potential?
yield <- result$analysis_results$yield_analysis
cat(sprintf("Yield Potential: %s (score: %.2f)\n", 
            yield$yield_potential_class,
            yield$composite_yield_index))

# Which indices contributed?
cat(sprintf("Based on %d indices: %s\n",
            yield$n_indices_used,
            paste(yield$indices_used, collapse = ", ")))

# Get individual index contributions
for (idx in names(yield$index_contributions)) {
  contrib <- yield$index_contributions[[idx]]
  cat(sprintf("%s: %.3f (raw: %.3f ± %.3f)\n",
              idx, 
              contrib$mean_normalized,
              contrib$raw_mean,
              contrib$raw_std))
}

2.4 summary_statistics

Purpose: Basic statistical summary for all calculated indices

Structure:

result$analysis_results$summary_statistics

Contents for each index:

Field Type Description
mean numeric Mean value across all pixels
median numeric Median value
std_dev numeric Standard deviation
min numeric Minimum value
max numeric Maximum value
count integer Number of valid pixels
na_count integer Number of NA pixels
range numeric Max - Min
cv numeric Coefficient of variation (std_dev/mean)
percentiles numeric vector 5th, 25th, 75th, 95th percentiles
coverage_percent numeric % of total pixels with valid data
histogram list (optional) Histogram data if ≥100 pixels

Plus Overall Summary:

result$analysis_results$summary_statistics$summary
# $total_indices_calculated: 6
# $indices_with_valid_data: c("NDVI", "EVI", "GNDVI", ...)
# $total_indices_requested: 6
# $success_rate: 100.0

Example Usage:

# Get statistics for all indices
stats <- result$analysis_results$summary_statistics

# NDVI statistics
ndvi_stats <- stats$NDVI
cat(sprintf("NDVI: %.3f ± %.3f (range: %.3f to %.3f)\n",
            ndvi_stats$mean,
            ndvi_stats$std_dev,
            ndvi_stats$min,
            ndvi_stats$max))

# Check data quality
cat(sprintf("Coverage: %.1f%% (%d pixels)\n",
            ndvi_stats$coverage_percent,
            ndvi_stats$count))

2.5 validation (if reference_data provided)

Purpose: Validates analysis against ground truth data

Note: This component only appears if you provide reference_data parameter

Structure: TBD (depends on reference data format)


3. metadata (List)

Purpose: Documents analysis parameters and processing information

Structure:

result$metadata

Contents:

Field Type Description Example
crop_type character Crop type analyzed “corn”
growth_stage character Growth stage specified “mid”
analysis_type character Type of analysis performed “comprehensive”
indices_used character vector Indices calculated c(“NDVI”, “EVI”, …)
processing_date POSIXct When analysis was performed 2025-11-03 10:30:45
input_bands integer Number of input spectral bands 8
spatial_resolution numeric vector Resolution (x, y) in map units c(10, 10)
spatial_extent numeric vector Extent (xmin, xmax, ymin, ymax) c(-95.5, -95.0, 41.5, 42.0)

Example Usage:

# Check what was analyzed
meta <- result$metadata
cat(sprintf("Analyzed %s at %s growth stage\n", 
            meta$crop_type, 
            meta$growth_stage))
cat(sprintf("Used %d indices: %s\n",
            length(meta$indices_used),
            paste(meta$indices_used, collapse = ", ")))
cat(sprintf("Processed on: %s\n", meta$processing_date))

Complete Example Workflow

library(geospatialsuite)
library(terra)

# Run comprehensive crop analysis
result <- analyze_crop_vegetation(
  spectral_data = "path/to/sentinel2_data.tif",
  crop_type = "corn",
  growth_stage = "mid",
  analysis_type = "comprehensive",
  verbose = TRUE
)

# ===== 1. Check what was calculated =====
cat("Indices calculated:\n")
print(names(result$vegetation_indices))

# ===== 2. Assess crop stress =====
stress <- result$analysis_results$stress_analysis$NDVI
cat(sprintf("\nStress Assessment:\n"))
cat(sprintf("  Healthy: %.1f%%\n", stress$healthy_percentage))
cat(sprintf("  Moderate stress: %.1f%%\n", stress$moderate_stress_percentage))
cat(sprintf("  Severe stress: %.1f%%\n", stress$severe_stress_percentage))

# ===== 3. Identify growth stage =====
growth <- result$analysis_results$growth_analysis
cat(sprintf("\nGrowth Stage: %s (%.0f%% confidence)\n",
            growth$predicted_growth_stage,
            growth$stage_confidence * 100))

# ===== 4. Estimate yield potential =====
yield <- result$analysis_results$yield_analysis
cat(sprintf("\nYield Potential: %s\n", yield$yield_potential_class))
cat(sprintf("Composite Yield Index: %.3f\n", yield$composite_yield_index))
cat(sprintf("Based on %d indices: %s\n",
            yield$n_indices_used,
            paste(yield$indices_used, collapse = ", ")))

# ===== 5. Visualize results =====
# Plot stress map
plot(result$vegetation_indices[["NDVI"]], 
     main = "NDVI - Stress Detection",
     col = terrain.colors(100))

# Create stress classification map
ndvi <- result$vegetation_indices[["NDVI"]]
stress_map <- classify(ndvi, 
                      matrix(c(-Inf, 0.4, 1,    # Severe stress
                              0.4, 0.6, 2,       # Moderate stress
                              0.6, Inf, 3),      # Healthy
                            ncol = 3, byrow = TRUE))
plot(stress_map, 
     main = "Crop Stress Classification",
     col = c("red", "yellow", "green"),
     legend = FALSE)
legend("topright", 
       legend = c("Severe Stress", "Moderate Stress", "Healthy"),
       fill = c("red", "yellow", "green"))

# ===== 6. Export results =====
# Save as geotiff
writeRaster(result$vegetation_indices, 
            "crop_indices.tif", 
            overwrite = TRUE)

# Save statistics as CSV
stats_df <- data.frame(
  Index = names(result$analysis_results$summary_statistics)[-length(names(result$analysis_results$summary_statistics))],
  Mean = sapply(result$analysis_results$summary_statistics[1:(length(result$analysis_results$summary_statistics)-1)], function(x) x$mean),
  StdDev = sapply(result$analysis_results$summary_statistics[1:(length(result$analysis_results$summary_statistics)-1)], function(x) x$std_dev),
  Min = sapply(result$analysis_results$summary_statistics[1:(length(result$analysis_results$summary_statistics)-1)], function(x) x$min),
  Max = sapply(result$analysis_results$summary_statistics[1:(length(result$analysis_results$summary_statistics)-1)], function(x) x$max)
)
write.csv(stats_df, "crop_analysis_statistics.csv", row.names = FALSE)

Tips for Using Results

1. Identifying Problem Areas

# Find pixels with severe stress
ndvi <- result$vegetation_indices[["NDVI"]]
severe_stress <- ndvi < 0.4
plot(severe_stress, main = "Severe Stress Areas")

2. Comparing Multiple Fields

# Run analysis for multiple fields and compare
field1_result <- analyze_crop_vegetation(field1_data, crop_type = "corn")
field2_result <- analyze_crop_vegetation(field2_data, crop_type = "corn")

# Compare yield potential
cat(sprintf("Field 1 yield: %s (%.3f)\n", 
            field1_result$analysis_results$yield_analysis$yield_potential_class,
            field1_result$analysis_results$yield_analysis$composite_yield_index))
cat(sprintf("Field 2 yield: %s (%.3f)\n",
            field2_result$analysis_results$yield_analysis$yield_potential_class,
            field2_result$analysis_results$yield_analysis$composite_yield_index))

3. Time Series Analysis

# Analyze the same field at different dates
early_season <- analyze_crop_vegetation(june_data, growth_stage = "early")
mid_season <- analyze_crop_vegetation(july_data, growth_stage = "mid")
late_season <- analyze_crop_vegetation(august_data, growth_stage = "late")

# Track NDVI progression
ndvi_progression <- c(
  early_season$analysis_results$growth_analysis$NDVI$mean,
  mid_season$analysis_results$growth_analysis$NDVI$mean,
  late_season$analysis_results$growth_analysis$NDVI$mean
)
plot(1:3, ndvi_progression, type = "b",
     xlab = "Time Period", ylab = "Mean NDVI",
     main = "NDVI Progression Through Season")

Validation Notes

Important Caveats: 1. Threshold-based: Stress and yield classifications use literature-based thresholds that may need adjustment for your specific region/conditions 2. Composite Yield Index: This is a vegetation-based proxy, not a direct yield prediction. Correlation with actual yield varies by crop, region, and year 3. Growth Stage: Predictions are based on NDVI patterns and may not align perfectly with field observations 4. No guarantee: These are analytical tools to support decision-making, not definitive assessments

Recommended Validation: - Compare with ground truth data (yield monitors, field scouting) - Calibrate thresholds for your specific conditions - Use multiple years of data to establish local patterns - Combine with other data sources (weather, soil, management)


Acknowledgments

This work was developed by the GeoSpatialSuite team with contributions from: Olatunde D. Akanbi, Vibha Mandayam, Yinghui Wu, Jeffrey Yarus, Erika I. Barcelos, and Roger H. French.