iSDA soil data

Keywords data used; iSDA soil, soil

Background

iSDAsoil is an open access soils data resource for the African continent. Soils data can be useful for a range of applications including land suitability mapping for agriculture, soil amelioration planning (e.g. liming to address acidic soils or fertiliser to address fertility constraints), and for construction/ infrastructure planning.

Description

This notebook demonstrates how to integrate this data with DE Africa products and workflows. For technical information on iSDAsoil, see Miller et al. 2021. Some of the code in this notebook has been adapted from the iSDA-Africa GitHub.

  1. Load iSDA data

  2. Visualise iSDA against DE Africa products


Getting started

To run this analysis, run all the cells in the notebook, starting with the “Load packages” cell.

Load packages

Import Python packages that are used for the analysis.

Note that we are loading a module called load_isda() for this notebook which takes a little while to load.

[1]:
%matplotlib inline

import datacube
import numpy as np
import pandas as pd
import xarray as xr
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import rasterio as rio
from pyproj import Transformer
import matplotlib.pyplot as plt
import os
import numpy as np

from urllib.parse import urlparse
import boto3
from pystac import stac_io, Catalog
from deafrica_tools.plotting import display_map, rgb
from deafrica_tools.load_isda import load_isda
/usr/local/lib/python3.8/dist-packages/geopandas/_compat.py:112: UserWarning: The Shapely GEOS version (3.8.0-CAPI-1.13.1 ) is incompatible with the GEOS version PyGEOS was compiled with (3.10.3-CAPI-1.16.1). Conversions between both will be slow.
  warnings.warn(

iSDA variables

iSDA offers numerous soil variables shown in the table below. Many of the variables have four layers: mean and standard deviation at 0-20cm and 20-5cm depth. We will see through the notebook that we can bring in each variable with the Access Names e.g. ‘bedrock_depth’.

Variable

Access Name

Layers

Aluminium, extractable

aluminium_extractable

0-20cm and 20-50cm, predicted mean and standard deviation

Depth to bedrock

bedrock_depth

0-200cm depth, predicted mean and standard deviation

Bulk density, <2mm fraction

bulk_density

0-20cm and 20-50cm, predicted mean and standard deviation

Calcium, extractable

calcium_extractable

0-20cm and 20-50cm, predicted mean and standard deviation

Carbon, organic

carbon_organic

0-20cm and 20-50cm, predicted mean and standard deviation

Carbon, total

carbon_total

0-20cm and 20-50cm, predicted mean and standard deviation

Effective Cation Exchange Capacity

cation_exchange_capacity

0-20cm and 20-50cm, predicted mean and standard deviation

Clay content

clay_content

0-20 cm and 20-50 cm, predicted mean and standard deviation

Fertility Capability Classification

fcc

Single classification layer

Iron, extractable

iron_extractable

0-20cm and 20-50cm, predicted mean and standard deviation

Magnesium, extractable

magnesium_extractable

0-20cm and 20-50cm, predicted mean and standard deviation

Nitrogen, total

nitrogen_total

0-20cm and 20-50cm, predicted mean and standard deviation

pH

ph

0-20cm and 20-50cm, predicted mean and standard deviation

Phosphorus, extractable

phosphorous_extractable

0-20cm and 20-50cm, predicted mean and standard deviation

Potassium, extractable

potassium_extractable

0-20cm and 20-50cm, predicted mean and standard deviation

Sand content

sand_content

0-20cm and 20-50cm, predicted mean and standard deviation

Silt content

silt_content

0-20cm and 20-50cm, predicted mean and standard deviation

Stone content

stone_content

Coarse fragments predicted at 0-20cm depth

Sulphur, extractable

sulphur_extractable

0-20cm and 20-50cm, predicted mean and standard deviation

USDA Texture Class

texture_class

0-20cm and 20-50cm depth, predicted mean

Zinc, extractable

zinc_extractable

0-20cm and 20-50cm, predicted mean and standard deviation

Define parameters

Let’s define some parameters for loading the Fertility Capability Classification in a coastal area near Cape Town, South Africa. We can see in the table above that we can access this variable with `fcc’.

[2]:
var = 'fcc'

lat = -32.9
lon = 18.7
buffer = 0.5

display_map((lon - buffer, lon + buffer), (lat - buffer, lat + buffer))
[2]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Load Fertility Capability Classification

The cell below uses the load_isda() function to bring in Fertility Capability Classification for the area defined above.

We can see below that the function returns a variable called band_data as float32.

[4]:
ds = load_isda(var, (lat-buffer, lat + buffer), (lon-buffer, lon + buffer))
ds
[4]:
<xarray.Dataset>
Dimensions:      (x: 3712, y: 4420)
Coordinates:
    band         int64 1
  * x            (x) float64 2.026e+06 2.026e+06 ... 2.137e+06 2.137e+06
  * y            (y) float64 -3.816e+06 -3.816e+06 ... -3.948e+06 -3.949e+06
    spatial_ref  int64 0
Data variables:
    band_data    (y, x) float32 ...

Transformation!

Importantly, much of the iSDA data needs to be transformed before it’s applied. We can use the object we stored called assets to get conversion metadata associated with each variable. This is performed for our selected variable below.

We can see that the fcc variable returns a conversion value of %3000.

[5]:
conversion = assets[var].extra_fields["back-transformation"]
conversion
[5]:
'%3000'

Below, the transformation is made and a new band added to the dataset. The values are converted to integers for easier interpretation and plotting.

[6]:
ds = ds.assign(fcc = (ds["band_data"]%3000).fillna(2535).astype('int16'))

Labels can also be drawn from the metadata. Below, classes of fertility are mapped to their respective integers for better plotting outputs.

[7]:
fcc_labels = pd.read_csv(assets['fcc'].assets["metadata"].href, index_col=0)

mappings = {val:fcc_labels.loc[int(val),"Description"] for val in np.unique(ds.fcc.astype('int16'))}

Plot Fertility Capability Classification

We can now plot the fcc data with labels.

[8]:
plt.figure(figsize=(12,10), dpi= 100)
im = ds.fcc.plot(cmap="magma", add_colorbar=False)
colors = [ im.cmap(im.norm(value)) for value in np.unique(ds.fcc)]
patches = [mpatches.Patch(color=colors[idx], label=str(mappings[val])) for idx, val in enumerate(np.unique(ds.fcc))]
plt.legend(handles=patches, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0. )
plt.title("Fertility Capability Classification with labels")
plt.show()
../../../_images/sandbox_notebooks_Datasets_iSDAsoil_23_0.png

Clay in Okavango Delta

Now we’ll inspect soil clay content in the Okavango Delta, Botswana.

New parameters

New parameters for data loading are defined in the cell below. The var object is taken from the relevant Access Name shown at the beginning of this notebook.

[9]:
var = 'clay_content'

lat = -19.6
lon = 22.6
buffer = 0.5

display_map((lon - buffer, lon + buffer), (lat - buffer, lat + buffer))
[9]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Use the load_isda() function to bring in clay content data.

[10]:
ds = load_isda(var, (lat-buffer, lat + buffer), (lon-buffer, lon + buffer))
ds
/usr/local/lib/python3.8/dist-packages/rioxarray/_io.py:314: NodataShadowWarning: The dataset's nodata attribute is shadowing the alpha band. All masks will be determined by the nodata attribute
  out = riods.read(band_key, window=window, masked=self.masked)
[10]:
<xarray.Dataset>
Dimensions:                                             (x: 3711, y: 3940)
Coordinates:
  * x                                                   (x) float64 2.46e+06 ...
  * y                                                   (y) float64 -2.167e+0...
    spatial_ref                                         int64 0
    band                                                <U9 'band_data'
Data variables:
    Clay content, predicted mean at 0-20 cm depth       (y, x) float32 13.0 ....
    Clay content, predicted mean at 20-50 cm depth      (y, x) float32 13.0 ....
    Clay content, standard deviation at 0-20 cm depth   (y, x) float32 2.0 .....
    Clay content, standard deviation at 20-50 cm depth  (y, x) float32 2.0 .....

Transformation!

Check if and how the data needs to be transformed. In this case, there is no transformation and the data can be interpreted as %, so we can proceed.

[11]:
conversion = assets[var].extra_fields["back-transformation"]
conversion
[12]:
fig, ax = plt.subplots(2, 2, figsize=(22, 16), sharey=True)
ds[list(ds.keys())[0]].plot(ax=ax[0, 0])
ds[list(ds.keys())[1]].plot(ax=ax[0, 1])
ds[list(ds.keys())[2]].plot(ax=ax[1, 0])
ds[list(ds.keys())[3]].plot(ax=ax[1, 1])
ax[0, 0].set_title(list(ds.keys())[0])
ax[0, 1].set_title(list(ds.keys())[1])
ax[1, 0].set_title(list(ds.keys())[2])
ax[1, 1].set_title(list(ds.keys())[3])
plt.tight_layout();
../../../_images/sandbox_notebooks_Datasets_iSDAsoil_31_0.png

Soil pH

Finally, we’ll inspect soil pH for the same Okavango Delta region.

The lat and lon definitions will remain constant, just the var is changed below, as per the table with Access Names at the beginning of the notebook.

[13]:
var = 'ph'
[14]:
ds = load_isda(var, (lat-buffer, lat + buffer), (lon-buffer, lon + buffer))
ds
/usr/local/lib/python3.8/dist-packages/rioxarray/_io.py:314: NodataShadowWarning: The dataset's nodata attribute is shadowing the alpha band. All masks will be determined by the nodata attribute
  out = riods.read(band_key, window=window, masked=self.masked)
[14]:
<xarray.Dataset>
Dimensions:                                   (x: 3711, y: 3940)
Coordinates:
  * x                                         (x) float64 2.46e+06 ... 2.571e+06
  * y                                         (y) float64 -2.167e+06 ... -2.2...
    spatial_ref                               int64 0
    band                                      <U9 'band_data'
Data variables:
    pH, predicted mean at 0-20 cm depth       (y, x) float32 65.0 65.0 ... 66.0
    pH, predicted mean at 20-50 cm depth      (y, x) float32 66.0 65.0 ... 67.0
    pH, standard deviation at 0-20 cm depth   (y, x) float32 1.0 1.0 ... 2.0 1.0
    pH, standard deviation at 20-50 cm depth  (y, x) float32 1.0 1.0 ... 1.0 1.0

Transformation!

Check if and how the data needs to be transformed.

[15]:
conversion = assets[var].extra_fields["back-transformation"]
conversion
[15]:
'x/10'

We need to divide by 10, which is executed below.

Inspecting the xarray.Dataset above, from when we called ds, also indicates the need for transformation. We would expect pH values to range between 0 and 14, but we can see values around 60-70. The range below, after dividing by 10, looks better.

[16]:
ds = ds / 10
ds
[16]:
<xarray.Dataset>
Dimensions:                                   (x: 3711, y: 3940)
Coordinates:
  * x                                         (x) float64 2.46e+06 ... 2.571e+06
  * y                                         (y) float64 -2.167e+06 ... -2.2...
    spatial_ref                               int64 0
    band                                      <U9 'band_data'
Data variables:
    pH, predicted mean at 0-20 cm depth       (y, x) float32 6.5 6.5 ... 6.7 6.6
    pH, predicted mean at 20-50 cm depth      (y, x) float32 6.6 6.5 ... 6.8 6.7
    pH, standard deviation at 0-20 cm depth   (y, x) float32 0.1 0.1 ... 0.2 0.1
    pH, standard deviation at 20-50 cm depth  (y, x) float32 0.1 0.1 ... 0.1 0.1
[17]:
fig, ax = plt.subplots(2, 2, figsize=(22, 16), sharey=True)
ds[list(ds.keys())[0]].plot(ax=ax[0, 0])
ds[list(ds.keys())[1]].plot(ax=ax[0, 1])
ds[list(ds.keys())[2]].plot(ax=ax[1, 0])
ds[list(ds.keys())[3]].plot(ax=ax[1, 1])
ax[0, 0].set_title(list(ds.keys())[0])
ax[0, 1].set_title(list(ds.keys())[1])
ax[1, 0].set_title(list(ds.keys())[2])
ax[1, 1].set_title(list(ds.keys())[3])
plt.tight_layout();
../../../_images/sandbox_notebooks_Datasets_iSDAsoil_39_0.png

Conclusions

This notebook gave an introduction into loading iSDA soil data into the Digital Earth Africa sandbox.

Are any patterns observable between the Okavango Delta formation, clay content, and soil pH? How does the Fertility Capability Classification map align with the true colour image?

This notebook is intended to be a starting point. Users may like to try loading data for other areas or other soil variables.


Additional information

License: The code in this notebook is licensed under the Apache License, Version 2.0. Digital Earth Africa data is licensed under the Creative Commons by Attribution 4.0 license.

Contact: If you need assistance, please post a question on the Open Data Cube Slack channel or on the GIS Stack Exchange using the open-data-cube tag (you can view previously asked questions here). If you would like to report an issue with this notebook, you can file one on Github.

Compatible datacube version:

[18]:
print(datacube.__version__)
1.8.8

Last Tested:

[19]:
from datetime import datetime
datetime.today().strftime('%Y-%m-%d')
[19]:
'2023-01-30'