| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- """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, quad_segs=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 = next(iter(p.interiors[0].buffer(1).exterior.coords))
- 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:] == []
|