rusterize 0.3.0__cp311-abi3-macosx_11_0_arm64.whl → 0.4.0__cp311-abi3-macosx_11_0_arm64.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 rusterize might be problematic. Click here for more details.
- rusterize/__init__.py +117 -1
- rusterize/rusterize.abi3.so +0 -0
- {rusterize-0.3.0.dist-info → rusterize-0.4.0.dist-info}/METADATA +33 -29
- rusterize-0.4.0.dist-info/RECORD +6 -0
- {rusterize-0.3.0.dist-info → rusterize-0.4.0.dist-info}/WHEEL +1 -1
- rusterize/core.py +0 -107
- rusterize-0.3.0.dist-info/RECORD +0 -7
- {rusterize-0.3.0.dist-info → rusterize-0.4.0.dist-info}/licenses/LICENSE +0 -0
rusterize/__init__.py
CHANGED
|
@@ -1,4 +1,120 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import importlib.metadata
|
|
2
|
-
|
|
3
|
+
|
|
4
|
+
from types import NoneType
|
|
5
|
+
from typing import List, Tuple
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import polars as pl
|
|
9
|
+
from geopandas import GeoDataFrame
|
|
10
|
+
import rioxarray
|
|
11
|
+
from xarray import DataArray
|
|
12
|
+
from .rusterize import _rusterize
|
|
3
13
|
|
|
4
14
|
__version__ = importlib.metadata.version("rusterize")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def rusterize(
|
|
18
|
+
gdf: GeoDataFrame,
|
|
19
|
+
res: Tuple | List | None = None,
|
|
20
|
+
out_shape: Tuple | List | None = None,
|
|
21
|
+
extent: Tuple | List | None = None,
|
|
22
|
+
field: str | None = None,
|
|
23
|
+
by: str | None = None,
|
|
24
|
+
burn: int | float | None = None,
|
|
25
|
+
fun: str = "last",
|
|
26
|
+
background: int | float | None = np.nan,
|
|
27
|
+
dtype: str = "float64",
|
|
28
|
+
) -> DataArray:
|
|
29
|
+
"""
|
|
30
|
+
Fast geopandas rasterization into xarray.DataArray
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
:param gdf: geopandas dataframe to rasterize.
|
|
34
|
+
:param res: (xres, yres) for rasterized data.
|
|
35
|
+
:param out_shape: (nrows, ncols) for regularized output shape.
|
|
36
|
+
:param extent: (xmin, xmax, ymin, ymax) for regularized extent.
|
|
37
|
+
:param field: field to rasterize, mutually exclusive with `burn`. Default is None.
|
|
38
|
+
:param by: column to rasterize, assigns each unique value to a layer in the stack based on field. Default is None.
|
|
39
|
+
:param burn: burn a value onto the raster, mutually exclusive with `field`. Default is None.
|
|
40
|
+
:param fun: pixel function to use. Available options are `sum`, `first`, `last`, `min`, `max`, `count`, or `any`. Default is `last`.
|
|
41
|
+
:param background: background value in final raster. Default is np.nan.
|
|
42
|
+
:param dtype: specify the output dtype. Default is `float64`.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Rasterized xarray.DataArray.
|
|
46
|
+
|
|
47
|
+
Notes:
|
|
48
|
+
When any of `res`, `out_shape`, or `extent` is not provided, it is inferred from the other arguments when applicable.
|
|
49
|
+
Unless `extent` is specified, a half-pixel buffer is applied to avoid missing points on the border.
|
|
50
|
+
The logics dictating the final spatial properties of the rasterized geometries follow those of GDAL.
|
|
51
|
+
|
|
52
|
+
If `field` is not in `gdf`, then a default `burn` value of 1 is rasterized.
|
|
53
|
+
|
|
54
|
+
A `None` value for `dtype` corresponds to the default of that dtype. An illegal value for a dtype will be replaced with the default of
|
|
55
|
+
that dtype. For example, a `background=np.nan` for `dtype="uint8"` will become `background=0`, where `0` is the default for `uint8`.
|
|
56
|
+
"""
|
|
57
|
+
# type checks
|
|
58
|
+
if not isinstance(gdf, GeoDataFrame):
|
|
59
|
+
raise TypeError("`gdf` must be a geopandas dataframe.")
|
|
60
|
+
if not isinstance(res, (tuple, list, NoneType)):
|
|
61
|
+
raise TypeError("`resolution` must be a tuple or list of (x, y).")
|
|
62
|
+
if not isinstance(out_shape, (tuple, list, NoneType)):
|
|
63
|
+
raise TypeError("`out_shape` must be a tuple or list of (nrows, ncols).")
|
|
64
|
+
if not isinstance(extent, (tuple, list, NoneType)):
|
|
65
|
+
raise TypeError("`extent` must be a tuple or list of (xmin, ymin, xmax, ymax).")
|
|
66
|
+
if not isinstance(field, (str, NoneType)):
|
|
67
|
+
raise TypeError("`field` must be a string column name.")
|
|
68
|
+
if not isinstance(by, (str, NoneType)):
|
|
69
|
+
raise TypeError("`by` must be a string column name.")
|
|
70
|
+
if not isinstance(burn, (int, float, NoneType)):
|
|
71
|
+
raise TypeError("`burn` must be an integer or float.")
|
|
72
|
+
if not isinstance(fun, str):
|
|
73
|
+
raise TypeError("`pixel_fn` must be one of sum, first, last, min, max, count, or any.")
|
|
74
|
+
if not isinstance(background, (int, float, NoneType)):
|
|
75
|
+
raise TypeError("`background` must be integer, float, or None.")
|
|
76
|
+
if not isinstance(dtype, str):
|
|
77
|
+
raise TypeError("`dtype` must be a one of uint8, uint16, uint32, uint64, int8, int16, int32, int64, float32, float64")
|
|
78
|
+
|
|
79
|
+
# value checks
|
|
80
|
+
if not res and not out_shape and not extent:
|
|
81
|
+
raise ValueError("One of `res`, `out_shape`, or `extent` must be provided.")
|
|
82
|
+
if extent and not res and not out_shape:
|
|
83
|
+
raise ValueError("Must also specify `res` or `out_shape` with extent.")
|
|
84
|
+
if res and (len(res) != 2 or any(r <= 0 for r in res) or any(not isinstance(r, (int, float)) for r in res)):
|
|
85
|
+
raise ValueError("Resolution must be 2 positive numbers.")
|
|
86
|
+
if out_shape and (len(out_shape) != 2 or any(s <= 0 for s in out_shape) or any(not isinstance(s, int) for s in out_shape)):
|
|
87
|
+
raise ValueError("Output shape must be 2 positive integers.")
|
|
88
|
+
if extent and len(extent) != 4:
|
|
89
|
+
raise ValueError("Extent must be 4 numbers (xmin, ymin, xmax, ymax).")
|
|
90
|
+
if field and burn:
|
|
91
|
+
raise ValueError("Only one of `field` or `burn` can be specified.")
|
|
92
|
+
|
|
93
|
+
# defaults
|
|
94
|
+
_res = res if res else (0, 0)
|
|
95
|
+
_shape = out_shape if out_shape else (0, 0)
|
|
96
|
+
(_bounds, _has_extent) = (extent, True) if extent else (gdf.total_bounds, False)
|
|
97
|
+
|
|
98
|
+
# RasterInfo
|
|
99
|
+
raster_info = {
|
|
100
|
+
"nrows": _shape[0],
|
|
101
|
+
"ncols": _shape[1],
|
|
102
|
+
"xmin": _bounds[0],
|
|
103
|
+
"ymin": _bounds[1],
|
|
104
|
+
"xmax": _bounds[2],
|
|
105
|
+
"ymax": _bounds[3],
|
|
106
|
+
"xres": _res[0],
|
|
107
|
+
"yres": _res[1],
|
|
108
|
+
"has_extent": _has_extent,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# extract columns of interest and convert to polars
|
|
112
|
+
cols = list(set([col for col in (field, by) if col]))
|
|
113
|
+
try:
|
|
114
|
+
df = pl.from_pandas(gdf[cols]) if cols else None
|
|
115
|
+
except KeyError as e:
|
|
116
|
+
raise KeyError("Column not found in GeoDataFrame") from e
|
|
117
|
+
|
|
118
|
+
# rusterize
|
|
119
|
+
r = _rusterize(gdf.geometry, raster_info, fun, df, field, by, burn, background, dtype)
|
|
120
|
+
return DataArray.from_dict(r).rio.write_crs(gdf.crs, inplace=True)
|
rusterize/rusterize.abi3.so
CHANGED
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rusterize
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Classifier: License :: OSI Approved :: MIT License
|
|
5
5
|
Classifier: Operating System :: OS Independent
|
|
6
6
|
Classifier: Programming Language :: Rust
|
|
@@ -14,7 +14,7 @@ Requires-Dist: xarray>=2025.1.1
|
|
|
14
14
|
Requires-Dist: rioxarray>=0.18.2
|
|
15
15
|
License-File: LICENSE
|
|
16
16
|
Summary: High performance rasterization tool for Python built in Rust
|
|
17
|
-
Keywords: fast,raster,geopandas,xarray
|
|
17
|
+
Keywords: rust,fast,raster,geometry,geopandas,xarray
|
|
18
18
|
Requires-Python: >=3.11
|
|
19
19
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
20
20
|
Project-URL: repository, https://github.com/ttrotto/rusterize
|
|
@@ -23,9 +23,9 @@ Project-URL: repository, https://github.com/ttrotto/rusterize
|
|
|
23
23
|
|
|
24
24
|
High performance rasterization tool for Python built in Rust. This
|
|
25
25
|
repository stems from the [fasterize](https://github.com/ecohealthalliance/fasterize.git) package built in C++
|
|
26
|
-
for R and ports parts of the logics into Python with a Rust backend, in addition to some useful improvements.
|
|
26
|
+
for R and ports parts of the logics into Python with a Rust backend, in addition to some useful improvements (see [API](#API)).
|
|
27
27
|
|
|
28
|
-
**rusterize** is designed to work on *(multi)polygons* and *(multi)linestrings
|
|
28
|
+
**rusterize** is designed to work on *(multi)polygons* and *(multi)linestrings*, even when they are nested inside complex geometry collections. Functionally, it takes an input [geopandas](https://geopandas.org/en/stable/) dataframe and returns a [xarray](https://docs.xarray.dev/en/stable/).
|
|
29
29
|
|
|
30
30
|
# Installation
|
|
31
31
|
|
|
@@ -49,7 +49,7 @@ git clone https://github.com/<username>/rusterize.git
|
|
|
49
49
|
cd rusterize
|
|
50
50
|
|
|
51
51
|
# Install the Rust nightly toolchain
|
|
52
|
-
rustup toolchain install nightly-2025-
|
|
52
|
+
rustup toolchain install nightly-2025-07-31
|
|
53
53
|
|
|
54
54
|
# Install maturin
|
|
55
55
|
pip install maturin
|
|
@@ -63,7 +63,7 @@ maturin develop --profile dist-release
|
|
|
63
63
|
This package has a simple API:
|
|
64
64
|
|
|
65
65
|
``` python
|
|
66
|
-
from rusterize
|
|
66
|
+
from rusterize import rusterize
|
|
67
67
|
|
|
68
68
|
# gdf = <import/modify dataframe as needed>
|
|
69
69
|
|
|
@@ -71,21 +71,25 @@ from rusterize.core import rusterize
|
|
|
71
71
|
rusterize(gdf,
|
|
72
72
|
res=(30, 30),
|
|
73
73
|
out_shape=(10, 10)
|
|
74
|
-
extent=(0,
|
|
74
|
+
extent=(0, 10, 10, 20)
|
|
75
75
|
field="field",
|
|
76
76
|
by="by",
|
|
77
|
+
burn=None,
|
|
77
78
|
fun="sum",
|
|
78
|
-
background=0
|
|
79
|
+
background=0,
|
|
80
|
+
dtype="uint8")
|
|
79
81
|
```
|
|
80
82
|
|
|
81
83
|
- `gdf`: geopandas dataframe to rasterize
|
|
82
|
-
- `res`:
|
|
83
|
-
- `out_shape`:
|
|
84
|
-
- `extent`:
|
|
85
|
-
- `field`:
|
|
86
|
-
- `by`: column
|
|
84
|
+
- `res`: (xres, yres) for desired resolution (default: `None`)
|
|
85
|
+
- `out_shape`: (nrows, ncols) for desired output shape (default: `None`)
|
|
86
|
+
- `extent`: (xmin, ymin, xmax, ymax) for desired output extent (default: `None`)
|
|
87
|
+
- `field`: column to rasterize. Mutually exclusive with `burn`. (default: `None` -> a value of `1` is rasterized)
|
|
88
|
+
- `by`: column for grouping. Assign each group to a band in the stack. Values are taken from `field` if specified, else `burn` is rasterized. (default: `None` -> singleband raster)
|
|
89
|
+
- `burn`: a single value to burn. Mutually exclusive with `field`. (default: `None`). If no field is found in `gdf` or if `field` is `None`, then `burn=1`
|
|
87
90
|
- `fun`: pixel function to use when multiple values overlap. Available options are `sum`, `first`, `last`, `min`, `max`, `count`, or `any`. (default: `last`)
|
|
88
|
-
- `background`: background value in final raster. (default: `np.nan`)
|
|
91
|
+
- `background`: background value in final raster. (default: `np.nan`). A `None` value corresponds to the default of the specified dtype. An illegal value for a dtype will be replaced with the default of that dtype. For example, a `background=np.nan` for `dtype="uint8"` will become `background=0`, where `0` is the default for `uint8`.
|
|
92
|
+
- `dtype`: dtype of the final raster. Possible values are `uint8`, `uint16`, `uint32`, `uint64`, `int8`, `int16`, `int32`, `int64`, `float32`, `float64` (default: `float64`)
|
|
89
93
|
|
|
90
94
|
Note that control over the desired extent is not as strict as for resolution and shape. That is,
|
|
91
95
|
when resolution, output shape, and extent are specified, priority is given to resolution and shape.
|
|
@@ -100,7 +104,7 @@ returns a dictionary that is converted to a xarray on the Python side
|
|
|
100
104
|
for simpliicty.
|
|
101
105
|
|
|
102
106
|
``` python
|
|
103
|
-
from rusterize
|
|
107
|
+
from rusterize import rusterize
|
|
104
108
|
import geopandas as gpd
|
|
105
109
|
from shapely import wkt
|
|
106
110
|
import matplotlib.pyplot as plt
|
|
@@ -110,7 +114,8 @@ geoms = [
|
|
|
110
114
|
"POLYGON ((-180 -20, -140 55, 10 0, -140 -60, -180 -20), (-150 -20, -100 -10, -110 20, -150 -20))",
|
|
111
115
|
"POLYGON ((-10 0, 140 60, 160 0, 140 -55, -10 0))",
|
|
112
116
|
"POLYGON ((-125 0, 0 60, 40 5, 15 -45, -125 0))",
|
|
113
|
-
"MULTILINESTRING ((-180 -70, -140 -50), (-140 -50, -100 -70), (-100 -70, -60 -50), (-60 -50, -20 -70), (-20 -70, 20 -50), (20 -50, 60 -70), (60 -70, 100 -50), (100 -50, 140 -70), (140 -70, 180 -50))"
|
|
117
|
+
"MULTILINESTRING ((-180 -70, -140 -50), (-140 -50, -100 -70), (-100 -70, -60 -50), (-60 -50, -20 -70), (-20 -70, 20 -50), (20 -50, 60 -70), (60 -70, 100 -50), (100 -50, 140 -70), (140 -70, 180 -50))",
|
|
118
|
+
"GEOMETRYCOLLECTION (POINT (50 -40), POLYGON ((75 -40, 75 -30, 100 -30, 100 -40, 75 -40)), LINESTRING (80 -40, 100 0), GEOMETRYCOLLECTION (POLYGON ((100 20, 100 30, 110 30, 110 20, 100 20))))"
|
|
114
119
|
]
|
|
115
120
|
|
|
116
121
|
# Convert WKT strings to Shapely geometries
|
|
@@ -124,7 +129,7 @@ output = rusterize(
|
|
|
124
129
|
gdf,
|
|
125
130
|
res=(1, 1),
|
|
126
131
|
field="value",
|
|
127
|
-
fun="sum"
|
|
132
|
+
fun="sum",
|
|
128
133
|
).squeeze()
|
|
129
134
|
|
|
130
135
|
# plot it
|
|
@@ -140,7 +145,7 @@ plt.show()
|
|
|
140
145
|
**rusterize** is fast! Let’s try it on small and large datasets.
|
|
141
146
|
|
|
142
147
|
``` python
|
|
143
|
-
from rusterize
|
|
148
|
+
from rusterize import rusterize
|
|
144
149
|
import geopandas as gpd
|
|
145
150
|
import requests
|
|
146
151
|
import zipfile
|
|
@@ -175,11 +180,10 @@ pytest <python file> --benchmark-min-rounds=20 --benchmark-time-unit='s'
|
|
|
175
180
|
--------------------------------------------- benchmark: 1 tests --------------------------------------------
|
|
176
181
|
Name (time in s) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
|
|
177
182
|
-------------------------------------------------------------------------------------------------------------
|
|
178
|
-
|
|
179
|
-
|
|
183
|
+
rusterize_small 0.0791 0.0899 0.0812 0.0027 0.0803 0.0020 2;2 12.3214 20 1
|
|
184
|
+
rusterize_large 1.379545 1.4474 1.4006 0.0178 1.3966 0.0214 5;1 0.7140 20 1
|
|
180
185
|
-------------------------------------------------------------------------------------------------------------
|
|
181
186
|
```
|
|
182
|
-
|
|
183
187
|
And fasterize:
|
|
184
188
|
``` r
|
|
185
189
|
library(sf)
|
|
@@ -202,9 +206,9 @@ microbenchmark(
|
|
|
202
206
|
```
|
|
203
207
|
```
|
|
204
208
|
Unit: seconds
|
|
205
|
-
expr min lq mean median
|
|
206
|
-
|
|
207
|
-
|
|
209
|
+
expr min lq mean median uq max neval
|
|
210
|
+
fasterize_small 0.4741043 0.4926114 0.5191707 0.5193289 0.536741 0.5859029 20
|
|
211
|
+
fasterize_large 9.2199426 10.3595465 10.6653139 10.5369429 11.025771 11.7944567 20
|
|
208
212
|
```
|
|
209
213
|
And on an even larger datasets? Here we use a layer from the province of Quebec, Canada representing ~2M polygons of forest stands, rasterized at 30 meters (20 rounds) with no field value and pixel function `any`. The comparison with `gdal_rasterize` was run with `hyperfine --runs 20 "gdal_rasterize -tr 30 30 -burn 1 <data_in> <data_out>"`.
|
|
210
214
|
```
|
|
@@ -212,7 +216,7 @@ And on an even larger datasets? Here we use a layer from the province of Quebec,
|
|
|
212
216
|
--------------------------------------------- benchmark: 1 tests --------------------------------------------
|
|
213
217
|
Name (time in s) Min Max Mean StdDev Median IQR Outliers OPS Rounds Iterations
|
|
214
218
|
-------------------------------------------------------------------------------------------------------------
|
|
215
|
-
rusterize
|
|
219
|
+
rusterize 5.9331 7.2308 6.1302 0.3183 5.9903 0.1736 2;4 0.1631 20 1
|
|
216
220
|
-------------------------------------------------------------------------------------------------------------
|
|
217
221
|
|
|
218
222
|
# fasterize
|
|
@@ -221,8 +225,8 @@ Unit: seconds
|
|
|
221
225
|
fasterize 157.4734 177.2055 194.3222 194.6455 213.9195 230.6504 20
|
|
222
226
|
|
|
223
227
|
# gdal_rasterize (CLI) - read from fast drive, write to fast drive
|
|
224
|
-
Time (mean ± σ): 5.
|
|
225
|
-
Range (min … max): 5.
|
|
228
|
+
Time (mean ± σ): 5.495 s ± 0.038 s [User: 4.268 s, System: 1.225 s]
|
|
229
|
+
Range (min … max): 5.452 s … 5.623 s 20 runs
|
|
226
230
|
```
|
|
227
231
|
In terms of (multi)line rasterization speed, here's a benchmark against `gdal_rasterize` using a layer from the province of Quebec, Canada, representing a subset of the road network for a total of ~535K multilinestrings.
|
|
228
232
|
```
|
|
@@ -239,11 +243,11 @@ Range (min … max): 8.658 s … 8.874 s 20 runs
|
|
|
239
243
|
```
|
|
240
244
|
# Comparison with other tools
|
|
241
245
|
|
|
242
|
-
While **rusterize** is fast, there are other fast alternatives out there, including `GDAL`, `rasterio` and `geocube`. However, **rusterize** allows for a seamless, Rust-native processing with similar or lower memory footprint that doesn't require you to leave Python, and returns the geoinformation you need for downstream processing with ample control over resolution, shape, and
|
|
246
|
+
While **rusterize** is fast, there are other fast alternatives out there, including `GDAL`, `rasterio` and `geocube`. However, **rusterize** allows for a seamless, Rust-native processing with similar or lower memory footprint that doesn't require you to leave Python, and returns the geoinformation you need for downstream processing with ample control over resolution, shape, extent, and data type.
|
|
243
247
|
|
|
244
248
|
The following is a time comparison on a single run on the same forest stands dataset used earlier.
|
|
245
249
|
```
|
|
246
|
-
rusterize:
|
|
250
|
+
rusterize: 5.9 sec
|
|
247
251
|
rasterio: 68 sec (but no spatial information)
|
|
248
252
|
fasterize: 157 sec (including raster creation)
|
|
249
253
|
geocube: 260 sec (larger memory footprint)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
rusterize-0.4.0.dist-info/METADATA,sha256=VxdEZ9jhsBsBCzPY-yZiktAmNs4wuSWEe3GpU863CTQ,11176
|
|
2
|
+
rusterize-0.4.0.dist-info/WHEEL,sha256=6FsaH53F12OzO11ZB57hTDesKbBIcqCM7w19loA4k_Q,103
|
|
3
|
+
rusterize-0.4.0.dist-info/licenses/LICENSE,sha256=v-2DqBji_azGEWFDxBhw-CNIRu8450vBbloLx6UNqLU,1108
|
|
4
|
+
rusterize/__init__.py,sha256=TZvnGqurMBCNrnTfdtjkFhQofqUk-w7TO19JhB5m1OQ,5525
|
|
5
|
+
rusterize/rusterize.abi3.so,sha256=G50QfxsCGAnX4dD6nGhD7x0nOI4uXmjnaBNUt2RQ9c4,38920816
|
|
6
|
+
rusterize-0.4.0.dist-info/RECORD,,
|
rusterize/core.py
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from types import NoneType
|
|
4
|
-
from typing import Any, Dict, Tuple
|
|
5
|
-
|
|
6
|
-
import polars as pl
|
|
7
|
-
from geopandas import GeoDataFrame
|
|
8
|
-
import rioxarray
|
|
9
|
-
from xarray import DataArray
|
|
10
|
-
from .rusterize import _rusterize
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def rusterize(gdf: GeoDataFrame,
|
|
14
|
-
res: Tuple[int, ...] | Tuple[float, ...] | None = None,
|
|
15
|
-
out_shape: Tuple[int, ...] | None = None,
|
|
16
|
-
extent: Tuple[int, ...] | Tuple[float, ...] | None = None,
|
|
17
|
-
field: str | None = None,
|
|
18
|
-
by: str | None = None,
|
|
19
|
-
fun: str = "last",
|
|
20
|
-
background: int | float | None = None,
|
|
21
|
-
) -> Dict[str, Any]:
|
|
22
|
-
"""
|
|
23
|
-
Fast geopandas rasterization into xarray.DataArray
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
:param gdf: geopandas dataframe to rasterize.
|
|
27
|
-
:param res: tuple of (xres, yres) for rasterized data.
|
|
28
|
-
:param out_shape: tuple of (nrows, ncols) for regularized output shape.
|
|
29
|
-
:param extent: tuple of (xmin, xmax, ymin, ymax) for regularized extent.
|
|
30
|
-
:param field: field to rasterize. Default is None.
|
|
31
|
-
:param by: column to rasterize, assigns each unique value to a layer in the stack based on field. Default is None.
|
|
32
|
-
:param fun: pixel function to use. Available options are `sum`, `first`, `last`, `min`, `max`, `count`, or `any`. Default is `last`.
|
|
33
|
-
:param background: background value in final raster. Default is None (NaN).
|
|
34
|
-
|
|
35
|
-
Returns:
|
|
36
|
-
Rasterized xarray.DataArray.
|
|
37
|
-
|
|
38
|
-
Note:
|
|
39
|
-
When any of `res`, `out_shape`, or `extent` is not provided, it is inferred from the other arguments when applicable.
|
|
40
|
-
Unless `extent` is specified, a half-pixel buffer is applied to avoid missing points on the border.
|
|
41
|
-
The logics dictating the final spatial properties of the rasterized geometries follow those of GDAL.
|
|
42
|
-
"""
|
|
43
|
-
# type checks
|
|
44
|
-
if not isinstance(gdf, GeoDataFrame):
|
|
45
|
-
raise TypeError("Must pass a valid geopandas dataframe.")
|
|
46
|
-
if not isinstance(res, (tuple, NoneType)):
|
|
47
|
-
raise TypeError("Must pass a valid resolution tuple (x, y).")
|
|
48
|
-
if not isinstance(out_shape, (tuple, NoneType)):
|
|
49
|
-
raise TypeError("Must pass a valid output shape tuple (nrows, ncols).")
|
|
50
|
-
if not isinstance(extent, (tuple, NoneType)):
|
|
51
|
-
raise TypeError("Must pass a valid extent tuple (xmin, ymin, xmax, ymax).")
|
|
52
|
-
if not isinstance(field, (str, NoneType)):
|
|
53
|
-
raise TypeError("Must pass a valid string to field.")
|
|
54
|
-
if not isinstance(by, (str, NoneType)):
|
|
55
|
-
raise TypeError("Must pass a valid string to by.")
|
|
56
|
-
if not isinstance(fun, str):
|
|
57
|
-
raise TypeError("Must pass a valid string to pixel_fn. Select one of sum, first, last, min, max, count, or any.")
|
|
58
|
-
if not isinstance(background, (int, float, NoneType)):
|
|
59
|
-
raise TypeError("Must pass a valid background type.")
|
|
60
|
-
|
|
61
|
-
# value checks
|
|
62
|
-
if not res and not out_shape and not extent:
|
|
63
|
-
raise ValueError("One of `res`, `out_shape`, or `extent` must be provided.")
|
|
64
|
-
if extent and not res and not out_shape:
|
|
65
|
-
raise ValueError("Must also specify `res` or `out_shape` with extent.")
|
|
66
|
-
if res and (len(res) != 2 or any(r <= 0 for r in res) or any(not isinstance(r, (int, float)) for r in res)):
|
|
67
|
-
raise ValueError("Resolution must be 2 positive numbers.")
|
|
68
|
-
if out_shape and (len(out_shape) != 2 or any(s <= 0 for s in out_shape) or any(not isinstance(s, int) for s in out_shape)):
|
|
69
|
-
raise ValueError("Output shape must be 2 positive integers.")
|
|
70
|
-
if extent and len(extent) != 4:
|
|
71
|
-
raise ValueError("Extent must be 4 numbers (xmin, ymin, xmax, ymax).")
|
|
72
|
-
if by and not field:
|
|
73
|
-
raise ValueError("If `by` is specified, `field` must also be specified.")
|
|
74
|
-
|
|
75
|
-
# defaults
|
|
76
|
-
_res = res if res else (0, 0)
|
|
77
|
-
_shape = out_shape if out_shape else (0, 0)
|
|
78
|
-
(_bounds, has_extent) = (extent, True) if extent else (gdf.total_bounds, False)
|
|
79
|
-
|
|
80
|
-
# RasterInfo
|
|
81
|
-
raster_info = {
|
|
82
|
-
"nrows": _shape[0],
|
|
83
|
-
"ncols": _shape[1],
|
|
84
|
-
"xmin": _bounds[0],
|
|
85
|
-
"ymin": _bounds[1],
|
|
86
|
-
"xmax": _bounds[2],
|
|
87
|
-
"ymax": _bounds[3],
|
|
88
|
-
"xres": _res[0],
|
|
89
|
-
"yres": _res[1],
|
|
90
|
-
"has_extent": has_extent
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
# extract columns of interest and convert to polars
|
|
94
|
-
cols = list(set([col for col in (field, by) if col]))
|
|
95
|
-
df = pl.from_pandas(gdf[cols]) if cols else None
|
|
96
|
-
|
|
97
|
-
# rusterize
|
|
98
|
-
r = _rusterize(
|
|
99
|
-
gdf.geometry,
|
|
100
|
-
raster_info,
|
|
101
|
-
fun,
|
|
102
|
-
df,
|
|
103
|
-
field,
|
|
104
|
-
by,
|
|
105
|
-
background
|
|
106
|
-
)
|
|
107
|
-
return DataArray.from_dict(r).rio.write_crs(gdf.crs, inplace=True)
|
rusterize-0.3.0.dist-info/RECORD
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
rusterize-0.3.0.dist-info/METADATA,sha256=z0ceNeiX5Leku6qTW37jxkAD7-VgoBDkjgxriZdHJkQ,10207
|
|
2
|
-
rusterize-0.3.0.dist-info/WHEEL,sha256=GFLOBS_eUpNumzKnyLn-gluj3NJlsgPc0HGmJ5G79ik,103
|
|
3
|
-
rusterize-0.3.0.dist-info/licenses/LICENSE,sha256=v-2DqBji_azGEWFDxBhw-CNIRu8450vBbloLx6UNqLU,1108
|
|
4
|
-
rusterize/__init__.py,sha256=OymrFdgWCN3VMuSM3oXoGKeagAd-5ayPsYyXnQ_HsDE,101
|
|
5
|
-
rusterize/core.py,sha256=_qigxFrreqbaNITMM0Xs-TGdI0wksggTdlKlYTQlWYI,4588
|
|
6
|
-
rusterize/rusterize.abi3.so,sha256=yVLDLS4sxp6p3tk3cEJ9MkQpnnwbvmwLbsUTQaVgc-c,32812064
|
|
7
|
-
rusterize-0.3.0.dist-info/RECORD,,
|
|
File without changes
|