Skip to content

Commit

Permalink
Merge pull request #432 from okolekar/#405-Add-Invert-data-tool-or-mo…
Browse files Browse the repository at this point in the history
…dify-Min-max-Scaling

Add proximity computation tool
  • Loading branch information
nmaarnio authored Oct 18, 2024
2 parents 7613375 + 3098259 commit 112002a
Show file tree
Hide file tree
Showing 4 changed files with 290 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/vector_processing/proximity_computation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Proximity computation

::: eis_toolkit.vector_processing.proximity_computation
72 changes: 72 additions & 0 deletions eis_toolkit/vector_processing/proximity_computation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from numbers import Number

import geopandas as gpd
import numpy as np
from beartype import beartype
from beartype.typing import Literal, Tuple, Union
from rasterio import profiles

from eis_toolkit.transformations.linear import _min_max_scaling
from eis_toolkit.vector_processing.distance_computation import distance_computation


@beartype
def proximity_computation(
geodataframe: gpd.GeoDataFrame,
raster_profile: Union[profiles.Profile, dict],
maximum_distance: Number,
scaling_method: Literal["linear"] = "linear",
scale_range: Tuple[Number, Number] = (1, 0),
) -> np.ndarray:
"""Compute proximity to the nearest geometries.
Args:
geodataframe: The GeoDataFrame with geometries to determine proximity to.
raster_profile: The raster profile of the raster in which the distances
to the nearest geometry are determined.
max_distance: The maximum distance in the output array beyond which proximity is considered 0.
scaling_method: Scaling method used to produce the proximity values. Defaults to 'linear'
scaling_range: Min and max values used for scaling the proximity values. Defaults to (1,0).
Returns:
A 2D numpy array with the scaled values.
Raises:
NonMatchingCrsException: The input raster profile and geodataframe have mismatching CRS.
EmptyDataFrameException: The input geodataframe is empty.
"""
out_matrix = _linear_proximity_computation(geodataframe, raster_profile, maximum_distance, scale_range)

return out_matrix


@beartype
def _linear_proximity_computation(
geodataframe: gpd.GeoDataFrame,
raster_profile: Union[profiles.Profile, dict],
maximum_distance: Number,
scaling_range: Tuple[Number, Number],
) -> np.ndarray:
"""Compute proximity to the nearest geometries.
Args:
geodataframe: The GeoDataFrame with geometries to determine proximity to.
raster_profile: The raster profile of the raster in which the distances
to the nearest geometry are determined.
max_distance: The maximum distance in the output array.
scaling_range: a tuple of maximum value in the scaling and minimum value.
Returns:
A 2D numpy array with the linearly scaled values.
Raises:
NonMatchingCrsException: The input raster profile and geodataframe have mismatching CRS.
EmptyDataFrameException: The input geodataframe is empty.
"""

out_matrix = distance_computation(geodataframe, raster_profile, maximum_distance)

out_matrix = _min_max_scaling(out_matrix, scaling_range)

return out_matrix
118 changes: 118 additions & 0 deletions notebooks/testing_proximity_computation.ipynb

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions tests/vector_processing/proximity_computation_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import sys

import geopandas as gpd
import numpy as np
import pytest
import rasterio

from eis_toolkit.exceptions import NumericValueSignException
from eis_toolkit.vector_processing.proximity_computation import proximity_computation
from tests.raster_processing.clip_test import polygon_path as polygon_path
from tests.raster_processing.clip_test import raster_path as SMALL_RASTER_PATH

sys.path.append("../..")
gdf = gpd.read_file(polygon_path)

with rasterio.open(SMALL_RASTER_PATH) as test_raster:
raster_profile = test_raster.profile

EXPECTED_SMALL_RASTER_SHAPE = raster_profile["height"], raster_profile["width"]


@pytest.mark.parametrize(
"geodataframe,raster_profile,expected_shape,maximum_distance,scale,scaling_range",
[
pytest.param(
gdf,
raster_profile,
EXPECTED_SMALL_RASTER_SHAPE,
25,
"linear",
(1, 0),
id="Inversion_and_scaling_between_1_and_0",
),
pytest.param(
gdf,
raster_profile,
EXPECTED_SMALL_RASTER_SHAPE,
25,
"linear",
(2, 1),
id="Inversion_and_scaling_between_2_and_1",
),
],
)
def test_proximity_computation_inversion_with_expected_result(
geodataframe, raster_profile, expected_shape, maximum_distance, scale, scaling_range
):
"""Tests if the enteries in the output matrix are between the minimum and maximum value."""

result = proximity_computation(geodataframe, raster_profile, maximum_distance, scale, scaling_range)

assert result.shape == expected_shape
# Assert that all values in result within scaling_range
assert np.all((result >= scaling_range[1]) & (result <= scaling_range[0])), "Scaling out of scaling_range"


@pytest.mark.parametrize(
"geodataframe,raster_profile,expected_shape,maximum_distance,scale,scaling_range",
[
pytest.param(
gdf,
raster_profile,
EXPECTED_SMALL_RASTER_SHAPE,
25,
"linear",
(0, 1),
id="Scaling_between_0_and_1",
),
pytest.param(
gdf,
raster_profile,
EXPECTED_SMALL_RASTER_SHAPE,
25,
"linear",
(1, 2),
id="Scaling_between_1_and_2",
),
],
)
def test_proximity_computation_with_expected_result(
geodataframe, raster_profile, expected_shape, maximum_distance, scale, scaling_range
):
"""Tests if the enteries in the output matrix are between the minimum and maximum value."""

result = proximity_computation(geodataframe, raster_profile, maximum_distance, scale, scaling_range)

assert result.shape == expected_shape
# Assert that all values in result within scaling_range
assert np.all((result <= scaling_range[1]) & (result >= scaling_range[0])), "Scaling out of scaling_range"


def test_proximity_computation_with_expected_error():
"""Tests if an exception is raised for a negative maximum distance."""

with pytest.raises(NumericValueSignException, match="Expected max distance to be a positive number."):
result = proximity_computation(gdf, raster_profile, -25, "linear", (1, 0))
assert np.all((result >= 0) & (result <= 1)), "Scaling out of scaling_range"

0 comments on commit 112002a

Please sign in to comment.