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.
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/workflows/cd.yml +3 -3
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/workflows/ci.yml +6 -6
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/workflows/draft-pdf.yml +2 -2
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.readthedocs.yaml +3 -0
- scrollstats-0.1.4/ARCHITECTURE.md +407 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/PKG-INFO +36 -3
- {scrollstats-0.1.2 → scrollstats-0.1.4}/README.md +33 -1
- {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/CalculateRidgeMetrics.md +3 -1
- scrollstats-0.1.4/docs/CreateVectorDatasets.md +317 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/DelineateRidgeAreas.md +20 -10
- {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/index.md +40 -2
- scrollstats-0.1.4/img/agreement_raster.png +0 -0
- scrollstats-0.1.4/img/agreement_v_denoised.png +0 -0
- scrollstats-0.1.4/img/bend-dem.png +0 -0
- scrollstats-0.1.4/img/bend_geometry.png +0 -0
- scrollstats-0.1.4/img/binary_classification.png +0 -0
- scrollstats-0.1.4/img/dem_v_agreement.png +0 -0
- scrollstats-0.1.4/img/migration_pathways.png +0 -0
- scrollstats-0.1.4/img/pc_v_dem_v_rt.png +0 -0
- scrollstats-0.1.4/img/ridge_amplitude.png +0 -0
- scrollstats-0.1.4/img/ridges_and_centerline.png +0 -0
- scrollstats-0.1.4/img/smoothed_ridges.png +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/noxfile.py +5 -0
- scrollstats-0.1.4/paper/paper.bib +121 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/paper.md +75 -8
- {scrollstats-0.1.2 → scrollstats-0.1.4}/pyproject.toml +4 -2
- scrollstats-0.1.4/scripts/calc_ridge_metrics.py +76 -0
- scrollstats-0.1.4/scripts/create_vector_data.py +149 -0
- scrollstats-0.1.4/scripts/delineate_ridge_area_raster.py +91 -0
- scrollstats-0.1.4/scripts/plots/calc_ridge_metrics.py +80 -0
- scrollstats-0.1.4/scripts/plots/create_vector_datasets.py +140 -0
- scrollstats-0.1.4/scripts/plots/delineate_ridge_areas.py +200 -0
- scrollstats-0.1.4/src/scrollstats/__init__.py +40 -0
- scrollstats-0.1.4/src/scrollstats/_version.py +24 -0
- scrollstats-0.1.4/src/scrollstats/delineation/__init__.py +16 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/array_types.py +7 -5
- scrollstats-0.1.4/src/scrollstats/ridge_metrics/__init__.py +17 -0
- scrollstats-0.1.4/src/scrollstats/transecting/__init__.py +9 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/tests/test_core.py +39 -0
- scrollstats-0.1.2/docs/CreateVectorDatasets.md +0 -329
- scrollstats-0.1.2/img/agreement_raster.png +0 -0
- scrollstats-0.1.2/img/agreement_v_denoised.png +0 -0
- scrollstats-0.1.2/img/bend_geometry.png +0 -0
- scrollstats-0.1.2/img/binary_classification.png +0 -0
- scrollstats-0.1.2/img/dem_v_agreement.png +0 -0
- scrollstats-0.1.2/img/migration_pathways.png +0 -0
- scrollstats-0.1.2/img/pc_v_dem_v_rt.png +0 -0
- scrollstats-0.1.2/img/ridge_amplitude.png +0 -0
- scrollstats-0.1.2/img/ridges_and_centerline.png +0 -0
- scrollstats-0.1.2/img/smoothed_ridges.png +0 -0
- scrollstats-0.1.2/paper/paper.bib +0 -45
- scrollstats-0.1.2/src/scrollstats/__init__.py +0 -39
- scrollstats-0.1.2/src/scrollstats/_version.py +0 -34
- scrollstats-0.1.2/src/scrollstats/delineation/__init__.py +0 -13
- scrollstats-0.1.2/src/scrollstats/ridge_metrics/__init__.py +0 -18
- scrollstats-0.1.2/src/scrollstats/transecting/__init__.py +0 -8
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.git_archival.txt +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.gitattributes +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/CONTRIBUTING.md +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/dependabot.yml +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.github/release.yml +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.gitignore +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/.pre-commit-config.yaml +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/LICENSE +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/CompleteScrollStatsWorkflow.md +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/docs/conf.py +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_bend.geojson +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_cl.geojson +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_dem.tif +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_packets.geojson +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/example_data/input/LBR_025_ridges_manual.geojson +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/img/README.md +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/img/migration_pathway.png +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/figs/digitized_dem.png +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/figs/example_metric.png +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/figs/example_output.png +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/paper/figs/example_transect.png +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/_version.pyi +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/line_smoother.py +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/raster_classifiers.py +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/raster_denoisers.py +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/delineation/ridge_area_raster.py +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/py.typed +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/ridge_metrics/calc_ridge_metrics.py +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/ridge_metrics/data_extractors.py +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/ridge_metrics/ridge_amplitude.py +0 -0
- {scrollstats-0.1.2 → scrollstats-0.1.4}/src/scrollstats/transecting/transect.py +0 -0
- {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@
|
|
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@
|
|
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@
|
|
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@
|
|
24
|
+
- uses: actions/checkout@v6
|
|
25
25
|
with:
|
|
26
26
|
fetch-depth: 0
|
|
27
|
-
- uses: actions/setup-python@
|
|
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@
|
|
47
|
+
- uses: actions/checkout@v6
|
|
48
48
|
with:
|
|
49
49
|
fetch-depth: 0
|
|
50
50
|
|
|
51
|
-
- uses: actions/setup-python@
|
|
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@
|
|
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@
|
|
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@
|
|
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.
|
|
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
|
+

|
|
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
|
-
|
|
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
|
+

|
|
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
|
-
|
|
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=(
|
|
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
|

|