scrollstats 0.1.2__tar.gz → 0.1.4__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.
Files changed (88) hide show
  1. {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/workflows/cd.yml +3 -3
  2. {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/workflows/ci.yml +6 -6
  3. {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/workflows/draft-pdf.yml +2 -2
  4. {scrollstats-0.1.2 → scrollstats-0.1.4}/.readthedocs.yaml +3 -0
  5. scrollstats-0.1.4/ARCHITECTURE.md +407 -0
  6. {scrollstats-0.1.2 → scrollstats-0.1.4}/PKG-INFO +36 -3
  7. {scrollstats-0.1.2 → scrollstats-0.1.4}/README.md +33 -1
  8. {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/CalculateRidgeMetrics.md +3 -1
  9. scrollstats-0.1.4/docs/CreateVectorDatasets.md +317 -0
  10. {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/DelineateRidgeAreas.md +20 -10
  11. {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/index.md +40 -2
  12. scrollstats-0.1.4/img/agreement_raster.png +0 -0
  13. scrollstats-0.1.4/img/agreement_v_denoised.png +0 -0
  14. scrollstats-0.1.4/img/bend-dem.png +0 -0
  15. scrollstats-0.1.4/img/bend_geometry.png +0 -0
  16. scrollstats-0.1.4/img/binary_classification.png +0 -0
  17. scrollstats-0.1.4/img/dem_v_agreement.png +0 -0
  18. scrollstats-0.1.4/img/migration_pathways.png +0 -0
  19. scrollstats-0.1.4/img/pc_v_dem_v_rt.png +0 -0
  20. scrollstats-0.1.4/img/ridge_amplitude.png +0 -0
  21. scrollstats-0.1.4/img/ridges_and_centerline.png +0 -0
  22. scrollstats-0.1.4/img/smoothed_ridges.png +0 -0
  23. {scrollstats-0.1.2 → scrollstats-0.1.4}/noxfile.py +5 -0
  24. scrollstats-0.1.4/paper/paper.bib +121 -0
  25. {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/paper.md +75 -8
  26. {scrollstats-0.1.2 → scrollstats-0.1.4}/pyproject.toml +4 -2
  27. scrollstats-0.1.4/scripts/calc_ridge_metrics.py +76 -0
  28. scrollstats-0.1.4/scripts/create_vector_data.py +149 -0
  29. scrollstats-0.1.4/scripts/delineate_ridge_area_raster.py +91 -0
  30. scrollstats-0.1.4/scripts/plots/calc_ridge_metrics.py +80 -0
  31. scrollstats-0.1.4/scripts/plots/create_vector_datasets.py +140 -0
  32. scrollstats-0.1.4/scripts/plots/delineate_ridge_areas.py +200 -0
  33. scrollstats-0.1.4/src/scrollstats/__init__.py +40 -0
  34. scrollstats-0.1.4/src/scrollstats/_version.py +24 -0
  35. scrollstats-0.1.4/src/scrollstats/delineation/__init__.py +16 -0
  36. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/array_types.py +7 -5
  37. scrollstats-0.1.4/src/scrollstats/ridge_metrics/__init__.py +17 -0
  38. scrollstats-0.1.4/src/scrollstats/transecting/__init__.py +9 -0
  39. {scrollstats-0.1.2 → scrollstats-0.1.4}/tests/test_core.py +39 -0
  40. scrollstats-0.1.2/docs/CreateVectorDatasets.md +0 -329
  41. scrollstats-0.1.2/img/agreement_raster.png +0 -0
  42. scrollstats-0.1.2/img/agreement_v_denoised.png +0 -0
  43. scrollstats-0.1.2/img/bend_geometry.png +0 -0
  44. scrollstats-0.1.2/img/binary_classification.png +0 -0
  45. scrollstats-0.1.2/img/dem_v_agreement.png +0 -0
  46. scrollstats-0.1.2/img/migration_pathways.png +0 -0
  47. scrollstats-0.1.2/img/pc_v_dem_v_rt.png +0 -0
  48. scrollstats-0.1.2/img/ridge_amplitude.png +0 -0
  49. scrollstats-0.1.2/img/ridges_and_centerline.png +0 -0
  50. scrollstats-0.1.2/img/smoothed_ridges.png +0 -0
  51. scrollstats-0.1.2/paper/paper.bib +0 -45
  52. scrollstats-0.1.2/src/scrollstats/__init__.py +0 -39
  53. scrollstats-0.1.2/src/scrollstats/_version.py +0 -34
  54. scrollstats-0.1.2/src/scrollstats/delineation/__init__.py +0 -13
  55. scrollstats-0.1.2/src/scrollstats/ridge_metrics/__init__.py +0 -18
  56. scrollstats-0.1.2/src/scrollstats/transecting/__init__.py +0 -8
  57. {scrollstats-0.1.2 → scrollstats-0.1.4}/.git_archival.txt +0 -0
  58. {scrollstats-0.1.2 → scrollstats-0.1.4}/.gitattributes +0 -0
  59. {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/CONTRIBUTING.md +0 -0
  60. {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/dependabot.yml +0 -0
  61. {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/release.yml +0 -0
  62. {scrollstats-0.1.2 → scrollstats-0.1.4}/.gitignore +0 -0
  63. {scrollstats-0.1.2 → scrollstats-0.1.4}/.pre-commit-config.yaml +0 -0
  64. {scrollstats-0.1.2 → scrollstats-0.1.4}/LICENSE +0 -0
  65. {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/CompleteScrollStatsWorkflow.md +0 -0
  66. {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/conf.py +0 -0
  67. {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_bend.geojson +0 -0
  68. {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_cl.geojson +0 -0
  69. {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_dem.tif +0 -0
  70. {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_packets.geojson +0 -0
  71. {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_ridges_manual.geojson +0 -0
  72. {scrollstats-0.1.2 → scrollstats-0.1.4}/img/README.md +0 -0
  73. {scrollstats-0.1.2 → scrollstats-0.1.4}/img/migration_pathway.png +0 -0
  74. {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/figs/digitized_dem.png +0 -0
  75. {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/figs/example_metric.png +0 -0
  76. {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/figs/example_output.png +0 -0
  77. {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/figs/example_transect.png +0 -0
  78. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/_version.pyi +0 -0
  79. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/line_smoother.py +0 -0
  80. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/raster_classifiers.py +0 -0
  81. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/raster_denoisers.py +0 -0
  82. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/ridge_area_raster.py +0 -0
  83. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/py.typed +0 -0
  84. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/ridge_metrics/calc_ridge_metrics.py +0 -0
  85. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/ridge_metrics/data_extractors.py +0 -0
  86. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/ridge_metrics/ridge_amplitude.py +0 -0
  87. {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/transecting/transect.py +0 -0
  88. {scrollstats-0.1.2 → scrollstats-0.1.4}/tests/test_package.py +0 -0
@@ -25,7 +25,7 @@ jobs:
25
25
  runs-on: ubuntu-latest
26
26
 
27
27
  steps:
28
- - uses: actions/checkout@v4
28
+ - uses: actions/checkout@v6
29
29
  with:
30
30
  fetch-depth: 0
31
31
 
@@ -43,13 +43,13 @@ jobs:
43
43
  if: github.event_name == 'release' && github.event.action == 'published'
44
44
 
45
45
  steps:
46
- - uses: actions/download-artifact@v6
46
+ - uses: actions/download-artifact@v8
47
47
  with:
48
48
  name: Packages
49
49
  path: dist
50
50
 
51
51
  - name: Generate artifact attestation for sdist and wheel
52
- uses: actions/attest-build-provenance@v2.2.3
52
+ uses: actions/attest-build-provenance@v4.1.0
53
53
  with:
54
54
  subject-path: "dist/*"
55
55
 
@@ -21,10 +21,10 @@ jobs:
21
21
  name: Format
22
22
  runs-on: ubuntu-latest
23
23
  steps:
24
- - uses: actions/checkout@v4
24
+ - uses: actions/checkout@v6
25
25
  with:
26
26
  fetch-depth: 0
27
- - uses: actions/setup-python@v5
27
+ - uses: actions/setup-python@v6
28
28
  with:
29
29
  python-version: "3.x"
30
30
  - uses: pre-commit/action@v3.0.1
@@ -40,15 +40,15 @@ jobs:
40
40
  strategy:
41
41
  fail-fast: false
42
42
  matrix:
43
- python-version: ["3.10", "3.11", "3.12"]
43
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
44
44
  runs-on: [ubuntu-latest, macos-14]
45
45
 
46
46
  steps:
47
- - uses: actions/checkout@v4
47
+ - uses: actions/checkout@v6
48
48
  with:
49
49
  fetch-depth: 0
50
50
 
51
- - uses: actions/setup-python@v5
51
+ - uses: actions/setup-python@v6
52
52
  with:
53
53
  python-version: ${{ matrix.python-version }}
54
54
  allow-prereleases: true
@@ -62,6 +62,6 @@ jobs:
62
62
  --durations=20
63
63
 
64
64
  - name: Upload coverage report
65
- uses: codecov/codecov-action@v5
65
+ uses: codecov/codecov-action@v6
66
66
  with:
67
67
  token: ${{ secrets.CODECOV_TOKEN }}
@@ -11,7 +11,7 @@ jobs:
11
11
  name: Paper Draft
12
12
  steps:
13
13
  - name: Checkout
14
- uses: actions/checkout@v4
14
+ uses: actions/checkout@v6
15
15
  - name: Build draft PDF
16
16
  uses: openjournals/openjournals-draft-action@master
17
17
  with:
@@ -19,7 +19,7 @@ jobs:
19
19
  # This should be the path to the paper within your repo.
20
20
  paper-path: paper/paper.md
21
21
  - name: Upload
22
- uses: actions/upload-artifact@v4
22
+ uses: actions/upload-artifact@v7
23
23
  with:
24
24
  name: paper
25
25
  # This is the output path where Pandoc will write the compiled
@@ -18,6 +18,9 @@ build:
18
18
  - uv pip install .[docs]
19
19
  build:
20
20
  html:
21
+ - uv run python scripts/plots/delineate_ridge_areas.py
22
+ - uv run python scripts/plots/create_vector_datasets.py
23
+ - uv run python scripts/plots/calc_ridge_metrics.py
21
24
  - .venv/bin/sphinx-apidoc -o docs/api --module-first --no-toc --force
22
25
  src/scrollstats
23
26
  - uv run python -m sphinx -T -b html -d docs/_build/doctrees -D
@@ -0,0 +1,407 @@
1
+ # Overview
2
+
3
+ ## 1. Project Structure
4
+
5
+ ```
6
+ scrollstats/
7
+ ├── docs/ # Project documentation published to https://scrollstats.readthedocs.io
8
+ │ └── conf.py # Configuration for documentation building with Sphinx
9
+ ├── example_data/ # Example datasets used in docs/ and tests/
10
+ ├── img/ # Images referenced in docs/
11
+ ├── paper/ # Contents for accompanying JOSS paper
12
+ │ ├── figs/ # Figures for JOSS paper
13
+ │ ├── paper.bib # Bibliography for JOSS paper
14
+ │ └── paper.md # JOSS paper
15
+ ├── scripts/ # Auxiliary scripts using scrollstats
16
+ │ ├── plots/ # Scripts to generate plots referenced in docs/
17
+ │ ├── calc_ridge_metrics.py # Example script to calculate ridge metrics
18
+ │ ├── create_vector_data.py # Example script to create vector data
19
+ │ └── delineate_ridge_area_raster.py # Example script to delineate ridge area rasters
20
+ ├── src/ # Source code for scrollstats
21
+ │ └── scrollstats/ # Top level folder for sub-packages
22
+ │ ├── delineation/ # Contains code for delineating ridges from DEM
23
+ │ │ ├── array_types.py # Type definitions for numpy arrays
24
+ │ │ ├── line_smoother.py # Smoothing code for manually digitized features
25
+ │ │ ├── raster_classifiers.py # Classifies ridge areas within DEM
26
+ │ │ ├── raster_denoisers.py # De-noises raw ridge area rasters
27
+ │ │ └── ridge_area_raster.py # Entry point to create ridge area raster
28
+ │ ├── ridge_metrics/ # Contains code to calculate ridge area metrics
29
+ │ │ ├── calc_ridge_metrics.py # Entry point to calculate ridge area metrics
30
+ │ │ ├── data_extractors.py # DataExtractor classes to calculate metrics at Bend, Transect, and Ridge scales
31
+ │ │ └── ridge_amplitude.py # Ridge amplitude calculation
32
+ │ └── transecting/ # Contains code to generate transects
33
+ │ └── transect.py # Entry point to generate transects
34
+ ├── tests/ # Contains test code
35
+ │ ├── test_core.py # Unit tests for scrollstats functionality
36
+ │ └── test_package.py # Unit tests for package version
37
+ ├── ARCHITECTURE.md # This document
38
+ ├── LICENSE # Project license file
39
+ ├── noxfile.py # Configuration for local development and testing with nox
40
+ ├── pyproject.toml # Project configuration and dependency declaration
41
+ └── README.md # Project overview and quickstart guide
42
+ ```
43
+
44
+ ## 2. High-Level Diagram
45
+
46
+ ```mermaid
47
+ flowchart TD
48
+
49
+ subgraph Digitize
50
+ direction TD
51
+ BB(Bend Boundary)
52
+ PB(Packet Boundaries)
53
+ RL(Ridge Lines)
54
+ CL(Channel Centerline)
55
+ BB ~~~ PB ~~~ RL ~~~ CL
56
+ end
57
+
58
+ subgraph CT[Create Transects]
59
+ direction TD
60
+ subgraph CTF[create_transects]
61
+ direction LR
62
+ MT(MultiTransect)
63
+ subgraph ForEachT[For each transect...]
64
+ direction LR
65
+ H74_TC1(H74TransectConstructor)
66
+ H74_TC2(H74TransectConstructor)
67
+ H74_T1(H74Transect)
68
+ H74_T2(H74Transect)
69
+ end
70
+
71
+ end
72
+ MT --> ForEachT
73
+ H74_TC1 --> H74_T1
74
+ H74_TC2 --> H74_T2
75
+ end
76
+
77
+ subgraph Delineate
78
+ direction TD
79
+ subgraph CRAR [create_ridge_area_raster]
80
+ direction TD
81
+ subgraph DEFAULT_CLASSIFIERS
82
+ direction TD
83
+ rt(residual_topography_classifier)
84
+ pc(profile_curvature_classifier)
85
+ end
86
+
87
+ rt & pc --> U(Union)
88
+ end
89
+
90
+ subgraph denoise_raster
91
+ DF("`**DEFAULT_DENOISERS:**
92
+ binary_opening
93
+ binary_closing
94
+ remove_small_feats`")
95
+ end
96
+
97
+ CRAR --> AGR(Agreement Raster)
98
+ AGR --> denoise_raster
99
+ end
100
+
101
+ subgraph CRM[Calculate Ridge Metrics]
102
+ direction TD
103
+ subgraph calc_ridge_metrics
104
+ direction LR
105
+ BDE("BendDataExtractor")
106
+ subgraph ForEachTE[For each transect...]
107
+ direction LR
108
+ TDE1("TransectDataExtractor")
109
+ TDE2("TransectDataExtractor")
110
+ end
111
+ subgraph ForEachRE[For each ridge...]
112
+ direction LR
113
+ RDE1("RidgeDataExtractor")
114
+ RDE2("RidgeDataExtractor")
115
+ RDE3("RidgeDataExtractor")
116
+ end
117
+ end
118
+
119
+ BDE --> ForEachTE
120
+ ForEachTE --> ForEachRE
121
+
122
+ end
123
+
124
+ subgraph Output
125
+ direction TD
126
+ rich_transects
127
+ itx_metrics
128
+ end
129
+
130
+
131
+ DEM(Input DEM)
132
+ DEM --> Digitize
133
+ DEM --> Delineate
134
+ Delineate --> RAR(Ridge Area Raster)
135
+
136
+ CL --> |LineSmoother|CT
137
+ RL --> |LineSmoother|CT
138
+ BB --> |Clip|AGR
139
+
140
+ CT --> MP("Migration Pathways")
141
+ MP & RAR --> CRM
142
+
143
+ CRM --> Output
144
+
145
+ ```
146
+
147
+ ## 3. Core Components
148
+
149
+ ### 3.1. Delineation
150
+
151
+ This subpackage contains the code that's used to delineate ridge areas from the
152
+ DEM. It relies heavily on the `rasterio` and `numpy` libraries to accomplish
153
+ this.
154
+
155
+ `scrollstats.delineation.ridge_area_raster.create_ridge_area_raster()` is the
156
+ main entry point for raster delineation and makes use of the functions found in
157
+ the accompanying `raster_classifiers.py` and `raster_denoisers.py` modules.
158
+
159
+ <!-- blacken-docs:off -->
160
+
161
+ ```python
162
+ def create_ridge_area_raster(
163
+ dem_ds: rasterio.DatasetReader,
164
+ geometry: Polygon,
165
+ classifier_funcs: tuple[BinaryClassifierFn, ...] = DEFAULT_CLASSIFIERS,
166
+ denoiser_funcs: tuple[BinaryDenoiserFn, ...] = DEFAULT_DENOISERS,
167
+ no_data_value: Any | None = None,
168
+ **kwargs: Any,
169
+ ) -> tuple[np.ndarray, np.ndarray, dict[Any, Any]]:
170
+ ```
171
+
172
+ <!-- blacken-docs:on -->
173
+
174
+ Raster classifier functions take a 2D numpy array of continuous data (like a
175
+ DEM) and return a binary 2D numpy array where values of 1 indicate the features
176
+ of interest, 0 is background, and `np.nan` is no data. Any number of classifier
177
+ functions are applied to the DEM in parallel then the union of all these binary
178
+ rasters are taken to result in what's called the Agreement Raster (where all
179
+ classifiers agree there is a ridge).
180
+
181
+ By default, `create_ridge_area_raster()` uses the 2 classifier functions
182
+ `profile_curvature_classifier()` and `residual_topography_classifier()` in the
183
+ tuple `DEFAULT_CLASSIFIERS` defined in `raster_classifiers.py`.
184
+ `DEFAULT_CLASSIFIERS` is then imported into `ridge_area_raster.py` and set as
185
+ the default value for the `classifier_funcs` argument in
186
+ `create_ridge_area_raster()`. The user may provide their own list of classifier
187
+ functions here too so long as they follow the same input/output pattern.
188
+
189
+ Denoiser functions are then applied to the Agreement Raster after it has been
190
+ clipped to the bend boundary Polygon. Denoiser functions take and return a
191
+ binary 2D numpy array. Denoiser functions are applied in series on the clipped
192
+ Agreement raster so that the output of the first is the input of the second, and
193
+ so on. This means that the order of the denoiser functions can change the end
194
+ result, unlike the classifier process from before.
195
+
196
+ By default, `create_ridge_area_raster()` uses the 3 denoiser functions
197
+ `scipy.ndimage.binary_closing()`, `scipy.ndimage.binary_opening()`, and
198
+ `remove_small_feats_w_flip()` in the tuple `DEFAULT_DENOISERS` defined in the
199
+ `raster_denoisers.py`. `DEFAULT_DENOISERS` is then imported into
200
+ `ridge_area_raster.py` and set as the default value for the `denoiser_funcs`
201
+ argument in `create_ridge_area_raster()`. The user may provide their own list of
202
+ denoiser functions here too so long as all functions in the list follow the same
203
+ input/output pattern.
204
+
205
+ Any new classifier or denoiser functions just need to be added to the respective
206
+ modules and follow the established input/output patterns, then they too can be
207
+ imported and used in `create_ridge_area_raster()`.
208
+
209
+ ### 3.2. Transecting
210
+
211
+ This subpackage contains all of the code needed to generate transects (migration
212
+ pathways) from a set of ridges and centerline for a bend.
213
+
214
+ `scrollstats.transecting.transect.py` is the only module in this subpackage and
215
+ it contains `create_transects()` - the main entry point for creating transects.
216
+
217
+ <!-- blacken-docs:off -->
218
+
219
+ ```python
220
+ def create_transects(
221
+ centerline: GeoDataFrame,
222
+ ridges: GeoDataFrame,
223
+ step: int,
224
+ shoot_distance: float,
225
+ search_distance: float,
226
+ dev_from_90: float,
227
+ ) -> GeoDataFrame:
228
+ ```
229
+
230
+ <!-- blacken-docs:on -->
231
+
232
+ `create_transects()` is responsible for identifying equally spaced starting
233
+ points (`starts`) along the centerline (distance determined by `step` arg) and
234
+ generating a transect for each of the starting points.
235
+
236
+ These `starts` are then passed to the `MultiTransect` class (along with other
237
+ bend geometries and parameters) which creates an `H74TransectConstructor` for
238
+ each start.
239
+
240
+ Each `H74TransectConstructor` is responsible for performing the geometric
241
+ calculations to generate the transect and other geometries. The primary control
242
+ loop for generating transects is the `H74TransectConstructor.walk_transect()`
243
+ method which "walks" the transect up the floodplain from ridge to ridge until it
244
+ fails to intersect another ridge.
245
+
246
+ Each `H74TransectConstructor` creates an`H74Transect` instance which is
247
+ responsible for storing all of the geometries calculated as the transect is
248
+ created.
249
+
250
+ Multiple sets of geometries are created while generating transects which can
251
+ each be returned as a `GeoDataFrame` by calling the various `create_` methods of
252
+ `MultiTransect`. Calling `create_all_geometries()` returns a tuple of all these
253
+ geometries - transects, points, search_areas, ridge_clips - which is useful for
254
+ [visualizing the geometric calculations](https://scrollstats.readthedocs.io/en/latest/_images/migration_pathway.png).
255
+
256
+ ### 3.3. Ridge Metrics
257
+
258
+ This subpackage contains all of the code needed to calculate ridge metrics from
259
+ the Ridge Area Raster and Migration Pathways created from the delineation and
260
+ transecting processes above.
261
+
262
+ `scrollstats.ridge_metrics.calc_ridge_metrics.calculate_ridge_metrics()` is the
263
+ main entry point for ridge metric calculation and makes use of the
264
+ `DataExtractor` classes imported from `data_extractors.py` as well as
265
+ `calc_ridge_amp()` from `ridge_amplitude.py`.
266
+
267
+ <!-- blacken-docs:off -->
268
+
269
+ ```python
270
+ def calculate_ridge_metrics(
271
+ in_transects: gpd.GeoDataFrame | Path,
272
+ in_ridges: gpd.GeoDataFrame | Path,
273
+ in_bin_raster: rasterio.io.DatasetReader | Path | None = None,
274
+ in_dem: rasterio.io.DatasetReader | Path | None = None,
275
+ in_packets: gpd.GeoDataFrame | Path | None = None,
276
+ ) -> tuple[gpd.GeoDataFrame, gpd.GeoDataFrame]:
277
+ ```
278
+
279
+ <!-- blacken-docs:on -->
280
+
281
+ `calculate_ridge_metrics()` takes the transects, ridges, ridge area raster, DEM,
282
+ and packets (optional) as input and returns 2 `GeoDataFrame`s as output:
283
+
284
+ 1. `rich_transects`
285
+ - transect geometries with 1D arrays of the DEM and Ridge Area Raster data
286
+ sampled along each transect.
287
+ 2. `itx_metrics`
288
+ - intersection points of all input ridges and transects with the ridge
289
+ metrics (amplitude, width, and spacing) as attributes
290
+
291
+ To calculate these metrics, `calculate_ridge_metrics()` creates a
292
+ `BendDataExtractor` instance to orchestrate all calculations for the bend. The
293
+ `BendDataExtractor` creates a `TransectDataExtractor` instance for each transect
294
+ in the bend, and each `TransectDataExtractor` creates a `RidgeDataExtractor`
295
+ instance for each intersection that transect has with a ridge.
296
+
297
+ The `BendDataExtractor` has two methods, `calc_transect_metrics()` and
298
+ `calc_itx_metrics()`, called in `BendDataExtractor.__init__()`, to calculate the
299
+ two `GeoDataFrame` outputs of `calculate_ridge_metrics()` listed above.
300
+
301
+ `calc_transect_metrics()` takes a transect Polyline as input and samples the
302
+ underlying Ridge Area Raster and DEM to create 1D numpy arrays representing
303
+ ridge presence and ridge profile along the transect. These 1D arrays as well as
304
+ other metrics are saved for each transect as a `GeoDataFrame` in
305
+ `BendDataExtractor.rich_transects` and returned as the `rich_transects` for
306
+ `calculate_ridge_metrics()`.
307
+
308
+ `calc_itx_metrics()` then iterates through `self.rich_transects`, and creates a
309
+ `TransectDataExtractor` instance for each row.
310
+ `TransectDataExtractor.__init__()` then executes a series of methods to process
311
+ the 1D raster signals, assign transect IDs, and create the intersection
312
+ (hereafter 'itx') points between ridges and transects.
313
+
314
+ A 3-vertex transect substring is created for each itx point along a transect.
315
+ The middle vertex of this substring is the 'current' itx point and the first and
316
+ third vertices are the itx points that proceed and follow the 'current' itx
317
+ point along the transect.
318
+
319
+ The vertices of these transect substrings are then stored in the instance
320
+ variable `self.itx_gdf` (type:`GeoDataFrame`) of `TransectDataExtractor` along
321
+ with other data required for ridge metric calculation such as the 1D array
322
+ segments that correspond to the substring.
323
+
324
+ Then `TransectDataExtractor.calc_ridge_metrics()` is called to iterate through
325
+ `self.itx_gdf` and creates a `RidgeDataExtractor` instance for each
326
+ row.`RidgeDataExtractor.__init__()` then executes a series of preprocessing
327
+ methods for the substring geometry and binary signal as well as calculating the
328
+ ridge metrics: amplitude, width, and spacing.
329
+
330
+ > NOTE: The function used to calculate ridge amplitude, `calc_ridge_amps()` is
331
+ > imported from `ridge_amplitude.py`. `calc_ridge_amps()` finds the maximum
332
+ > elevation value for each ridge area and the minimum elevation value for each
333
+ > swale area in the 1D arrays. Then, depending on the relative number ridge and
334
+ > swales found in the signal, `ridge_amplitude.determine_complex_strategy()` is
335
+ > used to determine the appropriate strategy to calculate ridge amplitude for
336
+ > the given intersection and return the corresponding function.
337
+
338
+ After ridge metric calculation, `RidgeDataExtractor.dump_data()` is called to
339
+ dump all data calculated at the intersection-scale as a `dict`. This `dict` is
340
+ then used to fill out `TransectDataExtractor.itx_gdf` which is ultimately
341
+ returned by `TransectDataExtractor.calc_ridge_metrics()` as the container for
342
+ all itx metrics for a given transect. These itx metrics for each transect are
343
+ then concatenated into one `GeoDataFrame` for the entire bend and stored in
344
+ `BendDataExtractor.itx_metrics` which is ultimately returned as `itx_metrics`
345
+ for `calculate_ridge_metrics()`
346
+
347
+ ## 4. Data Stores
348
+
349
+ The example dataset used in project docs and tests is included in the
350
+ [example_data/input](example_data/input/) folder.
351
+
352
+ No other external data is used.
353
+
354
+ ## 5. External Integrations / APIs
355
+
356
+ No external integrations / APIs are used.
357
+
358
+ ## 6. Deployment and Infrastructure
359
+
360
+ All CI/CD is ran through GitHub Actions. ScrollStats is distributed to PyPI (for
361
+ download with `pip`) and on conda-forge (for download with `conda`).
362
+
363
+ Docs are built and hosted on
364
+ [readthedocs.io](https://scrollstats.readthedocs.io/en/latest/).
365
+
366
+ ## 7. Security Considerations
367
+
368
+ ScrollStats is configured with
369
+ [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) to distribute
370
+ releases to PyPI.
371
+
372
+ ## 8. Development & Testing
373
+
374
+ See [README](README.md) for steps to get started developing with ScrollStats.
375
+
376
+ Once your development environment is set up, use
377
+ [`nox`](https://nox.thea.codes/en/stable/) for local development and testing.
378
+ See [noxfile.py](noxfile.py) for configuration details. Once you are happy with
379
+ your commits, simply run the command
380
+
381
+ ```
382
+ nox
383
+ ```
384
+
385
+ to lint and test your edits all in their own virtual environment.
386
+
387
+ ## 9. Future Considerations
388
+
389
+ See the project [roadmap](https://github.com/users/a-vanderheiden/projects/5)
390
+ for current work in progress and future considerations.
391
+
392
+ ## 10. Project Identification
393
+
394
+ **Project Name:** ScrollStats
395
+
396
+ **Repository URL:** https://github.com/tamu-edu/scrollstats
397
+
398
+ **Primary Contact/Team:** Andrew Vanderheiden | andrewloyd19@gmail.com
399
+
400
+ ## 11. Glossary / Acronyms
401
+
402
+ RST: Ridge and Swale Topography
403
+
404
+ DEM: Digital Elevation Model
405
+
406
+ H74: referencing the paper
407
+ [The Development of Meanders in Natural River-Channels (Hickin 1974)](https://doi.org/10.2475/ajs.274.4.414)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scrollstats
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: An open-source python library to calculate and extract morphometrics from scroll bar floodplains
5
5
  Project-URL: Homepage, https://github.com/tamu-edu/scrollstats
6
6
  Project-URL: Bug Tracker, https://github.com/tamu-edu/scrollstats/issues
@@ -20,12 +20,13 @@ Classifier: Programming Language :: Python :: 3.10
20
20
  Classifier: Programming Language :: Python :: 3.11
21
21
  Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
23
24
  Classifier: Topic :: Scientific/Engineering
24
25
  Classifier: Typing :: Typed
25
26
  Requires-Python: >=3.10
26
27
  Requires-Dist: geopandas
28
+ Requires-Dist: lazy-loader
27
29
  Requires-Dist: matplotlib
28
- Requires-Dist: nptyping
29
30
  Requires-Dist: numba
30
31
  Requires-Dist: rasterio
31
32
  Requires-Dist: scipy
@@ -78,6 +79,30 @@ Description-Content-Type: text/markdown
78
79
  An open-source python library to calculate and extract morphometrics from
79
80
  scrollbar floodplains.
80
81
 
82
+ For further reading on the rationale behind ScrollStats and practical examples
83
+ demonstrating how to use ScrollStats, check out the documentation here:
84
+ https://scrollstats.readthedocs.io/en/latest
85
+
86
+ ## Introduction
87
+
88
+ Scroll bar floodplains, often characterized by a series undulating ridges and
89
+ swales, exhibit a distinct geomorphic signature that can be seen in
90
+ high-resolution digital elevation models (DEMs). These fine-scale topographic
91
+ undulations reflect the dynamic balance between sediment deposition and
92
+ erosional scouring associated with the fluvial processes along the inner bank at
93
+ the time of their formation. DEM-based analyses of scroll bar floodplains
94
+ provide critical insight into the temporal evolution of channel morphology and
95
+ the underlying hydrologic and sedimentary controls that govern riverine
96
+ landscape change.
97
+
98
+ The figure below is a visualization of the DEM of
99
+ [a bend on the Lower Brazos River, TX](https://www.google.com/maps/@30.614411,-96.499508,16z).
100
+ We can clearly see the undulations in the DEM caused by the river migration as
101
+ well as features such as cross-channels formed by water flow through the
102
+ floodplain.
103
+
104
+ ![Image of a Bend in Brazos River](./img/bend-dem.png)
105
+
81
106
  ## Getting Started
82
107
 
83
108
  If you intend to use ScrollStats for your own analysis, follow the User
@@ -88,12 +113,20 @@ Installation instructions.
88
113
 
89
114
  ### User Installation
90
115
 
91
- Simply install ScrollStats to your python environment with `pip`
116
+ Install ScrollStats with `pip`
92
117
 
93
118
  ```shell
94
119
  python -m pip install scrollstats
95
120
  ```
96
121
 
122
+ Or install with `conda`
123
+
124
+ ```shell
125
+ conda install scrollstats
126
+ ```
127
+
128
+ > NOTE: scrollstats v0.1.3 must be installed with python <=3.12 when using conda
129
+
97
130
  ### Developer Installation
98
131
 
99
132
  First, clone the repo locally, create a virtual environment for the project,
@@ -29,6 +29,30 @@
29
29
  An open-source python library to calculate and extract morphometrics from
30
30
  scrollbar floodplains.
31
31
 
32
+ For further reading on the rationale behind ScrollStats and practical examples
33
+ demonstrating how to use ScrollStats, check out the documentation here:
34
+ https://scrollstats.readthedocs.io/en/latest
35
+
36
+ ## Introduction
37
+
38
+ Scroll bar floodplains, often characterized by a series undulating ridges and
39
+ swales, exhibit a distinct geomorphic signature that can be seen in
40
+ high-resolution digital elevation models (DEMs). These fine-scale topographic
41
+ undulations reflect the dynamic balance between sediment deposition and
42
+ erosional scouring associated with the fluvial processes along the inner bank at
43
+ the time of their formation. DEM-based analyses of scroll bar floodplains
44
+ provide critical insight into the temporal evolution of channel morphology and
45
+ the underlying hydrologic and sedimentary controls that govern riverine
46
+ landscape change.
47
+
48
+ The figure below is a visualization of the DEM of
49
+ [a bend on the Lower Brazos River, TX](https://www.google.com/maps/@30.614411,-96.499508,16z).
50
+ We can clearly see the undulations in the DEM caused by the river migration as
51
+ well as features such as cross-channels formed by water flow through the
52
+ floodplain.
53
+
54
+ ![Image of a Bend in Brazos River](./img/bend-dem.png)
55
+
32
56
  ## Getting Started
33
57
 
34
58
  If you intend to use ScrollStats for your own analysis, follow the User
@@ -39,12 +63,20 @@ Installation instructions.
39
63
 
40
64
  ### User Installation
41
65
 
42
- Simply install ScrollStats to your python environment with `pip`
66
+ Install ScrollStats with `pip`
43
67
 
44
68
  ```shell
45
69
  python -m pip install scrollstats
46
70
  ```
47
71
 
72
+ Or install with `conda`
73
+
74
+ ```shell
75
+ conda install scrollstats
76
+ ```
77
+
78
+ > NOTE: scrollstats v0.1.3 must be installed with python <=3.12 when using conda
79
+
48
80
  ### Developer Installation
49
81
 
50
82
  First, clone the repo locally, create a virtual environment for the project,
@@ -139,7 +139,7 @@ ridge_metrics_w_packets.columns = ridge_metrics_w_packets.columns.rename("metric
139
139
  ```python
140
140
  # Plot ridge amplitudes at intersections
141
141
  # Plot itx
142
- fig, ax = plt.subplots(1, 1, figsize=(10, 6))
142
+ fig, ax = plt.subplots(1, 1, figsize=(8, 5))
143
143
 
144
144
  ridges.plot(ax=ax, color="k", ls="--", lw=0.5, zorder=0)
145
145
  transects.plot(ax=ax, color="k", lw=1, zorder=1)
@@ -154,6 +154,8 @@ itx_w_packets.plot(
154
154
  )
155
155
 
156
156
  ax.set_title("Ridge amplitude at each intersection")
157
+
158
+ plt.tight_layout()
157
159
  ```
158
160
 
159
161
  ![ridge_amplitude.png](../img/ridge_amplitude.png)