Add stratigraphy from GeoTOP to CPT data#
This example shows how stratigraphic information from GeoTOP can easily be added to CPT data. This allows CPT parameters to be aggregated to calculate averages for geological units. For this example, we will use a selection of CPTs in the area of the Utrecht Science Park (USP).
We will first import the relevant modules and plot the locations of the CPTs in the Collection.
import xyzservices.providers as xyz # For accessing background map providers
from matplotlib import pyplot as plt
import geost
cpts = geost.data.cpts_usp()
cpts.header.explore(tiles=xyz.nlmaps.grijs, style_kwds={"weight": 4})
Downloading file 'cpts_usp.parquet' from 'https://github.com/Deltares-research/geost/raw/main/data/cpts_usp.parquet' to '/home/runner/.cache/geost'.
Adding information from a voxel model#
Any information from voxel models can be added to the CPT data. In this example, we show how to add stratigraphic information from GeoTOP to the CPTs. First, we read GeoTOP directly from the OpenDAP server for the USP area.
geotop = geost.read_geotop_from_opendap(
data_vars=["strat"], bbox=cpts.header.total_bounds
)
print(geotop)
<xarray.Dataset> Size: 262kB
Dimensions: (y: 13, x: 16, z: 313)
Coordinates:
* y (y) float64 104B 4.548e+05 4.548e+05 ... 4.558e+05 4.56e+05
* x (x) float64 128B 1.396e+05 1.396e+05 ... 1.41e+05 1.41e+05
* z (z) float32 1kB -49.75 -49.25 -48.75 ... 105.2 105.8 106.2
spatial_ref int64 8B 0
Data variables:
strat (y, x, z) float32 260kB dask.array<chunksize=(13, 16, 313), meta=np.ndarray>
Attributes:
title: geotop v01r6s1 (100.0m * 100.0m * 0.5m
references: http://www.dinoloket.nl/detaillering-van-de-bovenste-lagen...
comment: GeoTOP 1.6.1 (lithoklasse)
disclaimer: http://www.dinoloket.nl
terms_of_use: http://www.dinoloket.nl
institution: TNO / Geologische Dienst Nederland
Conventions: CF-1.4
source: Generated by nl.tno.dino.geo3dmodel.generator.GeoTOPVoxelM...
history: Generated on Thu May 01 22:54:36 CEST 2025
As you can see, this prints an xarray.DataArray containing the "strat" variable of GeoTOP. We can use this object to add the stratigraphic information to the CPT data. This adds a "strat" column to the data table of the Collection.
First, we need to create a "depth" column in the CPT data, as this is expected to be present. We can use the "penetration_length" column for this. Below, we add the variable and print the resulting column:
cpts.data["depth"] = cpts.data["depth"]
cpts = cpts.add_model_data(geotop, data_vars="strat")
print(cpts.data["strat"])
0 2010.0
1 2010.0
2 2010.0
3 2010.0
4 2010.0
...
73664 3100.0
73665 3100.0
73666 3100.0
73667 3100.0
73668 3100.0
Name: strat, Length: 73331, dtype: float32
Note that some of the resulting values are "NaN", which occurs when a CPT falls outside the 3D model extent. In this case, some CPTs extend deeper than the maximum depth of GeoTOP.
We can now easily aggregate any CPT parameter according to the new "strat" variable. For example, we can create a boxplot of the average cone resistance ("qc"):
fig, ax = plt.subplots()
cpts.data.boxplot(
ax=ax, column="coneresistance", by="strat", grid=False, showfliers=False
)
ax.tick_params(axis="x", rotation=45)
ax.set_title("")
ax.set_ylabel("Cone resistance [MPa]")
Text(0, 0.5, 'Cone resistance [MPa]')
Each number on the x-axis represents a geological unit. However, these numbers are not really intuitive, and it can be difficult to determine which geological unit they represent. GeoST provides an option to quickly load the stratigraphic metadata for GeoTOP supplied by the TNO Geological Survey. This allows us to work with GeoTOP in a more intuitive way and makes it easy to select specific stratigraphic units or relabel the unit numbers in the plot above with more meaningful names.
First, we select all the units that have been added to the CPT data to see which units they are:
strat_units = geost.bro.geotop_strat_units()
units_in_data = strat_units.select_voxel_nr(cpts.data["strat"].unique())
units_in_data
Downloading file 'geotop_v01r6s1_metadata_strat.parquet' from 'https://github.com/Deltares-research/geost/raw/main/data/geotop_v01r6s1_metadata_strat.parquet' to '/home/runner/.cache/geost'.
| STR_UNIT_CD | DESCRIPTION | SEQ_NR | RED_DEC | GREEN_DEC | BLUE_DEC | |
|---|---|---|---|---|---|---|
| VOXEL_NR | ||||||
| 2010 | NUEC2 | Formatie van Echteld, gedeelte onder Formatie ... | 28.0 | 170 | 255 | 245 |
| 3030 | NUBXWI-SI-KO | Formatie van Boxtel, laagpakketten van Wierden... | 39.0 | 255 | 255 | 80 |
| 3100 | NUBX | Formatie van Boxtel | 47.0 | 255 | 235 | 0 |
| 5000 | NUDR | Formatie van Drente | 62.0 | 255 | 127 | 80 |
| 5060 | NUUR2 | Formatie van Urk, gedeelte onder Formatie van ... | 68.0 | 189 | 183 | 107 |
| 6200 | NUECgc | Formatie van Echteld (geulafzettingen generati... | 22.0 | 170 | 196 | 255 |
| 4010 | NUKR-BXDE | Formatie van Kreftenheye en Formatie van Boxte... | 49.0 | 176 | 48 | 96 |
| 1000 | NUAAOP | Antropogene afzettingen, opgebrachte grond | 1.0 | 200 | 200 | 200 |
| 6100 | NUECgb | Formatie van Echteld (geulafzettingen generati... | 11.0 | 102 | 205 | 171 |
| 5070 | NUST | Formatie van Sterksel | 69.0 | 205 | 92 | 92 |
This returns a GeotopUnits instance showing the stratigraphic units that occur in the CPT data. We can now use the unit attribute of the GeotopUnits instance to replace the numbers in the added "strat" column and plot the figure again:
cpts.data.replace({"strat": units_in_data.unit}, inplace=True)
print(cpts.data["strat"])
fig, ax = plt.subplots()
cpts.data.boxplot(
ax=ax, column="coneresistance", by="strat", grid=False, showfliers=False
)
ax.tick_params(axis="x", rotation=45)
ax.set_title("")
ax.set_ylabel("Cone resistance [MPa]")
0 NUEC2
1 NUEC2
2 NUEC2
3 NUEC2
4 NUEC2
...
73664 NUBX
73665 NUBX
73666 NUBX
73667 NUBX
73668 NUBX
Name: strat, Length: 73331, dtype: object
Text(0, 0.5, 'Cone resistance [MPa]')
The x-axis in the plot now shows more meaningful abbreviations for the geological units, which can be found in the stratigraphic nomenclature. Most units belong to the “Boven-Noordzee Groep” and have the prefix “NU”. For example, the unit “EC” shown in the plot, together with its corresponding code, can be found here.
The GeotopUnits class can be used for various selections and groupings, allowing users to perform further analyses according to their specific needs.