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

Revert making all raster.array non-public #625

Merged
merged 2 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions geoutils/interface/raster_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import geoutils as gu
from geoutils._typing import NDArrayNum
from geoutils.raster.array import _get_mask_from_array
from geoutils.raster.array import get_mask_from_array
from geoutils.raster.georeferencing import _default_nodata, _xy2ij
from geoutils.raster.sampling import subsample_array

Expand Down Expand Up @@ -168,11 +168,11 @@ def _raster_to_pointcloud(
if skip_nodata:
if source_raster.is_loaded:
if source_raster.count == 1:
self_mask = _get_mask_from_array(
self_mask = get_mask_from_array(
source_raster.data
) # This is to avoid the case where the mask is just "False"
else:
self_mask = _get_mask_from_array(
self_mask = get_mask_from_array(
source_raster.data[data_band - 1, :, :]
) # This is to avoid the case where the mask is just "False"
valid_mask = ~self_mask
Expand Down
12 changes: 6 additions & 6 deletions geoutils/raster/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from geoutils._typing import MArrayNum, NDArrayBool, NDArrayNum


def _get_mask_from_array(array: NDArrayNum | NDArrayBool | MArrayNum) -> NDArrayBool:
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 @@ -22,7 +22,7 @@ def _get_mask_from_array(array: NDArrayNum | NDArrayBool | MArrayNum) -> NDArray
return mask.squeeze()


def _get_array_and_mask(
def get_array_and_mask(
array: NDArrayNum | MArrayNum, check_shape: bool = True, copy: bool = True
) -> tuple[NDArrayNum, NDArrayBool]:
"""
Expand Down Expand Up @@ -59,19 +59,19 @@ 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_from_array(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 | NDArrayBool | 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_from_array(array)
valid_mask = ~get_mask_from_array(array)
else:
# Not sure why Mypy is not recognizing that the type of the array can only be bool here
valid_mask = array # type: ignore
Expand All @@ -80,7 +80,7 @@ def _get_valid_extent(array: NDArrayNum | NDArrayBool | MArrayNum) -> tuple[int,
return rows_nonzero[0], rows_nonzero[-1], cols_nonzero[0], cols_nonzero[-1]


def _get_xy_rotated(raster: gu.Raster, along_track_angle: float) -> tuple[NDArrayNum, NDArrayNum]:
def get_xy_rotated(raster: gu.Raster, along_track_angle: float) -> tuple[NDArrayNum, NDArrayNum]:
"""
Rotate x, y axes of image to get along- and cross-track distances.
:param raster: Raster to get x,y positions from.
Expand Down
4 changes: 2 additions & 2 deletions geoutils/raster/multiraster.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import geoutils as gu
from geoutils._typing import NDArrayNum
from geoutils.raster.array import _get_array_and_mask
from geoutils.raster.array import get_array_and_mask
from geoutils.raster.geotransformations import _resampling_method_from_str
from geoutils.raster.raster import RasterType, _default_nodata

Expand Down Expand Up @@ -194,7 +194,7 @@ def stack_rasters(
# Optionally calculate difference
if diff:
diff_to_ref = (reference_raster.data - reprojected_raster.data).squeeze()
diff_to_ref, _ = _get_array_and_mask(diff_to_ref)
diff_to_ref, _ = get_array_and_mask(diff_to_ref)
data.append(diff_to_ref)
else:
# img_data, _ = get_array_and_mask(reprojected_raster.data.squeeze())
Expand Down
4 changes: 2 additions & 2 deletions geoutils/raster/sampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import numpy as np

from geoutils._typing import MArrayNum, NDArrayNum
from geoutils.raster.array import _get_mask_from_array
from geoutils.raster.array import get_mask_from_array


@overload
Expand Down Expand Up @@ -60,7 +60,7 @@ def subsample_array(
rng = np.random.default_rng(random_state)

# Remove invalid values and flatten array
mask = _get_mask_from_array(array) # -> need to remove .squeeze in get_mask
mask = get_mask_from_array(array) # -> need to remove .squeeze in get_mask
valids = np.argwhere(~mask.flatten()).squeeze()

# Get number of points to extract
Expand Down
52 changes: 24 additions & 28 deletions tests/test_raster/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@
import rasterio as rio

import geoutils as gu
from geoutils.raster.array import (
_get_array_and_mask,
_get_valid_extent,
_get_xy_rotated,
)
from geoutils.raster.array import get_array_and_mask, get_valid_extent, get_xy_rotated


class TestArray:
Expand Down Expand Up @@ -59,13 +55,13 @@ def test_get_array_and_mask(
# Validate that incorrect shapes raise the correct error.
if not check_should_pass:
with pytest.raises(ValueError, match="Invalid array shape given"):
_get_array_and_mask(array, check_shape=True)
get_array_and_mask(array, check_shape=True)

# Stop the test here as the failure is now validated.
return

# Get a copy of the array and check its shape (it should always pass at this point)
arr, _ = _get_array_and_mask(array, copy=True, check_shape=True)
arr, _ = get_array_and_mask(array, copy=True, check_shape=True)

# Validate that the array is a copy
assert not np.shares_memory(arr, array)
Expand All @@ -82,7 +78,7 @@ def test_get_array_and_mask(
warnings.simplefilter("always")

# Try to create a view.
arr_view, mask = _get_array_and_mask(array, copy=False)
arr_view, mask = get_array_and_mask(array, copy=False)

# If it should be possible, validate that there were no warnings.
if view_should_be_possible:
Expand All @@ -108,36 +104,36 @@ def test_get_valid_extent(self) -> None:

# For no invalid values, the function should return the edges
# For the array
assert (0, 4, 0, 4) == _get_valid_extent(arr)
assert (0, 4, 0, 4) == get_valid_extent(arr)
# For the masked-array
assert (0, 4, 0, 4) == _get_valid_extent(mask_ma)
assert (0, 4, 0, 4) == get_valid_extent(mask_ma)

# 1/ First column:
# If we mask it in the masked array
mask_ma[0, :] = np.ma.masked
assert (1, 4, 0, 4) == _get_valid_extent(mask_ma)
assert (1, 4, 0, 4) == get_valid_extent(mask_ma)

# If we changed the array to NaNs
arr[0, :] = np.nan
assert (1, 4, 0, 4) == _get_valid_extent(arr)
assert (1, 4, 0, 4) == get_valid_extent(arr)
mask_ma.data[0, :] = np.nan
mask_ma.mask = False
assert (1, 4, 0, 4) == _get_valid_extent(mask_ma)
assert (1, 4, 0, 4) == get_valid_extent(mask_ma)

# 2/ First row:
arr = np.ones(shape=(5, 5))
arr_mask = np.zeros(shape=(5, 5), dtype=bool)
mask_ma = np.ma.masked_array(data=arr, mask=arr_mask)
# If we mask it in the masked array
mask_ma[:, 0] = np.ma.masked
assert (0, 4, 1, 4) == _get_valid_extent(mask_ma)
assert (0, 4, 1, 4) == get_valid_extent(mask_ma)

# If we changed the array to NaNs
arr[:, 0] = np.nan
assert (0, 4, 1, 4) == _get_valid_extent(arr)
assert (0, 4, 1, 4) == get_valid_extent(arr)
mask_ma.data[:, 0] = np.nan
mask_ma.mask = False
assert (0, 4, 1, 4) == _get_valid_extent(mask_ma)
assert (0, 4, 1, 4) == get_valid_extent(mask_ma)

# 3/ Last column:
arr = np.ones(shape=(5, 5))
Expand All @@ -146,14 +142,14 @@ def test_get_valid_extent(self) -> None:

# If we mask it in the masked array
mask_ma[-1, :] = np.ma.masked
assert (0, 3, 0, 4) == _get_valid_extent(mask_ma)
assert (0, 3, 0, 4) == get_valid_extent(mask_ma)

# If we changed the array to NaNs
arr[-1, :] = np.nan
assert (0, 3, 0, 4) == _get_valid_extent(arr)
assert (0, 3, 0, 4) == get_valid_extent(arr)
mask_ma.data[-1, :] = np.nan
mask_ma.mask = False
assert (0, 3, 0, 4) == _get_valid_extent(mask_ma)
assert (0, 3, 0, 4) == get_valid_extent(mask_ma)

# 4/ Last row:
arr = np.ones(shape=(5, 5))
Expand All @@ -162,14 +158,14 @@ def test_get_valid_extent(self) -> None:

# If we mask it in the masked array
mask_ma[:, -1] = np.ma.masked
assert (0, 4, 0, 3) == _get_valid_extent(mask_ma)
assert (0, 4, 0, 3) == get_valid_extent(mask_ma)

# If we changed the array to NaNs
arr[:, -1] = np.nan
assert (0, 4, 0, 3) == _get_valid_extent(arr)
assert (0, 4, 0, 3) == get_valid_extent(arr)
mask_ma.data[:, -1] = np.nan
mask_ma.mask = False
assert (0, 4, 0, 3) == _get_valid_extent(mask_ma)
assert (0, 4, 0, 3) == get_valid_extent(mask_ma)

def test_get_xy_rotated(self) -> None:
"""Check the function to rotate array."""
Expand All @@ -184,27 +180,27 @@ def test_get_xy_rotated(self) -> None:
xx, yy = r1.coords(grid=True, force_offset="ll")

# Rotating the coordinates 90 degrees should be the same as rotating the array
xx90, yy90 = _get_xy_rotated(r1, along_track_angle=90)
xx90, yy90 = get_xy_rotated(r1, along_track_angle=90)
assert np.allclose(np.rot90(xx90), xx)
assert np.allclose(np.rot90(yy90), yy)

# Same for 180 degrees
xx180, yy180 = _get_xy_rotated(r1, along_track_angle=180)
xx180, yy180 = get_xy_rotated(r1, along_track_angle=180)
assert np.allclose(np.rot90(xx180, k=2), xx)
assert np.allclose(np.rot90(yy180, k=2), yy)

# Same for 270 degrees
xx270, yy270 = _get_xy_rotated(r1, along_track_angle=270)
xx270, yy270 = get_xy_rotated(r1, along_track_angle=270)
assert np.allclose(np.rot90(xx270, k=3), xx)
assert np.allclose(np.rot90(yy270, k=3), yy)

# 360 degrees should get us back on our feet
xx360, yy360 = _get_xy_rotated(r1, along_track_angle=360)
xx360, yy360 = get_xy_rotated(r1, along_track_angle=360)
assert np.allclose(xx360, xx)
assert np.allclose(yy360, yy)

# Test that the values make sense for 45 degrees
xx45, yy45 = _get_xy_rotated(r1, along_track_angle=45)
xx45, yy45 = get_xy_rotated(r1, along_track_angle=45)
# Should have zero on the upper left corner for xx
assert xx45[0, 0] == pytest.approx(0)
# Then a multiple of sqrt2 along each dimension
Expand All @@ -215,4 +211,4 @@ def test_get_xy_rotated(self) -> None:
# Finally, yy should be rotated by 90
assert np.allclose(np.rot90(xx45), yy45)

xx, yy = _get_xy_rotated(r1, along_track_angle=90)
xx, yy = get_xy_rotated(r1, along_track_angle=90)
2 changes: 1 addition & 1 deletion tests/test_raster/test_raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ def test_copy(self, example: str) -> None:

# When passing the new array as a NaN ndarray, only the valid data is equal, because masked data is NaN in one
# case, and -9999 in the other
r_arr = gu.raster.array._get_array_and_mask(r)[0]
r_arr = gu.raster.array.get_array_and_mask(r)[0]
r2 = r.copy(new_array=r_arr)
assert np.ma.allequal(r.data, r2.data)
# If a nodata value exists, and we update the NaN pixels to be that nodata value, then the two Rasters should
Expand Down
Loading