martibosch / pylandstats

Computing landscape metrics in the Python ecosystem
https://doi.org/10.1371/journal.pone.0225734
GNU General Public License v3.0
82 stars 16 forks source link

Zonal statistics, multiple regions, single class. #11

Closed emuise closed 3 years ago

emuise commented 3 years ago

Description

Run zonal statistics on a region with two zones. It works for many of my cases doing the exact same thing, but when a zone contains only one class in the raster it throws this error. More than happy to send you the files, as well as files generated in the same way with multiple rasters in the class.

What I Did

fName = r"C:\Users\evanmuis.stu\Sync\Masters\Data\outputs\InkaneepPark\rasters\InkaneepPark_PACE-2015-HMM.tif"
zones = r"C:\Users\evanmuis.stu\Sync\Masters\Data\outputs\InkaneepPark\InkaneepPark_PACE_dissolved.shp"

za = pls.ZonalAnalysis(fName, masks = zones, masks_index_col = 'ppa_gpe')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-36-a7dc8dd8d653> in <module>
      2 zones = r"C:\Users\evanmuis.stu\Sync\Masters\Data\outputs\InkaneepPark\InkaneepPark_PACE_dissolved.shp"
      3 
----> 4 za = pls.ZonalAnalysis(fName, masks = zones, masks_index_col = 'ppa_gpe')

~\Miniconda3\envs\frag\lib\site-packages\pylandstats\zonal.py in __init__(self, landscape, masks_arr, landscape_crs, landscape_transform, attribute_name, attribute_values, masks, masks_index_col, **kwargs)
    142                     # we first rasterize the geometries using the values of
    143                     # each geometry's index key in the raster
--> 144                     zone_arr = features.rasterize(
    145                         shapes=((geom, val)
    146                                 for geom, val in zip(masks_gser, masks.index)),

~\Miniconda3\envs\frag\lib\site-packages\rasterio\env.py in wrapper(*args, **kwds)
    383         else:
    384             with Env.from_defaults():
--> 385                 return f(*args, **kwds)
    386     return wrapper
    387 

~\Miniconda3\envs\frag\lib\site-packages\rasterio\features.py in rasterize(shapes, out_shape, fill, out, transform, all_touched, merge_alg, default_value, dtype)
    264     if fill != 0:
    265         fill_array = np.array([fill])
--> 266         if not validate_dtype(fill_array, valid_dtypes):
    267             raise ValueError(format_invalid_dtype('fill'))
    268 

~\Miniconda3\envs\frag\lib\site-packages\rasterio\dtypes.py in validate_dtype(values, valid_dtypes)
    185 
    186     return (values.dtype.name in valid_dtypes or
--> 187             get_minimum_dtype(values) in valid_dtypes)

~\Miniconda3\envs\frag\lib\site-packages\rasterio\dtypes.py in get_minimum_dtype(values)
    125 
    126     else:
--> 127         if min_value >= -3.4028235e+38 and max_value <= 3.4028235e+38:
    128             return float32
    129         return float64

TypeError: '>=' not supported between instances of 'NoneType' and 'float'
martibosch commented 3 years ago

Hello @emuise,

thank you for reporting this. I can imagine where the problem comes from. In any case, I suppose that if you could send me the files, the debugging would probably be easier - but otherwise I can try solving it anyway.

Best, Martí

martibosch commented 3 years ago

Hello @emuise,

did you manage to bypass the error? In any case, would it be possible to get access to your file (e.g., by mail) so that I can add a fix for this in the next release?

Thank you. Best, Martí

emuise commented 3 years ago

Hi @martibosch, I sent an email with a zip of the files. Not too large this time, but let me know if it doesn't go through. Evan

martibosch commented 3 years ago

Hello again @emuise,

I could find two issues. The first is with pylandstats and it should be fixed in 6ba529a. The second is that (unless I am mistaken) your raster file does not have an assigned nodata value, so even after the fix, the following:

import pylandstats as pls

landscape_filepath = 'path/to/InkaneepPark_PACE-2015-HMM.tif'
zones_filepath = 'path/to/InkaneepPark_PACE_dissolved.shp'
za = pls.ZonalAnalysis(landscape_filepath, masks=zones_filepath, masks_index_col='ppa_gpe')

will raise:

Truncated Traceback (Use C-c C-$ to view full TB):
~/libraries/pylandstats/pylandstats/zonal.py in <listcomp>(.0)
    198         landscapes = [
    199             pls_landscape.Landscape(
--> 200                 np.where(mask_arr, landscape_arr, landscape.nodata).astype(
    201                     landscape.landscape_arr.dtype),
    202                 res=(landscape.cell_width, landscape.cell_height),

TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'

because of landscape.nodata being None. If instead, we first do:

gdal_translate -of GTiff -a_nodata 0 path/to/InkaneepPark_PACE-2015-HMM.tif path/to/fix-nodata.tif

(or any other way to assign a nodata value to the raster) and then:

za = pls.ZonalAnalysis('path/to/fix-nodata.tif', masks=zones_filepath, masks_index_col='ppa_gpe')

you will have the proper ZonalAnalysis instance.

The fix will be included in the next release, which will hopefully be this or next week. In the meantime, you can install the development version of pylandstats following these instructions - just note that you will net to run git checkout develop before pip install -e . so that you install pylandstats from the source of the develop branch.

I am closing this, but feel free to reopen if you encounter any further problems related to this. Thank you again for reporting this. Best, Martí

martibosch commented 3 years ago

FYI, the fix is included in the new release v2.3.0. Best, Martí