eo-processor 0.2.0__cp312-cp312-manylinux_2_34_x86_64.whl
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.
Potentially problematic release.
This version of eo-processor might be problematic. Click here for more details.
- eo_processor/__init__.py +93 -0
- eo_processor/__init__.pyi +19 -0
- eo_processor/_core.cpython-312-x86_64-linux-gnu.so +0 -0
- eo_processor-0.2.0.dist-info/METADATA +288 -0
- eo_processor-0.2.0.dist-info/RECORD +7 -0
- eo_processor-0.2.0.dist-info/WHEEL +4 -0
- eo_processor-0.2.0.dist-info/licenses/LICENSE +21 -0
eo_processor/__init__.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
High-performance Earth Observation processing library.
|
|
3
|
+
|
|
4
|
+
This library provides Rust-accelerated functions for common EO/geospatial
|
|
5
|
+
computations that can be used within XArray/Dask workflows to bypass Python's GIL.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ._core import (
|
|
9
|
+
normalized_difference as _normalized_difference,
|
|
10
|
+
ndvi as _ndvi,
|
|
11
|
+
ndwi as _ndwi,
|
|
12
|
+
enhanced_vegetation_index as _enhanced_vegetation_index,
|
|
13
|
+
median as _median,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__version__ = "0.1.0"
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"normalized_difference",
|
|
20
|
+
"ndvi",
|
|
21
|
+
"ndwi",
|
|
22
|
+
"enhanced_vegetation_index",
|
|
23
|
+
"evi",
|
|
24
|
+
"median",
|
|
25
|
+
"composite",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def normalized_difference(a, b):
|
|
30
|
+
"""
|
|
31
|
+
Compute normalized difference (a - b) / (a + b) using the Rust core.
|
|
32
|
+
Supports 1D or 2D numpy float arrays; dimensional dispatch occurs in Rust.
|
|
33
|
+
"""
|
|
34
|
+
return _normalized_difference(a, b)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def ndvi(nir, red):
|
|
38
|
+
"""
|
|
39
|
+
Compute NDVI = (NIR - Red) / (NIR + Red) via Rust core (1D or 2D).
|
|
40
|
+
"""
|
|
41
|
+
return _ndvi(nir, red)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def ndwi(green, nir):
|
|
45
|
+
"""
|
|
46
|
+
Compute NDWI = (Green - NIR) / (Green + NIR) via Rust core (1D or 2D).
|
|
47
|
+
"""
|
|
48
|
+
return _ndwi(green, nir)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def enhanced_vegetation_index(nir, red, blue):
|
|
52
|
+
"""
|
|
53
|
+
Compute EVI = 2.5 * (NIR - Red) / (NIR + 6*Red - 7.5*Blue + 1) via Rust core (1D or 2D).
|
|
54
|
+
"""
|
|
55
|
+
return _enhanced_vegetation_index(nir, red, blue)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Alias
|
|
59
|
+
evi = enhanced_vegetation_index
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def median(arr, skip_na=True):
|
|
63
|
+
"""
|
|
64
|
+
Compute median over the time axis of a 1D, 2D, 3D, or 4D array.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
arr : numpy.ndarray
|
|
69
|
+
Input array.
|
|
70
|
+
skip_na : bool, optional
|
|
71
|
+
Whether to skip NaN values, by default True. If False, the median
|
|
72
|
+
of any pixel containing a NaN will be NaN.
|
|
73
|
+
"""
|
|
74
|
+
return _median(arr, skip_na=skip_na)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def composite(arr, method="median", **kwargs):
|
|
78
|
+
"""
|
|
79
|
+
Compute a composite over the time axis of a 1D, 2D, 3D, or 4D array.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
arr : numpy.ndarray
|
|
84
|
+
Input array.
|
|
85
|
+
method : str, optional
|
|
86
|
+
The compositing method to use, by default "median".
|
|
87
|
+
**kwargs
|
|
88
|
+
Additional keyword arguments to pass to the compositing function.
|
|
89
|
+
"""
|
|
90
|
+
if method == "median":
|
|
91
|
+
return median(arr, **kwargs)
|
|
92
|
+
else:
|
|
93
|
+
raise ValueError(f"Unknown composite method: {method}")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Type stubs for eo_processor"""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from numpy.typing import NDArray
|
|
5
|
+
|
|
6
|
+
__version__: str
|
|
7
|
+
|
|
8
|
+
def normalized_difference(
|
|
9
|
+
a: NDArray[np.float64], b: NDArray[np.float64]
|
|
10
|
+
) -> NDArray[np.float64]: ...
|
|
11
|
+
def ndvi(nir: NDArray[np.float64], red: NDArray[np.float64]) -> NDArray[np.float64]: ...
|
|
12
|
+
def ndwi(
|
|
13
|
+
green: NDArray[np.float64], nir: NDArray[np.float64]
|
|
14
|
+
) -> NDArray[np.float64]: ...
|
|
15
|
+
def enhanced_vegetation_index(
|
|
16
|
+
nir: NDArray[np.float64], red: NDArray[np.float64], blue: NDArray[np.float64]
|
|
17
|
+
) -> NDArray[np.float64]: ...
|
|
18
|
+
|
|
19
|
+
evi = enhanced_vegetation_index
|
|
Binary file
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: eo-processor
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
13
|
+
Requires-Dist: maturin>=1.9.6
|
|
14
|
+
Requires-Dist: numpy>=1.20.0
|
|
15
|
+
Requires-Dist: tox>=4.25.0
|
|
16
|
+
Requires-Dist: pytest>=7.0 ; extra == 'dev'
|
|
17
|
+
Requires-Dist: maturin>=1.0,<2.0 ; extra == 'dev'
|
|
18
|
+
Requires-Dist: dask[array]>=2023.0.0 ; extra == 'dask'
|
|
19
|
+
Requires-Dist: xarray>=2023.0.0 ; extra == 'dask'
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Provides-Extra: dask
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Summary: High-performance Rust UDFs for Earth Observation processing
|
|
24
|
+
Keywords: earth-observation,gis,remote-sensing,ndvi,dask,xarray,rust,pyo3,numpy
|
|
25
|
+
Author: Ben Smith
|
|
26
|
+
Requires-Python: >=3.8
|
|
27
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
28
|
+
|
|
29
|
+
# eo-processor
|
|
30
|
+
|
|
31
|
+
[](#test-coverage)
|
|
32
|
+
|
|
33
|
+
High-performance Rust UDFs for Earth Observation (EO) processing with Python bindings.
|
|
34
|
+
|
|
35
|
+
## Overview
|
|
36
|
+
|
|
37
|
+
`eo-processor` is a framework that provides Rust-based User Defined Functions (UDFs) for common Earth Observation and geospatial computations. These functions can be used within local and remote (Dask/Kubernetes) workflows, leveraging PyO3 to create highly efficient and optimized operations.
|
|
38
|
+
|
|
39
|
+
The Rust implementation bypasses Python's Global Interpreter Lock (GIL), making it ideal for:
|
|
40
|
+
- Long-running computations on large satellite imagery
|
|
41
|
+
- Parallel processing with Dask
|
|
42
|
+
- XArray `apply_ufunc` and `map_blocks` workflows
|
|
43
|
+
- CPU-intensive geospatial operations
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- **High Performance**: Rust-accelerated computations that bypass Python's GIL
|
|
48
|
+
- **Easy Integration**: Works seamlessly with NumPy, XArray, and Dask
|
|
49
|
+
- **Common EO Indices**: Pre-implemented functions for NDVI, NDWI, and generic normalized differences
|
|
50
|
+
- **Type Safe**: Full type hints for Python IDE support
|
|
51
|
+
- **Flexible**: Supports both 1D and 2D arrays with automatic dimension detection
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
### From Source
|
|
56
|
+
|
|
57
|
+
Requirements:
|
|
58
|
+
- Python 3.8+
|
|
59
|
+
- Rust toolchain (install from [rustup.rs](https://rustup.rs/))
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Install maturin for building
|
|
63
|
+
pip install maturin
|
|
64
|
+
|
|
65
|
+
# Build and install the package
|
|
66
|
+
maturin develop --release
|
|
67
|
+
|
|
68
|
+
# Or build wheel for distribution
|
|
69
|
+
maturin build --release
|
|
70
|
+
pip install target/wheels/*.whl
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
### Basic Usage
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import numpy as np
|
|
79
|
+
from eo_processor import ndvi, ndwi, normalized_difference
|
|
80
|
+
|
|
81
|
+
# Compute NDVI from NIR and Red bands
|
|
82
|
+
nir = np.array([0.8, 0.7, 0.6])
|
|
83
|
+
red = np.array([0.2, 0.1, 0.3])
|
|
84
|
+
ndvi_result = ndvi(nir, red)
|
|
85
|
+
print(ndvi_result) # [0.6, 0.75, 0.33333333]
|
|
86
|
+
|
|
87
|
+
# Note: the functions in `eo_processor` now return NumPy arrays directly.
|
|
88
|
+
# They no longer return a (array, dims) tuple or any dims metadata.
|
|
89
|
+
|
|
90
|
+
# Works with 2D arrays (images)
|
|
91
|
+
nir_image = np.random.rand(1000, 1000)
|
|
92
|
+
red_image = np.random.rand(1000, 1000)
|
|
93
|
+
ndvi_image = ndvi(nir_image, red_image)
|
|
94
|
+
|
|
95
|
+
# Compute NDWI (water index)
|
|
96
|
+
green = np.array([0.3, 0.4, 0.5])
|
|
97
|
+
ndwi_result = ndwi(green, nir)
|
|
98
|
+
|
|
99
|
+
# Generic normalized difference: (a - b) / (a + b)
|
|
100
|
+
custom_index = normalized_difference(band_a, band_b)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### XArray Integration
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import xarray as xr
|
|
107
|
+
from eo_processor import ndvi
|
|
108
|
+
|
|
109
|
+
# Create XArray DataArrays
|
|
110
|
+
nir = xr.DataArray(nir_data, dims=["y", "x"])
|
|
111
|
+
red = xr.DataArray(red_data, dims=["y", "x"])
|
|
112
|
+
|
|
113
|
+
# Apply using xr.apply_ufunc
|
|
114
|
+
ndvi_result = xr.apply_ufunc(
|
|
115
|
+
ndvi,
|
|
116
|
+
nir,
|
|
117
|
+
red,
|
|
118
|
+
dask="parallelized",
|
|
119
|
+
output_dtypes=[float],
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Dask Integration (Parallel Processing)
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
import dask.array as da
|
|
127
|
+
import xarray as xr
|
|
128
|
+
from eo_processor import ndvi
|
|
129
|
+
|
|
130
|
+
# Create large Dask arrays (chunked for parallel processing)
|
|
131
|
+
nir_dask = da.random.random((10000, 10000), chunks=(1000, 1000))
|
|
132
|
+
red_dask = da.random.random((10000, 10000), chunks=(1000, 1000))
|
|
133
|
+
|
|
134
|
+
# Wrap in XArray
|
|
135
|
+
nir_xr = xr.DataArray(nir_dask, dims=["y", "x"])
|
|
136
|
+
red_xr = xr.DataArray(red_dask, dims=["y", "x"])
|
|
137
|
+
|
|
138
|
+
# Compute NDVI (bypasses GIL, enables true parallelism)
|
|
139
|
+
ndvi_result = xr.apply_ufunc(
|
|
140
|
+
ndvi,
|
|
141
|
+
nir_xr,
|
|
142
|
+
red_xr,
|
|
143
|
+
dask="parallelized",
|
|
144
|
+
output_dtypes=[float],
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Compute result
|
|
148
|
+
ndvi_computed = ndvi_result.compute()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Using map_blocks with Dask
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
import dask.array as da
|
|
155
|
+
from eo_processor import ndvi
|
|
156
|
+
|
|
157
|
+
nir_dask = da.random.random((5000, 5000), chunks=(500, 500))
|
|
158
|
+
red_dask = da.random.random((5000, 5000), chunks=(500, 500))
|
|
159
|
+
|
|
160
|
+
# Apply to blocks (each block processed independently)
|
|
161
|
+
ndvi_result = da.map_blocks(
|
|
162
|
+
ndvi,
|
|
163
|
+
nir_dask,
|
|
164
|
+
red_dask,
|
|
165
|
+
dtype=np.float64,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
result = ndvi_result.compute()
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Available Functions
|
|
172
|
+
|
|
173
|
+
### Normalized Difference Functions
|
|
174
|
+
|
|
175
|
+
- `normalized_difference(a, b)`: Generic normalized difference `(a - b) / (a + b)`
|
|
176
|
+
- `normalized_difference_1d(a, b)`: 1D version
|
|
177
|
+
- `normalized_difference_2d(a, b)`: 2D version
|
|
178
|
+
|
|
179
|
+
### Vegetation Indices
|
|
180
|
+
|
|
181
|
+
- `ndvi(nir, red)`: Normalized Difference Vegetation Index
|
|
182
|
+
- `ndvi_1d(nir, red)`: 1D version
|
|
183
|
+
- `ndvi_2d(nir, red)`: 2D version
|
|
184
|
+
|
|
185
|
+
### Water Indices
|
|
186
|
+
|
|
187
|
+
- `ndwi(green, nir)`: Normalized Difference Water Index
|
|
188
|
+
- `ndwi_1d(green, nir)`: 1D version
|
|
189
|
+
- `ndwi_2d(green, nir)`: 2D version
|
|
190
|
+
|
|
191
|
+
## Performance
|
|
192
|
+
|
|
193
|
+
The Rust implementation provides significant performance improvements over pure Python/NumPy, especially for large arrays:
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
import numpy as np
|
|
197
|
+
import time
|
|
198
|
+
from eo_processor import ndvi
|
|
199
|
+
|
|
200
|
+
# Large array
|
|
201
|
+
nir = np.random.rand(5000, 5000)
|
|
202
|
+
red = np.random.rand(5000, 5000)
|
|
203
|
+
|
|
204
|
+
# Rust implementation
|
|
205
|
+
start = time.time()
|
|
206
|
+
result_rust = ndvi(nir, red)
|
|
207
|
+
time_rust = time.time() - start
|
|
208
|
+
|
|
209
|
+
# NumPy implementation
|
|
210
|
+
start = time.time()
|
|
211
|
+
result_numpy = (nir - red) / (nir + red)
|
|
212
|
+
time_numpy = time.time() - start
|
|
213
|
+
|
|
214
|
+
print(f"Rust: {time_rust:.4f}s")
|
|
215
|
+
print(f"NumPy: {time_numpy:.4f}s")
|
|
216
|
+
print(f"Speedup: {time_numpy/time_rust:.2f}x")
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Development
|
|
220
|
+
|
|
221
|
+
### Building
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# Development build
|
|
225
|
+
maturin develop
|
|
226
|
+
|
|
227
|
+
# Release build
|
|
228
|
+
maturin develop --release
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Testing
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
# Run Rust tests
|
|
235
|
+
cargo test
|
|
236
|
+
|
|
237
|
+
# Run Python tests (if pytest is installed)
|
|
238
|
+
pytest
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Running Examples
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Basic usage examples
|
|
245
|
+
python examples/basic_usage.py
|
|
246
|
+
|
|
247
|
+
# XArray/Dask examples (requires: pip install eo-processor[dask])
|
|
248
|
+
python examples/xarray_dask_usage.py
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Why Rust + PyO3?
|
|
252
|
+
|
|
253
|
+
1. **Performance**: Rust provides C-level performance with memory safety
|
|
254
|
+
2. **GIL-Free**: Rust code releases the Python GIL, enabling true parallelism
|
|
255
|
+
3. **Type Safety**: Compile-time guarantees reduce runtime errors
|
|
256
|
+
4. **Easy Integration**: PyO3 makes it seamless to call Rust from Python
|
|
257
|
+
5. **Modern Tooling**: Cargo and maturin provide excellent development experience
|
|
258
|
+
|
|
259
|
+
## Use Cases
|
|
260
|
+
|
|
261
|
+
- Processing large satellite imagery datasets (Sentinel, Landsat, etc.)
|
|
262
|
+
- Real-time vegetation monitoring using NDVI
|
|
263
|
+
- Water body detection using NDWI
|
|
264
|
+
- Custom spectral indices computation
|
|
265
|
+
- Distributed processing on Dask/Kubernetes clusters
|
|
266
|
+
- Time-series analysis of Earth Observation data
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
MIT License - see LICENSE file for details
|
|
271
|
+
|
|
272
|
+
## Contributing
|
|
273
|
+
|
|
274
|
+
Contributions are welcome! Please feel free to submit issues or pull requests.
|
|
275
|
+
|
|
276
|
+
## Citation
|
|
277
|
+
|
|
278
|
+
If you use this library in your research, please cite:
|
|
279
|
+
|
|
280
|
+
```bibtex
|
|
281
|
+
@software{eo_processor,
|
|
282
|
+
title = {eo-processor: High-performance Rust UDFs for Earth Observation},
|
|
283
|
+
author = {Ben},
|
|
284
|
+
year = {2025},
|
|
285
|
+
url = {https://github.com/BnJam/eo-processor}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
eo_processor-0.2.0.dist-info/METADATA,sha256=x2HtqNfyza5k3j8ZUNvhkXgTAxZONc2QFjGn54lEf1s,7544
|
|
2
|
+
eo_processor-0.2.0.dist-info/WHEEL,sha256=6SMSE7o6zBbAY_fbEHyYtLCyWUTWMuzw9qWPgdJzl40,109
|
|
3
|
+
eo_processor-0.2.0.dist-info/licenses/LICENSE,sha256=noiI0CshqVqEhzZuL0pDreo0XgmzA3P69UjdcQAXGuc,1060
|
|
4
|
+
eo_processor/__init__.py,sha256=LK59qvugof6T_veNozmgvMWY-ajxn04zCXvEzsaBxrw,2277
|
|
5
|
+
eo_processor/__init__.pyi,sha256=ayia_pbvDt3d30CBPMhQl-PtjkEGjUPcBfFgN1ulU4k,576
|
|
6
|
+
eo_processor/_core.cpython-312-x86_64-linux-gnu.so,sha256=_fvMoKte9rgmRLz8gA9YqEfshGHkzQzsB1Et5hoo-c4,591656
|
|
7
|
+
eo_processor-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ben
|
|
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.
|