Last active
April 25, 2025 13:22
-
-
Save rutj3/1dcae00e2ac56f392b29dc12fd74f480 to your computer and use it in GitHub Desktop.
Soil texture warped
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "806ea258-de01-4bd0-a733-39178ea7c88c", | |
| "metadata": { | |
| "tags": [] | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "import numpy as np\n", | |
| "from numpy import log, exp, sqrt, cos, sin\n", | |
| "import numba\n", | |
| "\n", | |
| "import matplotlib.pyplot as plt\n", | |
| "import matplotlib as mpl\n", | |
| "from collections import defaultdict\n", | |
| "import matplotlib.patheffects as PathEffects\n", | |
| "plt.style.use(\"seaborn-v0_8\")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "58b46be3-c307-44f1-b35c-fd18890581f9", | |
| "metadata": { | |
| "tags": [] | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def get_geometric_props(fy, ft, fd):\n", | |
| "\n", | |
| " # # Shirazi & Boersma (1984) in mm\n", | |
| " # clay_lo = 0\n", | |
| " # clay_hi = 0.002\n", | |
| " # silt_lo = 0.002\n", | |
| " # silt_hi = 0.05\n", | |
| " # sand_lo = 0.05\n", | |
| " # sand_hi = 2.00\n", | |
| " \n", | |
| " # clay_avg = (clay_lo + clay_hi) / 2\n", | |
| " # silt_avg = (silt_lo + silt_hi) / 2\n", | |
| " # sand_avg = (sand_lo + sand_hi) / 2\n", | |
| "\n", | |
| " # Boersma & Hart (1988) in μm\n", | |
| " clay_lo = 0.01\n", | |
| " clay_hi = 2.0\n", | |
| " silt_lo = 2.0\n", | |
| " silt_hi = 50.0\n", | |
| " sand_lo = 50.0\n", | |
| " sand_hi = 2000.0\n", | |
| " \n", | |
| " clay_avg = sqrt(clay_lo * clay_hi)\n", | |
| " silt_avg = sqrt(silt_lo * silt_hi)\n", | |
| " sand_avg = sqrt(sand_lo * sand_hi)\n", | |
| " \n", | |
| " a = 0.01 * (fy * log(clay_avg) + ft * log(silt_avg) + fd * log(sand_avg))\n", | |
| " b = sqrt(np.maximum((0.01 * (fy * log(clay_avg)**2 + ft * log(silt_avg)**2 + fd * log(sand_avg)**2)) - a**2, 0))\n", | |
| " dg = exp(a)\n", | |
| " σg = exp(b)\n", | |
| "\n", | |
| " return dg, σg" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "3446bc73-b1aa-4332-a3e2-522aa9b4e294", | |
| "metadata": { | |
| "tags": [] | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "clay_boundaries = []\n", | |
| "silt_boundaries = []\n", | |
| "sand_boundaries = []\n", | |
| "\n", | |
| "line_step = 10\n", | |
| "line_res = .01\n", | |
| "\n", | |
| "for i, fy in enumerate(range(0,100+line_step,line_step)):\n", | |
| " \n", | |
| " ft = np.arange(0, 100+line_res-fy, line_res)\n", | |
| " fy = np.full_like(ft, fy)\n", | |
| " fd = 100 - fy - ft\n", | |
| " dg, σg = get_geometric_props(fy, ft, fd)\n", | |
| " clay_boundaries.append((dg, σg))\n", | |
| " \n", | |
| "for i, ft in enumerate(range(0,100+line_step,line_step)):\n", | |
| " \n", | |
| " fy = np.arange(0, 100+line_res-ft, line_res)\n", | |
| " ft = np.full_like(fy, ft)\n", | |
| " fd = 100 - fy - ft\n", | |
| " dg, σg = get_geometric_props(fy, ft, fd)\n", | |
| " silt_boundaries.append((dg, σg))\n", | |
| " \n", | |
| "for i, fd in enumerate(range(0,100+line_step,line_step)):\n", | |
| " \n", | |
| " fy = np.arange(0,100+line_res-fd, line_res)\n", | |
| " fd = np.full_like(fy, fd)\n", | |
| " ft = 100 - fy - fd\n", | |
| " \n", | |
| " dg, σg = get_geometric_props(fy, ft, fd)\n", | |
| " sand_boundaries.append((dg, σg))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "831a47c6-6cab-47b6-beac-0984fe4f25ad", | |
| "metadata": { | |
| "tags": [] | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# name, (fy, ft, fd)\n", | |
| "class_nodes = { \n", | |
| " \"b\": (0, 0, 100),\n", | |
| " \"d\": (0, 30, 70),\n", | |
| " \"f\": (0, 50, 50),\n", | |
| " \"m\": (0, 80, 20),\n", | |
| " \"q\": (0, 100, 0),\n", | |
| " \"c\": (10, 0, 90),\n", | |
| " \"g\": (20, 0, 80),\n", | |
| " \"v\": (40, 40, 20),\n", | |
| " \"w\": (40, 60, 0),\n", | |
| " \"y\": (60, 40, 0),\n", | |
| " \"z\": (100, 0, 0),\n", | |
| " \"a\": (0, 15, 85),\n", | |
| " \"e\": (15, 0, 85),\n", | |
| " \"h\": (20, 28, 52),\n", | |
| " \"i\": (7, 41, 52),\n", | |
| " \"j\": (7, 50, 43),\n", | |
| " \"k\": (27, 28, 45),\n", | |
| " \"l\": (27, 50, 23),\n", | |
| " \"n\": (27, 73, 0),\n", | |
| " \"o\": (12, 88, 0),\n", | |
| " \"p\": (12, 80, 8),\n", | |
| " \"r\": (35, 0, 65),\n", | |
| " \"s\": (35, 20, 45),\n", | |
| " \"t\": (27, 53, 20),\n", | |
| " \"u\": (40, 15, 45),\n", | |
| " \"x\": (55, 0, 45), \n", | |
| "}\n", | |
| "\n", | |
| "class_polys = {\n", | |
| " \"1) sand\": [\"b\",\"c\",\"a\"],\n", | |
| " \"2) loamy sand\": [\"a\",\"c\",\"e\",\"d\"],\n", | |
| " \"3) sandy loam\": [\"d\",\"e\",\"g\",\"h\",\"i\",\"j\",\"f\"],\n", | |
| " \"4) loam\": [\"j\",\"i\",\"h\",\"k\",\"l\"],\n", | |
| " \"5) silt loam\": [\"f\",\"j\",\"l\",\"n\",\"o\",\"p\",\"m\"],\n", | |
| " \"6) silt\": [\"m\",\"p\",\"o\",\"q\"],\n", | |
| " \"7) sandy clay loam\": [\"g\",\"r\",\"s\",\"k\",\"h\"],\n", | |
| " \"8) clay loam\": [\"k\",\"u\",\"v\",\"t\",\"l\"],\n", | |
| " \"9) silt clay loam\": [\"t\",\"v\",\"w\",\"n\"],\n", | |
| " \"10) sandy clay\": [\"r\",\"x\",\"u\",\"s\"],\n", | |
| " \"11) silty clay\": [\"v\",\"y\",\"w\"],\n", | |
| " \"12) clay\": [\"x\",\"z\",\"y\",\"v\",\"u\"],\n", | |
| "}\n", | |
| "\n", | |
| "# clay/silt/sand\n", | |
| "class_label_pos = {\n", | |
| " \"1) sand\": (4, 4),\n", | |
| " \"2) loamy sand\": (5, 13),\n", | |
| " \"3) sandy loam\": (11, 23),\n", | |
| " \"4) loam\": (20, 40),\n", | |
| " \"5) silt loam\": (16, 65),\n", | |
| " \"6) silt\": (5, 87),\n", | |
| " \"7) sandy clay loam\": (26, 15),\n", | |
| " \"8) clay loam\": (33, 33),\n", | |
| " \"9) silt clay loam\": (33, 55),\n", | |
| " \"10) sandy clay\": (41, 8),\n", | |
| " \"11) silty clay\": (47, 47),\n", | |
| " \"12) clay\": (60, 20),\n", | |
| "}\n", | |
| "\n", | |
| "class_polys_full = {}\n", | |
| "\n", | |
| "for name, nodes in class_polys.items():\n", | |
| " class_polys_full[name] = list(map(class_nodes.get, nodes))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "2ddd2ed5-8b4a-4973-9ce2-8bd9e570d0fe", | |
| "metadata": { | |
| "tags": [] | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "class_bounds = defaultdict(list)\n", | |
| "\n", | |
| "for name, nodes in class_polys_full.items():\n", | |
| " \n", | |
| " for i in range(len(nodes)):\n", | |
| " fy1, ft1, fd1 = nodes[i-1]\n", | |
| " fy2, ft2, fd2 = nodes[i]\n", | |
| " \n", | |
| " distance = sqrt((fy1-fy2)**2 + (ft1-ft2)**2 + (fd1-fd2)**2)\n", | |
| " line_length = int(np.ceil(distance*100))\n", | |
| " \n", | |
| " fy = np.linspace(fy1, fy2, line_length)\n", | |
| " ft = np.linspace(ft1, ft2, line_length)\n", | |
| " fd = np.linspace(fd1, fd2, line_length)\n", | |
| "\n", | |
| " dg, σg = get_geometric_props(fy, ft, fd)\n", | |
| " \n", | |
| " class_bounds[name].append((dg, σg))" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "bc4f92a0-de48-4dbd-a113-019d0df08636", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "axes_poly = [\n", | |
| " ( 0, 0, 99.9),\n", | |
| " (99.9, 0, 0),\n", | |
| " ( 0, 99.9, 0),\n", | |
| " ( 0, 0, 99.9),\n", | |
| "]\n", | |
| "\n", | |
| "n = 1000\n", | |
| "axes_boundary = np.vstack((\n", | |
| " \n", | |
| " np.stack((\n", | |
| " np.linspace(0, 100, n),\n", | |
| " np.linspace(0, 0, n),\n", | |
| " np.linspace(100, 0, n),\n", | |
| " ), axis=1),\n", | |
| " \n", | |
| " np.stack((\n", | |
| " np.linspace(100, 0, n),\n", | |
| " np.linspace(0, 100, n),\n", | |
| " np.linspace(0, 0, n),\n", | |
| " ), axis=1),\n", | |
| " \n", | |
| " np.stack((\n", | |
| " np.linspace( 0, 0, n),\n", | |
| " np.linspace(100, 0, n),\n", | |
| " np.linspace(0, 100, n),\n", | |
| " ), axis=1)\n", | |
| "))\n", | |
| "\n", | |
| "boundary_dg, boundary_σg = get_geometric_props(\n", | |
| " axes_boundary[:, 0],\n", | |
| " axes_boundary[:, 1],\n", | |
| " axes_boundary[:, 2],\n", | |
| ")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "abfd8222-eff6-4b9e-92f3-c137b8cba970", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "def annotate(\n", | |
| " ax, label_min=20, label_max=80, label_step=20, position_offset=35, position_rotation_offset=0, txt_rotation_offset=0, \n", | |
| " color=\"C0\", plot_marker=True, axis=\"Silt\", txt=\"{value:1.0f}%\", \n", | |
| " coord_conversion=get_geometric_props, marker_angle=None, ha=None, va=None, align_with=\"boundary\", stroke=False,\n", | |
| " debug=False,\n", | |
| "):\n", | |
| "\n", | |
| " data_to_figure = ax.transData.transform\n", | |
| " data_to_axes = lambda x: ax.transAxes.inverted().transform(data_to_figure(x))\n", | |
| "\n", | |
| " for value in range(label_min, label_max+1, label_step):\n", | |
| "\n", | |
| " if align_with == \"boundary\":\n", | |
| " # finite difference along the boundary for angle\n", | |
| " if axis.lower() == \"silt\":\n", | |
| " ft0 = value\n", | |
| " ft1 = max(ft0 - 0.01, 0)\n", | |
| " ft2 = max(ft0 + 0.01, 0)\n", | |
| " \n", | |
| " fd0 = fd1 = fd2 = 0\n", | |
| " \n", | |
| " fy0 = 100 - ft0\n", | |
| " fy1 = 100 - ft1\n", | |
| " fy2 = 100 - ft2\n", | |
| " \n", | |
| " elif axis.lower() == \"clay\":\n", | |
| " fy0 = value\n", | |
| " fy1 = max(fy0 - 0.01, 0)\n", | |
| " fy2 = max(fy0 + 0.01, 0)\n", | |
| " \n", | |
| " ft0 = ft1 = ft2 = 0\n", | |
| " \n", | |
| " fd0 = 100 - fy0\n", | |
| " fd1 = 100 - fy1\n", | |
| " fd2 = 100 - fy2\n", | |
| " \n", | |
| " elif axis.lower() == \"sand\":\n", | |
| " fd0 = value\n", | |
| " fd1 = max(fd0 - 0.01, 0)\n", | |
| " fd2 = max(fd0 + 0.01, 0)\n", | |
| " \n", | |
| " ft0 = 100 - fd0\n", | |
| " ft1 = 100 - fd1\n", | |
| " ft2 = 100 - fd2\n", | |
| " \n", | |
| " fy0 = fy1 = fy2 = 0\n", | |
| "\n", | |
| " elif align_with == \"grid\":\n", | |
| " # finite difference along the gridline for angle\n", | |
| " if axis.lower() == \"silt\":\n", | |
| " ft0 = ft1 = ft2 = value\n", | |
| " \n", | |
| " fd0 = 0\n", | |
| " fd1 = 0.1\n", | |
| " fd2 = 0\n", | |
| " \n", | |
| " fy0 = 100 - ft0\n", | |
| " fy1 = 99.9 - ft1\n", | |
| " fy2 = 100 - ft2\n", | |
| " \n", | |
| " elif axis.lower() == \"clay\":\n", | |
| " fy0 = fy1 = fy2 = value\n", | |
| " \n", | |
| " ft0 = 0\n", | |
| " ft1 = 0\n", | |
| " ft2 = 0.1\n", | |
| " \n", | |
| " fd0 = 100 - fy0\n", | |
| " fd1 = 100 - fy1\n", | |
| " fd2 = 99.9 - fy2\n", | |
| " \n", | |
| " elif axis.lower() == \"sand\":\n", | |
| " fd0 = fd1 = fd2 = value\n", | |
| " \n", | |
| " ft0 = 100. - fd0\n", | |
| " ft1 = 99.9 - fd1\n", | |
| " ft2 = 100. - fd2\n", | |
| " \n", | |
| " fy0 = 0\n", | |
| " fy1 = 0.1\n", | |
| " fy2 = 0\n", | |
| " \n", | |
| " x0, y0 = coord_conversion(fy0, ft0, fd0)\n", | |
| " x1, y1 = coord_conversion(fy1, ft1, fd1)\n", | |
| " x2, y2 = coord_conversion(fy2, ft2, fd2)\n", | |
| " dx = (log(x2) - log(x1) ) / exp(1.4)\n", | |
| " # dx = (x2 - x1)\n", | |
| " dy = (y2 - y1) / 50\n", | |
| "\n", | |
| " ax1, ay1 = data_to_axes((x1, y1))\n", | |
| " ax2, ay2 = data_to_axes((x2, y2))\n", | |
| " # dx = (ax2 - ax1)\n", | |
| " # dy = (ay2 - ay1)\n", | |
| " \n", | |
| " fx1, fy1 = data_to_figure((x1, y1))\n", | |
| " fx2, fy2 = data_to_figure((x2, y2))\n", | |
| " fdx = (fx2 - fx1)\n", | |
| " fdy = (fy2 - fy1)\n", | |
| "\n", | |
| " angle_deg = (np.rad2deg(np.arctan2(-dx, dy))+90) % 360 \n", | |
| " angle_rad = np.deg2rad(angle_deg)\n", | |
| "\n", | |
| " fangle_deg = (np.rad2deg(np.arctan2(-fdx, fdy))+90) % 360\n", | |
| " fangle_rad = np.deg2rad(fangle_deg)\n", | |
| "\n", | |
| " if position_offset == 0:\n", | |
| " sgn = 1\n", | |
| " else:\n", | |
| " sgn = np.sign(position_offset)\n", | |
| " \n", | |
| " label_offset = position_offset\n", | |
| " px0 = (label_offset * cos(angle_rad + np.deg2rad(0) + np.deg2rad(position_rotation_offset)))\n", | |
| " py0 = (label_offset * sin(angle_rad + np.deg2rad(0) + np.deg2rad(position_rotation_offset)))\n", | |
| "\n", | |
| " if plot_marker:\n", | |
| " if marker_angle is None:\n", | |
| " marker_angle = angle_deg + txt_rotation_offset\n", | |
| " \n", | |
| " ax.plot(\n", | |
| " x0, y0, mec=color, ms=15, mfc=\"none\", mew=1., clip_on=False,\n", | |
| " marker=(1, 2, (fangle_deg + txt_rotation_offset + 90*((txt_rotation_offset > 0 and position_offset > 0)*2-1))),\n", | |
| " )\n", | |
| "\n", | |
| " if ha is None:\n", | |
| " ha = \"center\"\n", | |
| " if va is None:\n", | |
| " va = \"center\"\n", | |
| "\n", | |
| " if debug is True:\n", | |
| " bbox_props = dict(facecolor=\"none\", edgecolor=\"m\", lw=1)\n", | |
| " else:\n", | |
| " bbox_props = dict(facecolor=\"none\", edgecolor=\"none\", lw=0)\n", | |
| "\n", | |
| " if stroke is True:\n", | |
| " patheffects = [PathEffects.withStroke(linewidth=2, foreground=\"w\", alpha=0.8)]\n", | |
| " else:\n", | |
| " patheffects = None\n", | |
| " \n", | |
| " # rotation is 0 for east, rotates CCW\n", | |
| " # rotation is with respect for figure\n", | |
| " ax.annotate(\n", | |
| " txt.format(value=value, axis=axis),\n", | |
| " rotation=fangle_deg + txt_rotation_offset, color=color,\n", | |
| " # rotation_mode=\"default\",\n", | |
| " rotation_mode=\"anchor\",\n", | |
| " bbox=bbox_props, size=12,\n", | |
| " weight=\"bold\", ha=ha, va=va,\n", | |
| " xy=(x0, y0), xycoords='data',\n", | |
| " xytext=(px0, py0), textcoords='offset pixels',\n", | |
| " path_effects=patheffects,\n", | |
| " )" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "94c948ca-0b6f-404e-b47c-6dceaba2f052", | |
| "metadata": { | |
| "tags": [] | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "clay_color = \"C3\"\n", | |
| "silt_color = \"C1\"\n", | |
| "sand_color = \"C2\"\n", | |
| "\n", | |
| "fig, ax = plt.subplots(figsize=(10, 6), layout=\"compressed\", facecolor=\"w\", dpi=120)\n", | |
| "\n", | |
| "ax.add_patch(mpl.patches.Polygon(np.stack([boundary_dg, boundary_σg], axis=1), facecolor=\"#F4F4F9\", edgecolor=\"none\", zorder=1, alpha=0.5))\n", | |
| "\n", | |
| "txt_legend1 = \"\"\n", | |
| "txt_legend2 = \"\"\n", | |
| "\n", | |
| "for i, (class_name, (clay, silt)) in enumerate(class_label_pos.items()):\n", | |
| " sand = 100 - (clay+silt)\n", | |
| " x,y = get_geometric_props(clay, silt, sand)\n", | |
| "\n", | |
| " ax.text(\n", | |
| " x, y, class_name.split(\")\")[0], weight=\"bold\", \n", | |
| " color=\"k\", size=14, va=\"center\", ha=\"center\",\n", | |
| " alpha=0.5,\n", | |
| " path_effects=[PathEffects.withStroke(linewidth=2, foreground=\"w\", alpha=.5)],\n", | |
| " )\n", | |
| "\n", | |
| " txt_legend1 += f\"{class_name.split(\" \")[0]}\\n\"\n", | |
| " txt_legend2 += f\"{class_name.title().split(\") \")[1]}\\n\"\n", | |
| " \n", | |
| "ax.text(\n", | |
| " 0.08, 0.9, txt_legend1, va=\"top\", ha=\"right\", \n", | |
| " size=11, weight=\"normal\", alpha=0.8, transform=ax.transAxes,\n", | |
| " path_effects=[PathEffects.withStroke(linewidth=4, foreground=\"w\", alpha=.6)],\n", | |
| ")\n", | |
| "\n", | |
| "ax.text(\n", | |
| " 0.085, 0.9, txt_legend2, va=\"top\", ha=\"left\", \n", | |
| " size=11, weight=\"normal\", alpha=0.8, transform=ax.transAxes,\n", | |
| " path_effects=[PathEffects.withStroke(linewidth=4, foreground=\"w\", alpha=.6)],\n", | |
| ")\n", | |
| "\n", | |
| "for boundaries, color in [(clay_boundaries, clay_color), (silt_boundaries, silt_color), (sand_boundaries, sand_color)]:\n", | |
| " for dg, σg in boundaries:\n", | |
| " ax.plot(dg, σg, \"-\", lw=.3, alpha=1, color=color)\n", | |
| " \n", | |
| "for name, nodes in class_bounds.items():\n", | |
| " for dg, σg in nodes:\n", | |
| " ax.plot(dg, σg, \"-\", lw=1, alpha=1, color=\"k\")\n", | |
| "\n", | |
| "ax.set_xscale(\"log\", base=10)\n", | |
| "ax.set_xlabel(\"Geometric mean diameter\")\n", | |
| "ax.set_ylabel(\"Geometric\\nstandard deviation\", rotation=0, labelpad=45)\n", | |
| "ax.set_title(\"Soil texture diagram (Shirazi & Boersma (1984))\")\n", | |
| "\n", | |
| "func = get_geometric_props\n", | |
| "\n", | |
| "lbl_min=20\n", | |
| "lbl_max=80\n", | |
| "lbl_step=20\n", | |
| "\n", | |
| "debug = False\n", | |
| "\n", | |
| "annotate(\n", | |
| " ax, label_min=10, label_max=90, label_step=10, position_offset=-15, ha=\"left\", va=\"center\", \n", | |
| " position_rotation_offset=0, txt_rotation_offset=180, color=clay_color, plot_marker=True, axis=\"Clay\", \n", | |
| " txt=\"{value:1.0f}%\", coord_conversion=func, marker_angle=None, align_with=\"grid\", debug=debug, stroke=True,\n", | |
| ")\n", | |
| "annotate(\n", | |
| " ax, label_min=lbl_min, label_max=lbl_max, label_step=lbl_step, position_offset=18, ha=\"right\", va=\"center\", \n", | |
| " position_rotation_offset=0, txt_rotation_offset=180, color=silt_color,plot_marker=True, axis=\"Silt\", \n", | |
| " txt=\"{value:1.0f}%\", coord_conversion=func, marker_angle=None, align_with=\"grid\", debug=debug, stroke=True,\n", | |
| ")\n", | |
| "annotate(\n", | |
| " ax, label_min=lbl_min, label_max=lbl_max, label_step=lbl_step, position_offset=18, ha=\"left\", va=\"center\", \n", | |
| " position_rotation_offset=0, txt_rotation_offset=0, color=sand_color,plot_marker=True, axis=\"Sand\", \n", | |
| " txt=\"{value:1.0f}%\", coord_conversion=func, marker_angle=None, align_with=\"grid\", debug=debug, stroke=True,\n", | |
| ")\n", | |
| "\n", | |
| "annotate(\n", | |
| " ax, label_min=25, label_max=75, label_step=49, position_offset=15, ha=\"center\", va=\"center\", position_rotation_offset=-90, \n", | |
| " txt_rotation_offset=180, color=clay_color, plot_marker=False, axis=\"Clay\", txt=r\"$\\leftarrow${axis}\", \n", | |
| " coord_conversion=func, align_with=\"boundary\", debug=debug,\n", | |
| ")\n", | |
| "annotate(\n", | |
| " ax, label_min=50, label_max=50, label_step=50, position_offset=55, ha=\"center\", va=\"center\", position_rotation_offset=-90, \n", | |
| " txt_rotation_offset=0, color=silt_color, plot_marker=False, axis=\"Silt\", txt=r\"{axis}$\\rightarrow$\", \n", | |
| " coord_conversion=func, align_with=\"boundary\", debug=debug,\n", | |
| ")\n", | |
| "annotate(\n", | |
| " ax, label_min=50, label_max=50, label_step=50, position_offset=55, ha=\"center\", va=\"center\", position_rotation_offset=-90, \n", | |
| " txt_rotation_offset=0, color=sand_color, plot_marker=False, axis=\"Sand\", txt=r\"{axis}$\\rightarrow$\", \n", | |
| " coord_conversion=func, align_with=\"boundary\", debug=debug,\n", | |
| ")\n", | |
| "\n", | |
| "ax.grid(True, linestyle=\"--\", dashes=(10,10), color=\"k\", lw=0.5, alpha=.2, zorder=199)\n", | |
| "ax.set_ylim(-3, 52)\n", | |
| "ax.set_xlim(0.1, 450)\n", | |
| "ax.xaxis.set_major_locator(mpl.ticker.LogLocator(base=10, subs=(1, 2, 5)))\n", | |
| "ax.xaxis.set_major_formatter(mpl.ticker.FuncFormatter(lambda x, pos: f\"{round(x, 3)} μm\"))\n", | |
| "ax.set_yticks(range(0, 55, 5))\n", | |
| "ax.set_yticklabels(map(lambda x: f\"{x:1.0f} μm\", ax.get_yticks()))\n", | |
| "ax.set_facecolor(\"w\")\n", | |
| "\n", | |
| "fig.savefig(\"soil_texture_geometric_grid_aligned.png\", dpi=120, bbox_inches=0, pad_inches=0)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "39e3835e-ae02-41ca-bb32-43267153558d", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "id": "f5f23f39-876a-4734-b21e-f2abb290a506", | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "Python 3 (ipykernel)", | |
| "language": "python", | |
| "name": "python3" | |
| }, | |
| "language_info": { | |
| "codemirror_mode": { | |
| "name": "ipython", | |
| "version": 3 | |
| }, | |
| "file_extension": ".py", | |
| "mimetype": "text/x-python", | |
| "name": "python", | |
| "nbconvert_exporter": "python", | |
| "pygments_lexer": "ipython3", | |
| "version": "3.13.2" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 5 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment