Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Raster.to_pointcloud() modular and add Raster.get_mask() for memory-efficient operations requiring mask #501

Merged
merged 13 commits into from
Mar 19, 2024
3 changes: 2 additions & 1 deletion doc/source/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ documentation.
Raster.set_mask
Raster.set_nodata
Raster.get_nanarray
Raster.get_mask
Raster.subsample
```

Expand All @@ -118,7 +119,7 @@ documentation.

Raster.load
Raster.save
Raster.to_points
Raster.to_pointcloud
Raster.to_rio_dataset
Raster.to_xarray
```
Expand Down
4 changes: 2 additions & 2 deletions doc/source/raster_class.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ A {class}`~geoutils.Raster` can be exported to different formats, to facilitate
Those include exporting to:
- a {class}`xarray.Dataset` with {class}`~geoutils.Raster.to_xarray`,
- a {class}`rasterio.io.DatasetReader` with {class}`~geoutils.Raster.to_rio_dataset`,
- a {class}`numpy.ndarray` or {class}`geoutils.Vector` as a point cloud with {class}`~geoutils.Raster.to_points`.
- a {class}`numpy.ndarray` or {class}`geoutils.Vector` as a point cloud with {class}`~geoutils.Raster.to_pointcloud`.

```{code-cell} ipython3
# Export to rasterio dataset-reader through a memoryfile
Expand All @@ -341,7 +341,7 @@ rast_reproj.to_rio_dataset()

```{code-cell} ipython3
# Export to geopandas dataframe
rast_reproj.to_points()
rast_reproj.to_pointcloud()
```

```{code-cell} ipython3
Expand Down
4 changes: 2 additions & 2 deletions examples/handling/interface/topoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
# %%
# We convert the raster to points. By default, this returns a vector with columb geometry burned.

pts_rast = rast.to_points()
pts_rast = rast.to_pointcloud()
pts_rast

# %%
# We plot the point vector.

pts_rast.plot(column="b1", cmap="terrain", legend=True)
pts_rast.ds.plot(column="b1", cmap="terrain", legend=True)
rhugonnet marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 8 additions & 7 deletions geoutils/raster/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import numpy as np

import geoutils as gu
from geoutils._typing import MArrayNum, NDArrayNum
from geoutils._typing import MArrayNum, NDArrayBool, NDArrayNum


def get_mask(array: NDArrayNum | MArrayNum) -> NDArrayNum:
def get_mask_from_array(array: NDArrayNum | NDArrayBool | MArrayNum) -> NDArrayBool:
"""
Return the mask of invalid values, whether array is a ndarray with NaNs or a np.ma.masked_array.

Expand All @@ -24,7 +24,7 @@ def get_mask(array: NDArrayNum | MArrayNum) -> NDArrayNum:

def get_array_and_mask(
array: NDArrayNum | MArrayNum, check_shape: bool = True, copy: bool = True
) -> tuple[NDArrayNum, NDArrayNum]:
) -> tuple[NDArrayNum, NDArrayBool]:
"""
Return array with masked values set to NaN and the associated mask.
Works whether array is a ndarray with NaNs or a np.ma.masked_array.
Expand Down Expand Up @@ -59,21 +59,22 @@ def get_array_and_mask(
array_data = np.array(array).squeeze() if copy else np.asarray(array).squeeze()

# Get the mask of invalid pixels and set nans if it is occupied.
invalid_mask = get_mask(array)
invalid_mask = get_mask_from_array(array)
if np.any(invalid_mask):
array_data[invalid_mask] = np.nan

return array_data, invalid_mask


def get_valid_extent(array: NDArrayNum | MArrayNum) -> tuple[int, ...]:
def get_valid_extent(array: NDArrayNum | NDArrayBool | MArrayNum) -> tuple[int, ...]:
"""
Return (rowmin, rowmax, colmin, colmax), the first/last row/column of array with valid pixels
"""
if not array.dtype == "bool":
valid_mask = ~get_mask(array)
valid_mask = ~get_mask_from_array(array)
else:
valid_mask = array
# Not sure why Mypy is not recognizing that the type of the array can only be bool here
valid_mask = array # type: ignore
cols_nonzero = np.where(np.count_nonzero(valid_mask, axis=0) > 0)[0]
rows_nonzero = np.where(np.count_nonzero(valid_mask, axis=1) > 0)[0]
return rows_nonzero[0], rows_nonzero[-1], cols_nonzero[0], cols_nonzero[-1]
Expand Down
Loading
Loading