@@ -33,6 +33,7 @@ def _zonal_stats_rasterize(
33
33
stats : str | Callable | Sequence [str | Callable | tuple ] = "mean" ,
34
34
name : str = "geometry" ,
35
35
all_touched : bool = False ,
36
+ nodata : Any = None ,
36
37
** kwargs ,
37
38
) -> xr .DataArray | xr .Dataset :
38
39
try :
@@ -70,6 +71,11 @@ def _zonal_stats_rasterize(
70
71
unique .remove (length )
71
72
72
73
obj = acc ._obj .copy ()
74
+
75
+ # mask out nodata - note that this casts whole array to float
76
+ if nodata is not None :
77
+ obj = obj .where (obj != nodata )
78
+
73
79
if isinstance (obj , xr .Dataset ):
74
80
obj = obj .assign_coords (
75
81
__labels__ = xr .DataArray (labels , dims = (y_coords , x_coords ))
@@ -124,6 +130,7 @@ def _zonal_stats_iterative(
124
130
name : str = "geometry" ,
125
131
all_touched : bool = False ,
126
132
n_jobs : int = - 1 ,
133
+ nodata : Any = None ,
127
134
** kwargs : dict [str , Any ],
128
135
) -> xr .DataArray | xr .Dataset :
129
136
"""Extract the values from a dataset indexed by a set of geometries
@@ -158,6 +165,9 @@ def _zonal_stats_iterative(
158
165
n_jobs : int, optional
159
166
Number of parallel threads to use.
160
167
It is recommended to set this to the number of physical cores in the CPU.
168
+ nodata : Any
169
+ Value representing missing data. If not specified, the value is included in
170
+ the aggregation.
161
171
**kwargs : optional
162
172
Keyword arguments to be passed to the aggregation function
163
173
(as ``Dataset.mean(**kwargs)``).
@@ -198,6 +208,7 @@ def _zonal_stats_iterative(
198
208
y_coords ,
199
209
stats = stats ,
200
210
all_touched = all_touched ,
211
+ nodata = nodata ,
201
212
** kwargs ,
202
213
)
203
214
for geom in geometry
@@ -224,6 +235,7 @@ def _agg_geom(
224
235
y_coords : str | None = None ,
225
236
stats : str | Callable | Iterable [str | Callable | tuple ] = "mean" ,
226
237
all_touched : bool = False ,
238
+ nodata : Any = None ,
227
239
** kwargs ,
228
240
):
229
241
"""Aggregate the values from a dataset over a polygon geometry.
@@ -250,6 +262,9 @@ def _agg_geom(
250
262
If True, all pixels touched by geometries will be considered. If False, only
251
263
pixels whose center is within the polygon or that are selected by
252
264
Bresenham’s line algorithm will be considered.
265
+ nodata : Any
266
+ Value representing missing data. If not specified, the value is included in
267
+ the aggregation.
253
268
254
269
Returns
255
270
-------
@@ -270,6 +285,8 @@ def _agg_geom(
270
285
all_touched = all_touched ,
271
286
)
272
287
masked = acc ._obj .where (xr .DataArray (mask , dims = (y_coords , x_coords )))
288
+ if nodata is not None :
289
+ masked = masked .where (masked != nodata )
273
290
if pd .api .types .is_list_like (stats ):
274
291
agg = {}
275
292
for stat in stats : # type: ignore
@@ -309,6 +326,7 @@ def _zonal_stats_exactextract(
309
326
y_coords : Hashable ,
310
327
stats : str | Callable | Sequence [str | Callable | tuple ] = "mean" ,
311
328
name : str = "geometry" ,
329
+ nodata : Any = None ,
312
330
** kwargs ,
313
331
) -> xr .DataArray | xr .Dataset :
314
332
"""Extract the values from a dataset indexed by a set of geometries
@@ -334,6 +352,9 @@ def _zonal_stats_exactextract(
334
352
``"quantile(q=0.20)"``)
335
353
name : str, optional
336
354
Name of the dimension that will hold the ``geometry``, by default "geometry"
355
+ nodata : Any
356
+ Value representing missing data. If not specified, the value is included in
357
+ the aggregation.
337
358
338
359
Returns
339
360
-------
@@ -372,6 +393,7 @@ def _zonal_stats_exactextract(
372
393
stats ,
373
394
name ,
374
395
original_is_ds ,
396
+ nodata = nodata ,
375
397
** kwargs ,
376
398
)
377
399
i = 0
@@ -410,6 +432,7 @@ def _zonal_stats_exactextract(
410
432
stats ,
411
433
name ,
412
434
original_is_ds ,
435
+ nodata = nodata ,
413
436
** kwargs ,
414
437
)
415
438
# Unstack the result
@@ -447,6 +470,7 @@ def _agg_exactextract(
447
470
name : str = "geometry" ,
448
471
original_is_ds : bool = False ,
449
472
strategy : str = "feature-sequential" ,
473
+ nodata : Any = None ,
450
474
):
451
475
"""Extract the values from a dataset indexed by a set of geometries
452
476
@@ -476,6 +500,9 @@ def _agg_exactextract(
476
500
If True, all pixels touched by geometries will be considered. If False, only
477
501
pixels whose center is within the polygon or that are selected by
478
502
Bresenham’s line algorithm will be considered.
503
+ nodata : Any
504
+ Value representing missing data. If not specified, the value is included in
505
+ the aggregation.
479
506
strategy : str, optional
480
507
The strategy to use for the extraction, by default "feature-sequential"
481
508
Use either "feature-sequential" and "raster-sequential".
@@ -511,6 +538,10 @@ def _agg_exactextract(
511
538
# Check the order of dimensions
512
539
data = data .transpose ("location" , y_coords , x_coords )
513
540
541
+ # mask nodata
542
+ if nodata is not None :
543
+ data = data .where (data != nodata )
544
+
514
545
# Aggregation result
515
546
gdf = gpd .GeoDataFrame (geometry = geometry , crs = crs )
516
547
results = exactextract .exact_extract (
@@ -537,7 +568,16 @@ def _agg_exactextract(
537
568
538
569
539
570
def _get_mean (
540
- geom_arr , obj , x_coords , y_coords , transform , all_touched , stats , dims , ** kwargs
571
+ geom_arr ,
572
+ obj ,
573
+ x_coords ,
574
+ y_coords ,
575
+ transform ,
576
+ all_touched ,
577
+ stats ,
578
+ dims ,
579
+ nodata ,
580
+ ** kwargs ,
541
581
):
542
582
from rasterio import features
543
583
@@ -552,6 +592,10 @@ def _get_mean(
552
592
all_touched = all_touched ,
553
593
)
554
594
masked = obj .where (xr .DataArray (mask , dims = (y_coords , x_coords )))
595
+
596
+ if nodata is not None :
597
+ masked = masked .where (masked != nodata )
598
+
555
599
if pd .api .types .is_list_like (stats ):
556
600
agg = {}
557
601
for stat in stats : # type: ignore
@@ -589,6 +633,7 @@ def _variable_zonal(
589
633
y_coords : Hashable ,
590
634
stats = "mean" ,
591
635
all_touched : bool = False ,
636
+ nodata : Any = None ,
592
637
):
593
638
transform = acc ._obj .rio .transform ()
594
639
dims = variable_geometry .dims
@@ -597,7 +642,7 @@ def _variable_zonal(
597
642
598
643
for x in stacked :
599
644
m = _get_mean (
600
- x , acc ._obj , x_coords , y_coords , transform , all_touched , stats , dims
645
+ x , acc ._obj , x_coords , y_coords , transform , all_touched , stats , dims , nodata
601
646
)
602
647
m .name = "statistics"
603
648
r .append (m )
0 commit comments