Skip to content

Loimis — Soil Texture Parser

What is the Loimis field?

The Loimis (loimis) field encodes the particle-size composition of a soil profile across up to four depth layers, all in a single compact string. It tells you what the soil is physically made of — how much sand, silt, clay, and rock fragment content there is — and how that composition changes with depth.

A simple example:

l40-70/ls₂30/+ls₂

This reads as three layers:

Layer Notation Meaning
1 l40-70 Sand (liiv), from 40 to 70 cm depth
2 ls₂30 Sandy clay (liivsavi) with moderate rock content, upper boundary at 30 cm
3 +ls₂ Sandy clay, continuing deeper

The notation system

Fine earth (peenes)

The dominant soil material in each layer is described by a texture code:

Code Estonian name International class
l liiv Sand
pl peenliiv Fine sand
sl saviliiv Loamy sand / clayey sand
ls liivsavi Sandy loam / sandy clay loam
s savi Clay
tsl tolmjas saviliiv Silt loam
tls tolmjas liivsavi Silty clay loam
dk liivakivirähk Sandy grit / sandstone debris

Rock skeleton (kores)

Coarse fragments mixed into the layer are described by a skeleton code:

Code Estonian name Description
r rähk Gravel (2–60 mm fragments)
v veeris Cobbles (>60 mm rounded fragments)
k kruus Coarse gravel / limestone fragments
kb killustik Angular rubble
p paas Limestone bedrock plate
lu lupjakivi Calcareous / carbonate material
ck kiltkivirähk Slate or schist debris

Organic material (turfs)

Code Estonian name Description
th toorhuumus Raw/mor humus horizon (> 10 cm)
t₁ / t1 turvas I aste Weakly decomposed peat
t₂ / t2 turvas II aste Moderately decomposed peat
t₃ / t3 turvas III aste Strongly decomposed peat

Amplitude subscripts ₁–₅

An amplitude subscript immediately after a code indicates abundance or intensity of that component. The scale runs from 1 (weak / minor presence) to 5 (very strong / dominant). For example:

  • r₁ls — slight gravel content in a sandy clay layer
  • r₃ls — substantial gravel in sandy clay
  • ls₂ — moderately gravelly sandy clay (peenes with skeleton modifier)

Depth ranges

Depth is written in centimetres, either as a single value or a range:

Notation Meaning
l40 Sand layer with upper boundary at 40 cm
l40-70 Sand layer from 40 to 70 cm
+ls₂ Sandy clay continues deeper (no explicit lower boundary)

The + prefix means deeper than the previous layer — no explicit depth given.

Carbonate flag

A + immediately before a component code (not a depth marker) means the material is calcareous (e.g. +ls = calcareous sandy clay). Context distinguishes this from the depth-continuation +.

Layer separator

Layers are separated by /. Up to four layers per polygon.


The 7-step parsing pipeline

Because the loimis notation is complex and the raw data contains many encoding errors, parsing proceeds through seven stages:

  1. Normalise and split — apply error-correction lookups, discard secondary siffer annotations, and split the string into individual layer tokens.
  2. Bracket fix — validate and remove parenthesised numeric artefacts in each token.
  3. Grammar test — try each of ten grammar variants (covering all possible orderings of kores, peenes, and turfs components) and produce a canonical normalised string.
  4. Reconstituate — reassemble the normalised layers into a clean slash-separated string.
  5. Visitor — apply a structured tree-walker to extract a nested dictionary of all soil constituents, their codes, amplitudes, carbonate flags, and depth ranges.
  6. Layer depths — read the depth information from the structured dictionary to determine the number of layers and the depth of each layer boundary.
  7. Texture fractions — look up each constituent code in the texture rules table to derive clay, silt, sand, and rock-fragment percentages, and map to international texture classes.

Output fields

Depth and layer summary (11 values)

Field Type Description
nlayers int Number of distinct texture layers in this profile (1–4)
ZMX float or NaN Total encoded profile depth (cm); NaN if nothing was encoded
Z1 float or NaN Lower boundary of layer 1 (cm); NaN if unencoded
Z2 float or NaN Lower boundary of layer 2 (cm); NaN if fewer than 2 layers or unencoded
Z3 float or NaN Lower boundary of layer 3 (cm); NaN if fewer than 3 layers or unencoded
Z4 float or NaN Lower boundary of layer 4 (cm); NaN if fewer than 4 layers or unencoded
zmx_model float For modellers, not scientists — modelled total profile depth, always numeric
z1_model float Modelled lower boundary of layer 1 (cm); 0 if fewer than 1 layer
z2_model float Modelled lower boundary of layer 2 (cm); 0 if fewer than 2 layers
z3_model float Modelled lower boundary of layer 3 (cm); 0 if fewer than 3 layers
z4_model float Modelled lower boundary of layer 4 (cm); 0 if fewer than 4 layers

Per-layer texture (×4 layers, 32 values + 1 diagnostic)

For each layer number n (1 to 4):

Field Type Description
EST_TXTn str Estonian texture code for this layer (e.g. sl, ls)
EST_CRSn str Coarse fraction type code (e.g. r, k, v); empty if no skeleton
LXTYPEn str International texture class: SAND, LOAM, CLAY, PEAT, GRAVELS, …
CLAYn float Clay fraction (0–100 %)
SILTn float Silt fraction (0–100 %)
SANDn float Sand fraction (0–100 %)
ROCKn float Rock / skeleton fragment fraction (0–100 %)
KARBn int Carbonate flag: 1 if any constituent in the layer is calcareous (+ prefix), 0 otherwise

Diagnostic fields

Field Type Description
parse_ok_l bool True if all layers were parsed successfully. Used in the map viewer error-review style together with parse_ok_s and parse_ok_h.
parse_info str Parse status: "successful", "empty_loimis", "partial_no_info", "parse_error"
has_no_info bool True if one or more layers could not be parsed
loimis_grammar dict Full structured parse result (stored in DB but excluded from map viewer popup)
loimis_search dict Internal search-parameter snapshot (stored in DB but excluded from map viewer popup)

NaN values in texture fractions

For gravel- or rock-dominated layers (LXTYPE = "GRAVELS") and for organic layers (LXTYPE = "PEAT" or "HUMUS"), the clay, silt, and sand fractions are NaN — organic material has no mineral fine-earth fraction, and METK's loimis_vastavustabel_v1 leaves these blank (issue #19). The rock fragment content is recorded in ROCK instead. Guard for this when computing weighted averages.

Z1–Z4/ZMX: NULL means no value (issue #19, 2026-08-31)

Z1Z4/ZMX are NaN (SQL NULL) both when the profile has fewer layers (n > nlayers) and when a layer exists but its thickness was never encoded — nlayers is what tells the two cases apart, not the value itself.

Since issue #19 a layer that carries no thickness in the source no longer has one invented for these columns — previously the remainder of a hardcoded 100 cm was spread equally over such layers, so v⁰₁ls/l reported 50 cm and 50 cm from a value that states no depth at all.

Z1Z4 are cumulative lower boundaries, so a boundary is only meaningful when every layer above it has a known thickness. One unknown layer therefore makes every deeper boundary NaN as well. ZMX counts only thicknesses that were actually encoded, and is NaN only when none were.

zmx_model/z1_model–z4_model: for modellers, not scientists

These reproduce the pre-issue-#19 / EstSoil-EH behaviour: an unencoded layer's thickness is estimated by spreading the remaining depth over it, so v⁰₁ls/l gives z1_model=50, z2_model=100 even though Z1/Z2 are both NaN. Always numeric up to nlayers, 0 beyond it — never NaN. Do not use these for scientist-facing analysis or texture-fraction weighting; use Z1Z4.


Worked example

Raw field value: "sl60/r₁l"

Layer Token Meaning
1 sl60 Clayey sand (saviliiv), upper boundary at 60 cm
2 r₁l Sand with slight gravel content

Parsed output (selected fields):

nlayers=2, ZMX=60, Z1=60, Z2=NaN
zmx_model=100, z1_model=60, z2_model=100
EST_TXT1="sl", LXTYPE1="LOAM",  CLAY1=15, SILT1=20, SAND1=65, ROCK1=0, KARB1=0
EST_TXT2="l",  LXTYPE2="SAND",  CLAY2=4,  SILT2=6,  SAND2=90, ROCK2=5, KARB2=0