Cycles-utils 4.0.2__tar.gz → 4.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/Cycles_utils.egg-info/PKG-INFO +1 -1
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/PKG-INFO +1 -1
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/__init__.py +1 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles.py +2 -3
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_runner.py +2 -4
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/__init__.py +2 -2
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/_base_file.py +1 -1
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/control_file.py +29 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/nudge_file.py +8 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/output_file.py +1 -1
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/plot_tools.py +47 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/reinit_file.py +4 -4
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/soil_file.py +5 -49
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/gadm/__init__.py +1 -1
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/rotation_builder.py +3 -4
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/soilgrids/soilgrids.py +4 -6
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/ssurgo/ssurgo.py +4 -2
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/setup.py +1 -1
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/Cycles_utils.egg-info/SOURCES.txt +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/Cycles_utils.egg-info/dependency_links.txt +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/Cycles_utils.egg-info/requires.txt +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/Cycles_utils.egg-info/top_level.txt +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/LICENSE +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/MANIFEST.in +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/README.md +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/operation_file.py +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/cycles_tools/weather_file.py +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/data/GLDASp5_elevation_025d.nc4 +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/data/GLDASp5_landmask_025d.nc4 +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/data/NLDAS_elevation.nc4 +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/data/NLDAS_masks-veg-soil.nc4 +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/data/fips_gid_conversion.csv +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/data/gridMET_elevation_mask.nc +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/data/metdata_elevationdata.nc +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/data/us_states.csv +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/gadm/gadm.py +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/soilgrids/__init__.py +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/ssurgo/__init__.py +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/weather/__init__.py +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/cycles/weather/weather.py +0 -0
- {cycles_utils-4.0.2 → cycles_utils-4.0.3}/setup.cfg +0 -0
|
@@ -12,6 +12,7 @@ from .cycles_tools import SoilLayer
|
|
|
12
12
|
from .cycles_tools import plot_yield
|
|
13
13
|
from .cycles_tools import plot_operations
|
|
14
14
|
from .cycles_tools import plot_map
|
|
15
|
+
from .cycles_tools import plot_satellite_map
|
|
15
16
|
from .cycles_tools import Operation, Planting, Tillage, Harvest, Kill, FixedFertilization, FixedIrrigation, AutoIrrigation
|
|
16
17
|
from .cycles_runner import CyclesRunner
|
|
17
18
|
from .rotation_builder import CyclesRotationBuilder, Crop, CropGroup
|
|
@@ -5,7 +5,6 @@ import pandas as pd
|
|
|
5
5
|
import subprocess
|
|
6
6
|
from collections.abc import Collection
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
|
-
from datetime import datetime
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
from matplotlib.axes import Axes
|
|
11
10
|
from .cycles_tools import SoilLayer, ControlConfig
|
|
@@ -27,8 +26,8 @@ class Output:
|
|
|
27
26
|
class Cycles:
|
|
28
27
|
"""Interface for executing one Cycles simulation and reading its files.
|
|
29
28
|
|
|
30
|
-
Provides methods to run simulations, read outputs, and inspect soil/weather/operation
|
|
31
|
-
|
|
29
|
+
Provides methods to run simulations, read outputs, and inspect soil/weather/operation configurations. Automatically
|
|
30
|
+
loads the control file upon initialization.
|
|
32
31
|
|
|
33
32
|
Attributes:
|
|
34
33
|
path: Path to the simulation directory (containing input/ and output/ subdirs).
|
|
@@ -29,9 +29,8 @@ class SimulationContext:
|
|
|
29
29
|
class CyclesRunner:
|
|
30
30
|
"""Run one or many Cycles simulations with templated inputs.
|
|
31
31
|
|
|
32
|
-
Manages batch execution of Cycles simulations by generating control files,
|
|
33
|
-
|
|
34
|
-
Consolidates results into a summary CSV file.
|
|
32
|
+
Manages batch execution of Cycles simulations by generating control files, operation files, and nudge files from
|
|
33
|
+
templates and parameter dictionaries. Consolidates results into a summary CSV file.
|
|
35
34
|
|
|
36
35
|
Attributes:
|
|
37
36
|
executable: Absolute path to the Cycles executable binary.
|
|
@@ -110,7 +109,6 @@ class CyclesRunner:
|
|
|
110
109
|
|
|
111
110
|
|
|
112
111
|
def _resolve(self, simulation: dict[str, Any], control_dict: dict[str, Any], operation_dict: dict[str, Any] | None, calibration_dict: dict[str, Any] | None) -> SimulationContext:
|
|
113
|
-
"""Call control_dict once per row and resolve all derived values."""
|
|
114
112
|
control = resolve_dict_values(control_dict, simulation)
|
|
115
113
|
return SimulationContext(
|
|
116
114
|
name=control['simulation_name'],
|
|
@@ -9,13 +9,12 @@ from .soil_file import MAPPABLE_PARAMETERS
|
|
|
9
9
|
from .soil_file import SoilLayer
|
|
10
10
|
from .soil_file import generate_soil_file
|
|
11
11
|
from .soil_file import read_soil_file
|
|
12
|
-
from .soil_file import map_to_dataframe
|
|
13
|
-
from .soil_file import from_dataframe
|
|
14
12
|
from .weather_file import read_weather_file
|
|
15
13
|
from .reinit_file import generate_reinit_file
|
|
16
14
|
from .plot_tools import plot_yield
|
|
17
15
|
from .plot_tools import plot_operations
|
|
18
16
|
from .plot_tools import plot_map
|
|
17
|
+
from .plot_tools import plot_satellite_map
|
|
19
18
|
from ._base_file import resolve_dict_values
|
|
20
19
|
|
|
21
20
|
__all__ = [
|
|
@@ -31,4 +30,5 @@ __all__ = [
|
|
|
31
30
|
"plot_yield",
|
|
32
31
|
"plot_operations",
|
|
33
32
|
"plot_map",
|
|
33
|
+
"plot_satellite_map",
|
|
34
34
|
]
|
|
@@ -23,7 +23,7 @@ def write_file(fn: Path, config) -> None:
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def resolve_dict_values(user_dict: dict, simulation: dict[str, Any] | None) -> dict:
|
|
26
|
-
return {key: func(simulation) if callable(func) else func for key, func in user_dict.items()}
|
|
26
|
+
return {key.lower(): func(simulation) if callable(func) else func for key, func in user_dict.items()}
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def extract(dc_class, resolved: dict) -> dict:
|
|
@@ -78,6 +78,35 @@ def _get_soil_layers(fn: Path) -> int:
|
|
|
78
78
|
def generate_control_file(fn: str | Path, user_dict: dict, *, simulation_dict: dict[str, Any] | None=None) -> ControlConfig:
|
|
79
79
|
"""Generate and write a Cycles control file.
|
|
80
80
|
|
|
81
|
+
Provide either direct values or callables that accept a simulation row and return a value in `user_dict`. The
|
|
82
|
+
parameter names should be in lowercase and correspond to the fields in Cycles simulation control files. If a field
|
|
83
|
+
is not provided, it will be filled with a default value. If a field's value is a callable, it will be called with
|
|
84
|
+
the `simulation_dict` to resolve its value.
|
|
85
|
+
|
|
86
|
+
The following fields are required in `user_dict`:
|
|
87
|
+
|
|
88
|
+
- `simulation_start_year`
|
|
89
|
+
- `simulation_end_year`
|
|
90
|
+
- `rotation_size`
|
|
91
|
+
- `operation_file`
|
|
92
|
+
- `soil_file`
|
|
93
|
+
- `weather_file`
|
|
94
|
+
|
|
95
|
+
The default values for other fields are:
|
|
96
|
+
|
|
97
|
+
- `crop_file`: `GenericCrops.crop`
|
|
98
|
+
- `reinit_file`: `N/A`
|
|
99
|
+
- `soil_layers`: inferred from the soil file (if not provided)
|
|
100
|
+
- `co2_level`: `-999`
|
|
101
|
+
- `use_reinitialization`: `0`
|
|
102
|
+
- `adjusted_yields`: `0`
|
|
103
|
+
- `hydrology_option`: `1`
|
|
104
|
+
- `automatic_nitrogen`: `0`
|
|
105
|
+
- `automatic_phosphorus`: `0`
|
|
106
|
+
- `automatic_sulfur`: `0`
|
|
107
|
+
|
|
108
|
+
All output control fields default to `0`.
|
|
109
|
+
|
|
81
110
|
Args:
|
|
82
111
|
fn: Destination control file path.
|
|
83
112
|
user_dict: Values or callables for control fields.
|
|
@@ -45,6 +45,14 @@ def _build_nudge_config(user_dict: dict, simulation_dict: dict[str, Any] | None)
|
|
|
45
45
|
def generate_nudge_file(fn: str | Path, user_dict: dict, *, simulation_dict: dict[str, Any] | None=None) -> None:
|
|
46
46
|
"""Write a Cycles nudge file from user-provided values.
|
|
47
47
|
|
|
48
|
+
Provide either direct values or callables that accept a simulation row and return a value in `user_dict`. The
|
|
49
|
+
parameter names should be in lowercase and correspond to the fields in Cycles nudge (calibration) files. If a field
|
|
50
|
+
is not provided, it will be filled with a default value. If a field's value is a callable, it will be called with
|
|
51
|
+
the `simulation_dict` to resolve its value.
|
|
52
|
+
|
|
53
|
+
The default values for all calibration multipliers are `1.0`, and the default values for `kd_no3` and `kd_nh4` are
|
|
54
|
+
`0.0` and `5.6`, respectively.
|
|
55
|
+
|
|
48
56
|
Args:
|
|
49
57
|
fn: Destination nudge file path.
|
|
50
58
|
user_dict: Values or callables for nudge parameters.
|
|
@@ -15,7 +15,7 @@ def _parse_units(lines: list[str], columns) -> dict[str, str]:
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def read_output(path: str | Path, output_type: str) -> tuple[pd.DataFrame, dict[str, str]]:
|
|
18
|
-
"""Read one Cycles output
|
|
18
|
+
"""Read one Cycles output file and associated unit strings.
|
|
19
19
|
|
|
20
20
|
Args:
|
|
21
21
|
path: Directory containing output CSV files.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import cartopy.crs as ccrs
|
|
3
3
|
import cartopy.feature as feature
|
|
4
|
+
import cartopy.io.img_tiles as cimgt
|
|
4
5
|
import geopandas as gpd
|
|
6
|
+
import math
|
|
5
7
|
import matplotlib.lines as mlines
|
|
6
8
|
import matplotlib.pyplot as plt
|
|
7
9
|
import numpy as np
|
|
@@ -274,3 +276,48 @@ def plot_map(gdf: gpd.GeoDataFrame, column: str, *, projection: ccrs.Projection=
|
|
|
274
276
|
ax.set_title(title) # type: ignore
|
|
275
277
|
|
|
276
278
|
return fig, ax # type: ignore
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _zoom_from_extent(extent, *, desired_pixels: int=1024) -> int:
|
|
282
|
+
"""Determine the appropriate zoom level for a given map extent.
|
|
283
|
+
Number of global tiles at each zoom level is 2 ^ zoom_level
|
|
284
|
+
"""
|
|
285
|
+
PIXELS_PER_TILE = 256
|
|
286
|
+
|
|
287
|
+
dx = abs(extent[1] - extent[0])
|
|
288
|
+
dy = abs(extent[3] - extent[2])
|
|
289
|
+
|
|
290
|
+
return round(max(math.log(desired_pixels / PIXELS_PER_TILE * 180 / dy) / math.log(2), math.log(desired_pixels / PIXELS_PER_TILE * 360 / dx) / math.log(2)))
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def plot_satellite_map(fig: Figure, extent: tuple[float, float, float, float], *,
|
|
294
|
+
alpha: float=1.0, ax: tuple[float, float, float, float] | None=None, desired_pixels: int=1024, style: str | None=None) -> tuple[GeoAxes, ccrs.Projection]:
|
|
295
|
+
"""Render a satellite basemap over a geographic extent.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
fig: Matplotlib figure used to create the map axes.
|
|
299
|
+
extent: Geographic bounds as ``(west, east, south, north)`` in degrees.
|
|
300
|
+
alpha: Basemap transparency where ``1.0`` is fully opaque.
|
|
301
|
+
ax: Optional axes rectangle passed to ``Figure.add_axes``. If omitted, a full-figure subplot is created.
|
|
302
|
+
desired_pixels: Target figure resolution in pixels used to estimate tile zoom.
|
|
303
|
+
style: Optional Cartopy GoogleTiles style string. If omitted, ArcGIS World Imagery tiles are used.
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
Tuple containing:
|
|
307
|
+
- GeoAxes with the satellite basemap.
|
|
308
|
+
- Projection associated with the tile source.
|
|
309
|
+
"""
|
|
310
|
+
GOOGLE_URL = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.jpg'
|
|
311
|
+
|
|
312
|
+
google_satellite = cimgt.GoogleTiles(url=GOOGLE_URL) if style is None else cimgt.GoogleTiles(style=style)
|
|
313
|
+
|
|
314
|
+
if ax is None:
|
|
315
|
+
ax = fig.add_subplot(1, 1, 1, projection=google_satellite.crs) # type: ignore
|
|
316
|
+
else:
|
|
317
|
+
ax = fig.add_axes(ax, projection=google_satellite.crs) # type: ignore
|
|
318
|
+
|
|
319
|
+
assert isinstance(ax, GeoAxes)
|
|
320
|
+
ax.set_extent(extent, crs=ccrs.PlateCarree())
|
|
321
|
+
ax.add_image(google_satellite, _zoom_from_extent(extent, desired_pixels=desired_pixels), alpha=alpha)
|
|
322
|
+
|
|
323
|
+
return ax, google_satellite.crs
|
|
@@ -74,12 +74,12 @@ def _format_reinit_block(row: pd.Series, doy: int, n_layers: int) -> list[str]:
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
def generate_reinit_file(out_path: str | Path, in_path: str | Path, doy: int) -> None:
|
|
77
|
-
"""Write a
|
|
77
|
+
"""Write a re-initialization file from Cycles output for all years at a selected day-of-year.
|
|
78
78
|
|
|
79
79
|
Args:
|
|
80
|
-
out_path: Destination
|
|
81
|
-
in_path: Simulation output directory
|
|
82
|
-
doy: Day-of-year extracted from
|
|
80
|
+
out_path: Destination re-initialization file path.
|
|
81
|
+
in_path: Simulation output directory that contains Cycles output files.
|
|
82
|
+
doy: Day-of-year extracted from re-initialization.
|
|
83
83
|
"""
|
|
84
84
|
out_path = Path(out_path)
|
|
85
85
|
in_path = Path(in_path)
|
|
@@ -215,50 +215,18 @@ def _parse_token(token: str, param: SoilParameter) -> str | int | float | None:
|
|
|
215
215
|
return token
|
|
216
216
|
|
|
217
217
|
|
|
218
|
-
def
|
|
219
|
-
valid_fields = {f.name for f in fields(SoilLayer)}
|
|
220
|
-
kwargs = {col: (None if pd.isna(val) else val) for col, val in row.items() if col in valid_fields}
|
|
221
|
-
return SoilLayer(**kwargs) # type: ignore
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
def map_layers(profile: list[SoilLayer], target: list[SoilLayer]=DEFAULT_PROFILE, parameters: list[str]=MAPPABLE_PARAMETERS, soil_depth: float | None=None) -> list[SoilLayer]:
|
|
225
|
-
"""Map measured soil properties onto a target profile.
|
|
226
|
-
|
|
227
|
-
The mapping is performed by overlap-weighted averaging for each requested
|
|
228
|
-
parameter, after trimming target layers to the effective profile depth.
|
|
229
|
-
|
|
230
|
-
Args:
|
|
231
|
-
profile: Soil profile layers in depth order.
|
|
232
|
-
target: Target layer structure to map onto.
|
|
233
|
-
parameters: Soil parameters to map from measured profile to target.
|
|
234
|
-
soil_depth: Optional maximum depth (m) to include in the mapped profile.
|
|
235
|
-
|
|
236
|
-
Returns:
|
|
237
|
-
A list of mapped soil layers aligned to the target profile.
|
|
238
|
-
"""
|
|
218
|
+
def _map_layers(profile: list[SoilLayer], target: list[SoilLayer]=DEFAULT_PROFILE, parameters: list[str]=MAPPABLE_PARAMETERS, soil_depth: float | None=None) -> list[SoilLayer]:
|
|
239
219
|
trimmed = _trim(target, profile[-1].bottom, soil_depth)
|
|
240
220
|
return [_map_layer(layer, profile, parameters) for layer in trimmed]
|
|
241
221
|
|
|
242
222
|
|
|
243
|
-
def map_to_dataframe(layers: list[SoilLayer]) -> pd.DataFrame:
|
|
244
|
-
"""Convert soil layers to a tabular representation.
|
|
245
|
-
|
|
246
|
-
Args:
|
|
247
|
-
layers: Soil layers to convert.
|
|
248
|
-
|
|
249
|
-
Returns:
|
|
250
|
-
A DataFrame with one row per layer, including computed ``thickness``.
|
|
251
|
-
"""
|
|
252
|
-
return pd.DataFrame([{**asdict(layer), 'thickness': layer.thickness} for layer in layers])
|
|
253
|
-
|
|
254
|
-
|
|
255
223
|
def generate_soil_file(fn: str | Path, profile: list[SoilLayer], *,
|
|
256
224
|
target: list[SoilLayer]=DEFAULT_PROFILE, parameters: list[str]=MAPPABLE_PARAMETERS, soil_depth: float | None=None,
|
|
257
225
|
desc: str='', slope: float | None=None, curve_number: float | None=None, hsg: str='') -> list[SoilLayer]:
|
|
258
|
-
"""
|
|
226
|
+
"""Generate a Cycles-formatted soil file from a list of soil layers.
|
|
259
227
|
|
|
260
|
-
The input profile is first mapped to the target layering scheme, then
|
|
261
|
-
|
|
228
|
+
The input profile is first mapped to the target layering scheme, then rendered to Cycles soil-file format and
|
|
229
|
+
written to ``fn``.
|
|
262
230
|
|
|
263
231
|
Args:
|
|
264
232
|
fn: Output soil file path.
|
|
@@ -279,7 +247,7 @@ def generate_soil_file(fn: str | Path, profile: list[SoilLayer], *,
|
|
|
279
247
|
"""
|
|
280
248
|
if curve_number is not None and hsg:
|
|
281
249
|
raise ValueError("Only one of curve_number and hsg can be provided.")
|
|
282
|
-
layers =
|
|
250
|
+
layers = _map_layers(profile, target, parameters, soil_depth)
|
|
283
251
|
Path(fn).write_text('\n'.join(_render_soil_file(layers, desc, slope, curve_number, hsg)) + '\n')
|
|
284
252
|
return layers
|
|
285
253
|
|
|
@@ -301,15 +269,3 @@ def read_soil_file(fn: str | Path) -> tuple[list[SoilLayer], dict]:
|
|
|
301
269
|
meta, data_lines = _parse_header(lines)
|
|
302
270
|
layers = _parse_layers(data_lines)
|
|
303
271
|
return layers, meta
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
def from_dataframe(df: pd.DataFrame) -> list[SoilLayer]:
|
|
307
|
-
"""Convert a DataFrame into ``SoilLayer`` objects.
|
|
308
|
-
|
|
309
|
-
Args:
|
|
310
|
-
df: DataFrame containing columns matching ``SoilLayer`` fields.
|
|
311
|
-
|
|
312
|
-
Returns:
|
|
313
|
-
A list of ``SoilLayer`` instances created row-by-row.
|
|
314
|
-
"""
|
|
315
|
-
return [_row_to_layer(row) for _, row in df.iterrows()]
|
|
@@ -93,10 +93,9 @@ class RotationResult(NamedTuple):
|
|
|
93
93
|
class CyclesRotationBuilder:
|
|
94
94
|
"""Run Cycles iteratively and append economically optimal operations.
|
|
95
95
|
|
|
96
|
-
Executes Cycles simulations for candidate crop-operation combinations,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
constraints (e.g., minimum planting interval, crop group penalties).
|
|
96
|
+
Executes Cycles simulations for candidate crop-operation combinations, selects the highest-return option at each
|
|
97
|
+
break-point, and iteratively builds a multi-year rotation. Integrates economic scoring with agronomic constraints
|
|
98
|
+
(e.g., minimum planting interval, crop group penalties).
|
|
100
99
|
|
|
101
100
|
Attributes:
|
|
102
101
|
simulation: Base simulation name for generated input/output directories.
|
|
@@ -90,10 +90,8 @@ class SoilGrids:
|
|
|
90
90
|
"""Reproject loaded maps to a reference raster grid.
|
|
91
91
|
|
|
92
92
|
Args:
|
|
93
|
-
reference_xds: Reference raster whose CRS, transform, and resolution
|
|
94
|
-
|
|
95
|
-
reference_name: Column name used for the reference raster values in
|
|
96
|
-
the generated matched table.
|
|
93
|
+
reference_xds: Reference raster whose CRS, transform, and resolution are used for reprojection.
|
|
94
|
+
reference_name: Column name used for the reference raster values in the generated matched table.
|
|
97
95
|
boundary: Boundary geometry used to clip all rasters after reprojection.
|
|
98
96
|
|
|
99
97
|
Returns:
|
|
@@ -211,8 +209,8 @@ def download_soilgrids_data(path: str | Path, *,
|
|
|
211
209
|
maps: list[str]=ALL_MAPS, boundary: Polygon | None=None, bbox: tuple[float, float, float, float] | None=None, crs: str='epsg:4326') -> None:
|
|
212
210
|
"""Download SoilGrids raster layers via WCS.
|
|
213
211
|
|
|
214
|
-
You can provide either a boundary polygon or an explicit bounding box.
|
|
215
|
-
|
|
212
|
+
You can provide either a boundary polygon or an explicit bounding box. Bounding boxes are expected in
|
|
213
|
+
``(west, south, east, north)`` order.
|
|
216
214
|
|
|
217
215
|
Args:
|
|
218
216
|
path: Directory where downloaded GeoTIFF files are written.
|
|
@@ -90,8 +90,10 @@ class Ssurgo:
|
|
|
90
90
|
|
|
91
91
|
def __init__(self, path: str | Path, state: str, *, lat_lon: LatLon | None=None, boundary: gpd.GeoDataFrame | None=None) -> None:
|
|
92
92
|
"""Initialize SSURGO lookup tables and optional spatial subset.
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
|
|
94
|
+
When a location (`lat_lon`) or boundary polygon is provided, the map-unit table is filtered to include only
|
|
95
|
+
those map units that intersect the location or polygon; the map-units are also grouped by name and symbol, and
|
|
96
|
+
the major map unit is selected for profile extraction.
|
|
95
97
|
|
|
96
98
|
Args:
|
|
97
99
|
path: Directory containing SSURGO geodatabase and lookup CSV files.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|