345 lines
9.7 KiB
Python
345 lines
9.7 KiB
Python
import numpy as np
|
|
import pytest
|
|
from numpy.testing import assert_allclose, assert_array_equal
|
|
|
|
import shapely
|
|
from shapely import GeometryCollection, LineString, MultiPoint, Point, Polygon
|
|
from shapely.tests.common import (
|
|
empty,
|
|
geometry_collection,
|
|
ignore_invalid,
|
|
line_string,
|
|
linear_ring,
|
|
multi_line_string,
|
|
multi_point,
|
|
multi_polygon,
|
|
point,
|
|
point_polygon_testdata,
|
|
polygon,
|
|
polygon_with_hole,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geom",
|
|
[
|
|
point,
|
|
line_string,
|
|
linear_ring,
|
|
multi_point,
|
|
multi_line_string,
|
|
geometry_collection,
|
|
],
|
|
)
|
|
def test_area_non_polygon(geom):
|
|
assert shapely.area(geom) == 0.0
|
|
|
|
|
|
def test_area():
|
|
actual = shapely.area([polygon, polygon_with_hole, multi_polygon])
|
|
assert actual.tolist() == [4.0, 96.0, 1.01]
|
|
|
|
|
|
def test_distance():
|
|
actual = shapely.distance(*point_polygon_testdata)
|
|
expected = [2 * 2**0.5, 2**0.5, 0, 0, 0, 2**0.5]
|
|
np.testing.assert_allclose(actual, expected)
|
|
|
|
|
|
def test_distance_missing():
|
|
actual = shapely.distance(point, None)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
def test_distance_duplicated():
|
|
a = Point(1, 2)
|
|
b = LineString([(0, 0), (0, 0), (1, 1)])
|
|
with ignore_invalid(shapely.geos_version < (3, 12, 0)):
|
|
# https://github.com/shapely/shapely/issues/1552
|
|
# GEOS < 3.12 raises "invalid" floating point errors
|
|
actual = shapely.distance(a, b)
|
|
assert actual == 1.0
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geom,expected",
|
|
[
|
|
(point, [2, 3, 2, 3]),
|
|
([point, multi_point], [[2, 3, 2, 3], [0, 0, 1, 2]]),
|
|
(shapely.linestrings([[0, 0], [0, 1]]), [0, 0, 0, 1]),
|
|
(shapely.linestrings([[0, 0], [1, 0]]), [0, 0, 1, 0]),
|
|
(multi_point, [0, 0, 1, 2]),
|
|
(multi_polygon, [0, 0, 2.2, 2.2]),
|
|
(geometry_collection, [49, -1, 52, 2]),
|
|
(empty, [np.nan, np.nan, np.nan, np.nan]),
|
|
(None, [np.nan, np.nan, np.nan, np.nan]),
|
|
],
|
|
)
|
|
def test_bounds(geom, expected):
|
|
assert_array_equal(shapely.bounds(geom), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geom,shape",
|
|
[
|
|
(point, (4,)),
|
|
(None, (4,)),
|
|
([point, multi_point], (2, 4)),
|
|
([[point, multi_point], [polygon, point]], (2, 2, 4)),
|
|
([[[point, multi_point]], [[polygon, point]]], (2, 1, 2, 4)),
|
|
],
|
|
)
|
|
def test_bounds_dimensions(geom, shape):
|
|
assert shapely.bounds(geom).shape == shape
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geom,expected",
|
|
[
|
|
(point, [2, 3, 2, 3]),
|
|
(shapely.linestrings([[0, 0], [0, 1]]), [0, 0, 0, 1]),
|
|
(shapely.linestrings([[0, 0], [1, 0]]), [0, 0, 1, 0]),
|
|
(multi_point, [0, 0, 1, 2]),
|
|
(multi_polygon, [0, 0, 2.2, 2.2]),
|
|
(geometry_collection, [49, -1, 52, 2]),
|
|
(empty, [np.nan, np.nan, np.nan, np.nan]),
|
|
(None, [np.nan, np.nan, np.nan, np.nan]),
|
|
([empty, empty, None], [np.nan, np.nan, np.nan, np.nan]),
|
|
# mixed missing and non-missing coordinates
|
|
([point, None], [2, 3, 2, 3]),
|
|
([point, empty], [2, 3, 2, 3]),
|
|
([point, empty, None], [2, 3, 2, 3]),
|
|
([point, empty, None, multi_point], [0, 0, 2, 3]),
|
|
],
|
|
)
|
|
def test_total_bounds(geom, expected):
|
|
assert_array_equal(shapely.total_bounds(geom), expected)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geom",
|
|
[
|
|
point,
|
|
None,
|
|
[point, multi_point],
|
|
[[point, multi_point], [polygon, point]],
|
|
[[[point, multi_point]], [[polygon, point]]],
|
|
],
|
|
)
|
|
def test_total_bounds_dimensions(geom):
|
|
assert shapely.total_bounds(geom).shape == (4,)
|
|
|
|
|
|
def test_length():
|
|
actual = shapely.length(
|
|
[
|
|
point,
|
|
line_string,
|
|
linear_ring,
|
|
polygon,
|
|
polygon_with_hole,
|
|
multi_point,
|
|
multi_polygon,
|
|
]
|
|
)
|
|
assert actual.tolist() == [0.0, 2.0, 4.0, 8.0, 48.0, 0.0, 4.4]
|
|
|
|
|
|
def test_length_missing():
|
|
actual = shapely.length(None)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
def test_hausdorff_distance():
|
|
# example from GEOS docs
|
|
a = shapely.linestrings([[0, 0], [100, 0], [10, 100], [10, 100]])
|
|
b = shapely.linestrings([[0, 100], [0, 10], [80, 10]])
|
|
with ignore_invalid(shapely.geos_version < (3, 12, 0)):
|
|
# Hausdorff distance emits "invalid value encountered"
|
|
# (see https://github.com/libgeos/geos/issues/515)
|
|
actual = shapely.hausdorff_distance(a, b)
|
|
assert actual == pytest.approx(22.360679775, abs=1e-7)
|
|
|
|
|
|
def test_hausdorff_distance_densify():
|
|
# example from GEOS docs
|
|
a = shapely.linestrings([[0, 0], [100, 0], [10, 100], [10, 100]])
|
|
b = shapely.linestrings([[0, 100], [0, 10], [80, 10]])
|
|
with ignore_invalid(shapely.geos_version < (3, 12, 0)):
|
|
# Hausdorff distance emits "invalid value encountered"
|
|
# (see https://github.com/libgeos/geos/issues/515)
|
|
actual = shapely.hausdorff_distance(a, b, densify=0.001)
|
|
assert actual == pytest.approx(47.8, abs=0.1)
|
|
|
|
|
|
def test_hausdorff_distance_missing():
|
|
actual = shapely.hausdorff_distance(point, None)
|
|
assert np.isnan(actual)
|
|
actual = shapely.hausdorff_distance(point, None, densify=0.001)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
def test_hausdorff_densify_nan():
|
|
actual = shapely.hausdorff_distance(point, point, densify=np.nan)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
def test_distance_empty():
|
|
actual = shapely.distance(point, empty)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
def test_hausdorff_distance_empty():
|
|
actual = shapely.hausdorff_distance(point, empty)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
def test_hausdorff_distance_densify_empty():
|
|
actual = shapely.hausdorff_distance(point, empty, densify=0.2)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geom1, geom2, expected",
|
|
[
|
|
# identical geometries should have 0 distance
|
|
(
|
|
shapely.linestrings([[0, 0], [100, 0]]),
|
|
shapely.linestrings([[0, 0], [100, 0]]),
|
|
0,
|
|
),
|
|
# example from GEOS docs
|
|
(
|
|
shapely.linestrings([[0, 0], [50, 200], [100, 0], [150, 200], [200, 0]]),
|
|
shapely.linestrings([[0, 200], [200, 150], [0, 100], [200, 50], [0, 0]]),
|
|
200,
|
|
),
|
|
# same geometries but different curve direction results in maximum
|
|
# distance between vertices on the lines.
|
|
(
|
|
shapely.linestrings([[0, 0], [50, 200], [100, 0], [150, 200], [200, 0]]),
|
|
shapely.linestrings([[200, 0], [150, 200], [100, 0], [50, 200], [0, 0]]),
|
|
200,
|
|
),
|
|
# another example from GEOS docs
|
|
(
|
|
shapely.linestrings([[0, 0], [50, 200], [100, 0], [150, 200], [200, 0]]),
|
|
shapely.linestrings([[0, 0], [200, 50], [0, 100], [200, 150], [0, 200]]),
|
|
282.842712474619,
|
|
),
|
|
# example from GEOS tests
|
|
(
|
|
shapely.linestrings([[0, 0], [100, 0]]),
|
|
shapely.linestrings([[0, 0], [50, 50], [100, 0]]),
|
|
70.7106781186548,
|
|
),
|
|
],
|
|
)
|
|
def test_frechet_distance(geom1, geom2, expected):
|
|
actual = shapely.frechet_distance(geom1, geom2)
|
|
assert actual == pytest.approx(expected, abs=1e-12)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geom1, geom2, densify, expected",
|
|
[
|
|
# example from GEOS tests
|
|
(
|
|
shapely.linestrings([[0, 0], [100, 0]]),
|
|
shapely.linestrings([[0, 0], [50, 50], [100, 0]]),
|
|
0.002,
|
|
50,
|
|
)
|
|
],
|
|
)
|
|
def test_frechet_distance_densify(geom1, geom2, densify, expected):
|
|
actual = shapely.frechet_distance(geom1, geom2, densify=densify)
|
|
assert actual == pytest.approx(expected, abs=1e-12)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geom1, geom2",
|
|
[
|
|
(line_string, None),
|
|
(None, line_string),
|
|
(None, None),
|
|
(line_string, empty),
|
|
(empty, line_string),
|
|
(empty, empty),
|
|
],
|
|
)
|
|
def test_frechet_distance_nan_for_invalid_geometry_inputs(geom1, geom2):
|
|
actual = shapely.frechet_distance(geom1, geom2)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
def test_frechet_densify_ndarray():
|
|
actual = shapely.frechet_distance(
|
|
shapely.linestrings([[0, 0], [100, 0]]),
|
|
shapely.linestrings([[0, 0], [50, 50], [100, 0]]),
|
|
densify=[0.1, 0.2, 1],
|
|
)
|
|
expected = np.array([50, 50.99019514, 70.7106781186548])
|
|
np.testing.assert_array_almost_equal(actual, expected)
|
|
|
|
|
|
def test_frechet_densify_nan():
|
|
actual = shapely.frechet_distance(line_string, line_string, densify=np.nan)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
@pytest.mark.parametrize("densify", [0, -1, 2])
|
|
def test_frechet_densify_invalid_values(densify):
|
|
with pytest.raises(shapely.GEOSException, match="Fraction is not in range"):
|
|
shapely.frechet_distance(line_string, line_string, densify=densify)
|
|
|
|
|
|
def test_frechet_distance_densify_empty():
|
|
actual = shapely.frechet_distance(line_string, empty, densify=0.2)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
def test_minimum_clearance():
|
|
actual = shapely.minimum_clearance([polygon, polygon_with_hole, multi_polygon])
|
|
assert_allclose(actual, [2.0, 2.0, 0.1])
|
|
|
|
|
|
def test_minimum_clearance_nonexistent():
|
|
actual = shapely.minimum_clearance([point, empty])
|
|
assert np.isinf(actual).all()
|
|
|
|
|
|
def test_minimum_clearance_missing():
|
|
actual = shapely.minimum_clearance(None)
|
|
assert np.isnan(actual)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"geometry, expected",
|
|
[
|
|
(
|
|
Polygon([(0, 5), (5, 10), (10, 5), (5, 0), (0, 5)]),
|
|
5,
|
|
),
|
|
(
|
|
LineString([(1, 0), (1, 10)]),
|
|
5,
|
|
),
|
|
(
|
|
MultiPoint([(2, 2), (4, 2)]),
|
|
1,
|
|
),
|
|
(
|
|
Point(2, 2),
|
|
0,
|
|
),
|
|
(
|
|
GeometryCollection(),
|
|
0,
|
|
),
|
|
],
|
|
)
|
|
def test_minimum_bounding_radius(geometry, expected):
|
|
actual = shapely.minimum_bounding_radius(geometry)
|
|
assert actual == pytest.approx(expected, abs=1e-12)
|