initial commit

This commit is contained in:
Nico Melone
2023-08-24 17:49:47 -05:00
parent 23b7279c0f
commit 72d3f2c348
5422 changed files with 890638 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,86 @@
import numpy as np
import pytest
from shapely import LineString
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]

View File

@@ -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]

View File

@@ -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)

View File

@@ -0,0 +1,111 @@
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 precsions 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
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):
expected_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:
expected_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))"
)
assert f"{poly:.2G}" == expected_2G
# 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

View File

@@ -0,0 +1,274 @@
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)
@pytest.mark.parametrize("geom", geometries_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_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)
@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)
@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)
@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)
def test_array_argument_linear():
line = LineString([(0, 0), (0, 1), (1, 1)])
distances = np.array([0, 0.5, 1])
result = line.line_interpolate_point(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)
points = shapely.points([(0, 0), (0.5, 0.5), (1, 1)])
result = line.line_locate_point(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)
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)

View File

@@ -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))

View File

@@ -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

View File

@@ -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]

View File

@@ -0,0 +1,79 @@
import numpy as np
import pytest
from shapely import LineString, MultiLineString
from shapely.errors import EmptyPartError
from shapely.geometry.base import dump_coords
from .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

View File

@@ -0,0 +1,79 @@
import numpy as np
import pytest
from shapely import MultiPoint, Point
from shapely.errors import EmptyPartError
from shapely.geometry.base import dump_coords
from .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)

View File

@@ -0,0 +1,121 @@
import numpy as np
import pytest
from shapely import MultiPolygon, Polygon
from shapely.geometry.base import dump_coords
from .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 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

View File

@@ -0,0 +1,185 @@
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 p.y == 2.0
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
# 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

View File

@@ -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:] == []