Error in " mask_to_poly_geojson" if do_transform is False with empty mask
asdspal opened this issue · 3 comments
asdspal commented
Thank you for helping us improve solaris
!
Summary of the bug
I'm getting error message "AttributeError: 'NoneType' object has no attribute 'to_epsg' " when invoking ** mask_to_poly_geojson** with do_transform=False.
Steps to reproduce the bug
crs = rasterio.crs.CRS()
polygon_gdf = gpd.GeoDataFrame({'geometry': [], 'value': []},
crs=crs.to_wkt())
output_path = "empty.geojson"
save_empty_geojson(output_path, polygon_gdf.crs.to_epsg())
Paste bug-causing code here
def mask_to_poly_geojson(pred_arr, channel_scaling=None, reference_im=None,
output_path=None, output_type='geojson', min_area=40,
bg_threshold=0, do_transform=None, simplify=False,
tolerance=0.5, **kwargs):
"""Get polygons from an image mask.
Arguments
---------
pred_arr : :class:`numpy.ndarray`
A 2D array of integers. Multi-channel masks are not supported, and must
be simplified before passing to this function. Can also pass an image
file path here.
channel_scaling : :class:`list`-like, optional
If `pred_arr` is a 3D array, this argument defines how each channel
will be combined to generate a binary output. channel_scaling should
be a `list`-like of length equal to the number of channels in
`pred_arr`. The following operation will be performed to convert the
multi-channel prediction to a 2D output ::
sum(pred_arr[channel]*channel_scaling[channel])
If not provided, no scaling will be performend and channels will be
summed.
reference_im : str, optional
The path to a reference geotiff to use for georeferencing the polygons
in the mask. Required if saving to a GeoJSON (see the ``output_type``
argument), otherwise only required if ``do_transform=True``.
output_path : str, optional
Path to save the output file to. If not provided, no file is saved.
output_type : ``'csv'`` or ``'geojson'``, optional
If ``output_path`` is provided, this argument defines what type of file
will be generated - a CSV (``output_type='csv'``) or a geojson
(``output_type='geojson'``).
min_area : int, optional
The minimum area of a polygon to retain. Filtering is done AFTER
any coordinate transformation, and therefore will be in destination
units.
bg_threshold : int, optional
The cutoff in ``mask_arr`` that denotes background (non-object).
Defaults to ``0``.
simplify : bool, optional
If ``True``, will use the Douglas-Peucker algorithm to simplify edges,
saving memory and processing time later. Defaults to ``False``.
tolerance : float, optional
The tolerance value to use for simplification with the Douglas-Peucker
algorithm. Defaults to ``0.5``. Only has an effect if
``simplify=True``.
Returns
-------
gdf : :class:`geopandas.GeoDataFrame`
A GeoDataFrame of polygons.
"""
mask_arr = preds_to_binary(pred_arr, channel_scaling, bg_threshold)
if do_transform and reference_im is None:
raise ValueError(
'Coordinate transformation requires a reference image.')
if do_transform:
with rasterio.open(reference_im) as ref:
transform = ref.transform
crs = ref.crs
ref.close()
else:
transform = Affine(1, 0, 0, 0, 1, 0) # identity transform
crs = rasterio.crs.CRS()
mask = mask_arr > bg_threshold
mask = mask.astype('uint8')
polygon_generator = features.shapes(mask_arr,
transform=transform,
mask=mask)
polygons = []
values = [] # pixel values for the polygon in mask_arr
for polygon, value in polygon_generator:
p = shape(polygon).buffer(0.0)
if p.area >= min_area:
polygons.append(shape(polygon).buffer(0.0))
values.append(value)
polygon_gdf = gpd.GeoDataFrame({'geometry': polygons, 'value': values},
crs=crs.to_wkt())
if simplify:
polygon_gdf['geometry'] = polygon_gdf['geometry'].apply(
lambda x: x.simplify(tolerance=tolerance)
)
# save output files
if output_path is not None:
if output_type.lower() == 'geojson':
if len(polygon_gdf) > 0:
polygon_gdf.to_file(output_path, driver='GeoJSON')
else:
save_empty_geojson(output_path, polygon_gdf.crs.to_epsg())
elif output_type.lower() == 'csv':
polygon_gdf.to_csv(output_path, index=False)
return polygon_gdf
```Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
## Buggy behavior and/or error message
Please describe the buggy behavior and/or paste output here.
Paste output here
## Expected behavior
An empty geojson file should be created at the output_path
## Screenshots
If applicable, add screenshots to help explain your problem.
## Environment information
- OS: Ubuntu 18.04.3
- `solaris` version:0.4.0
- python version:3.7.8
- version of any relevant dependencies (optional - we may ask for this information later if not provided)
## Additional context
Add any other context about the problem here.
AnshMittal1811 commented
I'm facing the same issue.
Were you able to get a solution to your problem?
asdspal commented
No
FJSam commented
Hi,
To resolve this, I updated the else statement in the buggy code i.e. :
From
else:
save_empty_geojson(output_path, polygon_gdf.crs.to_epsg())
to:
else:
polygon_gdf.crs = "EPSG:4326" # set arbitary crs
save_empty_geojson(output_path, polygon_gdf.crs)
This worked for me, the issue was due to the crs object being None, so I just set an arbitrary value. Don't think it matters what you set it to, as it's an empty geojson anyways - bit crude but worked