refactor: excel parse
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from shapely import GeometryCollection, LineString, Point, wkt
|
||||
from shapely.geometry import shape
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def geometrycollection_geojson():
|
||||
return {
|
||||
"type": "GeometryCollection",
|
||||
"geometries": [
|
||||
{"type": "Point", "coordinates": (0, 3, 0)},
|
||||
{"type": "LineString", "coordinates": ((2, 0), (1, 0))},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"geom",
|
||||
[
|
||||
GeometryCollection(),
|
||||
shape({"type": "GeometryCollection", "geometries": []}),
|
||||
wkt.loads("GEOMETRYCOLLECTION EMPTY"),
|
||||
],
|
||||
)
|
||||
def test_empty(geom):
|
||||
assert geom.geom_type == "GeometryCollection"
|
||||
assert geom.is_empty
|
||||
assert len(geom.geoms) == 0
|
||||
assert list(geom.geoms) == []
|
||||
|
||||
|
||||
def test_empty_subgeoms():
|
||||
geom = GeometryCollection([Point(), LineString()])
|
||||
assert geom.geom_type == "GeometryCollection"
|
||||
assert geom.is_empty
|
||||
assert len(geom.geoms) == 2
|
||||
assert list(geom.geoms) == [Point(), LineString()]
|
||||
|
||||
|
||||
def test_child_with_deleted_parent():
|
||||
# test that we can remove a collection while keeping
|
||||
# children around
|
||||
a = LineString([(0, 0), (1, 1), (1, 2), (2, 2)])
|
||||
b = LineString([(0, 0), (1, 1), (2, 1), (2, 2)])
|
||||
collection = a.intersection(b)
|
||||
|
||||
child = collection.geoms[0]
|
||||
# delete parent of child
|
||||
del collection
|
||||
|
||||
# access geometry, this should not seg fault as 1.2.15 did
|
||||
assert child.wkt is not None
|
||||
|
||||
|
||||
def test_from_geojson(geometrycollection_geojson):
|
||||
geom = shape(geometrycollection_geojson)
|
||||
assert geom.geom_type == "GeometryCollection"
|
||||
assert len(geom.geoms) == 2
|
||||
|
||||
geom_types = [g.geom_type for g in geom.geoms]
|
||||
assert "Point" in geom_types
|
||||
assert "LineString" in geom_types
|
||||
|
||||
|
||||
def test_geointerface(geometrycollection_geojson):
|
||||
geom = shape(geometrycollection_geojson)
|
||||
assert geom.__geo_interface__ == geometrycollection_geojson
|
||||
|
||||
|
||||
def test_len_raises(geometrycollection_geojson):
|
||||
geom = shape(geometrycollection_geojson)
|
||||
with pytest.raises(TypeError):
|
||||
len(geom)
|
||||
|
||||
|
||||
def test_numpy_object_array():
|
||||
geom = GeometryCollection([LineString([(0, 0), (1, 1)])])
|
||||
ar = np.empty(1, object)
|
||||
ar[:] = [geom]
|
||||
assert ar[0] == geom
|
||||
@@ -0,0 +1,102 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from shapely import LineString
|
||||
from shapely.tests.common import line_string, line_string_z, point, point_z
|
||||
|
||||
|
||||
class TestCoords:
|
||||
"""
|
||||
Shapely assumes contiguous C-order float64 data for internal ops.
|
||||
Data should be converted to contiguous float64 if numpy exists.
|
||||
c9a0707 broke this a little bit.
|
||||
"""
|
||||
|
||||
def test_data_promotion(self):
|
||||
coords = np.array([[12, 34], [56, 78]], dtype=np.float32)
|
||||
processed_coords = np.array(LineString(coords).coords)
|
||||
|
||||
assert coords.tolist() == processed_coords.tolist()
|
||||
|
||||
def test_data_destriding(self):
|
||||
coords = np.array([[12, 34], [56, 78]], dtype=np.float32)
|
||||
|
||||
# Easy way to introduce striding: reverse list order
|
||||
processed_coords = np.array(LineString(coords[::-1]).coords)
|
||||
|
||||
assert coords[::-1].tolist() == processed_coords.tolist()
|
||||
|
||||
|
||||
class TestCoordsGetItem:
|
||||
def test_index_2d_coords(self):
|
||||
c = [(float(x), float(-x)) for x in range(4)]
|
||||
g = LineString(c)
|
||||
for i in range(-4, 4):
|
||||
assert g.coords[i] == c[i]
|
||||
with pytest.raises(IndexError):
|
||||
g.coords[4]
|
||||
with pytest.raises(IndexError):
|
||||
g.coords[-5]
|
||||
|
||||
def test_index_3d_coords(self):
|
||||
c = [(float(x), float(-x), float(x * 2)) for x in range(4)]
|
||||
g = LineString(c)
|
||||
for i in range(-4, 4):
|
||||
assert g.coords[i] == c[i]
|
||||
with pytest.raises(IndexError):
|
||||
g.coords[4]
|
||||
with pytest.raises(IndexError):
|
||||
g.coords[-5]
|
||||
|
||||
def test_index_coords_misc(self):
|
||||
g = LineString() # empty
|
||||
with pytest.raises(IndexError):
|
||||
g.coords[0]
|
||||
with pytest.raises(TypeError):
|
||||
g.coords[0.0]
|
||||
|
||||
def test_slice_2d_coords(self):
|
||||
c = [(float(x), float(-x)) for x in range(4)]
|
||||
g = LineString(c)
|
||||
assert g.coords[1:] == c[1:]
|
||||
assert g.coords[:-1] == c[:-1]
|
||||
assert g.coords[::-1] == c[::-1]
|
||||
assert g.coords[::2] == c[::2]
|
||||
assert g.coords[:4] == c[:4]
|
||||
assert g.coords[4:] == c[4:] == []
|
||||
|
||||
def test_slice_3d_coords(self):
|
||||
c = [(float(x), float(-x), float(x * 2)) for x in range(4)]
|
||||
g = LineString(c)
|
||||
assert g.coords[1:] == c[1:]
|
||||
assert g.coords[:-1] == c[:-1]
|
||||
assert g.coords[::-1] == c[::-1]
|
||||
assert g.coords[::2] == c[::2]
|
||||
assert g.coords[:4] == c[:4]
|
||||
assert g.coords[4:] == c[4:] == []
|
||||
|
||||
|
||||
class TestXY:
|
||||
"""New geometry/coordseq method 'xy' makes numpy interop easier"""
|
||||
|
||||
def test_arrays(self):
|
||||
x, y = LineString([(0, 0), (1, 1)]).xy
|
||||
assert len(x) == 2
|
||||
assert list(x) == [0.0, 1.0]
|
||||
assert len(y) == 2
|
||||
assert list(y) == [0.0, 1.0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("geom", [point, point_z, line_string, line_string_z])
|
||||
def test_coords_array_copy(geom):
|
||||
"""Test CoordinateSequence.__array__ method."""
|
||||
coord_seq = geom.coords
|
||||
assert np.array(coord_seq) is not np.array(coord_seq)
|
||||
assert np.array(coord_seq, copy=True) is not np.array(coord_seq, copy=True)
|
||||
|
||||
# Behaviour of copy=False is different between NumPy 1.x and 2.x
|
||||
if int(np.version.short_version.split(".", 1)[0]) >= 2:
|
||||
with pytest.raises(ValueError, match="A copy is always created"):
|
||||
np.array(coord_seq, copy=False)
|
||||
else:
|
||||
assert np.array(coord_seq, copy=False) is np.array(coord_seq, copy=False)
|
||||
@@ -0,0 +1,117 @@
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
|
||||
from shapely import (
|
||||
GeometryCollection,
|
||||
LinearRing,
|
||||
LineString,
|
||||
MultiLineString,
|
||||
MultiPoint,
|
||||
MultiPolygon,
|
||||
Point,
|
||||
Polygon,
|
||||
)
|
||||
|
||||
items2d = [
|
||||
[(0.0, 0.0), (70.0, 120.0), (140.0, 0.0), (0.0, 0.0)],
|
||||
[(60.0, 80.0), (80.0, 80.0), (70.0, 60.0), (60.0, 80.0)],
|
||||
]
|
||||
|
||||
items2d_mixed = [
|
||||
[
|
||||
(Decimal(0.0), Decimal(0.0)),
|
||||
(Decimal(70.0), 120.0),
|
||||
(140.0, Decimal(0.0)),
|
||||
(0.0, 0.0),
|
||||
],
|
||||
[
|
||||
(Decimal(60.0), Decimal(80.0)),
|
||||
(Decimal(80.0), 80.0),
|
||||
(70.0, Decimal(60.0)),
|
||||
(60.0, 80.0),
|
||||
],
|
||||
]
|
||||
|
||||
items2d_decimal = [
|
||||
[
|
||||
(Decimal(0.0), Decimal(0.0)),
|
||||
(Decimal(70.0), Decimal(120.0)),
|
||||
(Decimal(140.0), Decimal(0.0)),
|
||||
(Decimal(0.0), Decimal(0.0)),
|
||||
],
|
||||
[
|
||||
(Decimal(60.0), Decimal(80.0)),
|
||||
(Decimal(80.0), Decimal(80.0)),
|
||||
(Decimal(70.0), Decimal(60.0)),
|
||||
(Decimal(60.0), Decimal(80.0)),
|
||||
],
|
||||
]
|
||||
|
||||
items3d = [
|
||||
[(0.0, 0.0, 1), (70.0, 120.0, 2), (140.0, 0.0, 3), (0.0, 0.0, 1)],
|
||||
[(60.0, 80.0, 1), (80.0, 80.0, 2), (70.0, 60.0, 3), (60.0, 80.0, 1)],
|
||||
]
|
||||
|
||||
items3d_mixed = [
|
||||
[
|
||||
(Decimal(0.0), Decimal(0.0), Decimal(1)),
|
||||
(Decimal(70.0), 120.0, Decimal(2)),
|
||||
(140.0, Decimal(0.0), 3),
|
||||
(0.0, 0.0, 1),
|
||||
],
|
||||
[
|
||||
(Decimal(60.0), Decimal(80.0), Decimal(1)),
|
||||
(Decimal(80.0), 80.0, 2),
|
||||
(70.0, Decimal(60.0), Decimal(3)),
|
||||
(60.0, 80.0, 1),
|
||||
],
|
||||
]
|
||||
|
||||
items3d_decimal = [
|
||||
[
|
||||
(Decimal(0.0), Decimal(0.0), Decimal(1)),
|
||||
(Decimal(70.0), Decimal(120.0), Decimal(2)),
|
||||
(Decimal(140.0), Decimal(0.0), Decimal(3)),
|
||||
(Decimal(0.0), Decimal(0.0), Decimal(1)),
|
||||
],
|
||||
[
|
||||
(Decimal(60.0), Decimal(80.0), Decimal(1)),
|
||||
(Decimal(80.0), Decimal(80.0), Decimal(2)),
|
||||
(Decimal(70.0), Decimal(60.0), Decimal(3)),
|
||||
(Decimal(60.0), Decimal(80.0), Decimal(1)),
|
||||
],
|
||||
]
|
||||
|
||||
all_geoms = [
|
||||
[
|
||||
Point(items[0][0]),
|
||||
Point(*items[0][0]),
|
||||
MultiPoint(items[0]),
|
||||
LinearRing(items[0]),
|
||||
LineString(items[0]),
|
||||
MultiLineString(items),
|
||||
Polygon(items[0]),
|
||||
MultiPolygon(
|
||||
[
|
||||
Polygon(items[1]),
|
||||
Polygon(items[0], holes=items[1:]),
|
||||
]
|
||||
),
|
||||
GeometryCollection([Point(items[0][0]), Polygon(items[0])]),
|
||||
]
|
||||
for items in [
|
||||
items2d,
|
||||
items2d_mixed,
|
||||
items2d_decimal,
|
||||
items3d,
|
||||
items3d_mixed,
|
||||
items3d_decimal,
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("geoms", list(zip(*all_geoms)))
|
||||
def test_decimal(geoms):
|
||||
assert geoms[0] == geoms[1] == geoms[2]
|
||||
assert geoms[3] == geoms[4] == geoms[5]
|
||||
@@ -0,0 +1,98 @@
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from shapely import (
|
||||
GeometryCollection,
|
||||
LinearRing,
|
||||
LineString,
|
||||
MultiLineString,
|
||||
MultiPoint,
|
||||
MultiPolygon,
|
||||
Point,
|
||||
Polygon,
|
||||
)
|
||||
from shapely.geometry import mapping, shape
|
||||
from shapely.geometry.base import BaseGeometry, EmptyGeometry
|
||||
|
||||
|
||||
def empty_generator():
|
||||
return iter([])
|
||||
|
||||
|
||||
class TestEmptiness:
|
||||
def test_empty_class(self):
|
||||
with pytest.warns(FutureWarning):
|
||||
g = EmptyGeometry()
|
||||
assert g.is_empty
|
||||
|
||||
def test_empty_base(self):
|
||||
with pytest.warns(FutureWarning):
|
||||
g = BaseGeometry()
|
||||
assert g.is_empty
|
||||
|
||||
def test_empty_point(self):
|
||||
assert Point().is_empty
|
||||
|
||||
def test_empty_multipoint(self):
|
||||
assert MultiPoint().is_empty
|
||||
|
||||
def test_empty_geometry_collection(self):
|
||||
assert GeometryCollection().is_empty
|
||||
|
||||
def test_empty_linestring(self):
|
||||
assert LineString().is_empty
|
||||
assert LineString(None).is_empty
|
||||
assert LineString([]).is_empty
|
||||
assert LineString(empty_generator()).is_empty
|
||||
|
||||
def test_empty_multilinestring(self):
|
||||
assert MultiLineString([]).is_empty
|
||||
|
||||
def test_empty_polygon(self):
|
||||
assert Polygon().is_empty
|
||||
assert Polygon(None).is_empty
|
||||
assert Polygon([]).is_empty
|
||||
assert Polygon(empty_generator()).is_empty
|
||||
|
||||
def test_empty_multipolygon(self):
|
||||
assert MultiPolygon([]).is_empty
|
||||
|
||||
def test_empty_linear_ring(self):
|
||||
assert LinearRing().is_empty
|
||||
assert LinearRing(None).is_empty
|
||||
assert LinearRing([]).is_empty
|
||||
assert LinearRing(empty_generator()).is_empty
|
||||
|
||||
|
||||
def test_numpy_object_array():
|
||||
geoms = [Point(), GeometryCollection()]
|
||||
arr = np.empty(2, object)
|
||||
arr[:] = geoms
|
||||
|
||||
|
||||
def test_shape_empty():
|
||||
empty_mp = MultiPolygon()
|
||||
empty_json = mapping(empty_mp)
|
||||
empty_shape = shape(empty_json)
|
||||
assert empty_shape.is_empty
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"geom",
|
||||
[
|
||||
Point(),
|
||||
LineString(),
|
||||
Polygon(),
|
||||
MultiPoint(),
|
||||
MultiLineString(),
|
||||
MultiPolygon(),
|
||||
GeometryCollection(),
|
||||
LinearRing(),
|
||||
],
|
||||
)
|
||||
def test_empty_geometry_bounds(geom):
|
||||
"""The bounds of an empty geometry is a tuple of NaNs"""
|
||||
assert len(geom.bounds) == 4
|
||||
assert all(math.isnan(v) for v in geom.bounds)
|
||||
@@ -0,0 +1,237 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import shapely
|
||||
from shapely import LinearRing, LineString, MultiLineString, Point, Polygon
|
||||
from shapely.tests.common import all_types, all_types_z, ignore_invalid
|
||||
|
||||
|
||||
@pytest.mark.parametrize("geom", all_types + all_types_z)
|
||||
def test_equality(geom):
|
||||
assert geom == geom
|
||||
transformed = shapely.transform(geom, lambda x: x, include_z=True)
|
||||
assert geom == transformed
|
||||
assert not (geom != transformed)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"left, right",
|
||||
[
|
||||
# (slightly) different coordinate values
|
||||
(LineString([(0, 0), (1, 1)]), LineString([(0, 0), (1, 2)])),
|
||||
(LineString([(0, 0), (1, 1)]), LineString([(0, 0), (1, 1 + 1e-12)])),
|
||||
# different coordinate order
|
||||
(LineString([(0, 0), (1, 1)]), LineString([(1, 1), (0, 0)])),
|
||||
# different number of coordinates (but spatially equal)
|
||||
(LineString([(0, 0), (1, 1)]), LineString([(0, 0), (1, 1), (1, 1)])),
|
||||
(LineString([(0, 0), (1, 1)]), LineString([(0, 0), (0.5, 0.5), (1, 1)])),
|
||||
# different order of sub-geometries
|
||||
(
|
||||
MultiLineString([[(1, 1), (2, 2)], [(2, 2), (3, 3)]]),
|
||||
MultiLineString([[(2, 2), (3, 3)], [(1, 1), (2, 2)]]),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_equality_false(left, right):
|
||||
assert left != right
|
||||
|
||||
|
||||
with ignore_invalid():
|
||||
cases1 = [
|
||||
(LineString([(0, 1), (2, np.nan)]), LineString([(0, 1), (2, np.nan)])),
|
||||
(
|
||||
LineString([(0, 1), (np.nan, np.nan)]),
|
||||
LineString([(0, 1), (np.nan, np.nan)]),
|
||||
),
|
||||
(LineString([(np.nan, 1), (2, 3)]), LineString([(np.nan, 1), (2, 3)])),
|
||||
(LineString([(0, np.nan), (2, 3)]), LineString([(0, np.nan), (2, 3)])),
|
||||
(
|
||||
LineString([(np.nan, np.nan), (np.nan, np.nan)]),
|
||||
LineString([(np.nan, np.nan), (np.nan, np.nan)]),
|
||||
),
|
||||
# NaN as explicit Z coordinate
|
||||
# TODO: if first z is NaN -> considered as 2D -> tested below explicitly
|
||||
# (
|
||||
# LineString([(0, 1, np.nan), (2, 3, np.nan)]),
|
||||
# LineString([(0, 1, np.nan), (2, 3, np.nan)]),
|
||||
# ),
|
||||
(
|
||||
LineString([(0, 1, 2), (2, 3, np.nan)]),
|
||||
LineString([(0, 1, 2), (2, 3, np.nan)]),
|
||||
),
|
||||
# (
|
||||
# LineString([(0, 1, np.nan), (2, 3, 4)]),
|
||||
# LineString([(0, 1, np.nan), (2, 3, 4)]),
|
||||
# ),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("left, right", cases1)
|
||||
def test_equality_with_nan(left, right):
|
||||
# TODO currently those evaluate as not equal, but we are considering to change this
|
||||
# assert left == right
|
||||
assert not (left == right)
|
||||
# assert not (left != right)
|
||||
assert left != right
|
||||
|
||||
|
||||
with ignore_invalid():
|
||||
cases2 = [
|
||||
(
|
||||
LineString([(0, 1, np.nan), (2, 3, np.nan)]),
|
||||
LineString([(0, 1, np.nan), (2, 3, np.nan)]),
|
||||
),
|
||||
(
|
||||
LineString([(0, 1, np.nan), (2, 3, 4)]),
|
||||
LineString([(0, 1, np.nan), (2, 3, 4)]),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("left, right", cases2)
|
||||
def test_equality_with_nan_z(left, right):
|
||||
# TODO: those are currently considered equal because z dimension is ignored
|
||||
if shapely.geos_version < (3, 12, 0):
|
||||
assert left == right
|
||||
assert not (left != right)
|
||||
else:
|
||||
# on GEOS main z dimension is not ignored -> NaNs cause inequality
|
||||
assert left != right
|
||||
|
||||
|
||||
with ignore_invalid():
|
||||
cases3 = [
|
||||
(LineString([(0, np.nan), (2, 3)]), LineString([(0, 1), (2, 3)])),
|
||||
(LineString([(0, 1), (2, np.nan)]), LineString([(0, 1), (2, 3)])),
|
||||
(LineString([(0, 1, np.nan), (2, 3, 4)]), LineString([(0, 1, 2), (2, 3, 4)])),
|
||||
(LineString([(0, 1, 2), (2, 3, np.nan)]), LineString([(0, 1, 2), (2, 3, 4)])),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("left, right", cases3)
|
||||
def test_equality_with_nan_false(left, right):
|
||||
assert left != right
|
||||
|
||||
|
||||
def test_equality_with_nan_z_false():
|
||||
with ignore_invalid():
|
||||
left = LineString([(0, 1, np.nan), (2, 3, np.nan)])
|
||||
right = LineString([(0, 1, np.nan), (2, 3, 4)])
|
||||
|
||||
if shapely.geos_version < (3, 10, 0):
|
||||
# GEOS <= 3.9 fill the NaN with 0, so the z dimension is different
|
||||
# assert left != right
|
||||
# however, has_z still returns False, so z dimension is ignored in .coords
|
||||
assert left == right
|
||||
elif shapely.geos_version < (3, 12, 0):
|
||||
# GEOS 3.10-3.11 ignore NaN for Z also when explicitly created with 3D
|
||||
# and so the geometries are considered as 2D (and thus z dimension is ignored)
|
||||
assert left == right
|
||||
else:
|
||||
assert left != right
|
||||
|
||||
|
||||
def test_equality_z():
|
||||
# different dimensionality
|
||||
geom1 = Point(0, 1)
|
||||
geom2 = Point(0, 1, 0)
|
||||
assert geom1 != geom2
|
||||
|
||||
# different dimensionality with NaN z
|
||||
geom2 = Point(0, 1, np.nan)
|
||||
if shapely.geos_version < (3, 10, 0):
|
||||
# GEOS < 3.8 fill the NaN with 0, so the z dimension is different
|
||||
# assert geom1 != geom2
|
||||
# however, has_z still returns False, so z dimension is ignored in .coords
|
||||
assert geom1 == geom2
|
||||
elif shapely.geos_version < (3, 12, 0):
|
||||
# GEOS 3.10-3.11 ignore NaN for Z also when explicitly created with 3D
|
||||
# and so the geometries are considered as 2D (and thus z dimension is ignored)
|
||||
assert geom1 == geom2
|
||||
else:
|
||||
assert geom1 != geom2
|
||||
|
||||
|
||||
def test_equality_exact_type():
|
||||
# geometries with different type but same coord seq are not equal
|
||||
geom1 = LineString([(0, 0), (1, 1), (0, 1), (0, 0)])
|
||||
geom2 = LinearRing([(0, 0), (1, 1), (0, 1), (0, 0)])
|
||||
geom3 = Polygon([(0, 0), (1, 1), (0, 1), (0, 0)])
|
||||
assert geom1 != geom2
|
||||
assert geom1 != geom3
|
||||
assert geom2 != geom3
|
||||
|
||||
# empty with different type
|
||||
geom1 = shapely.from_wkt("POINT EMPTY")
|
||||
geom2 = shapely.from_wkt("LINESTRING EMPTY")
|
||||
assert geom1 != geom2
|
||||
|
||||
|
||||
def test_equality_polygon():
|
||||
# different exterior rings
|
||||
geom1 = shapely.from_wkt("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")
|
||||
geom2 = shapely.from_wkt("POLYGON ((0 0, 10 0, 10 10, 0 15, 0 0))")
|
||||
assert geom1 != geom2
|
||||
|
||||
# different number of holes
|
||||
geom1 = shapely.from_wkt(
|
||||
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 1))"
|
||||
)
|
||||
geom2 = shapely.from_wkt(
|
||||
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 1), (3 3, 4 3, 4 4, 3 3))"
|
||||
)
|
||||
assert geom1 != geom2
|
||||
|
||||
# different order of holes
|
||||
geom1 = shapely.from_wkt(
|
||||
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (3 3, 4 3, 4 4, 3 3), (1 1, 2 1, 2 2, 1 1))"
|
||||
)
|
||||
geom2 = shapely.from_wkt(
|
||||
"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 2 1, 2 2, 1 1), (3 3, 4 3, 4 4, 3 3))"
|
||||
)
|
||||
assert geom1 != geom2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("geom", all_types)
|
||||
def test_comparison_notimplemented(geom):
|
||||
# comparing to a non-geometry class should return NotImplemented in __eq__
|
||||
# to ensure proper delegation to other (eg to ensure comparison of scalar
|
||||
# with array works)
|
||||
# https://github.com/shapely/shapely/issues/1056
|
||||
assert geom.__eq__(1) is NotImplemented
|
||||
|
||||
# with array
|
||||
arr = np.array([geom, geom], dtype=object)
|
||||
|
||||
result = arr == geom
|
||||
assert isinstance(result, np.ndarray)
|
||||
assert result.all()
|
||||
|
||||
result = geom == arr
|
||||
assert isinstance(result, np.ndarray)
|
||||
assert result.all()
|
||||
|
||||
result = arr != geom
|
||||
assert isinstance(result, np.ndarray)
|
||||
assert not result.any()
|
||||
|
||||
result = geom != arr
|
||||
assert isinstance(result, np.ndarray)
|
||||
assert not result.any()
|
||||
|
||||
|
||||
def test_comparison_not_supported():
|
||||
geom1 = Point(1, 1)
|
||||
geom2 = Point(2, 2)
|
||||
|
||||
with pytest.raises(TypeError, match="not supported between instances"):
|
||||
geom1 > geom2
|
||||
|
||||
with pytest.raises(TypeError, match="not supported between instances"):
|
||||
geom1 < geom2
|
||||
|
||||
with pytest.raises(TypeError, match="not supported between instances"):
|
||||
geom1 >= geom2
|
||||
|
||||
with pytest.raises(TypeError, match="not supported between instances"):
|
||||
geom1 <= geom2
|
||||
@@ -0,0 +1,116 @@
|
||||
import pytest
|
||||
|
||||
from shapely import Point, Polygon
|
||||
from shapely.geos import geos_version
|
||||
|
||||
|
||||
def test_format_invalid():
|
||||
# check invalid spec formats
|
||||
pt = Point(1, 2)
|
||||
test_list = [
|
||||
("5G", ValueError, "invalid format specifier"),
|
||||
(".f", ValueError, "invalid format specifier"),
|
||||
("0.2e", ValueError, "invalid format specifier"),
|
||||
(".1x", ValueError, "hex representation does not specify precision"),
|
||||
]
|
||||
for format_spec, err, match in test_list:
|
||||
with pytest.raises(err, match=match):
|
||||
format(pt, format_spec)
|
||||
|
||||
|
||||
def test_format_point():
|
||||
# example coordinate data
|
||||
xy1 = (0.12345678901234567, 1.2345678901234567e10)
|
||||
xy2 = (-169.910918, -18.997564)
|
||||
xyz3 = (630084, 4833438, 76)
|
||||
|
||||
# list of tuples to test; see structure at top of the for-loop
|
||||
test_list = [
|
||||
(".0f", xy1, "POINT (0 12345678901)", True),
|
||||
(".1f", xy1, "POINT (0.1 12345678901.2)", True),
|
||||
("0.2f", xy2, "POINT (-169.91 -19.00)", True),
|
||||
(".3F", (float("inf"), -float("inf")), "POINT (INF -INF)", True),
|
||||
]
|
||||
if geos_version < (3, 10, 0):
|
||||
# 'g' format varies depending on GEOS version
|
||||
test_list += [
|
||||
(".1g", xy1, "POINT (0.1 1e+10)", True),
|
||||
(".6G", xy1, "POINT (0.123457 1.23457E+10)", True),
|
||||
("0.12g", xy1, "POINT (0.123456789012 12345678901.2)", True),
|
||||
("0.4g", xy2, "POINT (-169.9 -19)", True),
|
||||
]
|
||||
else:
|
||||
test_list += [
|
||||
(".1g", xy1, "POINT (0.1 12345678901.2)", False),
|
||||
(".6G", xy1, "POINT (0.123457 12345678901.234568)", False),
|
||||
("0.12g", xy1, "POINT (0.123456789012 12345678901.234568)", False),
|
||||
("g", xy2, "POINT (-169.910918 -18.997564)", False),
|
||||
("0.2g", xy2, "POINT (-169.91 -19)", False),
|
||||
]
|
||||
# without precisions test GEOS rounding_precision=-1; different than Python
|
||||
test_list += [
|
||||
("f", (1, 2), f"POINT ({1:.16f} {2:.16f})", False),
|
||||
("F", xyz3, "POINT Z ({:.16f} {:.16f} {:.16f})".format(*xyz3), False),
|
||||
("g", xyz3, "POINT Z (630084 4833438 76)", False),
|
||||
]
|
||||
for format_spec, coords, expt_wkt, same_python_float in test_list:
|
||||
pt = Point(*coords)
|
||||
# basic checks
|
||||
assert f"{pt}" == pt.wkt
|
||||
assert format(pt, "") == pt.wkt
|
||||
assert format(pt, "x") == pt.wkb_hex.lower()
|
||||
assert format(pt, "X") == pt.wkb_hex
|
||||
# check formatted WKT to expected
|
||||
assert format(pt, format_spec) == expt_wkt, format_spec
|
||||
# check Python's format consistency
|
||||
text_coords = expt_wkt[expt_wkt.index("(") + 1 : expt_wkt.index(")")]
|
||||
is_same = []
|
||||
for coord, expt_coord in zip(coords, text_coords.split()):
|
||||
py_fmt_float = format(float(coord), format_spec)
|
||||
if same_python_float:
|
||||
assert py_fmt_float == expt_coord, format_spec
|
||||
else:
|
||||
is_same.append(py_fmt_float == expt_coord)
|
||||
if not same_python_float:
|
||||
assert not all(is_same), f"{format_spec!r} with {expt_wkt}"
|
||||
|
||||
|
||||
def test_format_polygon():
|
||||
# check basic cases
|
||||
poly = Point(0, 0).buffer(10, 2)
|
||||
assert f"{poly}" == poly.wkt
|
||||
assert format(poly, "") == poly.wkt
|
||||
assert format(poly, "x") == poly.wkb_hex.lower()
|
||||
assert format(poly, "X") == poly.wkb_hex
|
||||
|
||||
# Use f-strings with extra characters and rounding precision
|
||||
if geos_version < (3, 13, 0):
|
||||
assert f"<{poly:.2f}>" == (
|
||||
"<POLYGON ((10.00 0.00, 7.07 -7.07, 0.00 -10.00, -7.07 -7.07, "
|
||||
"-10.00 -0.00, -7.07 7.07, -0.00 10.00, 7.07 7.07, 10.00 0.00))>"
|
||||
)
|
||||
else:
|
||||
assert f"<{poly:.2f}>" == (
|
||||
"<POLYGON ((10.00 0.00, 7.07 -7.07, 0.00 -10.00, -7.07 -7.07, "
|
||||
"-10.00 0.00, -7.07 7.07, 0.00 10.00, 7.07 7.07, 10.00 0.00))>"
|
||||
)
|
||||
|
||||
# 'g' format varies depending on GEOS version
|
||||
if geos_version < (3, 10, 0):
|
||||
assert f"{poly:.2G}" == (
|
||||
"POLYGON ((10 0, 7.1 -7.1, 1.6E-14 -10, -7.1 -7.1, "
|
||||
"-10 -3.2E-14, -7.1 7.1, -4.6E-14 10, 7.1 7.1, 10 0))"
|
||||
)
|
||||
else:
|
||||
assert f"{poly:.2G}" == (
|
||||
"POLYGON ((10 0, 7.07 -7.07, 0 -10, -7.07 -7.07, "
|
||||
"-10 0, -7.07 7.07, 0 10, 7.07 7.07, 10 0))"
|
||||
)
|
||||
|
||||
# check empty
|
||||
empty = Polygon()
|
||||
assert f"{empty}" == "POLYGON EMPTY"
|
||||
assert format(empty, "") == empty.wkt
|
||||
assert format(empty, ".2G") == empty.wkt
|
||||
assert format(empty, "x") == empty.wkb_hex.lower()
|
||||
assert format(empty, "X") == empty.wkb_hex
|
||||
@@ -0,0 +1,281 @@
|
||||
import platform
|
||||
import weakref
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import shapely
|
||||
from shapely import (
|
||||
GeometryCollection,
|
||||
LinearRing,
|
||||
LineString,
|
||||
MultiLineString,
|
||||
MultiPoint,
|
||||
MultiPolygon,
|
||||
Point,
|
||||
Polygon,
|
||||
)
|
||||
from shapely.errors import ShapelyDeprecationWarning
|
||||
from shapely.testing import assert_geometries_equal
|
||||
|
||||
|
||||
def test_polygon():
|
||||
assert bool(Polygon()) is False
|
||||
|
||||
|
||||
def test_linestring():
|
||||
assert bool(LineString()) is False
|
||||
|
||||
|
||||
def test_point():
|
||||
assert bool(Point()) is False
|
||||
|
||||
|
||||
def test_geometry_collection():
|
||||
assert bool(GeometryCollection()) is False
|
||||
|
||||
|
||||
geometries_all_types = [
|
||||
Point(1, 1),
|
||||
LinearRing([(0, 0), (1, 1), (0, 1), (0, 0)]),
|
||||
LineString([(0, 0), (1, 1), (0, 1), (0, 0)]),
|
||||
Polygon([(0, 0), (1, 1), (0, 1), (0, 0)]),
|
||||
MultiPoint([(1, 1)]),
|
||||
MultiLineString([[(0, 0), (1, 1), (0, 1), (0, 0)]]),
|
||||
MultiPolygon([Polygon([(0, 0), (1, 1), (0, 1), (0, 0)])]),
|
||||
GeometryCollection([Point(1, 1)]),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
platform.python_implementation() == "PyPy",
|
||||
reason="Setting custom attributes doesn't fail on PyPy",
|
||||
)
|
||||
@pytest.mark.parametrize("geom", geometries_all_types)
|
||||
def test_setattr_disallowed(geom):
|
||||
with pytest.raises(AttributeError):
|
||||
geom.name = "test"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("geom", geometries_all_types)
|
||||
def test_weakrefable(geom):
|
||||
_ = weakref.ref(geom)
|
||||
|
||||
|
||||
def test_base_class_not_callable():
|
||||
with pytest.raises(TypeError):
|
||||
shapely.Geometry("POINT (1 1)")
|
||||
|
||||
|
||||
def test_GeometryType_deprecated():
|
||||
geom = Point(1, 1)
|
||||
|
||||
with pytest.warns(ShapelyDeprecationWarning):
|
||||
geom_type = geom.geometryType()
|
||||
|
||||
assert geom_type == geom.geom_type
|
||||
|
||||
|
||||
def test_type_deprecated():
|
||||
geom = Point(1, 1)
|
||||
|
||||
with pytest.warns(ShapelyDeprecationWarning):
|
||||
geom_type = geom.type
|
||||
|
||||
assert geom_type == geom.geom_type
|
||||
|
||||
|
||||
@pytest.mark.skipif(shapely.geos_version < (3, 10, 0), reason="GEOS < 3.10")
|
||||
def test_segmentize():
|
||||
line = LineString([(0, 0), (0, 10)])
|
||||
result = line.segmentize(max_segment_length=5)
|
||||
assert result.equals(LineString([(0, 0), (0, 5), (0, 10)]))
|
||||
|
||||
|
||||
@pytest.mark.skipif(shapely.geos_version < (3, 7, 0), reason="GEOS < 3.7")
|
||||
def test_reverse():
|
||||
coords = [(0, 0), (1, 2)]
|
||||
line = LineString(coords)
|
||||
result = line.reverse()
|
||||
assert result.coords[:] == coords[::-1]
|
||||
|
||||
|
||||
@pytest.mark.skipif(shapely.geos_version < (3, 9, 0), reason="GEOS < 3.9")
|
||||
@pytest.mark.parametrize(
|
||||
"op", ["union", "intersection", "difference", "symmetric_difference"]
|
||||
)
|
||||
@pytest.mark.parametrize("grid_size", [0, 1, 2])
|
||||
def test_binary_op_grid_size(op, grid_size):
|
||||
geom1 = shapely.box(0, 0, 2.5, 2.5)
|
||||
geom2 = shapely.box(2, 2, 3, 3)
|
||||
|
||||
result = getattr(geom1, op)(geom2, grid_size=grid_size)
|
||||
expected = getattr(shapely, op)(geom1, geom2, grid_size=grid_size)
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.skipif(shapely.geos_version < (3, 10, 0), reason="GEOS < 3.10")
|
||||
def test_dwithin():
|
||||
point = Point(1, 1)
|
||||
line = LineString([(0, 0), (0, 10)])
|
||||
assert point.dwithin(line, 0.5) is False
|
||||
assert point.dwithin(line, 1.5) is True
|
||||
|
||||
|
||||
def test_contains_properly():
|
||||
polygon = Polygon([(0, 0), (10, 10), (10, -10)])
|
||||
line = LineString([(0, 0), (10, 0)])
|
||||
assert polygon.contains_properly(line) is False
|
||||
assert polygon.contains(line) is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"op", ["convex_hull", "envelope", "oriented_envelope", "minimum_rotated_rectangle"]
|
||||
)
|
||||
def test_constructive_properties(op):
|
||||
geom = LineString([(0, 0), (0, 10), (10, 10)])
|
||||
result = getattr(geom, op)
|
||||
expected = getattr(shapely, op)(geom)
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"op",
|
||||
[
|
||||
"crosses",
|
||||
"contains",
|
||||
"contains_properly",
|
||||
"covered_by",
|
||||
"covers",
|
||||
"disjoint",
|
||||
"equals",
|
||||
"intersects",
|
||||
"overlaps",
|
||||
"touches",
|
||||
"within",
|
||||
],
|
||||
)
|
||||
def test_array_argument_binary_predicates(op):
|
||||
polygon = Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)])
|
||||
points = shapely.points([(0, 0), (0.5, 0.5), (1, 1)])
|
||||
|
||||
result = getattr(polygon, op)(points)
|
||||
assert isinstance(result, np.ndarray)
|
||||
expected = np.array([getattr(polygon, op)(p) for p in points], dtype=bool)
|
||||
np.testing.assert_array_equal(result, expected)
|
||||
|
||||
# check scalar
|
||||
result = getattr(polygon, op)(points[0])
|
||||
assert type(result) is bool
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"op, kwargs",
|
||||
[
|
||||
pytest.param(
|
||||
"dwithin",
|
||||
dict(distance=0.5),
|
||||
marks=pytest.mark.skipif(
|
||||
shapely.geos_version < (3, 10, 0), reason="GEOS < 3.10"
|
||||
),
|
||||
),
|
||||
("equals_exact", dict(tolerance=0.01)),
|
||||
("relate_pattern", dict(pattern="T*F**F***")),
|
||||
],
|
||||
)
|
||||
def test_array_argument_binary_predicates2(op, kwargs):
|
||||
polygon = Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)])
|
||||
points = shapely.points([(0, 0), (0.5, 0.5), (1, 1)])
|
||||
|
||||
result = getattr(polygon, op)(points, **kwargs)
|
||||
assert isinstance(result, np.ndarray)
|
||||
expected = np.array([getattr(polygon, op)(p, **kwargs) for p in points], dtype=bool)
|
||||
np.testing.assert_array_equal(result, expected)
|
||||
|
||||
# check scalar
|
||||
result = getattr(polygon, op)(points[0], **kwargs)
|
||||
assert type(result) is bool
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"op",
|
||||
[
|
||||
"difference",
|
||||
"intersection",
|
||||
"symmetric_difference",
|
||||
"union",
|
||||
],
|
||||
)
|
||||
def test_array_argument_binary_geo(op):
|
||||
box = Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)])
|
||||
polygons = shapely.buffer(shapely.points([(0, 0), (0.5, 0.5), (1, 1)]), 0.5)
|
||||
|
||||
result = getattr(box, op)(polygons)
|
||||
assert isinstance(result, np.ndarray)
|
||||
expected = np.array([getattr(box, op)(g) for g in polygons], dtype=object)
|
||||
assert_geometries_equal(result, expected)
|
||||
|
||||
# check scalar
|
||||
result = getattr(box, op)(polygons[0])
|
||||
assert isinstance(result, (Polygon, MultiPolygon))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("op", ["distance", "hausdorff_distance"])
|
||||
def test_array_argument_float(op):
|
||||
polygon = Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)])
|
||||
points = shapely.points([(0, 0), (0.5, 0.5), (1, 1)])
|
||||
|
||||
result = getattr(polygon, op)(points)
|
||||
assert isinstance(result, np.ndarray)
|
||||
expected = np.array([getattr(polygon, op)(p) for p in points], dtype="float64")
|
||||
np.testing.assert_array_equal(result, expected)
|
||||
|
||||
# check scalar
|
||||
result = getattr(polygon, op)(points[0])
|
||||
assert type(result) is float
|
||||
|
||||
|
||||
@pytest.mark.parametrize("op", ["line_interpolate_point", "interpolate"])
|
||||
def test_array_argument_linear_point(op):
|
||||
line = LineString([(0, 0), (0, 1), (1, 1)])
|
||||
distances = np.array([0, 0.5, 1])
|
||||
|
||||
result = getattr(line, op)(distances)
|
||||
assert isinstance(result, np.ndarray)
|
||||
expected = np.array(
|
||||
[line.line_interpolate_point(d) for d in distances], dtype=object
|
||||
)
|
||||
assert_geometries_equal(result, expected)
|
||||
|
||||
# check scalar
|
||||
result = getattr(line, op)(distances[0])
|
||||
assert isinstance(result, Point)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("op", ["line_locate_point", "project"])
|
||||
def test_array_argument_linear_float(op):
|
||||
line = LineString([(0, 0), (0, 1), (1, 1)])
|
||||
points = shapely.points([(0, 0), (0.5, 0.5), (1, 1)])
|
||||
|
||||
result = getattr(line, op)(points)
|
||||
assert isinstance(result, np.ndarray)
|
||||
expected = np.array([line.line_locate_point(p) for p in points], dtype="float64")
|
||||
np.testing.assert_array_equal(result, expected)
|
||||
|
||||
# check scalar
|
||||
result = getattr(line, op)(points[0])
|
||||
assert type(result) is float
|
||||
|
||||
|
||||
def test_array_argument_buffer():
|
||||
point = Point(1, 1)
|
||||
distances = np.array([0, 0.5, 1])
|
||||
|
||||
result = point.buffer(distances)
|
||||
assert isinstance(result, np.ndarray)
|
||||
expected = np.array([point.buffer(d) for d in distances], dtype=object)
|
||||
assert_geometries_equal(result, expected)
|
||||
|
||||
# check scalar
|
||||
result = point.buffer(distances[0])
|
||||
assert isinstance(result, Polygon)
|
||||
@@ -0,0 +1,28 @@
|
||||
import pytest
|
||||
|
||||
import shapely
|
||||
from shapely.affinity import translate
|
||||
from shapely.geometry import GeometryCollection, LineString, MultiPoint, Point
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"geom",
|
||||
[
|
||||
Point(1, 2),
|
||||
MultiPoint([(1, 2), (3, 4)]),
|
||||
LineString([(1, 2), (3, 4)]),
|
||||
Point(0, 0).buffer(1.0),
|
||||
GeometryCollection([Point(1, 2), LineString([(1, 2), (3, 4)])]),
|
||||
],
|
||||
ids=[
|
||||
"Point",
|
||||
"MultiPoint",
|
||||
"LineString",
|
||||
"Polygon",
|
||||
"GeometryCollection",
|
||||
],
|
||||
)
|
||||
def test_hash(geom):
|
||||
h1 = hash(geom)
|
||||
assert h1 == hash(shapely.from_wkb(geom.wkb))
|
||||
assert h1 != hash(translate(geom, 1.0, 2.0))
|
||||
@@ -0,0 +1,213 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import shapely
|
||||
from shapely import LinearRing, LineString, Point
|
||||
from shapely.coords import CoordinateSequence
|
||||
|
||||
|
||||
def test_from_coordinate_sequence():
|
||||
# From coordinate tuples
|
||||
line = LineString([(1.0, 2.0), (3.0, 4.0)])
|
||||
assert len(line.coords) == 2
|
||||
assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
|
||||
|
||||
line = LineString([(1.0, 2.0), (3.0, 4.0)])
|
||||
assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
|
||||
|
||||
|
||||
def test_from_coordinate_sequence_3D():
|
||||
line = LineString([(1.0, 2.0, 3.0), (3.0, 4.0, 5.0)])
|
||||
assert line.has_z
|
||||
assert line.coords[:] == [(1.0, 2.0, 3.0), (3.0, 4.0, 5.0)]
|
||||
|
||||
|
||||
def test_from_points():
|
||||
# From Points
|
||||
line = LineString([Point(1.0, 2.0), Point(3.0, 4.0)])
|
||||
assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
|
||||
|
||||
line = LineString([Point(1.0, 2.0), Point(3.0, 4.0)])
|
||||
assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
|
||||
|
||||
|
||||
def test_from_mix():
|
||||
# From mix of tuples and Points
|
||||
line = LineString([Point(1.0, 2.0), (2.0, 3.0), Point(3.0, 4.0)])
|
||||
assert line.coords[:] == [(1.0, 2.0), (2.0, 3.0), (3.0, 4.0)]
|
||||
|
||||
|
||||
def test_from_linestring():
|
||||
# From another linestring
|
||||
line = LineString([(1.0, 2.0), (3.0, 4.0)])
|
||||
copy = LineString(line)
|
||||
assert copy.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
|
||||
assert copy.geom_type == "LineString"
|
||||
|
||||
|
||||
def test_from_linearring():
|
||||
coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
ring = LinearRing(coords)
|
||||
copy = LineString(ring)
|
||||
assert copy.coords[:] == coords
|
||||
assert copy.geom_type == "LineString"
|
||||
|
||||
|
||||
def test_from_linestring_z():
|
||||
coords = [(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]
|
||||
line = LineString(coords)
|
||||
copy = LineString(line)
|
||||
assert copy.coords[:] == coords
|
||||
assert copy.geom_type == "LineString"
|
||||
|
||||
|
||||
def test_from_generator():
|
||||
gen = (coord for coord in [(1.0, 2.0), (3.0, 4.0)])
|
||||
line = LineString(gen)
|
||||
assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
|
||||
|
||||
|
||||
def test_from_empty():
|
||||
line = LineString()
|
||||
assert line.is_empty
|
||||
assert isinstance(line.coords, CoordinateSequence)
|
||||
assert line.coords[:] == []
|
||||
|
||||
line = LineString([])
|
||||
assert line.is_empty
|
||||
assert isinstance(line.coords, CoordinateSequence)
|
||||
assert line.coords[:] == []
|
||||
|
||||
|
||||
def test_from_numpy():
|
||||
# Construct from a numpy array
|
||||
line = LineString(np.array([[1.0, 2.0], [3.0, 4.0]]))
|
||||
assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
|
||||
|
||||
|
||||
def test_numpy_empty_linestring_coords():
|
||||
# Check empty
|
||||
line = LineString([])
|
||||
la = np.asarray(line.coords)
|
||||
|
||||
assert la.shape == (0, 2)
|
||||
|
||||
|
||||
def test_numpy_object_array():
|
||||
geom = LineString([(0.0, 0.0), (0.0, 1.0)])
|
||||
ar = np.empty(1, object)
|
||||
ar[:] = [geom]
|
||||
assert ar[0] == geom
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:Creating an ndarray from ragged nested sequences:")
|
||||
def test_from_invalid_dim():
|
||||
# TODO(shapely-2.0) better error message?
|
||||
# pytest.raises(ValueError, match="at least 2 coordinate tuples|at least 2 coordinates"):
|
||||
with pytest.raises(shapely.GEOSException):
|
||||
LineString([(1, 2)])
|
||||
|
||||
# exact error depends on numpy version
|
||||
with pytest.raises((ValueError, TypeError)):
|
||||
LineString([(1, 2, 3), (4, 5)])
|
||||
|
||||
with pytest.raises((ValueError, TypeError)):
|
||||
LineString([(1, 2), (3, 4, 5)])
|
||||
|
||||
msg = r"The ordinate \(last\) dimension should be 2 or 3, got {}"
|
||||
with pytest.raises(ValueError, match=msg.format(4)):
|
||||
LineString([(1, 2, 3, 4), (4, 5, 6, 7)])
|
||||
|
||||
with pytest.raises(ValueError, match=msg.format(1)):
|
||||
LineString([(1,), (4,)])
|
||||
|
||||
|
||||
def test_from_single_coordinate():
|
||||
"""Test for issue #486"""
|
||||
coords = [[-122.185933073564, 37.3629353839073]]
|
||||
with pytest.raises(shapely.GEOSException):
|
||||
ls = LineString(coords)
|
||||
ls.geom_type # caused segfault before fix
|
||||
|
||||
|
||||
class TestLineString:
|
||||
def test_linestring(self):
|
||||
|
||||
# From coordinate tuples
|
||||
line = LineString([(1.0, 2.0), (3.0, 4.0)])
|
||||
assert len(line.coords) == 2
|
||||
assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
|
||||
|
||||
# Bounds
|
||||
assert line.bounds == (1.0, 2.0, 3.0, 4.0)
|
||||
|
||||
# Coordinate access
|
||||
assert tuple(line.coords) == ((1.0, 2.0), (3.0, 4.0))
|
||||
assert line.coords[0] == (1.0, 2.0)
|
||||
assert line.coords[1] == (3.0, 4.0)
|
||||
with pytest.raises(IndexError):
|
||||
line.coords[2] # index out of range
|
||||
|
||||
# Geo interface
|
||||
assert line.__geo_interface__ == {
|
||||
"type": "LineString",
|
||||
"coordinates": ((1.0, 2.0), (3.0, 4.0)),
|
||||
}
|
||||
|
||||
def test_linestring_empty(self):
|
||||
# Test Non-operability of Null geometry
|
||||
l_null = LineString()
|
||||
assert l_null.wkt == "LINESTRING EMPTY"
|
||||
assert l_null.length == 0.0
|
||||
|
||||
def test_equals_argument_order(self):
|
||||
"""
|
||||
Test equals predicate functions correctly regardless of the order
|
||||
of the inputs. See issue #317.
|
||||
"""
|
||||
coords = ((0, 0), (1, 0), (1, 1), (0, 0))
|
||||
ls = LineString(coords)
|
||||
lr = LinearRing(coords)
|
||||
|
||||
assert ls.__eq__(lr) is False # previously incorrectly returned True
|
||||
assert lr.__eq__(ls) is False
|
||||
assert (ls == lr) is False
|
||||
assert (lr == ls) is False
|
||||
|
||||
ls_clone = LineString(coords)
|
||||
lr_clone = LinearRing(coords)
|
||||
|
||||
assert ls.__eq__(ls_clone) is True
|
||||
assert lr.__eq__(lr_clone) is True
|
||||
assert (ls == ls_clone) is True
|
||||
assert (lr == lr_clone) is True
|
||||
|
||||
def test_numpy_linestring_coords(self):
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
line = LineString([(1.0, 2.0), (3.0, 4.0)])
|
||||
expected = np.array([[1.0, 2.0], [3.0, 4.0]])
|
||||
|
||||
# Coordinate sequences can be adapted as well
|
||||
la = np.asarray(line.coords)
|
||||
assert_array_equal(la, expected)
|
||||
|
||||
|
||||
def test_linestring_immutable():
|
||||
line = LineString([(1.0, 2.0), (3.0, 4.0)])
|
||||
|
||||
with pytest.raises(AttributeError):
|
||||
line.coords = [(-1.0, -1.0), (1.0, 1.0)]
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
line.coords[0] = (-1.0, -1.0)
|
||||
|
||||
|
||||
def test_linestring_array_coercion():
|
||||
# don't convert to array of coordinates, keep objects
|
||||
line = LineString([(1.0, 2.0), (3.0, 4.0)])
|
||||
arr = np.array(line)
|
||||
assert arr.ndim == 0
|
||||
assert arr.size == 1
|
||||
assert arr.dtype == np.dtype("object")
|
||||
assert arr.item() == line
|
||||
@@ -0,0 +1,11 @@
|
||||
import numpy as np
|
||||
|
||||
test_int_types = [int, np.int16, np.int32, np.int64]
|
||||
|
||||
|
||||
class MultiGeometryTestCase:
|
||||
def subgeom_access_test(self, cls, geoms):
|
||||
geom = cls(geoms)
|
||||
for t in test_int_types:
|
||||
for i, g in enumerate(geoms):
|
||||
assert geom.geoms[t(i)] == geoms[i]
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from shapely import LineString, MultiLineString
|
||||
from shapely.errors import EmptyPartError
|
||||
from shapely.geometry.base import dump_coords
|
||||
from shapely.tests.geometry.test_multi import MultiGeometryTestCase
|
||||
|
||||
|
||||
class TestMultiLineString(MultiGeometryTestCase):
|
||||
def test_multilinestring(self):
|
||||
|
||||
# From coordinate tuples
|
||||
geom = MultiLineString([[(1.0, 2.0), (3.0, 4.0)]])
|
||||
assert isinstance(geom, MultiLineString)
|
||||
assert len(geom.geoms) == 1
|
||||
assert dump_coords(geom) == [[(1.0, 2.0), (3.0, 4.0)]]
|
||||
|
||||
# From lines
|
||||
a = LineString([(1.0, 2.0), (3.0, 4.0)])
|
||||
ml = MultiLineString([a])
|
||||
assert len(ml.geoms) == 1
|
||||
assert dump_coords(ml) == [[(1.0, 2.0), (3.0, 4.0)]]
|
||||
|
||||
# From another multi-line
|
||||
ml2 = MultiLineString(ml)
|
||||
assert len(ml2.geoms) == 1
|
||||
assert dump_coords(ml2) == [[(1.0, 2.0), (3.0, 4.0)]]
|
||||
|
||||
# Sub-geometry Access
|
||||
geom = MultiLineString([(((0.0, 0.0), (1.0, 2.0)))])
|
||||
assert isinstance(geom.geoms[0], LineString)
|
||||
assert dump_coords(geom.geoms[0]) == [(0.0, 0.0), (1.0, 2.0)]
|
||||
with pytest.raises(IndexError): # index out of range
|
||||
geom.geoms[1]
|
||||
|
||||
# Geo interface
|
||||
assert geom.__geo_interface__ == {
|
||||
"type": "MultiLineString",
|
||||
"coordinates": (((0.0, 0.0), (1.0, 2.0)),),
|
||||
}
|
||||
|
||||
def test_from_multilinestring_z(self):
|
||||
coords1 = [(0.0, 1.0, 2.0), (3.0, 4.0, 5.0)]
|
||||
coords2 = [(6.0, 7.0, 8.0), (9.0, 10.0, 11.0)]
|
||||
|
||||
# From coordinate tuples
|
||||
ml = MultiLineString([coords1, coords2])
|
||||
copy = MultiLineString(ml)
|
||||
assert isinstance(copy, MultiLineString)
|
||||
assert copy.geom_type == "MultiLineString"
|
||||
assert len(copy.geoms) == 2
|
||||
assert dump_coords(copy.geoms[0]) == coords1
|
||||
assert dump_coords(copy.geoms[1]) == coords2
|
||||
|
||||
def test_numpy(self):
|
||||
# Construct from a numpy array
|
||||
geom = MultiLineString([np.array(((0.0, 0.0), (1.0, 2.0)))])
|
||||
assert isinstance(geom, MultiLineString)
|
||||
assert len(geom.geoms) == 1
|
||||
assert dump_coords(geom) == [[(0.0, 0.0), (1.0, 2.0)]]
|
||||
|
||||
def test_subgeom_access(self):
|
||||
line0 = LineString([(0.0, 1.0), (2.0, 3.0)])
|
||||
line1 = LineString([(4.0, 5.0), (6.0, 7.0)])
|
||||
self.subgeom_access_test(MultiLineString, [line0, line1])
|
||||
|
||||
def test_create_multi_with_empty_component(self):
|
||||
msg = "Can't create MultiLineString with empty component"
|
||||
with pytest.raises(EmptyPartError, match=msg):
|
||||
MultiLineString([LineString([(0, 0), (1, 1), (2, 2)]), LineString()]).wkt
|
||||
|
||||
|
||||
def test_numpy_object_array():
|
||||
geom = MultiLineString([[[5.0, 6.0], [7.0, 8.0]]])
|
||||
ar = np.empty(1, object)
|
||||
ar[:] = [geom]
|
||||
assert ar[0] == geom
|
||||
@@ -0,0 +1,78 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from shapely import MultiPoint, Point
|
||||
from shapely.errors import EmptyPartError
|
||||
from shapely.geometry.base import dump_coords
|
||||
from shapely.tests.geometry.test_multi import MultiGeometryTestCase
|
||||
|
||||
|
||||
class TestMultiPoint(MultiGeometryTestCase):
|
||||
def test_multipoint(self):
|
||||
|
||||
# From coordinate tuples
|
||||
geom = MultiPoint([(1.0, 2.0), (3.0, 4.0)])
|
||||
assert len(geom.geoms) == 2
|
||||
assert dump_coords(geom) == [[(1.0, 2.0)], [(3.0, 4.0)]]
|
||||
|
||||
# From points
|
||||
geom = MultiPoint([Point(1.0, 2.0), Point(3.0, 4.0)])
|
||||
assert len(geom.geoms) == 2
|
||||
assert dump_coords(geom) == [[(1.0, 2.0)], [(3.0, 4.0)]]
|
||||
|
||||
# From another multi-point
|
||||
geom2 = MultiPoint(geom)
|
||||
assert len(geom2.geoms) == 2
|
||||
assert dump_coords(geom2) == [[(1.0, 2.0)], [(3.0, 4.0)]]
|
||||
|
||||
# Sub-geometry Access
|
||||
assert isinstance(geom.geoms[0], Point)
|
||||
assert geom.geoms[0].x == 1.0
|
||||
assert geom.geoms[0].y == 2.0
|
||||
with pytest.raises(IndexError): # index out of range
|
||||
geom.geoms[2]
|
||||
|
||||
# Geo interface
|
||||
assert geom.__geo_interface__ == {
|
||||
"type": "MultiPoint",
|
||||
"coordinates": ((1.0, 2.0), (3.0, 4.0)),
|
||||
}
|
||||
|
||||
def test_multipoint_from_numpy(self):
|
||||
# Construct from a numpy array
|
||||
geom = MultiPoint(np.array([[0.0, 0.0], [1.0, 2.0]]))
|
||||
assert isinstance(geom, MultiPoint)
|
||||
assert len(geom.geoms) == 2
|
||||
assert dump_coords(geom) == [[(0.0, 0.0)], [(1.0, 2.0)]]
|
||||
|
||||
def test_subgeom_access(self):
|
||||
p0 = Point(1.0, 2.0)
|
||||
p1 = Point(3.0, 4.0)
|
||||
self.subgeom_access_test(MultiPoint, [p0, p1])
|
||||
|
||||
def test_create_multi_with_empty_component(self):
|
||||
msg = "Can't create MultiPoint with empty component"
|
||||
with pytest.raises(EmptyPartError, match=msg):
|
||||
MultiPoint([Point(0, 0), Point()]).wkt
|
||||
|
||||
|
||||
def test_multipoint_array_coercion():
|
||||
geom = MultiPoint([(1.0, 2.0), (3.0, 4.0)])
|
||||
arr = np.array(geom)
|
||||
assert arr.ndim == 0
|
||||
assert arr.size == 1
|
||||
assert arr.dtype == np.dtype("object")
|
||||
assert arr.item() == geom
|
||||
|
||||
|
||||
def test_numpy_object_array():
|
||||
geom = MultiPoint([(1.0, 2.0), (3.0, 4.0)])
|
||||
ar = np.empty(1, object)
|
||||
ar[:] = [geom]
|
||||
assert ar[0] == geom
|
||||
|
||||
|
||||
def test_len_raises():
|
||||
geom = MultiPoint([[5.0, 6.0], [7.0, 8.0]])
|
||||
with pytest.raises(TypeError):
|
||||
len(geom)
|
||||
@@ -0,0 +1,134 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from shapely import MultiPolygon, Polygon
|
||||
from shapely.geometry.base import dump_coords
|
||||
from shapely.tests.geometry.test_multi import MultiGeometryTestCase
|
||||
|
||||
|
||||
class TestMultiPolygon(MultiGeometryTestCase):
|
||||
def test_multipolygon(self):
|
||||
# From coordinate tuples
|
||||
coords = [
|
||||
(
|
||||
((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),
|
||||
[((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))],
|
||||
)
|
||||
]
|
||||
geom = MultiPolygon(coords)
|
||||
assert isinstance(geom, MultiPolygon)
|
||||
assert len(geom.geoms) == 1
|
||||
assert dump_coords(geom) == [
|
||||
[
|
||||
(0.0, 0.0),
|
||||
(0.0, 1.0),
|
||||
(1.0, 1.0),
|
||||
(1.0, 0.0),
|
||||
(0.0, 0.0),
|
||||
[(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)],
|
||||
]
|
||||
]
|
||||
|
||||
# Or without holes
|
||||
coords2 = [(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),)]
|
||||
geom = MultiPolygon(coords2)
|
||||
assert isinstance(geom, MultiPolygon)
|
||||
assert len(geom.geoms) == 1
|
||||
assert dump_coords(geom) == [
|
||||
[
|
||||
(0.0, 0.0),
|
||||
(0.0, 1.0),
|
||||
(1.0, 1.0),
|
||||
(1.0, 0.0),
|
||||
(0.0, 0.0),
|
||||
]
|
||||
]
|
||||
|
||||
# Or from polygons
|
||||
p = Polygon(
|
||||
((0, 0), (0, 1), (1, 1), (1, 0)),
|
||||
[((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))],
|
||||
)
|
||||
geom = MultiPolygon([p])
|
||||
assert len(geom.geoms) == 1
|
||||
assert dump_coords(geom) == [
|
||||
[
|
||||
(0.0, 0.0),
|
||||
(0.0, 1.0),
|
||||
(1.0, 1.0),
|
||||
(1.0, 0.0),
|
||||
(0.0, 0.0),
|
||||
[(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)],
|
||||
]
|
||||
]
|
||||
|
||||
# Or from another multi-polygon
|
||||
geom2 = MultiPolygon(geom)
|
||||
assert len(geom2.geoms) == 1
|
||||
assert dump_coords(geom2) == [
|
||||
[
|
||||
(0.0, 0.0),
|
||||
(0.0, 1.0),
|
||||
(1.0, 1.0),
|
||||
(1.0, 0.0),
|
||||
(0.0, 0.0),
|
||||
[(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)],
|
||||
]
|
||||
]
|
||||
|
||||
# Sub-geometry Access
|
||||
assert isinstance(geom.geoms[0], Polygon)
|
||||
assert dump_coords(geom.geoms[0]) == [
|
||||
(0.0, 0.0),
|
||||
(0.0, 1.0),
|
||||
(1.0, 1.0),
|
||||
(1.0, 0.0),
|
||||
(0.0, 0.0),
|
||||
[(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)],
|
||||
]
|
||||
with pytest.raises(IndexError): # index out of range
|
||||
geom.geoms[1]
|
||||
|
||||
# Geo interface
|
||||
assert geom.__geo_interface__ == {
|
||||
"type": "MultiPolygon",
|
||||
"coordinates": [
|
||||
(
|
||||
((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)),
|
||||
((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)),
|
||||
)
|
||||
],
|
||||
}
|
||||
|
||||
def test_subgeom_access(self):
|
||||
poly0 = Polygon([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)])
|
||||
poly1 = Polygon([(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25)])
|
||||
self.subgeom_access_test(MultiPolygon, [poly0, poly1])
|
||||
|
||||
|
||||
def test_fail_list_of_multipolygons():
|
||||
"""A list of multipolygons is not a valid multipolygon ctor argument"""
|
||||
multi = MultiPolygon(
|
||||
[
|
||||
(
|
||||
((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),
|
||||
[((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))],
|
||||
)
|
||||
]
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
MultiPolygon([multi])
|
||||
|
||||
|
||||
def test_numpy_object_array():
|
||||
geom = MultiPolygon(
|
||||
[
|
||||
(
|
||||
((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),
|
||||
[((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))],
|
||||
)
|
||||
]
|
||||
)
|
||||
ar = np.empty(1, object)
|
||||
ar[:] = [geom]
|
||||
assert ar[0] == geom
|
||||
@@ -0,0 +1,188 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from shapely import Point
|
||||
from shapely.coords import CoordinateSequence
|
||||
from shapely.errors import DimensionError
|
||||
|
||||
|
||||
def test_from_coordinates():
|
||||
# 2D points
|
||||
p = Point(1.0, 2.0)
|
||||
assert p.coords[:] == [(1.0, 2.0)]
|
||||
assert p.has_z is False
|
||||
|
||||
# 3D Point
|
||||
p = Point(1.0, 2.0, 3.0)
|
||||
assert p.coords[:] == [(1.0, 2.0, 3.0)]
|
||||
assert p.has_z
|
||||
|
||||
# empty
|
||||
p = Point()
|
||||
assert p.is_empty
|
||||
assert isinstance(p.coords, CoordinateSequence)
|
||||
assert p.coords[:] == []
|
||||
|
||||
|
||||
def test_from_sequence():
|
||||
# From single coordinate pair
|
||||
p = Point((3.0, 4.0))
|
||||
assert p.coords[:] == [(3.0, 4.0)]
|
||||
p = Point([3.0, 4.0])
|
||||
assert p.coords[:] == [(3.0, 4.0)]
|
||||
|
||||
# From coordinate sequence
|
||||
p = Point([(3.0, 4.0)])
|
||||
assert p.coords[:] == [(3.0, 4.0)]
|
||||
p = Point([[3.0, 4.0]])
|
||||
assert p.coords[:] == [(3.0, 4.0)]
|
||||
|
||||
# 3D
|
||||
p = Point((3.0, 4.0, 5.0))
|
||||
assert p.coords[:] == [(3.0, 4.0, 5.0)]
|
||||
p = Point([3.0, 4.0, 5.0])
|
||||
assert p.coords[:] == [(3.0, 4.0, 5.0)]
|
||||
p = Point([(3.0, 4.0, 5.0)])
|
||||
assert p.coords[:] == [(3.0, 4.0, 5.0)]
|
||||
|
||||
|
||||
def test_from_numpy():
|
||||
# Construct from a numpy array
|
||||
p = Point(np.array([1.0, 2.0]))
|
||||
assert p.coords[:] == [(1.0, 2.0)]
|
||||
|
||||
p = Point(np.array([1.0, 2.0, 3.0]))
|
||||
assert p.coords[:] == [(1.0, 2.0, 3.0)]
|
||||
|
||||
|
||||
def test_from_numpy_xy():
|
||||
# Construct from separate x, y numpy arrays - if those are length 1,
|
||||
# this is allowed for compat with shapely 1.8
|
||||
# (https://github.com/shapely/shapely/issues/1587)
|
||||
p = Point(np.array([1.0]), np.array([2.0]))
|
||||
assert p.coords[:] == [(1.0, 2.0)]
|
||||
|
||||
p = Point(np.array([1.0]), np.array([2.0]), np.array([3.0]))
|
||||
assert p.coords[:] == [(1.0, 2.0, 3.0)]
|
||||
|
||||
|
||||
def test_from_point():
|
||||
# From another point
|
||||
p = Point(3.0, 4.0)
|
||||
q = Point(p)
|
||||
assert q.coords[:] == [(3.0, 4.0)]
|
||||
|
||||
p = Point(3.0, 4.0, 5.0)
|
||||
q = Point(p)
|
||||
assert q.coords[:] == [(3.0, 4.0, 5.0)]
|
||||
|
||||
|
||||
def test_from_generator():
|
||||
gen = (coord for coord in [(1.0, 2.0)])
|
||||
p = Point(gen)
|
||||
assert p.coords[:] == [(1.0, 2.0)]
|
||||
|
||||
|
||||
def test_from_invalid():
|
||||
|
||||
with pytest.raises(TypeError, match="takes at most 3 arguments"):
|
||||
Point(1, 2, 3, 4)
|
||||
|
||||
# this worked in shapely 1.x, just ignoring the other coords
|
||||
with pytest.raises(
|
||||
ValueError, match="takes only scalar or 1-size vector arguments"
|
||||
):
|
||||
Point([(2, 3), (11, 4)])
|
||||
|
||||
|
||||
class TestPoint:
|
||||
def test_point(self):
|
||||
|
||||
# Test 2D points
|
||||
p = Point(1.0, 2.0)
|
||||
assert p.x == 1.0
|
||||
assert type(p.x) is float
|
||||
assert p.y == 2.0
|
||||
assert type(p.y) is float
|
||||
assert p.coords[:] == [(1.0, 2.0)]
|
||||
assert str(p) == p.wkt
|
||||
assert p.has_z is False
|
||||
with pytest.raises(DimensionError):
|
||||
p.z
|
||||
|
||||
# Check 3D
|
||||
p = Point(1.0, 2.0, 3.0)
|
||||
assert p.coords[:] == [(1.0, 2.0, 3.0)]
|
||||
assert str(p) == p.wkt
|
||||
assert p.has_z is True
|
||||
assert p.z == 3.0
|
||||
assert type(p.z) is float
|
||||
|
||||
# Coordinate access
|
||||
p = Point((3.0, 4.0))
|
||||
assert p.x == 3.0
|
||||
assert p.y == 4.0
|
||||
assert tuple(p.coords) == ((3.0, 4.0),)
|
||||
assert p.coords[0] == (3.0, 4.0)
|
||||
with pytest.raises(IndexError): # index out of range
|
||||
p.coords[1]
|
||||
|
||||
# Bounds
|
||||
assert p.bounds == (3.0, 4.0, 3.0, 4.0)
|
||||
|
||||
# Geo interface
|
||||
assert p.__geo_interface__ == {"type": "Point", "coordinates": (3.0, 4.0)}
|
||||
|
||||
def test_point_empty(self):
|
||||
# Test Non-operability of Null geometry
|
||||
p_null = Point()
|
||||
assert p_null.wkt == "POINT EMPTY"
|
||||
assert p_null.coords[:] == []
|
||||
assert p_null.area == 0.0
|
||||
|
||||
def test_coords(self):
|
||||
# From Array.txt
|
||||
p = Point(0.0, 0.0, 1.0)
|
||||
coords = p.coords[0]
|
||||
assert coords == (0.0, 0.0, 1.0)
|
||||
|
||||
# Convert to Numpy array, passing through Python sequence
|
||||
a = np.asarray(coords)
|
||||
assert a.ndim == 1
|
||||
assert a.size == 3
|
||||
assert a.shape == (3,)
|
||||
|
||||
|
||||
def test_point_immutable():
|
||||
p = Point(3.0, 4.0)
|
||||
|
||||
with pytest.raises(AttributeError):
|
||||
p.coords = (2.0, 1.0)
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
p.coords[0] = (2.0, 1.0)
|
||||
|
||||
|
||||
def test_point_array_coercion():
|
||||
# don't convert to array of coordinates, keep objects
|
||||
p = Point(3.0, 4.0)
|
||||
arr = np.array(p)
|
||||
assert arr.ndim == 0
|
||||
assert arr.size == 1
|
||||
assert arr.dtype == np.dtype("object")
|
||||
assert arr.item() == p
|
||||
|
||||
|
||||
def test_numpy_empty_point_coords():
|
||||
pe = Point()
|
||||
|
||||
# Access the coords
|
||||
a = np.asarray(pe.coords)
|
||||
assert a.shape == (0, 2)
|
||||
|
||||
|
||||
def test_numpy_object_array():
|
||||
geom = Point(3.0, 4.0)
|
||||
ar = np.empty(1, object)
|
||||
ar[:] = [geom]
|
||||
assert ar[0] == geom
|
||||
@@ -0,0 +1,463 @@
|
||||
"""Polygons and Linear Rings
|
||||
"""
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from shapely import LinearRing, LineString, Point, Polygon
|
||||
from shapely.coords import CoordinateSequence
|
||||
from shapely.errors import TopologicalError
|
||||
from shapely.wkb import loads as load_wkb
|
||||
|
||||
|
||||
def test_empty_linearring_coords():
|
||||
assert LinearRing().coords[:] == []
|
||||
|
||||
|
||||
def test_linearring_from_coordinate_sequence():
|
||||
expected_coords = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
|
||||
ring = LinearRing([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)])
|
||||
assert ring.coords[:] == expected_coords
|
||||
|
||||
ring = LinearRing([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)])
|
||||
assert ring.coords[:] == expected_coords
|
||||
|
||||
|
||||
def test_linearring_from_points():
|
||||
# From Points
|
||||
expected_coords = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
|
||||
ring = LinearRing([Point(0.0, 0.0), Point(0.0, 1.0), Point(1.0, 1.0)])
|
||||
assert ring.coords[:] == expected_coords
|
||||
|
||||
|
||||
def test_linearring_from_closed_linestring():
|
||||
coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
line = LineString(coords)
|
||||
ring = LinearRing(line)
|
||||
assert len(ring.coords) == 4
|
||||
assert ring.coords[:] == coords
|
||||
assert ring.geom_type == "LinearRing"
|
||||
|
||||
|
||||
def test_linearring_from_unclosed_linestring():
|
||||
coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
line = LineString(coords[:-1]) # Pass in unclosed line
|
||||
ring = LinearRing(line)
|
||||
assert len(ring.coords) == 4
|
||||
assert ring.coords[:] == coords
|
||||
assert ring.geom_type == "LinearRing"
|
||||
|
||||
|
||||
def test_linearring_from_invalid():
|
||||
coords = [(0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
|
||||
line = LineString(coords)
|
||||
assert not line.is_valid
|
||||
with pytest.raises(TopologicalError):
|
||||
LinearRing(line)
|
||||
|
||||
|
||||
def test_linearring_from_too_short_linestring():
|
||||
# Creation of LinearRing request at least 3 coordinates (unclosed) or
|
||||
# 4 coordinates (closed)
|
||||
coords = [(0.0, 0.0), (1.0, 1.0)]
|
||||
line = LineString(coords)
|
||||
with pytest.raises(ValueError, match="requires at least 4 coordinates"):
|
||||
LinearRing(line)
|
||||
|
||||
|
||||
def test_linearring_from_linearring():
|
||||
coords = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
ring = LinearRing(coords)
|
||||
assert ring.coords[:] == coords
|
||||
|
||||
|
||||
def test_linearring_from_generator():
|
||||
coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
gen = (coord for coord in coords)
|
||||
ring = LinearRing(gen)
|
||||
assert ring.coords[:] == coords
|
||||
|
||||
|
||||
def test_linearring_from_empty():
|
||||
ring = LinearRing()
|
||||
assert ring.is_empty
|
||||
assert isinstance(ring.coords, CoordinateSequence)
|
||||
assert ring.coords[:] == []
|
||||
|
||||
ring = LinearRing([])
|
||||
assert ring.is_empty
|
||||
assert isinstance(ring.coords, CoordinateSequence)
|
||||
assert ring.coords[:] == []
|
||||
|
||||
|
||||
def test_linearring_from_numpy():
|
||||
# Construct from a numpy array
|
||||
coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
|
||||
ring = LinearRing(np.array(coords))
|
||||
assert ring.coords[:] == [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
|
||||
|
||||
def test_numpy_linearring_coords():
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
ring = LinearRing([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)])
|
||||
ra = np.asarray(ring.coords)
|
||||
expected = np.asarray([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (0.0, 0.0)])
|
||||
assert_array_equal(ra, expected)
|
||||
|
||||
|
||||
def test_numpy_empty_linearring_coords():
|
||||
ring = LinearRing()
|
||||
assert np.asarray(ring.coords).shape == (0, 2)
|
||||
|
||||
|
||||
def test_numpy_object_array():
|
||||
geom = Polygon([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)])
|
||||
ar = np.empty(1, object)
|
||||
ar[:] = [geom]
|
||||
assert ar[0] == geom
|
||||
|
||||
|
||||
def test_polygon_from_coordinate_sequence():
|
||||
coords = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
|
||||
# Construct a polygon, exterior ring only
|
||||
polygon = Polygon([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)])
|
||||
assert polygon.exterior.coords[:] == coords
|
||||
assert len(polygon.interiors) == 0
|
||||
|
||||
polygon = Polygon([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)])
|
||||
assert polygon.exterior.coords[:] == coords
|
||||
assert len(polygon.interiors) == 0
|
||||
|
||||
|
||||
def test_polygon_from_coordinate_sequence_with_holes():
|
||||
coords = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
|
||||
# Interior rings (holes)
|
||||
polygon = Polygon(coords, [[(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25)]])
|
||||
assert polygon.exterior.coords[:] == coords
|
||||
assert len(polygon.interiors) == 1
|
||||
assert len(polygon.interiors[0].coords) == 5
|
||||
|
||||
# Multiple interior rings with different length
|
||||
coords = [(0, 0), (0, 10), (10, 10), (10, 0), (0, 0)]
|
||||
holes = [
|
||||
[(1, 1), (2, 1), (2, 2), (1, 2), (1, 1)],
|
||||
[(3, 3), (3, 4), (4, 5), (5, 4), (5, 3), (3, 3)],
|
||||
]
|
||||
polygon = Polygon(coords, holes)
|
||||
assert polygon.exterior.coords[:] == coords
|
||||
assert len(polygon.interiors) == 2
|
||||
assert len(polygon.interiors[0].coords) == 5
|
||||
assert len(polygon.interiors[1].coords) == 6
|
||||
|
||||
|
||||
def test_polygon_from_linearring():
|
||||
coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
ring = LinearRing(coords)
|
||||
|
||||
polygon = Polygon(ring)
|
||||
assert polygon.exterior.coords[:] == coords
|
||||
assert len(polygon.interiors) == 0
|
||||
|
||||
# from shell and holes linearrings
|
||||
shell = LinearRing([(0.0, 0.0), (70.0, 120.0), (140.0, 0.0), (0.0, 0.0)])
|
||||
holes = [
|
||||
LinearRing([(60.0, 80.0), (80.0, 80.0), (70.0, 60.0), (60.0, 80.0)]),
|
||||
LinearRing([(30.0, 10.0), (50.0, 10.0), (40.0, 30.0), (30.0, 10.0)]),
|
||||
LinearRing([(90.0, 10), (110.0, 10.0), (100.0, 30.0), (90.0, 10.0)]),
|
||||
]
|
||||
polygon = Polygon(shell, holes)
|
||||
assert polygon.exterior.coords[:] == shell.coords[:]
|
||||
assert len(polygon.interiors) == 3
|
||||
for i in range(3):
|
||||
assert polygon.interiors[i].coords[:] == holes[i].coords[:]
|
||||
|
||||
|
||||
def test_polygon_from_linestring():
|
||||
coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
line = LineString(coords)
|
||||
polygon = Polygon(line)
|
||||
assert polygon.exterior.coords[:] == coords
|
||||
|
||||
# from unclosed linestring
|
||||
line = LineString(coords[:-1])
|
||||
polygon = Polygon(line)
|
||||
assert polygon.exterior.coords[:] == coords
|
||||
|
||||
|
||||
def test_polygon_from_points():
|
||||
polygon = Polygon([Point(0.0, 0.0), Point(0.0, 1.0), Point(1.0, 1.0)])
|
||||
expected_coords = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
assert polygon.exterior.coords[:] == expected_coords
|
||||
|
||||
|
||||
def test_polygon_from_polygon():
|
||||
coords = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)]
|
||||
polygon = Polygon(coords, [[(0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25)]])
|
||||
|
||||
# Test from another Polygon
|
||||
copy = Polygon(polygon)
|
||||
assert len(copy.exterior.coords) == 5
|
||||
assert len(copy.interiors) == 1
|
||||
assert len(copy.interiors[0].coords) == 5
|
||||
|
||||
|
||||
def test_polygon_from_invalid():
|
||||
# Error handling
|
||||
with pytest.raises(ValueError):
|
||||
# A LinearRing must have at least 3 coordinate tuples
|
||||
Polygon([[1, 2], [2, 3]])
|
||||
|
||||
|
||||
def test_polygon_from_empty():
|
||||
polygon = Polygon()
|
||||
assert polygon.is_empty
|
||||
assert polygon.exterior.coords[:] == []
|
||||
|
||||
polygon = Polygon([])
|
||||
assert polygon.is_empty
|
||||
assert polygon.exterior.coords[:] == []
|
||||
|
||||
|
||||
def test_polygon_from_numpy():
|
||||
a = np.array(((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)))
|
||||
polygon = Polygon(a)
|
||||
assert len(polygon.exterior.coords) == 5
|
||||
assert polygon.exterior.coords[:] == [
|
||||
(0.0, 0.0),
|
||||
(0.0, 1.0),
|
||||
(1.0, 1.0),
|
||||
(1.0, 0.0),
|
||||
(0.0, 0.0),
|
||||
]
|
||||
assert len(polygon.interiors) == 0
|
||||
|
||||
|
||||
def test_polygon_from_generator():
|
||||
coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
|
||||
gen = (coord for coord in coords)
|
||||
polygon = Polygon(gen)
|
||||
assert polygon.exterior.coords[:] == coords
|
||||
|
||||
|
||||
class TestPolygon:
|
||||
def test_linearring(self):
|
||||
|
||||
# Initialization
|
||||
# Linear rings won't usually be created by users, but by polygons
|
||||
coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
|
||||
ring = LinearRing(coords)
|
||||
assert len(ring.coords) == 5
|
||||
assert ring.coords[0] == ring.coords[4]
|
||||
assert ring.coords[0] == ring.coords[-1]
|
||||
assert ring.is_ring is True
|
||||
|
||||
def test_polygon(self):
|
||||
coords = ((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0))
|
||||
|
||||
# Construct a polygon, exterior ring only
|
||||
polygon = Polygon(coords)
|
||||
assert len(polygon.exterior.coords) == 5
|
||||
|
||||
# Ring Access
|
||||
assert isinstance(polygon.exterior, LinearRing)
|
||||
ring = polygon.exterior
|
||||
assert len(ring.coords) == 5
|
||||
assert ring.coords[0] == ring.coords[4]
|
||||
assert ring.coords[0] == (0.0, 0.0)
|
||||
assert ring.is_ring is True
|
||||
assert len(polygon.interiors) == 0
|
||||
|
||||
# Create a new polygon from WKB
|
||||
data = polygon.wkb
|
||||
polygon = None
|
||||
ring = None
|
||||
polygon = load_wkb(data)
|
||||
ring = polygon.exterior
|
||||
assert len(ring.coords) == 5
|
||||
assert ring.coords[0] == ring.coords[4]
|
||||
assert ring.coords[0] == (0.0, 0.0)
|
||||
assert ring.is_ring is True
|
||||
polygon = None
|
||||
|
||||
# Interior rings (holes)
|
||||
polygon = Polygon(
|
||||
coords, [((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25))]
|
||||
)
|
||||
assert len(polygon.exterior.coords) == 5
|
||||
assert len(polygon.interiors[0].coords) == 5
|
||||
with pytest.raises(IndexError): # index out of range
|
||||
polygon.interiors[1]
|
||||
|
||||
# Coordinate getter raises exceptions
|
||||
with pytest.raises(NotImplementedError):
|
||||
polygon.coords
|
||||
|
||||
# Geo interface
|
||||
assert polygon.__geo_interface__ == {
|
||||
"type": "Polygon",
|
||||
"coordinates": (
|
||||
((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)),
|
||||
((0.25, 0.25), (0.25, 0.5), (0.5, 0.5), (0.5, 0.25), (0.25, 0.25)),
|
||||
),
|
||||
}
|
||||
|
||||
def test_linearring_empty(self):
|
||||
# Test Non-operability of Null rings
|
||||
r_null = LinearRing()
|
||||
assert r_null.wkt == "LINEARRING EMPTY"
|
||||
assert r_null.length == 0.0
|
||||
|
||||
def test_dimensions(self):
|
||||
|
||||
# Background: see http://trac.gispython.org/lab/ticket/168
|
||||
# http://lists.gispython.org/pipermail/community/2008-August/001859.html
|
||||
|
||||
coords = ((0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 0.0, 0.0))
|
||||
polygon = Polygon(coords)
|
||||
assert polygon._ndim == 3
|
||||
gi = polygon.__geo_interface__
|
||||
assert gi["coordinates"] == (
|
||||
(
|
||||
(0.0, 0.0, 0.0),
|
||||
(0.0, 1.0, 0.0),
|
||||
(1.0, 1.0, 0.0),
|
||||
(1.0, 0.0, 0.0),
|
||||
(0.0, 0.0, 0.0),
|
||||
),
|
||||
)
|
||||
|
||||
e = polygon.exterior
|
||||
assert e._ndim == 3
|
||||
gi = e.__geo_interface__
|
||||
assert gi["coordinates"] == (
|
||||
(0.0, 0.0, 0.0),
|
||||
(0.0, 1.0, 0.0),
|
||||
(1.0, 1.0, 0.0),
|
||||
(1.0, 0.0, 0.0),
|
||||
(0.0, 0.0, 0.0),
|
||||
)
|
||||
|
||||
def test_attribute_chains(self):
|
||||
|
||||
# Attribute Chaining
|
||||
# See also ticket #151.
|
||||
p = Polygon([(0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)])
|
||||
assert list(p.boundary.coords) == [
|
||||
(0.0, 0.0),
|
||||
(0.0, 1.0),
|
||||
(-1.0, 1.0),
|
||||
(-1.0, 0.0),
|
||||
(0.0, 0.0),
|
||||
]
|
||||
|
||||
ec = list(Point(0.0, 0.0).buffer(1.0, 1).exterior.coords)
|
||||
assert isinstance(ec, list) # TODO: this is a poor test
|
||||
|
||||
# Test chained access to interiors
|
||||
p = Polygon(
|
||||
[(0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)],
|
||||
[[(-0.25, 0.25), (-0.25, 0.75), (-0.75, 0.75), (-0.75, 0.25)]],
|
||||
)
|
||||
assert p.area == 0.75
|
||||
|
||||
"""Not so much testing the exact values here, which are the
|
||||
responsibility of the geometry engine (GEOS), but that we can get
|
||||
chain functions and properties using anonymous references.
|
||||
"""
|
||||
assert list(p.interiors[0].coords) == [
|
||||
(-0.25, 0.25),
|
||||
(-0.25, 0.75),
|
||||
(-0.75, 0.75),
|
||||
(-0.75, 0.25),
|
||||
(-0.25, 0.25),
|
||||
]
|
||||
xy = list(p.interiors[0].buffer(1).exterior.coords)[0]
|
||||
assert len(xy) == 2
|
||||
|
||||
# Test multiple operators, boundary of a buffer
|
||||
ec = list(p.buffer(1).boundary.coords)
|
||||
assert isinstance(ec, list) # TODO: this is a poor test
|
||||
|
||||
def test_empty_equality(self):
|
||||
# Test equals operator, including empty geometries
|
||||
# see issue #338
|
||||
|
||||
point1 = Point(0, 0)
|
||||
polygon1 = Polygon([(0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)])
|
||||
polygon2 = Polygon([(0.0, 0.0), (0.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)])
|
||||
polygon_empty1 = Polygon()
|
||||
polygon_empty2 = Polygon()
|
||||
|
||||
assert point1 != polygon1
|
||||
assert polygon_empty1 == polygon_empty2
|
||||
assert polygon1 != polygon_empty1
|
||||
assert polygon1 == polygon2
|
||||
assert polygon_empty1 is not None
|
||||
|
||||
def test_from_bounds(self):
|
||||
xmin, ymin, xmax, ymax = -180, -90, 180, 90
|
||||
coords = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)]
|
||||
assert Polygon(coords) == Polygon.from_bounds(xmin, ymin, xmax, ymax)
|
||||
|
||||
def test_empty_polygon_exterior(self):
|
||||
p = Polygon()
|
||||
assert p.exterior == LinearRing()
|
||||
|
||||
|
||||
def test_linearring_immutable():
|
||||
ring = LinearRing([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)])
|
||||
|
||||
with pytest.raises(AttributeError):
|
||||
ring.coords = [(1.0, 1.0), (2.0, 2.0), (1.0, 2.0)]
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
ring.coords[0] = (1.0, 1.0)
|
||||
|
||||
|
||||
class TestLinearRingGetItem:
|
||||
def test_index_linearring(self):
|
||||
shell = LinearRing([(0.0, 0.0), (70.0, 120.0), (140.0, 0.0), (0.0, 0.0)])
|
||||
holes = [
|
||||
LinearRing([(60.0, 80.0), (80.0, 80.0), (70.0, 60.0), (60.0, 80.0)]),
|
||||
LinearRing([(30.0, 10.0), (50.0, 10.0), (40.0, 30.0), (30.0, 10.0)]),
|
||||
LinearRing([(90.0, 10), (110.0, 10.0), (100.0, 30.0), (90.0, 10.0)]),
|
||||
]
|
||||
g = Polygon(shell, holes)
|
||||
for i in range(-3, 3):
|
||||
assert g.interiors[i].equals(holes[i])
|
||||
with pytest.raises(IndexError):
|
||||
g.interiors[3]
|
||||
with pytest.raises(IndexError):
|
||||
g.interiors[-4]
|
||||
|
||||
def test_index_linearring_misc(self):
|
||||
g = Polygon() # empty
|
||||
with pytest.raises(IndexError):
|
||||
g.interiors[0]
|
||||
with pytest.raises(TypeError):
|
||||
g.interiors[0.0]
|
||||
|
||||
def test_slice_linearring(self):
|
||||
shell = LinearRing([(0.0, 0.0), (70.0, 120.0), (140.0, 0.0), (0.0, 0.0)])
|
||||
holes = [
|
||||
LinearRing([(60.0, 80.0), (80.0, 80.0), (70.0, 60.0), (60.0, 80.0)]),
|
||||
LinearRing([(30.0, 10.0), (50.0, 10.0), (40.0, 30.0), (30.0, 10.0)]),
|
||||
LinearRing([(90.0, 10), (110.0, 10.0), (100.0, 30.0), (90.0, 10.0)]),
|
||||
]
|
||||
g = Polygon(shell, holes)
|
||||
t = [a.equals(b) for (a, b) in zip(g.interiors[1:], holes[1:])]
|
||||
assert all(t)
|
||||
t = [a.equals(b) for (a, b) in zip(g.interiors[:-1], holes[:-1])]
|
||||
assert all(t)
|
||||
t = [a.equals(b) for (a, b) in zip(g.interiors[::-1], holes[::-1])]
|
||||
assert all(t)
|
||||
t = [a.equals(b) for (a, b) in zip(g.interiors[::2], holes[::2])]
|
||||
assert all(t)
|
||||
t = [a.equals(b) for (a, b) in zip(g.interiors[:3], holes[:3])]
|
||||
assert all(t)
|
||||
assert g.interiors[3:] == holes[3:] == []
|
||||
Reference in New Issue
Block a user