soil-aggregation-tool 0.1.0__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.
@@ -0,0 +1,41 @@
1
+ name: Publish Python Package
2
+
3
+ # Runs when a version tag such as v0.1.0 is pushed.
4
+ # The package version is taken from the tag automatically.
5
+ on:
6
+ push:
7
+ tags:
8
+ - 'v*'
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ with:
17
+ fetch-depth: 0 # needed to read the version from the tag
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: '3.12'
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install build twine
28
+
29
+ - name: Run tests
30
+ run: |
31
+ pip install .
32
+ python -m unittest discover -s tests -v
33
+
34
+ - name: Build package
35
+ run: python -m build
36
+
37
+ - name: Publish package
38
+ env:
39
+ TWINE_USERNAME: __token__
40
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
41
+ run: twine upload --skip-existing dist/*
@@ -0,0 +1,24 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .venv/
6
+ venv/
7
+ build/
8
+ dist/
9
+ .soil_test_*/
10
+
11
+ # Large local inputs and generated outputs
12
+ usersoil*.csv
13
+ Soil_usersoil.csv
14
+ Soil_lookup.csv
15
+ *.tif
16
+ *.tiff
17
+ soil_output/
18
+ output/
19
+
20
+ # Editor and operating-system files
21
+ .idea/
22
+ .vscode/
23
+ .DS_Store
24
+ Thumbs.db
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yashas Kumar and Chandan Kumar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,368 @@
1
+ Metadata-Version: 2.4
2
+ Name: soil-aggregation-tool
3
+ Version: 0.1.0
4
+ Summary: Aggregate SWAT usersoil classes and reclassify a matching soil raster.
5
+ Author: Yashas Kumar
6
+ Maintainer-email: Chandan Kumar <chandankr014@gmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/chandankr014/swatplus-soil-aggregation-tool
9
+ Project-URL: Issues, https://github.com/chandankr014/swatplus-soil-aggregation-tool/issues
10
+ Keywords: SWAT,SWAT+,soil,hydrology,raster,clustering
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Topic :: Scientific/Engineering :: GIS
15
+ Classifier: Topic :: Scientific/Engineering :: Hydrology
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: numpy>=1.24
20
+ Requires-Dist: pandas>=2.0
21
+ Requires-Dist: rasterio>=1.3
22
+ Requires-Dist: scikit-learn>=1.3
23
+ Dynamic: license-file
24
+
25
+ # Soil Aggregation Tool
26
+
27
+ `Soil_aggregation_tool` is an installable Python package for aggregating SWAT
28
+ usersoil classes and reclassifying a matching soil raster. It groups comparable
29
+ soils, selects an appropriate number of k-means clusters, and produces a smaller
30
+ usersoil table, a matching raster, and a two-column lookup table.
31
+
32
+ Developed by Yashas Kumar. Packaged and maintained by
33
+ [chandankr014](https://github.com/chandankr014).
34
+
35
+ ## Outputs
36
+
37
+ Each run creates exactly these three files:
38
+
39
+ | Output file | Contents |
40
+ | --- | --- |
41
+ | `Soil_usersoil.csv` | Aggregated usersoil table with the same columns as the input usersoil CSV. |
42
+ | `Soil_lookup.csv` | Lookup containing only `MUID` and `SNAM`, copied from `Soil_usersoil.csv`. |
43
+ | `Soil.tif` | Input soil raster reclassified to the new aggregated MUID values. |
44
+
45
+ Generated soil names are `Soil_1`, `Soil_2`, `Soil_3`, and so on. Therefore,
46
+ the `MUID` and `SNAM` pairs in `Soil_lookup.csv` always match those in
47
+ `Soil_usersoil.csv`.
48
+
49
+ The program does not delete unrelated files already present in the output
50
+ folder. For a folder containing only these three files, use a new or empty
51
+ output folder.
52
+
53
+ ## How the tool works
54
+
55
+ 1. It reads the unique MUID values and cell areas from the input soil raster.
56
+ 2. It selects usersoil rows whose `MUID` values occur in that raster.
57
+ 3. It divides the selected soils into comparable strata using `HYDGRP`,
58
+ `TEXTURE`, `SOL_ZMX`, and `NLAYERS`.
59
+ 4. Within each stratum, it clusters soils using `SOL_K1`, `SOL_AWC1`,
60
+ `SOL_BD1`, and `USLE_K1`.
61
+ 5. It compares candidate cluster counts using the silhouette score.
62
+ 6. It aggregates each selected cluster into one usersoil row and assigns new
63
+ sequential MUIDs beginning with `1`.
64
+ 7. It reclassifies the raster from the original MUID values to the new MUIDs.
65
+ 8. It copies `MUID` and `SNAM` from the output usersoil into the lookup file.
66
+
67
+ ## Input requirements
68
+
69
+ ### Usersoil CSV
70
+
71
+ The input CSV must contain these columns:
72
+
73
+ | Column | Use |
74
+ | --- | --- |
75
+ | `MUID` | Soil identifier corresponding to raster cell values. |
76
+ | `SNAM` | Soil name. Output names are replaced with `Soil_1`, `Soil_2`, etc. |
77
+ | `HYDGRP` | Hydrologic soil group used to define strata. |
78
+ | `TEXTURE` | Soil texture used to define strata. |
79
+ | `SOL_ZMX` | Maximum soil depth used to define strata. |
80
+ | `NLAYERS` | Number of soil layers used to define strata. |
81
+ | `SOL_K1` | Layer-one saturated hydraulic conductivity used for clustering. |
82
+ | `SOL_AWC1` | Layer-one available water capacity used for clustering. |
83
+ | `SOL_BD1` | Layer-one bulk density used for clustering. |
84
+ | `USLE_K1` | Layer-one soil erodibility factor used for clustering. |
85
+
86
+ All other input usersoil columns are retained in `Soil_usersoil.csv`. If the
87
+ input includes every column from `SOL_Z1` through `SOL_Z10`, the tool corrects
88
+ an inconsistent `NLAYERS` value from the number of positive layer depths.
89
+
90
+ ### Soil raster
91
+
92
+ The raster must:
93
+
94
+ - be a single-band GeoTIFF;
95
+ - contain cell values corresponding to `MUID` values in the usersoil CSV; and
96
+ - contain valid raster dimensions, transform, and coordinate-system metadata.
97
+
98
+ Values `0` and `65535` are treated as background or no-data by default. Raster
99
+ MUIDs that do not occur in the usersoil CSV are written as `0` in `Soil.tif`
100
+ and reported in the terminal.
101
+
102
+ ## Software requirements
103
+
104
+ - Python 3.10 or newer
105
+ - NumPy
106
+ - pandas
107
+ - Rasterio
108
+ - scikit-learn
109
+
110
+ Python installs the package dependencies from `pyproject.toml`.
111
+
112
+ ## Download and install from GitHub
113
+
114
+ First, install Git and Python 3.10 or newer. Then follow these steps.
115
+
116
+ ### 1. Download the repository
117
+
118
+ Open PowerShell or a terminal and run:
119
+
120
+ ```powershell
121
+ git clone https://github.com/chandankr014/swatplus-soil-aggregation-tool.git
122
+ ```
123
+
124
+ ### 2. Enter the downloaded folder
125
+
126
+ ```powershell
127
+ cd swatplus-soil-aggregation-tool
128
+ ```
129
+
130
+ ### 3. Create a virtual environment
131
+
132
+ ```powershell
133
+ python -m venv .venv
134
+ ```
135
+
136
+ Activate it on Windows PowerShell:
137
+
138
+ ```powershell
139
+ .\.venv\Scripts\Activate.ps1
140
+ ```
141
+
142
+ On macOS or Linux, activate it with:
143
+
144
+ ```bash
145
+ source .venv/bin/activate
146
+ ```
147
+
148
+ ### 4. Install the package
149
+
150
+ ```powershell
151
+ python -m pip install .
152
+ ```
153
+
154
+ The required Python packages are installed automatically. Confirm that the
155
+ installation succeeded:
156
+
157
+ ```powershell
158
+ soil-aggregation-tool --help
159
+ ```
160
+
161
+ ### Install directly without cloning
162
+
163
+ The package can also be installed directly from GitHub:
164
+
165
+ ```powershell
166
+ python -m pip install "git+https://github.com/chandankr014/swatplus-soil-aggregation-tool.git"
167
+ ```
168
+
169
+ ### Install in Jupyter Notebook
170
+
171
+ Run this command in a notebook cell to install the package into the environment
172
+ used by the current notebook kernel:
173
+
174
+ ```python
175
+ %pip install "git+https://github.com/chandankr014/swatplus-soil-aggregation-tool.git"
176
+ ```
177
+
178
+ Restart the notebook kernel after installation. The Python API can then be used
179
+ directly in another cell:
180
+
181
+ ```python
182
+ from soil_aggregation_tool import aggregate_soils
183
+
184
+ result = aggregate_soils(
185
+ usersoil_path="usersoil.csv",
186
+ raster_path="soil_input.tif",
187
+ output_dir="soil_output",
188
+ )
189
+
190
+ print(result.usersoil_path)
191
+ print(result.lookup_path)
192
+ print(result.raster_path)
193
+ ```
194
+
195
+ ## Running with interactive input
196
+
197
+ Run the command without file arguments:
198
+
199
+ ```powershell
200
+ soil-aggregation-tool
201
+ ```
202
+
203
+ The program asks for the inputs as follows:
204
+
205
+ ```text
206
+ Usersoil CSV path:
207
+ Soil raster path:
208
+ Output folder path:
209
+ ```
210
+
211
+ Enter a complete or relative path after each prompt. The prompts do not use
212
+ default filenames or square brackets.
213
+
214
+ Example:
215
+
216
+ ```text
217
+ Usersoil CSV path: D:\data\usersoil.csv
218
+ Soil raster path: D:\data\soil_input.tif
219
+ Output folder path: D:\data\soil_output
220
+ ```
221
+
222
+ ## Running with command-line arguments
223
+
224
+ File paths can be supplied directly to avoid interactive prompts:
225
+
226
+ ```powershell
227
+ soil-aggregation-tool `
228
+ --usersoil "D:\data\usersoil.csv" `
229
+ --raster "D:\data\soil_input.tif" `
230
+ --output-dir "D:\data\soil_output"
231
+ ```
232
+
233
+ The Python module form is equivalent:
234
+
235
+ ```powershell
236
+ python -m soil_aggregation_tool `
237
+ --usersoil "D:\data\usersoil.csv" `
238
+ --raster "D:\data\soil_input.tif" `
239
+ --output-dir "D:\data\soil_output"
240
+ ```
241
+
242
+ Paths are quoted so paths containing spaces work correctly. The output folder
243
+ is created automatically when it does not exist.
244
+
245
+ ### Optional settings
246
+
247
+ | Argument | Default | Description |
248
+ | --- | ---: | --- |
249
+ | `--max-optimal-clusters` | `15` | Maximum number of clusters evaluated within each soil stratum. |
250
+ | `--seed` | `123` | Random seed that makes k-means results reproducible. |
251
+ | `--ignore-raster-values` | `0 65535` | Space-separated raster values treated as background. |
252
+
253
+ Example using `0` and `-9999` as ignored values:
254
+
255
+ ```powershell
256
+ soil-aggregation-tool `
257
+ --usersoil "D:\data\usersoil.csv" `
258
+ --raster "D:\data\soil_input.tif" `
259
+ --output-dir "D:\data\soil_output" `
260
+ --ignore-raster-values 0 -9999
261
+ ```
262
+
263
+ ## Using the package from Python
264
+
265
+ ```python
266
+ from soil_aggregation_tool import aggregate_soils
267
+
268
+ result = aggregate_soils(
269
+ usersoil_path="usersoil.csv",
270
+ raster_path="soil_input.tif",
271
+ output_dir="soil_output",
272
+ )
273
+
274
+ print(result.usersoil_path)
275
+ print(result.lookup_path)
276
+ print(result.raster_path)
277
+ print(result.aggregated_soil_count)
278
+ ```
279
+
280
+ ## Running the tests
281
+
282
+ Run the automated test from the repository folder:
283
+
284
+ ```powershell
285
+ python -m unittest discover -s tests -v
286
+ ```
287
+
288
+ The tests create a small usersoil CSV and GeoTIFF, run the complete workflow,
289
+ and check that:
290
+
291
+ - only the three documented output files are created;
292
+ - `Soil_lookup.csv` has only the `MUID` and `SNAM` columns;
293
+ - lookup values exactly match `Soil_usersoil.csv`;
294
+ - the new MUID values are written into `Soil.tif`;
295
+ - raster MUIDs missing from the usersoil CSV are written as `0`; and
296
+ - a missing required column raises a clear error.
297
+
298
+ The same tests run in GitHub Actions before each release is published.
299
+
300
+ ## Publishing a release to PyPI
301
+
302
+ Pushing a tag that starts with `v` runs `.github/workflows/publish.yml`, which
303
+ tests, builds, and uploads the package to PyPI. The package version is taken
304
+ from the tag, so there is no version number to edit by hand.
305
+
306
+ One-time setup: create a PyPI API token and add it on GitHub under
307
+ **Settings → Secrets and variables → Actions** as `PYPI_API_TOKEN`.
308
+
309
+ To release a new version:
310
+
311
+ ```powershell
312
+ git add .
313
+ git commit -m "Release v0.1.0"
314
+ git push origin main
315
+ git tag v0.1.0
316
+ git push origin v0.1.0
317
+ ```
318
+
319
+ Check progress in the repository's **Actions** tab. After it finishes, install
320
+ with:
321
+
322
+ ```powershell
323
+ pip install --no-cache-dir soil-aggregation-tool
324
+ ```
325
+
326
+ ## License
327
+
328
+ Released under the MIT License. See [LICENSE](LICENSE).
329
+
330
+ ## Repository structure
331
+
332
+ ```text
333
+ Soil_aggregation_tool/
334
+ |-- .github/
335
+ | `-- workflows/
336
+ | `-- publish.yml
337
+ |-- .gitignore
338
+ |-- LICENSE
339
+ |-- README.md
340
+ |-- pyproject.toml
341
+ |-- src/
342
+ | `-- soil_aggregation_tool/
343
+ | |-- __init__.py
344
+ | |-- __main__.py
345
+ | |-- cli.py
346
+ | `-- core.py
347
+ `-- tests/
348
+ `-- test_aggregation.py
349
+ ```
350
+
351
+ Large CSV and TIFF datasets are excluded by `.gitignore`. Keep input data
352
+ outside the repository, or check that it is not staged before committing.
353
+
354
+ ## Common errors
355
+
356
+ - **Usersoil CSV not found:** check the path entered at `Usersoil CSV path` or
357
+ supplied to `--usersoil`.
358
+ - **Soil raster not found:** check the path entered at `Soil raster path` or
359
+ supplied to `--raster`.
360
+ - **Missing required columns:** add or rename the columns listed in the usersoil
361
+ input section.
362
+ - **No raster MUID values matched:** confirm that raster cell values and the
363
+ usersoil `MUID` column use the same identifiers.
364
+ - **Output file is open:** close the file in other software and run the tool
365
+ again. When an output is locked, the tool warns and uses a timestamped
366
+ filename rather than overwriting the open file.
367
+ - **Arguments are required when input is not interactive:** when running from
368
+ a script or scheduled job, pass `--usersoil`, `--raster`, and `--output-dir`.