USDA CropScape
Kyle Messier, with assistance from GitHub Copilot
2026-05-20
Source:vignettes/cropscape_workflow.Rmd
cropscape_workflow.RmdThis article demonstrates a compact workflow for USDA CropScape (Cropland Data Layer) rasters.
This vignette runs its live workflow when rendered locally. The heavy
download, processing, extraction, and plotting chunks are skipped
automatically on CI, CRAN checks, and pkgdown builds; set
AMADEUS_RUN_VIGNETTES=true to force live execution in those
environments.
Available inputs and data availability
-
download_data(dataset_name = "cropscape", ...)accepts ayearandsource = "USDA"or"GMU". - CropScape is an annual 30 m Cropland Data Layer product: USDA downloads are available from 2008 through the latest release, while GMU extends back to 1997.
- File formats depend on source: USDA serves yearly ZIP archives and
GMU serves yearly
.tar.gzarchives. - CropScape values are categorical crop/land-use classes. Covariate calculation are a fraction of each land cover class within the specified radius or areal unit.
The table below reproduces the crop and related land-use classes listed in the official USDA CDL metadata used by CropScape; see the 2024 CDL metadata for the complete legend and additional class details.
| CDL code | cropland class |
|---|---|
| 1 | Corn |
| 2 | Cotton |
| 3 | Rice |
| 4 | Sorghum |
| 5 | Soybeans |
| 6 | Sunflower |
| 10 | Peanuts |
| 11 | Tobacco |
| 12 | Sweet Corn |
| 13 | Pop or Orn Corn |
| 14 | Mint |
| 21 | Barley |
| 22 | Durum Wheat |
| 23 | Spring Wheat |
| 24 | Winter Wheat |
| 25 | Other Small Grains |
| 26 | Dbl Crop WinWht/Soybeans |
| 27 | Rye |
| 28 | Oats |
| 29 | Millet |
| 30 | Speltz |
| 31 | Canola |
| 32 | Flaxseed |
| 33 | Safflower |
| 34 | Rape Seed |
| 35 | Mustard |
| 36 | Alfalfa |
| 37 | Other Hay/Non Alfalfa |
| 38 | Camelina |
| 39 | Buckwheat |
| 41 | Sugarbeets |
| 42 | Dry Beans |
| 43 | Potatoes |
| 44 | Other Crops |
| 45 | Sugarcane |
| 46 | Sweet Potatoes |
| 47 | Misc Vegs & Fruits |
| 48 | Watermelons |
| 49 | Onions |
| 50 | Cucumbers |
| 51 | Chick Peas |
| 52 | Lentils |
| 53 | Peas |
| 54 | Tomatoes |
| 55 | Caneberries |
| 56 | Hops |
| 57 | Herbs |
| 58 | Clover/Wildflowers |
| 59 | Sod/Grass Seed |
| 60 | Switchgrass |
| 61 | Fallow/Idle Cropland |
| 64 | Shrubland |
| 66 | Cherries |
| 67 | Peaches |
| 68 | Apples |
| 69 | Grapes |
| 70 | Christmas Trees |
| 71 | Other Tree Crops |
| 72 | Citrus |
| 74 | Pecans |
| 75 | Almonds |
| 76 | Walnuts |
| 77 | Pears |
| 92 | Aquaculture |
| 204 | Pistachios |
| 205 | Triticale |
| 206 | Carrots |
| 207 | Asparagus |
| 208 | Garlic |
| 209 | Cantaloupes |
| 210 | Prunes |
| 211 | Olives |
| 212 | Oranges |
| 213 | Honeydew Melons |
| 214 | Broccoli |
| 215 | Avocados |
| 216 | Peppers |
| 217 | Pomegranates |
| 218 | Nectarines |
| 219 | Greens |
| 220 | Plums |
| 221 | Strawberries |
| 222 | Squash |
| 223 | Apricots |
| 224 | Vetch |
| 225 | Dbl Crop WinWht/Corn |
| 226 | Dbl Crop Oats/Corn |
| 227 | Lettuce |
| 228 | Dbl Crop Triticale/Corn |
| 229 | Pumpkins |
| 230 | Dbl Crop Lettuce/Durum Wht |
| 231 | Dbl Crop Lettuce/Cantaloupe |
| 232 | Dbl Crop Lettuce/Cotton |
| 233 | Dbl Crop Lettuce/Barley |
| 236 | Dbl Crop WinWht/Sorghum |
| 237 | Dbl Crop Barley/Corn |
| 238 | Dbl Crop WinWht/Cotton |
| 239 | Dbl Crop Soybeans/Cotton |
| 240 | Dbl Crop Soybeans/Oats |
| 241 | Dbl Crop Corn/Soybeans |
| 242 | Blueberries |
| 243 | Cabbage |
| 244 | Cauliflower |
| 245 | Celery |
| 246 | Radishes |
| 247 | Turnips |
| 248 | Eggplants |
| 249 | Gourds |
| 250 | Cranberries |
| 254 | Dbl Crop Barley/Soybeans |
Download representative requests
directory_to_save <- file.path(tempdir(), "cropscape_workflow")
download_data(
dataset_name = "cropscape",
year = 2020,
source = "USDA",
directory_to_save = file.path(directory_to_save, "usda_2020"),
acknowledgement = TRUE,
unzip = TRUE
)Process one workflow-ready data product
cropscape_path <- list.files(
file.path(directory_to_save, "usda_2020"),
pattern = "2020.*\\.tif$",
recursive = TRUE,
full.names = TRUE
)[1]
cropscape_reference <- terra::rast(cropscape_path)
print(cropscape_reference)
processed_data <- process_covariates(
covariate = "cropscape",
path = cropscape_path,
year = 2020,
extent = {
ref_ext <- terra::ext(cropscape_reference)
dx <- terra::xmax(ref_ext) - terra::xmin(ref_ext)
dy <- terra::ymax(ref_ext) - terra::ymin(ref_ext)
terra::ext(
terra::xmin(ref_ext) + 0.35 * dx,
terra::xmin(ref_ext) + 0.65 * dx,
terra::ymin(ref_ext) + 0.35 * dy,
terra::ymin(ref_ext) + 0.65 * dy
)
}
)
plot(processed_data, main = "Processed CropScape raster")Calculate covariates at points
Note that if no radius is specified, the point extraction will return the value of the raster cell containing each point. In this case, the output columns are binary indicators of whether each CropScape class is present at that location. With a radius specified, the output columns are a fraction of each class within the circular buffer around each point, so values can range from 0 to 1 and multiple classes can be present at the same location with varying proportions.
domain_x <- c(terra::xmin(processed_data), terra::xmax(processed_data))
domain_y <- c(terra::ymin(processed_data), terra::ymax(processed_data))
domain_dx <- diff(domain_x)
domain_dy <- diff(domain_y)
candidate_xy <- expand.grid(
lon = seq(domain_x[1] + 0.12 * domain_dx, domain_x[2] - 0.12 * domain_dx, length.out = 5),
lat = seq(domain_y[1] + 0.12 * domain_dy, domain_y[2] - 0.12 * domain_dy, length.out = 5)
)
raster_crs <- sf::st_crs(terra::crs(processed_data))
if (is.na(raster_crs)) {
stop("`processed_data` is missing CRS; cannot build extraction points.")
}
example_points_sf <- sf::st_as_sf(
candidate_xy,
coords = c("lon", "lat"),
crs = raster_crs
)
example_points_sf$site_id <- paste0("site_", seq_len(nrow(example_points_sf)))
point_values <- calculate_covariates(
covariate = "cropscape",
from = processed_data,
locs = example_points_sf,
locs_id = "site_id",
radius = 0,
geom = "sf"
)
print(point_values)
point_proportion_100m <- calculate_covariates(
covariate = "cropscape",
from = processed_data,
locs = example_points_sf,
locs_id = "site_id",
radius = 100,
geom = "sf"
)
print(point_proportion_100m)