Die Oberfläche ist lokalisiert; Skill-Beschreibungen und -Dokumente werden per KI automatisch in Ihre Sprache übersetzt. Code, Befehle, Pfade und Fachbegriffe bleiben im Original, maßgeblich ist das Dokument des Autors.
Skills entdecken
Daten und Forschung · SKILL.md

soil-data-download

soil-data-download

Downloads SoilGrids global soil property rasters (clay, sand, silt, bulk density, organic carbon, pH, water content at multiple depths) and optionally stacks them into a validated soil datacube. Trigger this skill whenever the user mentions SoilGrids, soil texture, soil organic carbon, soil pH, soil water retention, soil depth layers (`0-5`, `5-15`, `15-30`…

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill-Dokumentation

GitHub

name: soil-data-download description: "Downloads SoilGrids global soil property rasters (clay, sand, silt, bulk density, organic carbon, pH, water content at multiple depths) and optionally stacks them into a validated soil datacube. Trigger this skill whenever the user mentions SoilGrids, soil texture, soil organic carbon, soil pH, soil water retention, soil depth layers (0-5, 5-15, 15-30, 30-60, 60-100 cm), or asks to download / build / stack soil data for any country, region, or bounding box — even if they don't say 'SoilGrids' explicitly (e.g. 'I need clay and sand for Malawi', 'soil cube for Mzimba', 'download soil data for the Sahel'). Also trigger when the user hits a reference_variable: non-null string required schema error trying to use climate-data-download for soil — that pipeline is climate-only; this skill handles the soil path. Do NOT trigger for climate variables (precipitation, temperature, RH, ET) — use climate-data-download for those." license: "MIT"

Soil Data Download

You orchestrate aggeodata's SoilGrids downloader and the soil cube builder. The user tells you what soil properties they need and over which area — you decide which API to use (YAML pipeline or direct Python), download the rasters, and optionally stack them into a validated NetCDF datacube that downstream tools (spatial-crop-modeler, geospatial-cube-processor) can consume directly.

This skill is the soil counterpart to climate-data-download. For mixed climate + soil (e.g. crop modeling), use spatial-crop-modeler instead — it runs both via ag-cube-cm's combined schema.


WHAT SOILGRIDS PROVIDES

SoilGrids is the only soil source aggeodata currently routes to. It serves two different products through two different endpoints, and the difference matters:

250 m — the WCS coverage API

VariableDescriptionDefault?
clayClay content (g/kg)yes
sandSand content (g/kg)yes
siltSilt content (g/kg)yes
bdodBulk density (cg/cm³)yes
cfvoCoarse fragments volumetric (cm³/dm³)yes
socSoil organic carbon (dg/kg)yes
phh2opH in H₂Oyes
nitrogenTotal nitrogen (cg/kg)optional
cecCation exchange capacity (mmol(c)/kg)optional

1 km — the aggregated hydraulic layers, streamed from ISRIC's pre-release bucket. These are the water-retention points a crop model needs:

VariableDescriptionDefault?
wv0010Volumetric water content at 10 kPacrop modeling
wv0033Volumetric water content at 33 kPa (field capacity)crop modeling
wv1500Volumetric water content at 1500 kPa (wilting point)crop modeling

The resolution split is the thing to tell the user about. A request mixing texture with water content produces a cube at one resolution — whichever reference_variable names — so one group is always resampled:

  • reference_variable: sand (or any 250 m variable) → the water layers are upsampled from 1 km. The extra pixels are replicated, not measured; the cube looks sharper than the water data actually is.
  • reference_variable: wv1500 (the package default) → the texture layers are downsampled to 1 km. Nothing is invented, but real 250 m detail is averaged away.

Neither is wrong. For crop modeling the cube is usually resampled onto a coarser climate grid anyway (CHIRPS is 5 km), so wv1500 costs nothing and avoids fake precision. For soil mapping, where texture detail is the point, prefer a 250 m reference. Say which you picked and why.

Not available: ocd (organic carbon density) and ocs (organic carbon stocks) are routed by neither endpoint. Asking for them logs Unknown SoilGrids variable and silently produces no file — the run appears to succeed with those layers simply absent. Do not offer them; if the user asks, say they are not wired up rather than letting the gap show up later as a missing variable.

Standard depths: 0-5, 5-15, 15-30, 30-60, 60-100 cm.

Native CRS: Interrupted Goode Homolosine. You must reproject explicitly to EPSG:4326 to use the cube with climate data — see Step 4.


STEP-BY-STEP WORKFLOW

Step 0 — Environment check

Before doing anything else, run:

python -c "
import importlib.metadata, sys
for pkg in ['aggeodata']:
    try:
        print('OK', pkg, importlib.metadata.version(pkg))
    except importlib.metadata.PackageNotFoundError:
        print('MISSING', pkg); sys.exit(1)
"

If missing, install:

pip install "aggeodata[download,mcp] @ git+https://github.com/CGIAR-Climate-Data-Hub/aggeodata.git"

Step 1 — Collect parameters

ParameterQuestionDefault
Country / bboxISO3 code, country name, or [xmin, ymin, xmax, ymax] in EPSG:4326?required
VariablesWhich soil properties?the 10 rows marked yes or crop modeling above. ocd / ocs are not available — do not offer them
DepthsWhich depth layers?all five (0-5, 5-15, 15-30, 30-60, 60-100)
Output folderWhere to save? No spaces in path.required
Build cube?Stack the downloaded rasters into a single NetCDF?yes
Reference variableThe variable whose grid defines the cube's resolution — see the 250 m / 1 km split above, because this decides which group gets resampledwv1500 (package default; use a 250 m variable when texture detail is the point)

If the user gives a country name, resolve to ISO3 + bounding box:

from aggeodata.ingestion.boundaries import _fetch_geojson_cached
gdf = _fetch_geojson_cached("MWI", 0)        # ISO3, admin level 0
extent = [round(v, 4) for v in gdf.total_bounds.tolist()]   # [xmin, ymin, xmax, ymax]

Step 2 — Show the plan and confirm

Display this table and wait for confirmation before downloading:

Soil download plan
| Setting     | Value                                                            |
|-------------|------------------------------------------------------------------|
| Source      | SoilGrids                                                        |
| Variables   | clay, sand, silt, bdod, cfvo, soc, phh2o, wv0010, wv0033, wv1500 |
| Depths      | 0-5, 5-15, 15-30, 30-60, 60-100 cm                               |
| Extent      | [32.67, -17.13, 35.92, -9.36]  (Malawi)                          |
| Output      | D:/data/malawi/soil_raw                                          |
| Build cube? | yes → D:/data/malawi/soil_malawi.nc                              |
| Reference   | sand                                                             |

~50 raster tiles, 2–10 min depending on extent. Shall I proceed?

Step 3 — Download

Two paths — pick based on whether the user is integrating with the wider aggeodata config pipeline or just wants the rasters.

Path A — Python API (recommended for soil-only)

Fastest, no schema overhead. Use this by default.

from aggeodata.ingestion.soil import SoilGridsDownloader

dl = SoilGridsDownloader(
    soil_layers=["clay","sand","silt","bdod","cfvo","soc","phh2o","wv0010","wv0033","wv1500"],
    depths=["0-5","5-15","15-30","30-60","60-100"],
    output_folder="{OUTPUT}/soil_raw",
)
downloaded = dl.download(boundaries=[{XMIN},{YMIN},{XMAX},{YMAX}])
# returns {filename: local_path}

Full signature is in references/python_api.md.

Path B — YAML pipeline (use when integrating with a climate config)

The aggeodata YAML pipeline accepts a soil block. general.reference_variable must be a non-null soil layer name — use sand as the default. Setting it to null or omitting it raises reference_variable: non-null string required.

Section keys are lowercase canonical (spatial_info, soil, general, paths); the historical UPPERCASE keys are still accepted as aliases.

spatial_info:
  extent: [{XMIN}, {YMIN}, {XMAX}, {YMAX}]
soil:
  enabled:   true
  variables: [clay, sand, silt, bdod, cfvo, soc, phh2o, wv0010, wv0033, wv1500]
  depths:    ["0-5", "5-15", "15-30", "30-60", "60-100"]
general:
  suffix:             "{SUFFIX}"
  ncores:             1
  task:               "download"
  reference_variable: sand    # non-null; any enabled soil layer is fine
paths:
  output_path: "{OUTPUT}/soil_raw"

Run with:

from aggeodata.pipelines.download import run_download
run_download("{OUTPUT}/config.yaml")

dates and climate may be omitted entirely for soil-only configs.

Step 4 — Build the soil cube (optional but usually wanted)

Downstream tools want a single multi-variable NetCDF, not loose tiles. Use SoilDataCubeBuilder — and always pass the CRS explicitly.

from aggeodata.transform.soil_cube import SoilDataCubeBuilder

# SoilGrids native projection — never accept the builder's default ESRI:54052,
# it produces a cube with bad geographic extents.
IGH = "+proj=igh +lat_0=0 +lon_0=0 +datum=WGS84 +units=m +no_defs"

builder = SoilDataCubeBuilder(
    data_folder="{OUTPUT}/soil_raw",
    variables=["clay","sand","silt","bdod","cfvo","soc","phh2o","wv0010","wv0033","wv1500"],
    reference_variable="sand",
    crs=IGH,
    target_crs="EPSG:4326",
)
builder.build_and_save(
    output_path="{OUTPUT}",
    filename="soil_{SUFFIX}.nc",
)

Zarr output. build_and_save also takes output_format="zarr" with codec="zstd", which writes a Zarr v3 store instead of a NetCDF file — the same format the climate cube can use, so a pipeline can keep one format throughout:

builder.build_and_save(
    output_path="{OUTPUT}",
    filename="soil_{SUFFIX}.zarr",
    output_format="zarr",      # "netcdf" (default) | "zarr"
    codec="zstd",              # "blosc" is smaller; "zstd" is the one GDAL reads
)

Two things to know before choosing it. Soil variables are written as float32, not scaled integers — a soil profile has no time axis, so the archive is small enough that quantisation buys little, and each property would need its own scale. And the depth coordinate is a string ('0-5'), a dtype Zarr v3 has no specification for; Python readers handle it, ag-cube-cm opens such a cube directly, but another Zarr implementation may not. Prefer NetCDF if the cube is going somewhere outside Python.

The reference_variable here is the SAME one chosen in Step 1 / 3 — it defines the cube's reference grid.

Step 5 — Validate the cube

Always validate before passing the cube to a model. Three checks:

import xarray as xr
soil = xr.open_dataset("{OUTPUT}/soil_{SUFFIX}.nc")

# 1. Flat-format check — older pipelines emit one variable per depth (e.g. clay_0-5cm_mean).
flat_vars = [v for v in soil.data_vars if "_cm_mean" in v]
if flat_vars:
    from aggeodata.transform.soil_cube import reshape_flat_soil_cube
    soil = reshape_flat_soil_cube(soil)
    soil.to_netcdf("{OUTPUT}/soil_{SUFFIX}.nc")   # overwrite with fixed version

# 2. CRS check — coordinates must be in degrees, not projected metres.
assert abs(float(soil.y.min())) < 90,  "Soil y is projected (metres). Rebuild with target_crs='EPSG:4326'."
assert abs(float(soil.x.min())) < 180, "Soil x is projected (metres). Rebuild with target_crs='EPSG:4326'."

# 3. Variable & depth check
expected_vars = {"clay","sand","silt","bdod","cfvo","soc","phh2o","wv0010","wv0033","wv1500"}
missing = expected_vars - set(soil.data_vars)
assert not missing, f"Missing soil variables: {missing}"
print("Soil cube OK:", dict(soil.dims))

If a soil cube will be paired with a climate cube downstream, also assert extent overlap:

weather = xr.open_dataset("{OUTPUT}/climate_{SUFFIX}.nc")
assert float(soil.y.min()) <= float(weather.y.max()), "Soil and weather extents don't overlap on Y axis."
assert float(soil.x.min()) <= float(weather.x.max()), "Soil and weather extents don't overlap on X axis."

Step 6 — Summary

Report back to the user:

Soil download complete:

| Stage      | Action                                             | Status |
|------------|----------------------------------------------------|--------|
| Download   | {N_VARS} variables × {N_DEPTHS} depths → soil_raw  | ✓ done |
| Build cube | SoilDataCubeBuilder → soil_{SUFFIX}.nc (EPSG:4326) | ✓ done |
| Validate   | flat-format / CRS / variable checks                | ✓ pass |

Output cube: {OUTPUT}/soil_{SUFFIX}.nc
Shape: {DIMS}   (y, x, depth, variable as appropriate)

COMMON ISSUES

SymptomCauseFix
reference_variable: non-null string requiredUsed the climate YAML schema with soil.enabled: true but didn't set general.reference_variableSet general.reference_variable: sand (or another enabled soil layer)
Cube coordinates are huge numbers (e.g. y = 1500000)The builder kept SoilGrids' native projected CRSPass crs=IGH and target_crs="EPSG:4326" explicitly to SoilDataCubeBuilder
no valid index for a 0-dimensional object when sampling the cubeCube is in flat format (clay_0-5cm_mean, etc.)Run reshape_flat_soil_cube(ds).to_netcdf(...) then reopen
Downloads fail with HTTP 403 / connection errorsSoilGrids rate limitWait, then retry — SoilGridsDownloader resumes; existing tiles are skipped
CPLE_AppDefined in wv0010_0-5cm_mean.tif: This file used to have optimizations in its layout, but those have been... invalidated by later changesHarmless. The 1 km wv* GeoTIFFs in ISRIC's pre-release bucket were written as cloud-optimized files and later edited in place, so part of the COG layout no longer matches. GDAL still reads the data correctly; it just can't use the optimized streaming path, so those reads need more HTTP range requests and run slowerNothing to fix — reassure the user rather than letting them think the download failed. One line per wv* file is expected. To quiet just this message without hiding real GDAL warnings, filter the rasterio._env logger on the substring invalidated by later changes
UnicodeEncodeError on WindowsNon-ASCII chars in output pathUse ASCII-only paths (e.g. D:/data/malawi)
Soil cube doesn't overlap weather cubeExtent rounding or CRS mismatchRe-clip both to the same bounding box and ensure both are EPSG:4326

RESPONSE STYLE

  • Show the plan table before doing anything; wait for explicit confirmation.
  • After each stage (download, build, validate): one short sentence — what landed on disk and the output path.
  • On error, quote the message, identify which step (download / build / validate) it came from, then propose the specific fix from the table above.
  • After the cube is validated, point the user at geospatial-cube-processor (to clip / aggregate per admin unit) or spatial-crop-modeler (if they're doing yield simulation next).

Evaluation examples: references/evals.json

Gespeicherte Skills