structure saas with tools

This commit is contained in:
Davidson Gomes
2025-04-25 15:30:54 -03:00
commit 1aef473937
16434 changed files with 6584257 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
import numpy
import pytest
shapely20_todo = pytest.mark.xfail(
strict=True, reason="Not yet implemented for Shapely 2.0"
)
shapely20_wontfix = pytest.mark.xfail(strict=True, reason="Will fail for Shapely 2.0")
def pytest_report_header(config):
"""Header for pytest."""
return f"dependencies: numpy-{numpy.__version__}"

View File

@@ -0,0 +1,308 @@
import unittest
from math import pi
import numpy as np
import pytest
from shapely import affinity
from shapely.geometry import Point
from shapely.wkt import loads as load_wkt
class AffineTestCase(unittest.TestCase):
def test_affine_params(self):
g = load_wkt("LINESTRING(2.4 4.1, 2.4 3, 3 3)")
with pytest.raises(TypeError):
affinity.affine_transform(g, None)
with pytest.raises(ValueError):
affinity.affine_transform(g, [1, 2, 3, 4, 5, 6, 7, 8, 9])
with pytest.raises(AttributeError):
affinity.affine_transform(None, [1, 2, 3, 4, 5, 6])
def test_affine_geom_types(self):
# identity matrices, which should result with no transformation
matrix2d = (1, 0, 0, 1, 0, 0)
matrix3d = (1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
# empty in, empty out
empty2d = load_wkt("MULTIPOLYGON EMPTY")
assert affinity.affine_transform(empty2d, matrix2d).is_empty
def test_geom(g2, g3=None):
assert not g2.has_z
a2 = affinity.affine_transform(g2, matrix2d)
assert not a2.has_z
assert g2.equals(a2)
if g3 is not None:
assert g3.has_z
a3 = affinity.affine_transform(g3, matrix3d)
assert a3.has_z
assert g3.equals(a3)
pt2d = load_wkt("POINT(12.3 45.6)")
pt3d = load_wkt("POINT(12.3 45.6 7.89)")
test_geom(pt2d, pt3d)
ls2d = load_wkt("LINESTRING(0.9 3.4, 0.7 2, 2.5 2.7)")
ls3d = load_wkt("LINESTRING(0.9 3.4 3.3, 0.7 2 2.3, 2.5 2.7 5.5)")
test_geom(ls2d, ls3d)
lr2d = load_wkt("LINEARRING(0.9 3.4, 0.7 2, 2.5 2.7, 0.9 3.4)")
lr3d = load_wkt("LINEARRING(0.9 3.4 3.3, 0.7 2 2.3, 2.5 2.7 5.5, 0.9 3.4 3.3)")
test_geom(lr2d, lr3d)
test_geom(
load_wkt(
"POLYGON((0.9 2.3, 0.5 1.1, 2.4 0.8, 0.9 2.3), "
"(1.1 1.7, 0.9 1.3, 1.4 1.2, 1.1 1.7), "
"(1.6 1.3, 1.7 1, 1.9 1.1, 1.6 1.3))"
)
)
test_geom(
load_wkt("MULTIPOINT ((-300 300), (700 300), (-800 -1100), (200 -300))")
)
test_geom(
load_wkt(
"MULTILINESTRING((0 0, -0.7 -0.7, 0.6 -1), (-0.5 0.5, 0.7 0.6, 0 -0.6))"
)
)
test_geom(
load_wkt(
"MULTIPOLYGON(((900 4300, -1100 -400, 900 -800, 900 4300)), "
"((1200 4300, 2300 4400, 1900 1000, 1200 4300)))"
)
)
test_geom(
load_wkt(
"GEOMETRYCOLLECTION(POINT(20 70),"
" POLYGON((60 70, 13 35, 60 -30, 60 70)),"
" LINESTRING(60 70, 50 100, 80 100))"
)
)
def test_affine_2d(self):
g = load_wkt("LINESTRING(2.4 4.1, 2.4 3, 3 3)")
# custom scale and translate
expected2d = load_wkt("LINESTRING(-0.2 14.35, -0.2 11.6, 1 11.6)")
matrix2d = (2, 0, 0, 2.5, -5, 4.1)
a2 = affinity.affine_transform(g, matrix2d)
assert a2.equals_exact(expected2d, 1e-6)
assert not a2.has_z
# Make sure a 3D matrix does not make a 3D shape from a 2D input
matrix3d = (2, 0, 0, 0, 2.5, 0, 0, 0, 10, -5, 4.1, 100)
a3 = affinity.affine_transform(g, matrix3d)
assert a3.equals_exact(expected2d, 1e-6)
assert not a3.has_z
def test_affine_3d(self):
g2 = load_wkt("LINESTRING(2.4 4.1, 2.4 3, 3 3)")
g3 = load_wkt("LINESTRING(2.4 4.1 100.2, 2.4 3 132.8, 3 3 128.6)")
# custom scale and translate
matrix2d = (2, 0, 0, 2.5, -5, 4.1)
matrix3d = (2, 0, 0, 0, 2.5, 0, 0, 0, 0.3048, -5, 4.1, 100)
# Combinations of 2D and 3D geometries and matrices
a22 = affinity.affine_transform(g2, matrix2d)
a23 = affinity.affine_transform(g2, matrix3d)
a32 = affinity.affine_transform(g3, matrix2d)
a33 = affinity.affine_transform(g3, matrix3d)
# Check dimensions
assert not a22.has_z
assert not a23.has_z
assert a32.has_z
assert a33.has_z
# 2D equality checks
expected2d = load_wkt("LINESTRING(-0.2 14.35, -0.2 11.6, 1 11.6)")
expected3d = load_wkt(
"LINESTRING(-0.2 14.35 130.54096, -0.2 11.6 140.47744, 1 11.6 139.19728)"
)
expected32 = load_wkt(
"LINESTRING(-0.2 14.35 100.2, -0.2 11.6 132.8, 1 11.6 128.6)"
)
assert a22.equals_exact(expected2d, 1e-6)
assert a23.equals_exact(expected2d, 1e-6)
# Do explicit 3D check of coordinate values
for a, e in zip(a32.coords, expected32.coords):
for ap, ep in zip(a, e):
self.assertAlmostEqual(ap, ep)
for a, e in zip(a33.coords, expected3d.coords):
for ap, ep in zip(a, e):
self.assertAlmostEqual(ap, ep)
class TransformOpsTestCase(unittest.TestCase):
def test_rotate(self):
ls = load_wkt("LINESTRING(240 400, 240 300, 300 300)")
# counter-clockwise degrees
rls = affinity.rotate(ls, 90)
els = load_wkt("LINESTRING(220 320, 320 320, 320 380)")
assert rls.equals(els)
# retest with named parameters for the same result
rls = affinity.rotate(geom=ls, angle=90, origin="center")
assert rls.equals(els)
# clockwise radians
rls = affinity.rotate(ls, -pi / 2, use_radians=True)
els = load_wkt("LINESTRING(320 380, 220 380, 220 320)")
assert rls.equals(els)
## other `origin` parameters
# around the centroid
rls = affinity.rotate(ls, 90, origin="centroid")
els = load_wkt("LINESTRING(182.5 320, 282.5 320, 282.5 380)")
assert rls.equals(els)
# around the second coordinate tuple
rls = affinity.rotate(ls, 90, origin=ls.coords[1])
els = load_wkt("LINESTRING(140 300, 240 300, 240 360)")
assert rls.equals(els)
# around the absolute Point of origin
rls = affinity.rotate(ls, 90, origin=Point(0, 0))
els = load_wkt("LINESTRING(-400 240, -300 240, -300 300)")
assert rls.equals(els)
def test_rotate_empty(self):
rls = affinity.rotate(load_wkt("LINESTRING EMPTY"), 90)
els = load_wkt("LINESTRING EMPTY")
assert rls.equals(els)
def test_rotate_angle_array(self):
ls = load_wkt("LINESTRING(240 400, 240 300, 300 300)")
els = load_wkt("LINESTRING(220 320, 320 320, 320 380)")
# check with degrees
theta = np.array(90.0)
rls = affinity.rotate(ls, theta)
assert theta.item() == 90.0
assert rls.equals(els)
# check with radians
theta = np.array(pi / 2)
rls = affinity.rotate(ls, theta, use_radians=True)
assert theta.item() == pi / 2
assert rls.equals(els)
def test_scale(self):
ls = load_wkt("LINESTRING(240 400 10, 240 300 30, 300 300 20)")
# test defaults of 1.0
sls = affinity.scale(ls)
assert sls.equals(ls)
# different scaling in different dimensions
sls = affinity.scale(ls, 2, 3, 0.5)
els = load_wkt("LINESTRING(210 500 5, 210 200 15, 330 200 10)")
assert sls.equals(els)
# Do explicit 3D check of coordinate values
for a, b in zip(sls.coords, els.coords):
for ap, bp in zip(a, b):
self.assertEqual(ap, bp)
# retest with named parameters for the same result
sls = affinity.scale(geom=ls, xfact=2, yfact=3, zfact=0.5, origin="center")
assert sls.equals(els)
## other `origin` parameters
# around the centroid
sls = affinity.scale(ls, 2, 3, 0.5, origin="centroid")
els = load_wkt("LINESTRING(228.75 537.5, 228.75 237.5, 348.75 237.5)")
assert sls.equals(els)
# around the second coordinate tuple
sls = affinity.scale(ls, 2, 3, 0.5, origin=ls.coords[1])
els = load_wkt("LINESTRING(240 600, 240 300, 360 300)")
assert sls.equals(els)
# around some other 3D Point of origin
sls = affinity.scale(ls, 2, 3, 0.5, origin=Point(100, 200, 1000))
els = load_wkt("LINESTRING(380 800 505, 380 500 515, 500 500 510)")
assert sls.equals(els)
# Do explicit 3D check of coordinate values
for a, b in zip(sls.coords, els.coords):
for ap, bp in zip(a, b):
assert ap == bp
def test_scale_empty(self):
sls = affinity.scale(load_wkt("LINESTRING EMPTY"))
els = load_wkt("LINESTRING EMPTY")
assert sls.equals(els)
def test_skew(self):
ls = load_wkt("LINESTRING(240 400 10, 240 300 30, 300 300 20)")
# test default shear angles of 0.0
sls = affinity.skew(ls)
assert sls.equals(ls)
# different shearing in x- and y-directions
sls = affinity.skew(ls, 15, -30)
els = load_wkt(
"LINESTRING (253.39745962155615 417.3205080756888, "
"226.60254037844385 317.3205080756888, "
"286.60254037844385 282.67949192431126)"
)
assert sls.equals_exact(els, 1e-6)
# retest with radians for the same result
sls = affinity.skew(ls, pi / 12, -pi / 6, use_radians=True)
assert sls.equals_exact(els, 1e-6)
# retest with named parameters for the same result
sls = affinity.skew(geom=ls, xs=15, ys=-30, origin="center", use_radians=False)
assert sls.equals_exact(els, 1e-6)
## other `origin` parameters
# around the centroid
sls = affinity.skew(ls, 15, -30, origin="centroid")
els = load_wkt(
"LINESTRING(258.42150697963973 406.49519052838332, "
"231.6265877365273980 306.4951905283833185, "
"291.6265877365274264 271.8541743770057337)"
)
assert sls.equals_exact(els, 1e-6)
# around the second coordinate tuple
sls = affinity.skew(ls, 15, -30, origin=ls.coords[1])
els = load_wkt(
"LINESTRING(266.7949192431123038 400, 240 300, 300 265.3589838486224153)"
)
assert sls.equals_exact(els, 1e-6)
# around the absolute Point of origin
sls = affinity.skew(ls, 15, -30, origin=Point(0, 0))
els = load_wkt(
"LINESTRING(347.179676972449101 261.435935394489832, "
"320.3847577293367976 161.4359353944898317, "
"380.3847577293367976 126.7949192431122754)"
)
assert sls.equals_exact(els, 1e-6)
def test_skew_empty(self):
sls = affinity.skew(load_wkt("LINESTRING EMPTY"))
els = load_wkt("LINESTRING EMPTY")
assert sls.equals(els)
def test_skew_xs_ys_array(self):
ls = load_wkt("LINESTRING(240 400 10, 240 300 30, 300 300 20)")
els = load_wkt(
"LINESTRING (253.39745962155615 417.3205080756888, "
"226.60254037844385 317.3205080756888, "
"286.60254037844385 282.67949192431126)"
)
# check with degrees
xs_ys = np.array([15.0, -30.0])
sls = affinity.skew(ls, xs_ys[0, ...], xs_ys[1, ...])
assert xs_ys[0] == 15.0
assert xs_ys[1] == -30.0
assert sls.equals_exact(els, 1e-6)
# check with radians
xs_ys = np.array([pi / 12, -pi / 6])
sls = affinity.skew(ls, xs_ys[0, ...], xs_ys[1, ...], use_radians=True)
assert xs_ys[0] == pi / 12
assert xs_ys[1] == -pi / 6
assert sls.equals_exact(els, 1e-6)
def test_translate(self):
ls = load_wkt("LINESTRING(240 400 10, 240 300 30, 300 300 20)")
# test default offset of 0.0
tls = affinity.translate(ls)
assert tls.equals(ls)
# test all offsets
tls = affinity.translate(ls, 100, 400, -10)
els = load_wkt("LINESTRING(340 800 0, 340 700 20, 400 700 10)")
assert tls.equals(els)
# Do explicit 3D check of coordinate values
for a, b in zip(tls.coords, els.coords):
for ap, bp in zip(a, b):
assert ap == bp
# retest with named parameters for the same result
tls = affinity.translate(geom=ls, xoff=100, yoff=400, zoff=-10)
assert tls.equals(els)
def test_translate_empty(self):
tls = affinity.translate(load_wkt("LINESTRING EMPTY"))
els = load_wkt("LINESTRING EMPTY")
self.assertTrue(tls.equals(els))
assert tls.equals(els)

View File

@@ -0,0 +1,20 @@
import unittest
from shapely import geometry
class BoxTestCase(unittest.TestCase):
def test_ccw(self):
b = geometry.box(0, 0, 1, 1, ccw=True)
assert b.exterior.coords[0] == (1.0, 0.0)
assert b.exterior.coords[1] == (1.0, 1.0)
def test_ccw_default(self):
b = geometry.box(0, 0, 1, 1)
assert b.exterior.coords[0] == (1.0, 0.0)
assert b.exterior.coords[1] == (1.0, 1.0)
def test_cw(self):
b = geometry.box(0, 0, 1, 1, ccw=False)
assert b.exterior.coords[0] == (0.0, 0.0)
assert b.exterior.coords[1] == (0.0, 1.0)

View File

@@ -0,0 +1,173 @@
import unittest
import pytest
from shapely import geometry
from shapely.constructive import BufferCapStyle, BufferJoinStyle
from shapely.geometry.base import CAP_STYLE, JOIN_STYLE
@pytest.mark.parametrize("distance", [float("nan"), float("inf")])
def test_non_finite_distance(distance):
g = geometry.Point(0, 0)
with pytest.raises(ValueError, match="distance must be finite"):
g.buffer(distance)
class BufferTests(unittest.TestCase):
"""Test Buffer Point/Line/Polygon with and without single_sided params"""
def test_empty(self):
g = geometry.Point(0, 0)
h = g.buffer(0)
assert h.is_empty
def test_point(self):
g = geometry.Point(0, 0)
h = g.buffer(1, quad_segs=1)
assert h.geom_type == "Polygon"
expected_coord = [(1.0, 0.0), (0, -1.0), (-1.0, 0), (0, 1.0), (1.0, 0.0)]
for index, coord in enumerate(h.exterior.coords):
assert coord[0] == pytest.approx(expected_coord[index][0])
assert coord[1] == pytest.approx(expected_coord[index][1])
def test_point_single_sidedd(self):
g = geometry.Point(0, 0)
h = g.buffer(1, quad_segs=1, single_sided=True)
assert h.geom_type == "Polygon"
expected_coord = [(1.0, 0.0), (0, -1.0), (-1.0, 0), (0, 1.0), (1.0, 0.0)]
for index, coord in enumerate(h.exterior.coords):
assert coord[0] == pytest.approx(expected_coord[index][0])
assert coord[1] == pytest.approx(expected_coord[index][1])
def test_line(self):
g = geometry.LineString([[0, 0], [0, 1]])
h = g.buffer(1, quad_segs=1)
assert h.geom_type == "Polygon"
expected_coord = [
(-1.0, 1.0),
(0, 2.0),
(1.0, 1.0),
(1.0, 0.0),
(0, -1.0),
(-1.0, 0.0),
(-1.0, 1.0),
]
for index, coord in enumerate(h.exterior.coords):
assert coord[0] == pytest.approx(expected_coord[index][0])
assert coord[1] == pytest.approx(expected_coord[index][1])
def test_line_single_sideded_left(self):
g = geometry.LineString([[0, 0], [0, 1]])
h = g.buffer(1, quad_segs=1, single_sided=True)
assert h.geom_type == "Polygon"
expected_coord = [(0.0, 1.0), (0.0, 0.0), (-1.0, 0.0), (-1.0, 1.0), (0.0, 1.0)]
for index, coord in enumerate(h.exterior.coords):
assert coord[0] == pytest.approx(expected_coord[index][0])
assert coord[1] == pytest.approx(expected_coord[index][1])
def test_line_single_sideded_right(self):
g = geometry.LineString([[0, 0], [0, 1]])
h = g.buffer(-1, quad_segs=1, single_sided=True)
assert h.geom_type == "Polygon"
expected_coord = [(0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)]
for index, coord in enumerate(h.exterior.coords):
assert coord[0] == pytest.approx(expected_coord[index][0])
assert coord[1] == pytest.approx(expected_coord[index][1])
def test_polygon(self):
g = geometry.Polygon([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]])
h = g.buffer(1, quad_segs=1)
assert h.geom_type == "Polygon"
expected_coord = [
(-1.0, 0.0),
(-1.0, 1.0),
(0.0, 2.0),
(1.0, 2.0),
(2.0, 1.0),
(2.0, 0.0),
(1.0, -1.0),
(0.0, -1.0),
(-1.0, 0.0),
]
for index, coord in enumerate(h.exterior.coords):
assert coord[0] == pytest.approx(expected_coord[index][0])
assert coord[1] == pytest.approx(expected_coord[index][1])
def test_polygon_single_sideded(self):
g = geometry.Polygon([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]])
h = g.buffer(1, quad_segs=1, single_sided=True)
assert h.geom_type == "Polygon"
expected_coord = [
(-1.0, 0.0),
(-1.0, 1.0),
(0.0, 2.0),
(1.0, 2.0),
(2.0, 1.0),
(2.0, 0.0),
(1.0, -1.0),
(0.0, -1.0),
(-1.0, 0.0),
]
for index, coord in enumerate(h.exterior.coords):
assert coord[0] == pytest.approx(expected_coord[index][0])
assert coord[1] == pytest.approx(expected_coord[index][1])
def test_enum_values(self):
assert CAP_STYLE.round == 1
assert CAP_STYLE.round == BufferCapStyle.round
assert CAP_STYLE.flat == 2
assert CAP_STYLE.flat == BufferCapStyle.flat
assert CAP_STYLE.square == 3
assert CAP_STYLE.square == BufferCapStyle.square
assert JOIN_STYLE.round == 1
assert JOIN_STYLE.round == BufferJoinStyle.round
assert JOIN_STYLE.mitre == 2
assert JOIN_STYLE.mitre == BufferJoinStyle.mitre
assert JOIN_STYLE.bevel == 3
assert JOIN_STYLE.bevel == BufferJoinStyle.bevel
def test_cap_style(self):
g = geometry.LineString([[0, 0], [1, 0]])
h = g.buffer(1, cap_style=BufferCapStyle.round)
assert h == g.buffer(1, cap_style=CAP_STYLE.round)
assert h == g.buffer(1, cap_style="round")
h = g.buffer(1, cap_style=BufferCapStyle.flat)
assert h == g.buffer(1, cap_style=CAP_STYLE.flat)
assert h == g.buffer(1, cap_style="flat")
h = g.buffer(1, cap_style=BufferCapStyle.square)
assert h == g.buffer(1, cap_style=CAP_STYLE.square)
assert h == g.buffer(1, cap_style="square")
def test_buffer_style(self):
g = geometry.LineString([[0, 0], [1, 0]])
h = g.buffer(1, join_style=BufferJoinStyle.round)
assert h == g.buffer(1, join_style=JOIN_STYLE.round)
assert h == g.buffer(1, join_style="round")
h = g.buffer(1, join_style=BufferJoinStyle.mitre)
assert h == g.buffer(1, join_style=JOIN_STYLE.mitre)
assert h == g.buffer(1, join_style="mitre")
h = g.buffer(1, join_style=BufferJoinStyle.bevel)
assert h == g.buffer(1, join_style=JOIN_STYLE.bevel)
assert h == g.buffer(1, join_style="bevel")
def test_deprecated_quadsegs():
point = geometry.Point(0, 0)
with pytest.warns(FutureWarning):
result = point.buffer(1, quadsegs=1)
expected = point.buffer(1, quad_segs=1)
assert result.equals(expected)
def test_deprecated_resolution():
point = geometry.Point(0, 0)
with pytest.deprecated_call(match="Use 'quad_segs' instead"):
result = point.buffer(1, resolution=1)
expected = point.buffer(1, quad_segs=1)
assert result.equals(expected)

View File

@@ -0,0 +1,51 @@
import unittest
import pytest
from shapely.geometry.polygon import LinearRing, Polygon, orient, signed_area
class SignedAreaTestCase(unittest.TestCase):
def test_triangle(self):
tri = LinearRing([(0, 0), (2, 5), (7, 0)])
assert signed_area(tri) == pytest.approx(-7 * 5 / 2)
def test_square(self):
xmin, xmax = (-1, 1)
ymin, ymax = (-2, 3)
rect = LinearRing(
[(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax), (xmin, ymin)]
)
assert signed_area(rect) == pytest.approx(10.0)
class RingOrientationTestCase(unittest.TestCase):
def test_ccw(self):
ring = LinearRing([(1, 0), (0, 1), (0, 0)])
assert ring.is_ccw
def test_cw(self):
ring = LinearRing([(0, 0), (0, 1), (1, 0)])
assert not ring.is_ccw
class PolygonOrienterTestCase(unittest.TestCase):
def test_no_holes(self):
ring = LinearRing([(0, 0), (0, 1), (1, 0)])
polygon = Polygon(ring)
assert not polygon.exterior.is_ccw
polygon = orient(polygon, 1)
assert polygon.exterior.is_ccw
def test_holes(self):
# fmt: off
polygon = Polygon(
[(0, 0), (0, 1), (1, 0)],
[[(0.5, 0.25), (0.25, 0.5), (0.25, 0.25)]]
)
# fmt: on
assert not polygon.exterior.is_ccw
assert polygon.interiors[0].is_ccw
polygon = orient(polygon, 1)
assert polygon.exterior.is_ccw
assert not polygon.interiors[0].is_ccw

View File

@@ -0,0 +1,119 @@
"""
Tests for GEOSClipByRect based on unit tests from libgeos.
There are some expected differences due to Shapely's handling of empty
geometries.
"""
import pytest
from shapely.ops import clip_by_rect
from shapely.wkt import dumps as dump_wkt, loads as load_wkt
def test_point_outside():
"""Point outside"""
geom1 = load_wkt("POINT (0 0)")
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert dump_wkt(geom2, rounding_precision=0) == "GEOMETRYCOLLECTION EMPTY"
def test_point_inside():
"""Point inside"""
geom1 = load_wkt("POINT (15 15)")
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert dump_wkt(geom2, rounding_precision=0) == "POINT (15 15)"
def test_point_on_boundary():
"""Point on boundary"""
geom1 = load_wkt("POINT (15 10)")
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert dump_wkt(geom2, rounding_precision=0) == "GEOMETRYCOLLECTION EMPTY"
def test_line_outside():
"""Line outside"""
geom1 = load_wkt("LINESTRING (0 0, -5 5)")
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert dump_wkt(geom2, rounding_precision=0) == "GEOMETRYCOLLECTION EMPTY"
def test_line_inside():
"""Line inside"""
geom1 = load_wkt("LINESTRING (15 15, 16 15)")
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert dump_wkt(geom2, rounding_precision=0) == "LINESTRING (15 15, 16 15)"
def test_line_on_boundary():
"""Line on boundary"""
geom1 = load_wkt("LINESTRING (10 15, 10 10, 15 10)")
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert dump_wkt(geom2, rounding_precision=0) == "GEOMETRYCOLLECTION EMPTY"
def test_line_splitting_rectangle():
"""Line splitting rectangle"""
geom1 = load_wkt("LINESTRING (10 5, 25 20)")
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert dump_wkt(geom2, rounding_precision=0) == "LINESTRING (15 10, 20 15)"
@pytest.mark.xfail(reason="TODO issue to CCW")
def test_polygon_shell_ccw_fully_on_rectangle_boundary():
"""Polygon shell (CCW) fully on rectangle boundary"""
geom1 = load_wkt("POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))")
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert (
dump_wkt(geom2, rounding_precision=0)
== "POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))"
)
@pytest.mark.xfail(reason="TODO issue to CW")
def test_polygon_shell_cc_fully_on_rectangle_boundary():
"""Polygon shell (CW) fully on rectangle boundary"""
geom1 = load_wkt("POLYGON ((10 10, 10 20, 20 20, 20 10, 10 10))")
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert (
dump_wkt(geom2, rounding_precision=0)
== "POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))"
)
def polygon_hole_ccw_fully_on_rectangle_boundary():
"""Polygon hole (CCW) fully on rectangle boundary"""
geom1 = load_wkt(
"POLYGON ((0 0, 0 30, 30 30, 30 0, 0 0), (10 10, 20 10, 20 20, 10 20, 10 10))"
)
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert dump_wkt(geom2, rounding_precision=0) == "GEOMETRYCOLLECTION EMPTY"
def polygon_hole_cw_fully_on_rectangle_boundary():
"""Polygon hole (CW) fully on rectangle boundary"""
geom1 = load_wkt(
"POLYGON ((0 0, 0 30, 30 30, 30 0, 0 0), (10 10, 10 20, 20 20, 20 10, 10 10))"
)
geom2 = clip_by_rect(geom1, 10, 10, 20, 20)
assert dump_wkt(geom2, rounding_precision=0) == "GEOMETRYCOLLECTION EMPTY"
def polygon_fully_within_rectangle():
"""Polygon fully within rectangle"""
wkt = "POLYGON ((1 1, 1 30, 30 30, 30 1, 1 1), (10 10, 20 10, 20 20, 10 20, 10 10))"
geom1 = load_wkt(wkt)
geom2 = clip_by_rect(geom1, 0, 0, 40, 40)
assert dump_wkt(geom2, rounding_precision=0) == wkt
def polygon_overlapping_rectangle():
"""Polygon overlapping rectangle"""
wkt = "POLYGON ((0 0, 0 30, 30 30, 30 0, 0 0), (10 10, 20 10, 20 20, 10 20, 10 10))"
geom1 = load_wkt(wkt)
geom2 = clip_by_rect(geom1, 5, 5, 15, 15)
assert (
dump_wkt(geom2, rounding_precision=0)
== "POLYGON ((5 5, 5 15, 10 15, 10 10, 15 10, 15 5, 5 5))"
)

View File

@@ -0,0 +1,217 @@
"""
When a "context" passed to shape/asShape has a coordinate
which is missing a dimension we should raise a descriptive error.
When we use mixed dimensions in a WKT geometry, the parser strips
any dimension which is not present in every coordinate.
"""
import pytest
import shapely
from shapely import wkt
from shapely.errors import GEOSException
from shapely.geometry import LineString, Polygon, shape
geojson_cases = [
{"type": "LineString", "coordinates": [[1, 1, 1], [2, 2]]},
# Specific test case from #869
{
"type": "Polygon",
"coordinates": [
[
[55.12916764533149, 24.980385694214384, 2.5],
[55.13098248044217, 24.979828079961905],
[55.13966519231666, 24.97801442415322],
[55.13966563924936, 24.97801442415322],
[55.14139286840762, 24.982307444496097],
[55.14169331277646, 24.983717465495562],
[55.14203489144224, 24.985419446276566, 2.5],
[55.14180327151276, 24.98428602667792, 2.5],
[55.14170091915952, 24.984242720177235, 2.5],
[55.14122966992623, 24.984954809433702, 2.5],
[55.14134021791831, 24.985473928648396, 2.5],
[55.141405876161286, 24.986090184809793, 2.5],
[55.141361358941225, 24.986138101357326, 2.5],
[55.14093322994411, 24.986218753894093, 2.5],
[55.140897653420964, 24.986214283545635, 2.5],
[55.14095492976058, 24.9863027591922, 2.5],
[55.140900447388745, 24.98628436557094, 2.5],
[55.140867059473706, 24.98628869622101, 2.5],
[55.14089155325796, 24.986402364143782, 2.5],
[55.14090938808566, 24.986479011993385, 2.5],
[55.140943893587824, 24.986471188883584, 2.5],
[55.1410161176551, 24.9864174050037, 2.5],
[55.140996932409635, 24.986521806266644, 2.5],
[55.14163554031332, 24.986910400619593, 2.5],
[55.14095781686062, 24.987033474900578, 2.5],
[55.14058258698692, 24.98693261266349, 2.5],
[55.14032624044253, 24.98747538747211, 2.5],
[55.14007240846915, 24.988001119077232, 2.5],
[55.14013122149105, 24.98831115636925, 2.5],
[55.13991827457961, 24.98834356639557, 2.5],
[55.139779460946755, 24.988254625087706, 2.5],
[55.13974742344948, 24.988261377176524, 2.5],
[55.139515198160304, 24.98841811876934, 2.5],
[55.13903617238334, 24.98817914139135, 2.5],
[55.1391330764994, 24.988660542040925, 2.5],
[55.13914369357698, 24.989438289540374, 2.5],
[55.136431216517785, 24.98966711550207, 2.0],
[55.13659028641709, 24.99041706302204, 2.0],
[55.1355852030721, 24.990933481401207, 2.5],
[55.13535549235394, 24.99110470506038, 2.5],
[55.13512578163577, 24.99127592871955, 2.5],
[55.129969653784556, 24.991440074326995, 2.5],
[55.130221623112746, 24.988070688875112, 2.5],
[55.130451333830905, 24.98789946521594, 2.5],
[55.13089208224919, 24.98742639990359, 2.5],
[55.132177586827666, 24.989003408454433, 2.5],
[55.13238862452779, 24.988701566801254, 2.5],
[55.132482594977674, 24.988501518707757, 2.5],
[55.132525994610624, 24.988048802794115, 2.5],
[55.13249018525683, 24.987180623870653, 2.5],
[55.13253358488978, 24.986727907957015, 2.5],
[55.1322761673244, 24.985827132742713, 2.5],
[55.13163341503516, 24.98503862846729, 2.5],
[55.131514764536504, 24.984469124700183, 2.5],
[55.131275600894, 24.983796337257242, 2.0],
[55.13066865795855, 24.98387601190528, 2.0],
[55.13026930682963, 24.981537228037503, 2.0],
[55.130260412698846, 24.981495691049748, 2.0],
[55.13025151856806, 24.981454154061993, 2.0],
[55.13022925995803, 24.98096497686874, 2.5],
[55.12984453059386, 24.9804285816199, 2.5],
[55.129998291954365, 24.98021419115843, 2.5],
[55.12916764533149, 24.980385694214384, 2.5],
]
],
},
]
direct_cases = [
(LineString, [[[0, 0, 0], [1, 1]]]),
(Polygon, [[[0, 0, 0], [1, 1, 0], [1, 1], [0, 1, 0], [0, 0, 0]]]),
# Specific test case from #869
(
Polygon,
[
[
[55.12916764533149, 24.980385694214384, 2.5],
[55.13098248044217, 24.979828079961905],
[55.13966519231666, 24.97801442415322],
[55.13966563924936, 24.97801442415322],
[55.14139286840762, 24.982307444496097],
[55.14169331277646, 24.983717465495562],
[55.14203489144224, 24.985419446276566, 2.5],
[55.14180327151276, 24.98428602667792, 2.5],
[55.14170091915952, 24.984242720177235, 2.5],
[55.14122966992623, 24.984954809433702, 2.5],
[55.14134021791831, 24.985473928648396, 2.5],
[55.141405876161286, 24.986090184809793, 2.5],
[55.141361358941225, 24.986138101357326, 2.5],
[55.14093322994411, 24.986218753894093, 2.5],
[55.140897653420964, 24.986214283545635, 2.5],
[55.14095492976058, 24.9863027591922, 2.5],
[55.140900447388745, 24.98628436557094, 2.5],
[55.140867059473706, 24.98628869622101, 2.5],
[55.14089155325796, 24.986402364143782, 2.5],
[55.14090938808566, 24.986479011993385, 2.5],
[55.140943893587824, 24.986471188883584, 2.5],
[55.1410161176551, 24.9864174050037, 2.5],
[55.140996932409635, 24.986521806266644, 2.5],
[55.14163554031332, 24.986910400619593, 2.5],
[55.14095781686062, 24.987033474900578, 2.5],
[55.14058258698692, 24.98693261266349, 2.5],
[55.14032624044253, 24.98747538747211, 2.5],
[55.14007240846915, 24.988001119077232, 2.5],
[55.14013122149105, 24.98831115636925, 2.5],
[55.13991827457961, 24.98834356639557, 2.5],
[55.139779460946755, 24.988254625087706, 2.5],
[55.13974742344948, 24.988261377176524, 2.5],
[55.139515198160304, 24.98841811876934, 2.5],
[55.13903617238334, 24.98817914139135, 2.5],
[55.1391330764994, 24.988660542040925, 2.5],
[55.13914369357698, 24.989438289540374, 2.5],
[55.136431216517785, 24.98966711550207, 2.0],
[55.13659028641709, 24.99041706302204, 2.0],
[55.1355852030721, 24.990933481401207, 2.5],
[55.13535549235394, 24.99110470506038, 2.5],
[55.13512578163577, 24.99127592871955, 2.5],
[55.129969653784556, 24.991440074326995, 2.5],
[55.130221623112746, 24.988070688875112, 2.5],
[55.130451333830905, 24.98789946521594, 2.5],
[55.13089208224919, 24.98742639990359, 2.5],
[55.132177586827666, 24.989003408454433, 2.5],
[55.13238862452779, 24.988701566801254, 2.5],
[55.132482594977674, 24.988501518707757, 2.5],
[55.132525994610624, 24.988048802794115, 2.5],
[55.13249018525683, 24.987180623870653, 2.5],
[55.13253358488978, 24.986727907957015, 2.5],
[55.1322761673244, 24.985827132742713, 2.5],
[55.13163341503516, 24.98503862846729, 2.5],
[55.131514764536504, 24.984469124700183, 2.5],
[55.131275600894, 24.983796337257242, 2.0],
[55.13066865795855, 24.98387601190528, 2.0],
[55.13026930682963, 24.981537228037503, 2.0],
[55.130260412698846, 24.981495691049748, 2.0],
[55.13025151856806, 24.981454154061993, 2.0],
[55.13022925995803, 24.98096497686874, 2.5],
[55.12984453059386, 24.9804285816199, 2.5],
[55.129998291954365, 24.98021419115843, 2.5],
[55.12916764533149, 24.980385694214384, 2.5],
]
],
),
]
wkt_cases = [
# preserve 3rd dimension
("MULTIPOINT (1 1 1, 2 2)", "MULTIPOINT Z (1 1 1, 2 2 0)"),
("MULTIPOINT (1 1, 2 2 2)", "MULTIPOINT Z (1 1 0, 2 2 2)"),
("LINESTRING (1 1 1, 2 2)", "LINESTRING Z (1 1 1, 2 2 0)"),
(
"POLYGON ((0 0 0, 1 0 0, 1 1, 0 1 0, 0 0 0))",
"POLYGON Z ((0 0 0, 1 0 0, 1 1 0, 0 1 0, 0 0 0))",
),
# drop 3rd dimension
("LINESTRING (1 1, 2 2 2)", "LINESTRING (1 1, 2 2)"),
("POLYGON ((0 0, 1 0 1, 1 1, 0 1, 0 0))", "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"),
]
@pytest.mark.filterwarnings("ignore:Creating an ndarray from ragged nested sequences:")
@pytest.mark.parametrize("geojson", geojson_cases)
def test_create_from_geojson(geojson):
# exact error depends on numpy version
with pytest.raises((ValueError, TypeError)) as exc:
shape(geojson).wkt
assert exc.match(
"Inconsistent coordinate dimensionality|Input operand 0 does not have enough "
"dimensions|ufunc 'linestrings' not supported for the input types|setting an "
"array element with a sequence. The requested array has an inhomogeneous shape"
)
@pytest.mark.filterwarnings("ignore:Creating an ndarray from ragged nested sequences:")
@pytest.mark.parametrize("constructor, args", direct_cases)
def test_create_directly(constructor, args):
with pytest.raises((ValueError, TypeError)) as exc:
constructor(*args)
assert exc.match(
"Inconsistent coordinate dimensionality|Input operand 0 does not have enough "
"dimensions|ufunc 'linestrings' not supported for the input types|setting an "
"array element with a sequence. The requested array has an inhomogeneous shape"
)
@pytest.mark.parametrize("wkt_geom,expected", wkt_cases)
def test_create_from_wkt(wkt_geom, expected):
if shapely.geos_version >= (3, 12, 0):
# https://github.com/shapely/shapely/issues/1541
with pytest.raises(GEOSException):
wkt.loads(wkt_geom)
else:
geom = wkt.loads(wkt_geom)
assert geom.wkt == expected

View File

@@ -0,0 +1,32 @@
import unittest
from shapely.geometry import LineString, Point, Polygon
from shapely.ops import triangulate
class DelaunayTriangulation(unittest.TestCase):
"""
Only testing the number of triangles and their type here.
This doesn't actually test the points in the resulting geometries.
"""
def setUp(self):
self.p = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
def test_polys(self):
polys = triangulate(self.p)
assert len(polys) == 2
for p in polys:
assert isinstance(p, Polygon)
def test_lines(self):
polys = triangulate(self.p, edges=True)
assert len(polys) == 5
for p in polys:
assert isinstance(p, LineString)
def test_point(self):
p = Point(1, 1)
polys = triangulate(p)
assert len(polys) == 0

View File

@@ -0,0 +1,22 @@
from shapely.geometry import MultiPolygon, Point, Polygon
def test_empty_polygon():
"""No constructor arg makes an empty polygon geometry."""
assert Polygon().is_empty
def test_empty_multipolygon():
"""No constructor arg makes an empty multipolygon geometry."""
assert MultiPolygon().is_empty
def test_multipolygon_empty_polygon():
"""An empty polygon passed to MultiPolygon() makes an empty
multipolygon geometry."""
assert MultiPolygon([Polygon()]).is_empty
def test_multipolygon_empty_among_polygon():
"""An empty polygon passed to MultiPolygon() is ignored."""
assert len(MultiPolygon([Point(0, 0).buffer(1.0), Polygon()]).geoms) == 1

View File

@@ -0,0 +1,24 @@
from shapely import Point, Polygon
def test_equals_exact():
p1 = Point(1.0, 1.0)
p2 = Point(2.0, 2.0)
p3 = Point(1.0, 1.0 + 1e-7)
assert not p1.equals(p2)
assert not p1.equals_exact(p2, 0.001)
assert not p1.equals_exact(p3)
assert p1.equals_exact(p3, 1e-6)
# test polygons
shell = [(10, 10), (10, -10), (-10, -10), (-10, 10)]
holes = [[(1, 1), (1, -1), (-1, -1), (-1, 1)]]
p1 = Polygon(shell, holes)
p2 = Polygon(shell, holes=[holes[0][::-1]])
assert p1.equals(p2)
assert not p1.equals_exact(p2, 1e-5)
assert p1.equals_exact(p2, 1e-5, normalize=True)
hole2 = [(1, 1), (1, -1), (-1, -1), (-1, 1.01)]
p3 = Polygon(shell, holes=[hole2])
assert not p1.equals_exact(p3, 1e-5)

View File

@@ -0,0 +1,118 @@
import unittest
from shapely import wkt
from shapely.geometry import shape
from shapely.geometry.linestring import LineString
from shapely.geometry.multilinestring import MultiLineString
from shapely.geometry.multipoint import MultiPoint
from shapely.geometry.multipolygon import MultiPolygon
from shapely.geometry.polygon import LinearRing, Polygon
class GeoThing:
def __init__(self, d):
self.__geo_interface__ = d
class GeoInterfaceTestCase(unittest.TestCase):
def test_geointerface(self):
# Convert a dictionary
d = {"type": "Point", "coordinates": (0.0, 0.0)}
geom = shape(d)
assert geom.geom_type == "Point"
assert tuple(geom.coords) == ((0.0, 0.0),)
# Convert an object that implements the geo protocol
geom = None
thing = GeoThing({"type": "Point", "coordinates": (0.0, 0.0)})
geom = shape(thing)
assert geom.geom_type == "Point"
assert tuple(geom.coords) == ((0.0, 0.0),)
# Check line string
geom = shape({"type": "LineString", "coordinates": ((-1.0, -1.0), (1.0, 1.0))})
assert isinstance(geom, LineString)
assert tuple(geom.coords) == ((-1.0, -1.0), (1.0, 1.0))
# Check linearring
geom = shape(
{
"type": "LinearRing",
"coordinates": (
(0.0, 0.0),
(0.0, 1.0),
(1.0, 1.0),
(2.0, -1.0),
(0.0, 0.0),
),
}
)
assert isinstance(geom, LinearRing)
assert tuple(geom.coords) == (
(0.0, 0.0),
(0.0, 1.0),
(1.0, 1.0),
(2.0, -1.0),
(0.0, 0.0),
)
# polygon
geom = shape(
{
"type": "Polygon",
"coordinates": (
((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (2.0, -1.0), (0.0, 0.0)),
((0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.2, 0.1), (0.1, 0.1)),
),
}
)
assert isinstance(geom, Polygon)
assert tuple(geom.exterior.coords) == (
(0.0, 0.0),
(0.0, 1.0),
(1.0, 1.0),
(2.0, -1.0),
(0.0, 0.0),
)
assert len(geom.interiors) == 1
# multi point
geom = shape({"type": "MultiPoint", "coordinates": ((1.0, 2.0), (3.0, 4.0))})
assert isinstance(geom, MultiPoint)
assert len(geom.geoms) == 2
# multi line string
geom = shape(
{"type": "MultiLineString", "coordinates": (((0.0, 0.0), (1.0, 2.0)),)}
)
assert isinstance(geom, MultiLineString)
assert len(geom.geoms) == 1
# multi polygon
geom = shape(
{
"type": "MultiPolygon",
"coordinates": [
(
((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)),
((0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.2, 0.1), (0.1, 0.1)),
)
],
}
)
assert isinstance(geom, MultiPolygon)
assert len(geom.geoms) == 1
def test_empty_wkt_polygon():
"""Confirm fix for issue #450"""
g = wkt.loads("POLYGON EMPTY")
assert g.__geo_interface__["type"] == "Polygon"
assert g.__geo_interface__["coordinates"] == ()
def test_empty_polygon():
"""Confirm fix for issue #450"""
g = Polygon()
assert g.__geo_interface__["type"] == "Polygon"
assert g.__geo_interface__["coordinates"] == ()

View File

@@ -0,0 +1,26 @@
"""Test recovery from operation on invalid geometries"""
import unittest
import pytest
import shapely
from shapely.errors import TopologicalError
from shapely.geometry import Polygon
class InvalidGeometriesTestCase(unittest.TestCase):
def test_invalid_intersection(self):
# Make a self-intersecting polygon
polygon_invalid = Polygon([(0, 0), (1, 1), (1, -1), (0, 1), (0, 0)])
assert not polygon_invalid.is_valid
# Intersect with a valid polygon
polygon = Polygon([(-0.5, -0.5), (-0.5, 0.5), (0.5, 0.5), (0.5, -5)])
assert polygon.is_valid
assert polygon_invalid.intersects(polygon)
with pytest.raises((TopologicalError, shapely.GEOSException)):
polygon_invalid.intersection(polygon)
with pytest.raises((TopologicalError, shapely.GEOSException)):
polygon.intersection(polygon_invalid)

View File

@@ -0,0 +1,72 @@
import unittest
import pytest
import shapely
from shapely.geometry import LineString, MultiLineString, Point
class LinearReferencingTestCase(unittest.TestCase):
def setUp(self):
self.point = Point(1, 1)
self.line1 = LineString([(0, 0), (2, 0)])
self.line2 = LineString([(3, 0), (3, 6)])
self.multiline = MultiLineString(
[list(self.line1.coords), list(self.line2.coords)]
)
def test_line1_project(self):
assert self.line1.project(self.point) == 1.0
assert self.line1.project(self.point, normalized=True) == 0.5
def test_alias_project(self):
assert self.line1.line_locate_point(self.point) == 1.0
assert self.line1.line_locate_point(self.point, normalized=True) == 0.5
def test_line2_project(self):
assert self.line2.project(self.point) == 1.0
assert self.line2.project(self.point, normalized=True) == pytest.approx(
0.16666666666, 8
)
def test_multiline_project(self):
assert self.multiline.project(self.point) == 1.0
assert self.multiline.project(self.point, normalized=True) == 0.125
def test_not_supported_project(self):
with pytest.raises(shapely.GEOSException, match="IllegalArgumentException"):
self.point.buffer(1.0).project(self.point)
def test_not_on_line_project(self):
# Points that aren't on the line project to 0.
assert self.line1.project(Point(-10, -10)) == 0.0
def test_line1_interpolate(self):
assert self.line1.interpolate(0.5).equals(Point(0.5, 0.0))
assert self.line1.interpolate(-0.5).equals(Point(1.5, 0.0))
assert self.line1.interpolate(0.5, normalized=True).equals(Point(1, 0))
assert self.line1.interpolate(-0.5, normalized=True).equals(Point(1, 0))
def test_alias_interpolate(self):
assert self.line1.line_interpolate_point(0.5).equals(Point(0.5, 0.0))
assert self.line1.line_interpolate_point(-0.5).equals(Point(1.5, 0.0))
assert self.line1.line_interpolate_point(0.5, normalized=True).equals(
Point(1, 0)
)
assert self.line1.line_interpolate_point(-0.5, normalized=True).equals(
Point(1, 0)
)
def test_line2_interpolate(self):
assert self.line2.interpolate(0.5).equals(Point(3.0, 0.5))
assert self.line2.interpolate(0.5, normalized=True).equals(Point(3, 3))
def test_multiline_interpolate(self):
assert self.multiline.interpolate(0.5).equals(Point(0.5, 0))
assert self.multiline.interpolate(0.5, normalized=True).equals(Point(3.0, 2.0))
def test_line_ends_interpolate(self):
# Distances greater than length of the line or less than
# zero yield the line's ends.
assert self.line1.interpolate(-1000).equals(Point(0.0, 0.0))
assert self.line1.interpolate(1000).equals(Point(2.0, 0.0))

View File

@@ -0,0 +1,43 @@
import unittest
from shapely.geometry import LineString, MultiLineString
from shapely.ops import linemerge
class LineMergeTestCase(unittest.TestCase):
def test_linemerge(self):
lines = MultiLineString([[(0, 0), (1, 1)], [(2, 0), (2, 1), (1, 1)]])
result = linemerge(lines)
assert isinstance(result, LineString)
assert not result.is_ring
assert len(result.coords) == 4
assert result.coords[0] == (0.0, 0.0)
assert result.coords[3] == (2.0, 0.0)
lines2 = MultiLineString([((0, 0), (1, 1)), ((0, 0), (2, 0), (2, 1), (1, 1))])
result = linemerge(lines2)
assert result.is_ring
assert len(result.coords) == 5
lines3 = [
LineString([(0, 0), (1, 1)]),
LineString([(0, 0), (0, 1)]),
]
result = linemerge(lines3)
assert not result.is_ring
assert len(result.coords) == 3
assert result.coords[0] == (0.0, 1.0)
assert result.coords[2] == (1.0, 1.0)
lines4 = [
[(0, 0), (1, 1)],
[(0, 0), (0, 1)],
]
assert result.equals(linemerge(lines4))
lines5 = [
((0, 0), (1, 1)),
((1, 0), (0, 1)),
]
result = linemerge(lines5)
assert result.geom_type == "MultiLineString"

View File

@@ -0,0 +1,54 @@
"""Test locale independence of WKT"""
import locale
import sys
import unittest
from shapely.wkt import dumps, loads
# Set locale to one that uses a comma as decimal separator
# TODO: try a few other common locales
if sys.platform == "win32":
test_locales = {"Portuguese": "portuguese_brazil", "Italian": "italian_italy"}
else:
test_locales = {
"Portuguese": "pt_BR.UTF-8",
"Italian": "it_IT.UTF-8",
}
do_test_locale = False
def setUpModule():
global do_test_locale
for name in test_locales:
try:
test_locale = test_locales[name]
locale.setlocale(locale.LC_ALL, test_locale)
do_test_locale = True
break
except Exception:
pass
if not do_test_locale:
raise unittest.SkipTest("test locale not found")
def tearDownModule():
if sys.platform == "win32" or sys.version_info[0:2] >= (3, 11):
locale.setlocale(locale.LC_ALL, "")
else:
# Deprecated since version 3.11, will be removed in version 3.13
locale.resetlocale()
class LocaleTestCase(unittest.TestCase):
# @unittest.skipIf(not do_test_locale, 'test locale not found')
def test_wkt_locale(self):
# Test reading and writing
p = loads("POINT (0.0 0.0)")
assert p.x == 0.0
assert p.y == 0.0
wkt = dumps(p)
assert wkt.startswith("POINT")
assert "," not in wkt

View File

@@ -0,0 +1,15 @@
from shapely.geometry import Polygon
from shapely.validation import make_valid
def test_make_valid_invalid_input():
geom = Polygon([(0, 0), (0, 2), (1, 1), (2, 2), (2, 0), (1, 1), (0, 0)])
valid = make_valid(geom)
assert len(valid.geoms) == 2
assert all(geom.geom_type == "Polygon" for geom in valid.geoms)
def test_make_valid_input():
geom = Polygon([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])
valid = make_valid(geom)
assert id(valid) == id(geom)

View File

@@ -0,0 +1,14 @@
import unittest
from shapely.geometry import Point, Polygon, mapping
class MappingTestCase(unittest.TestCase):
def test_point(self):
m = mapping(Point(0, 0))
assert m["type"] == "Point"
assert m["coordinates"] == (0.0, 0.0)
def test_empty_polygon(self):
"""Empty polygons will round trip without error"""
assert mapping(Polygon()) is not None

View File

@@ -0,0 +1,30 @@
"""
Tests for the minimum clearance property.
"""
import math
from shapely.wkt import loads as load_wkt
def test_point():
point = load_wkt("POINT (0 0)")
assert point.minimum_clearance == math.inf
def test_linestring():
line = load_wkt("LINESTRING (0 0, 1 1, 2 2)")
assert round(line.minimum_clearance, 6) == 1.414214
def test_simple_polygon():
poly = load_wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))")
assert poly.minimum_clearance == 1.0
def test_more_complicated_polygon():
poly = load_wkt(
"POLYGON ((20 20, 34 124, 70 140, 130 130, 70 100, 110 70, 170 20, 90 10, "
"20 20))"
)
assert round(poly.minimum_clearance, 6) == 35.777088

View File

@@ -0,0 +1,42 @@
# Tests of support for Numpy ndarrays. See
# https://github.com/sgillies/shapely/issues/26 for discussion.
import unittest
from functools import reduce
import numpy as np
from shapely import geometry
class TransposeTestCase(unittest.TestCase):
def test_multipoint(self):
arr = np.array([[1.0, 1.0, 2.0, 2.0, 1.0], [3.0, 4.0, 4.0, 3.0, 3.0]])
tarr = arr.T
shape = geometry.MultiPoint(tarr)
coords = reduce(lambda x, y: x + y, [list(g.coords) for g in shape.geoms])
assert coords == [(1.0, 3.0), (1.0, 4.0), (2.0, 4.0), (2.0, 3.0), (1.0, 3.0)]
def test_linestring(self):
a = np.array([[1.0, 1.0, 2.0, 2.0, 1.0], [3.0, 4.0, 4.0, 3.0, 3.0]])
t = a.T
s = geometry.LineString(t)
assert list(s.coords) == [
(1.0, 3.0),
(1.0, 4.0),
(2.0, 4.0),
(2.0, 3.0),
(1.0, 3.0),
]
def test_polygon(self):
a = np.array([[1.0, 1.0, 2.0, 2.0, 1.0], [3.0, 4.0, 4.0, 3.0, 3.0]])
t = a.T
s = geometry.Polygon(t)
assert list(s.exterior.coords) == [
(1.0, 3.0),
(1.0, 4.0),
(2.0, 4.0),
(2.0, 3.0),
(1.0, 3.0),
]

View File

@@ -0,0 +1,18 @@
import unittest
import pytest
from shapely.geometry import Point
from shapely.ops import nearest_points
class Nearest(unittest.TestCase):
def test_nearest(self):
first, second = nearest_points(
Point(0, 0).buffer(1.0),
Point(3, 0).buffer(1.0),
)
assert first.x == pytest.approx(1.0)
assert second.x == pytest.approx(2.0)
assert first.y == pytest.approx(0.0)
assert second.y == pytest.approx(0.0)

View File

@@ -0,0 +1,124 @@
import unittest
import pytest
import shapely
from shapely import geos_version
from shapely.errors import TopologicalError
from shapely.geometry import GeometryCollection, LineString, MultiPoint, Point, Polygon
from shapely.wkt import loads
class OperationsTestCase(unittest.TestCase):
def test_operations(self):
point = Point(0.0, 0.0)
# General geometry
assert point.area == 0.0
assert point.length == 0.0
assert point.distance(Point(-1.0, -1.0)) == pytest.approx(1.4142135623730951)
# Topology operations
# Envelope
assert isinstance(point.envelope, Point)
# Intersection
assert point.intersection(Point(-1, -1)).is_empty
# Buffer
assert isinstance(point.buffer(10.0), Polygon)
assert isinstance(point.buffer(10.0, quad_segs=32), Polygon)
# Simplify
p = loads(
"POLYGON ((120 120, 140 199, 160 200, 180 199, 220 120, 122 122, 121 121, "
"120 120))"
)
expected = loads(
"POLYGON ((120 120, 140 199, 160 200, 180 199, 220 120, 120 120))"
)
s = p.simplify(10.0, preserve_topology=False)
assert s.equals_exact(expected, 0.001)
p = loads(
"POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),"
"(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))"
)
expected = loads(
"POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200),"
"(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))"
)
s = p.simplify(10.0, preserve_topology=True)
assert s.equals_exact(expected, 0.001)
# Convex Hull
assert isinstance(point.convex_hull, Point)
# Differences
assert isinstance(point.difference(Point(-1, 1)), Point)
assert isinstance(point.symmetric_difference(Point(-1, 1)), MultiPoint)
# Boundary
assert isinstance(point.boundary, GeometryCollection)
# Union
assert isinstance(point.union(Point(-1, 1)), MultiPoint)
assert isinstance(point.representative_point(), Point)
assert isinstance(point.point_on_surface(), Point)
assert point.representative_point() == point.point_on_surface()
assert isinstance(point.centroid, Point)
def test_relate(self):
# Relate
assert Point(0, 0).relate(Point(-1, -1)) == "FF0FFF0F2"
# issue #294: should raise TopologicalError on exception
invalid_polygon = loads(
"POLYGON ((40 100, 80 100, 80 60, 40 60, 40 100), "
"(60 60, 80 60, 80 40, 60 40, 60 60))"
)
assert not invalid_polygon.is_valid
if geos_version < (3, 13, 0):
with pytest.raises((TopologicalError, shapely.GEOSException)):
invalid_polygon.relate(invalid_polygon)
else: # resolved with RelateNG
assert invalid_polygon.relate(invalid_polygon) == "2FFF1FFF2"
def test_hausdorff_distance(self):
point = Point(1, 1)
line = LineString([(2, 0), (2, 4), (3, 4)])
distance = point.hausdorff_distance(line)
assert distance == point.distance(Point(3, 4))
def test_interpolate(self):
# successful interpolation
test_line = LineString([(1, 1), (1, 2)])
known_point = Point(1, 1.5)
interpolated_point = test_line.interpolate(0.5, normalized=True)
assert interpolated_point == known_point
# Issue #653; should nog segfault for empty geometries
empty_line = loads("LINESTRING EMPTY")
assert empty_line.is_empty
interpolated_point = empty_line.interpolate(0.5, normalized=True)
assert interpolated_point.is_empty
# invalid geometry should raise TypeError on exception
polygon = loads("POLYGON EMPTY")
with pytest.raises(TypeError, match="incorrect geometry type"):
polygon.interpolate(0.5, normalized=True)
def test_normalize(self):
point = Point(1, 1)
result = point.normalize()
assert result == point
line = loads("MULTILINESTRING ((1 1, 0 0), (1 1, 1 2))")
result = line.normalize()
expected = loads("MULTILINESTRING ((1 1, 1 2), (0 0, 1 1))")
assert result == expected

View File

@@ -0,0 +1,60 @@
import unittest
from shapely.geometry import LineString, MultiPoint, Point, Polygon
class OperatorsTestCase(unittest.TestCase):
def test_point(self):
point = Point(0, 0)
point2 = Point(-1, 1)
assert point.union(point2).equals(point | point2)
assert (point & point2).is_empty
assert point.equals(point - point2)
assert point.symmetric_difference(point2).equals(point ^ point2)
assert point != point2
point_dupe = Point(0, 0)
assert point, point_dupe
def test_multipoint(self):
mp1 = MultiPoint([(0, 0), (1, 1)])
mp1_dup = MultiPoint([(0, 0), (1, 1)])
mp1_rev = MultiPoint([(1, 1), (0, 0)])
mp2 = MultiPoint([(0, 0), (1, 1), (2, 2)])
mp3 = MultiPoint([(0, 0), (1, 1), (2, 3)])
assert mp1 == mp1_dup
assert mp1 != mp1_rev
assert mp1 != mp2
assert mp2 != mp3
p = Point(0, 0)
mp = MultiPoint([(0, 0)])
assert p != mp
assert mp != p
def test_polygon(self):
shell = ((0, 0), (3, 0), (3, 3), (0, 3))
hole = ((1, 1), (2, 1), (2, 2), (1, 2))
p_solid = Polygon(shell)
p2_solid = Polygon(shell)
p_hole = Polygon(shell, holes=[hole])
p2_hole = Polygon(shell, holes=[hole])
assert p_solid == p2_solid
assert p_hole == p2_hole
assert p_solid != p_hole
shell2 = ((-5, 2), (10.5, 3), (7, 3))
p3_hole = Polygon(shell2, holes=[hole])
assert p_hole != p3_hole
def test_linestring(self):
line1 = LineString([(0, 0), (1, 1), (2, 2)])
line2 = LineString([(0, 0), (2, 2)])
line2_dup = LineString([(0, 0), (2, 2)])
# .equals() indicates these are the same
assert line1.equals(line2)
# but != indicates these are different
assert line1 != line2
# but dupes are the same with ==
assert line2 == line2_dup

View File

@@ -0,0 +1,92 @@
import unittest
from numpy.testing import assert_array_equal
from shapely.geometry import (
GeometryCollection,
LinearRing,
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
)
from shapely.ops import orient
class OrientTestCase(unittest.TestCase):
def test_point(self):
point = Point(0, 0)
assert orient(point, 1) == point
assert orient(point, -1) == point
def test_multipoint(self):
multipoint = MultiPoint([(0, 0), (1, 1)])
assert orient(multipoint, 1) == multipoint
assert orient(multipoint, -1) == multipoint
def test_linestring(self):
linestring = LineString([(0, 0), (1, 1)])
assert orient(linestring, 1) == linestring
assert orient(linestring, -1) == linestring
def test_multilinestring(self):
multilinestring = MultiLineString([[(0, 0), (1, 1)], [(1, 0), (0, 1)]])
assert orient(multilinestring, 1) == multilinestring
assert orient(multilinestring, -1) == multilinestring
def test_linearring(self):
linearring = LinearRing([(0, 0), (0, 1), (1, 0)])
assert orient(linearring, 1) == linearring
assert orient(linearring, -1) == linearring
def test_empty_polygon(self):
polygon = Polygon()
assert orient(polygon) == polygon
def test_polygon(self):
polygon = Polygon([(0, 0), (0, 1), (1, 0)])
polygon_reversed = Polygon(polygon.exterior.coords[::-1])
assert (orient(polygon, 1)) == polygon_reversed
assert (orient(polygon, -1)) == polygon
def test_multipolygon(self):
polygon1 = Polygon([(0, 0), (0, 1), (1, 0)])
polygon2 = Polygon([(1, 0), (2, 0), (2, 1)])
polygon1_reversed = Polygon(polygon1.exterior.coords[::-1])
polygon2_reversed = Polygon(polygon2.exterior.coords[::-1])
multipolygon = MultiPolygon([polygon1, polygon2])
assert not polygon1.exterior.is_ccw
assert polygon2.exterior.is_ccw
assert orient(multipolygon, 1) == MultiPolygon([polygon1_reversed, polygon2])
assert orient(multipolygon, -1) == MultiPolygon([polygon1, polygon2_reversed])
def test_geometrycollection(self):
polygon = Polygon([(0, 0), (0, 1), (1, 0)])
polygon_reversed = Polygon(polygon.exterior.coords[::-1])
collection = GeometryCollection([polygon])
assert orient(collection, 1) == GeometryCollection([polygon_reversed])
assert orient(collection, -1) == GeometryCollection([polygon])
def test_polygon_with_holes(self):
ring_cw = LinearRing([(0, 0), (0, 1), (1, 1), (0, 0)])
ring_cw2 = LinearRing([(0, 0), (0, 3), (3, 3), (0, 0)])
ring_ccw = LinearRing([(0, 0), (1, 1), (0, 1), (0, 0)])
ring_ccw2 = LinearRing([(0, 0), (2, 2), (0, 2), (0, 0)])
polygon_with_holes_mixed = Polygon(
ring_ccw, [ring_cw, ring_ccw2, ring_cw2, ring_ccw]
)
polygon_with_holes_ccw = Polygon(
ring_ccw, [ring_cw, ring_ccw2.reverse(), ring_cw2, ring_ccw.reverse()]
)
assert_array_equal(orient(polygon_with_holes_ccw, 1), polygon_with_holes_ccw)
assert_array_equal(
orient(polygon_with_holes_ccw, -1), polygon_with_holes_ccw.reverse()
)
assert_array_equal(orient(polygon_with_holes_mixed, 1), polygon_with_holes_ccw)
assert_array_equal(
orient(polygon_with_holes_mixed, -1), polygon_with_holes_ccw.reverse()
)

View File

@@ -0,0 +1,60 @@
import unittest
import pytest
from shapely.geometry import LinearRing, LineString
from shapely.testing import assert_geometries_equal
@pytest.mark.parametrize("distance", [float("nan"), float("inf")])
def test_non_finite_distance(distance):
g = LineString([(0, 0), (10, 0)])
with pytest.raises(ValueError, match="distance must be finite"):
g.parallel_offset(distance)
class OperationsTestCase(unittest.TestCase):
def test_parallel_offset_linestring(self):
line1 = LineString([(0, 0), (10, 0)])
left = line1.parallel_offset(5, "left")
assert_geometries_equal(left, LineString([(0, 5), (10, 5)]))
right = line1.parallel_offset(5, "right")
assert_geometries_equal(right, LineString([(10, -5), (0, -5)]), normalize=True)
right = line1.parallel_offset(-5, "left")
assert_geometries_equal(right, LineString([(10, -5), (0, -5)]), normalize=True)
left = line1.parallel_offset(-5, "right")
assert_geometries_equal(left, LineString([(0, 5), (10, 5)]))
# by default, parallel_offset is right-handed
assert_geometries_equal(line1.parallel_offset(5), right)
line2 = LineString([(0, 0), (5, 0), (5, -5)])
assert_geometries_equal(
line2.parallel_offset(2, "left", join_style=3),
LineString([(0, 2), (5, 2), (7, 0), (7, -5)]),
)
assert_geometries_equal(
line2.parallel_offset(2, "left", join_style=2),
LineString([(0, 2), (7, 2), (7, -5)]),
)
# offset_curve alias
assert_geometries_equal(
line1.offset_curve(2, quad_segs=10),
line1.parallel_offset(2, "left", resolution=10),
)
assert_geometries_equal(
line1.offset_curve(-2, join_style="mitre"),
line1.parallel_offset(2, "right", join_style=2),
)
def test_parallel_offset_linear_ring(self):
lr1 = LinearRing([(0, 0), (5, 0), (5, 5), (0, 5), (0, 0)])
assert_geometries_equal(
lr1.parallel_offset(2, "left", resolution=1),
LineString([(2, 2), (3, 2), (3, 3), (2, 3), (2, 2)]),
)
# offset_curve alias
assert_geometries_equal(
lr1.offset_curve(2, quad_segs=1),
lr1.parallel_offset(2, "left", resolution=1),
)

View File

@@ -0,0 +1,48 @@
"""Persistence tests"""
import pickle
import struct
import unittest
from shapely import wkb, wkt
from shapely.geometry import Point
class PersistTestCase(unittest.TestCase):
def test_pickle(self):
p = Point(0.0, 0.0)
data = pickle.dumps(p)
q = pickle.loads(data)
assert q.equals(p)
def test_wkb(self):
p = Point(0.0, 0.0)
wkb_big_endian = wkb.dumps(p, big_endian=True)
wkb_little_endian = wkb.dumps(p, big_endian=False)
# Regardless of byte order, loads ought to correctly recover the
# geometry
assert p.equals(wkb.loads(wkb_big_endian))
assert p.equals(wkb.loads(wkb_little_endian))
def test_wkb_dumps_endianness(self):
p = Point(0.5, 2.0)
wkb_big_endian = wkb.dumps(p, big_endian=True)
wkb_little_endian = wkb.dumps(p, big_endian=False)
assert wkb_big_endian != wkb_little_endian
# According to WKB specification in section 3.3 of OpenGIS
# Simple Features Specification for SQL, revision 1.1, the
# first byte of a WKB representation indicates byte order.
# Big-endian is 0, little-endian is 1.
assert wkb_big_endian[0] == 0
assert wkb_little_endian[0] == 1
# Check that the doubles (0.5, 2.0) are in correct byte order
double_size = struct.calcsize("d")
assert wkb_big_endian[(-2 * double_size) :] == struct.pack(">2d", p.x, p.y)
assert wkb_little_endian[(-2 * double_size) :] == struct.pack("<2d", p.x, p.y)
def test_wkt(self):
p = Point(0.0, 0.0)
text = wkt.dumps(p)
assert text.startswith("POINT")
pt = wkt.loads(text)
assert pt.equals(p)

View File

@@ -0,0 +1,81 @@
import pathlib
import pickle
import warnings
from pickle import HIGHEST_PROTOCOL, dumps, loads
import pytest
import shapely
from shapely import wkt
from shapely.geometry import (
GeometryCollection,
LinearRing,
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
box,
)
HERE = pathlib.Path(__file__).parent
TEST_DATA = {
"point2d": Point([(1.0, 2.0)]),
"point3d": Point([(1.0, 2.0, 3.0)]),
"linestring": LineString([(0.0, 0.0), (0.0, 1.0), (1.0, 1.0)]),
"linearring": LinearRing([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]),
"polygon": Polygon([(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]),
"multipoint": MultiPoint([(1.0, 2.0), (3.0, 4.0), (5.0, 6.0)]),
"multilinestring": MultiLineString(
[[(0.0, 0.0), (1.0, 1.0)], [(1.0, 2.0), (3.0, 3.0)]]
),
"multipolygon": MultiPolygon([box(0, 0, 1, 1), box(2, 2, 3, 3)]),
"geometrycollection": GeometryCollection([Point(1.0, 2.0), box(0, 0, 1, 1)]),
"emptypoint": wkt.loads("POINT EMPTY"),
"emptypolygon": wkt.loads("POLYGON EMPTY"),
}
TEST_NAMES, TEST_GEOMS = zip(*TEST_DATA.items())
@pytest.mark.parametrize("geom1", TEST_GEOMS, ids=TEST_NAMES)
def test_pickle_round_trip(geom1):
data = dumps(geom1, HIGHEST_PROTOCOL)
with warnings.catch_warnings():
warnings.simplefilter("error")
geom2 = loads(data)
assert geom2.has_z == geom1.has_z
assert type(geom2) is type(geom1)
assert geom2.geom_type == geom1.geom_type
assert geom2.wkt == geom1.wkt
@pytest.mark.parametrize(
"fname", (HERE / "data").glob("*.pickle"), ids=lambda fname: fname.name
)
def test_unpickle_pre_20(fname):
from shapely.testing import assert_geometries_equal
geom_type = fname.name.split("_")[0]
expected = TEST_DATA[geom_type]
with open(fname, "rb") as f:
with pytest.warns(UserWarning, match="may be removed in a future version"):
result = pickle.load(f)
assert_geometries_equal(result, expected)
if __name__ == "__main__":
datadir = HERE / "data"
datadir.mkdir(exist_ok=True)
shapely_version = shapely.__version__
print(shapely_version)
print(shapely.geos_version)
for name, geom in TEST_DATA.items():
with open(datadir / f"{name}_{shapely_version}.pickle", "wb") as f:
pickle.dump(geom, f)

View File

@@ -0,0 +1,43 @@
import unittest
from shapely.geometry import LineString, Point, Polygon
from shapely.geometry.base import dump_coords
from shapely.ops import polygonize, polygonize_full
class PolygonizeTestCase(unittest.TestCase):
def test_polygonize(self):
lines = [
LineString([(0, 0), (1, 1)]),
LineString([(0, 0), (0, 1)]),
LineString([(0, 1), (1, 1)]),
LineString([(1, 1), (1, 0)]),
LineString([(1, 0), (0, 0)]),
LineString([(5, 5), (6, 6)]),
Point(0, 0),
]
result = list(polygonize(lines))
assert all(isinstance(x, Polygon) for x in result)
def test_polygonize_full(self):
lines2 = [
[(0, 0), (1, 1)],
[(0, 0), (0, 1)],
[(0, 1), (1, 1)],
[(1, 1), (1, 0)],
[(1, 0), (0, 0)],
[(5, 5), (6, 6)],
[(1, 1), (100, 100)],
]
result2, cuts, dangles, invalids = polygonize_full(lines2)
assert len(result2.geoms) == 2
assert all(isinstance(x, Polygon) for x in result2.geoms)
assert list(cuts.geoms) == []
assert all(isinstance(x, LineString) for x in dangles.geoms)
assert dump_coords(dangles) == [
[(1.0, 1.0), (100.0, 100.0)],
[(5.0, 5.0), (6.0, 6.0)],
]
assert list(invalids.geoms) == []

View File

@@ -0,0 +1,82 @@
import unittest
import pytest
import shapely
from shapely.algorithms.polylabel import polylabel
from shapely.geometry import LineString, Point, Polygon
class PolylabelTestCase(unittest.TestCase):
def test_polylabel(self):
"""
Finds pole of inaccessibility for a polygon with a tolerance of 10
"""
polygon = LineString(
[(0, 0), (50, 200), (100, 100), (20, 50), (-100, -20), (-150, -200)]
).buffer(100)
label = polylabel(polygon, tolerance=0.001)
expected = Point(59.733, 111.330)
assert expected.equals_exact(label, 1e-3)
def test_concave_polygon(self):
"""
Finds pole of inaccessibility for a concave polygon and ensures that
the point is inside.
"""
concave_polygon = LineString([(500, 0), (0, 0), (0, 500), (500, 500)]).buffer(
100
)
label = polylabel(concave_polygon)
assert concave_polygon.contains(label)
def test_rectangle_special_case(self):
"""
The centroid algorithm used is vulnerable to floating point errors
and can give unexpected results for rectangular polygons. Test
that this special case is handled correctly.
https://github.com/mapbox/polylabel/issues/3
"""
polygon = Polygon(
[
(32.71997, -117.19310),
(32.71997, -117.21065),
(32.72408, -117.21065),
(32.72408, -117.19310),
]
)
label = polylabel(polygon)
if shapely.geos_version >= (3, 12, 0):
# recent GEOS corrects for this
assert label.coords[:] == [(32.722025, -117.201875)]
else:
# older versions not
assert label.coords[:] == [(32.722025, -117.208595)]
def test_polygon_with_hole(self):
"""
Finds pole of inaccessibility for a polygon with a hole
https://github.com/shapely/shapely/issues/817
"""
polygon = Polygon(
shell=[(0, 0), (10, 0), (10, 10), (0, 10), (0, 0)],
holes=[[(2, 2), (6, 2), (6, 6), (2, 6), (2, 2)]],
)
label = polylabel(polygon, 0.05)
assert label.x == pytest.approx(7.65625)
assert label.y == pytest.approx(7.65625)
@pytest.mark.skipif(
shapely.geos_version < (3, 12, 0), reason="Fails with GEOS < 3.12"
)
def test_polygon_infinite_loop(self):
# https://github.com/shapely/shapely/issues/1836
# corner case that caused an infinite loop in the old custom implemetation
polygon = shapely.from_wkt(
"POLYGON ((536520.0679737709 5438764.374763639, 536520.0679737704 5438764.374763602, 536520.0679737709 5438764.374763642, 536520.0679737709 5438764.374763639))" # noqa: E501
)
label = polylabel(polygon)
assert label.x == pytest.approx(536520.068)
assert label.y == pytest.approx(5438764.375)

View File

@@ -0,0 +1,95 @@
"""Test GEOS predicates"""
import unittest
import pytest
import shapely
from shapely import geos_version
from shapely.geometry import Point, Polygon
class PredicatesTestCase(unittest.TestCase):
def test_binary_predicates(self):
point = Point(0.0, 0.0)
point2 = Point(2.0, 2.0)
assert point.disjoint(Point(-1.0, -1.0))
assert not point.touches(Point(-1.0, -1.0))
assert not point.crosses(Point(-1.0, -1.0))
assert not point.within(Point(-1.0, -1.0))
assert not point.contains(Point(-1.0, -1.0))
assert not point.equals(Point(-1.0, -1.0))
assert not point.touches(Point(-1.0, -1.0))
assert point.equals(Point(0.0, 0.0))
assert point.covers(Point(0.0, 0.0))
assert point.covered_by(Point(0.0, 0.0))
assert not point.covered_by(point2)
assert not point2.covered_by(point)
assert not point.covers(Point(-1.0, -1.0))
def test_unary_predicates(self):
point = Point(0.0, 0.0)
assert not point.is_empty
assert point.is_valid
assert point.is_simple
assert not point.is_ring
assert not point.has_z
def test_binary_predicate_exceptions(self):
p1 = [
(339, 346),
(459, 346),
(399, 311),
(340, 277),
(399, 173),
(280, 242),
(339, 415),
(280, 381),
(460, 207),
(339, 346),
]
p2 = [
(339, 207),
(280, 311),
(460, 138),
(399, 242),
(459, 277),
(459, 415),
(399, 381),
(519, 311),
(520, 242),
(519, 173),
(399, 450),
(339, 207),
]
g1 = Polygon(p1)
g2 = Polygon(p2)
assert not g1.is_valid
assert not g2.is_valid
if geos_version < (3, 13, 0):
with pytest.raises(shapely.GEOSException):
g1.within(g2)
else: # resolved with RelateNG
assert not g1.within(g2)
def test_relate_pattern(self):
# a pair of partially overlapping polygons, and a nearby point
g1 = Polygon([(0, 0), (0, 1), (3, 1), (3, 0), (0, 0)])
g2 = Polygon([(1, -1), (1, 2), (2, 2), (2, -1), (1, -1)])
g3 = Point(5, 5)
assert g1.relate(g2) == "212101212"
assert g1.relate_pattern(g2, "212101212")
assert g1.relate_pattern(g2, "*********")
assert g1.relate_pattern(g2, "2********")
assert g1.relate_pattern(g2, "T********")
assert not g1.relate_pattern(g2, "112101212")
assert not g1.relate_pattern(g2, "1********")
assert g1.relate_pattern(g3, "FF2FF10F2")
# an invalid pattern should raise an exception
with pytest.raises(shapely.GEOSException, match="IllegalArgumentException"):
g1.relate_pattern(g2, "fail")

View File

@@ -0,0 +1,65 @@
import numpy as np
import pytest
from shapely.geometry import Point, Polygon
from shapely.prepared import PreparedGeometry, prep
def test_prepared_geometry():
polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
p = PreparedGeometry(polygon)
assert p.contains(Point(0.5, 0.5))
assert not p.contains(Point(0.5, 1.5))
def test_prep():
polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
p = prep(polygon)
assert p.contains(Point(0.5, 0.5))
assert not p.contains(Point(0.5, 1.5))
def test_op_not_allowed():
p = PreparedGeometry(Point(0.0, 0.0).buffer(1.0))
with pytest.raises(TypeError):
Point(0.0, 0.0).union(p)
def test_predicate_not_allowed():
p = PreparedGeometry(Point(0.0, 0.0).buffer(1.0))
with pytest.raises(TypeError):
Point(0.0, 0.0).contains(p)
def test_prepared_predicates():
# check prepared predicates give the same result as regular predicates
polygon1 = Polygon([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)])
polygon2 = Polygon([(0.5, 0.5), (1.5, 0.5), (1.0, 1.0), (0.5, 0.5)])
point2 = Point(0.5, 0.5)
polygon_empty = Polygon()
prepared_polygon1 = PreparedGeometry(polygon1)
for geom2 in (polygon2, point2, polygon_empty):
with np.errstate(invalid="ignore"):
assert polygon1.disjoint(geom2) == prepared_polygon1.disjoint(geom2)
assert polygon1.touches(geom2) == prepared_polygon1.touches(geom2)
assert polygon1.intersects(geom2) == prepared_polygon1.intersects(geom2)
assert polygon1.crosses(geom2) == prepared_polygon1.crosses(geom2)
assert polygon1.within(geom2) == prepared_polygon1.within(geom2)
assert polygon1.contains(geom2) == prepared_polygon1.contains(geom2)
assert polygon1.contains_properly(
geom2
) == prepared_polygon1.contains_properly(geom2)
assert polygon1.overlaps(geom2) == prepared_polygon1.overlaps(geom2)
def test_prepare_already_prepared():
polygon = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
prepared = prep(polygon)
# attempt to prepare an already prepared geometry with `prep`
result = prep(prepared)
assert isinstance(result, PreparedGeometry)
assert result.context is polygon
# attempt to prepare an already prepared geometry with `PreparedGeometry`
result = PreparedGeometry(prepared)
assert isinstance(result, PreparedGeometry)
assert result.context is polygon

View File

@@ -0,0 +1,13 @@
import unittest
from shapely.geometry import LineString
class ProductZTestCase(unittest.TestCase):
def test_line_intersection(self):
line1 = LineString([(0, 0, 0), (1, 1, 1)])
line2 = LineString([(0, 1, 1), (1, 0, 0)])
interxn = line1.intersection(line2)
assert interxn.has_z
assert interxn._ndim == 3
assert 0.0 <= interxn.z <= 1.0

View File

@@ -0,0 +1,63 @@
import pytest
from shapely.geometry import MultiLineString, Point, Polygon, shape
from shapely.geometry.geo import _is_coordinates_empty
@pytest.mark.parametrize(
"geom",
[{"type": "Polygon", "coordinates": None}, {"type": "Polygon", "coordinates": []}],
)
def test_polygon_no_coords(geom):
assert shape(geom) == Polygon()
def test_polygon_empty_np_array():
np = pytest.importorskip("numpy")
geom = {"type": "Polygon", "coordinates": np.array([])}
assert shape(geom) == Polygon()
def test_polygon_with_coords_list():
geom = {"type": "Polygon", "coordinates": [[[5, 10], [10, 10], [10, 5]]]}
obj = shape(geom)
assert obj == Polygon([(5, 10), (10, 10), (10, 5)])
def test_polygon_not_empty_np_array():
np = pytest.importorskip("numpy")
geom = {"type": "Polygon", "coordinates": np.array([[[5, 10], [10, 10], [10, 5]]])}
obj = shape(geom)
assert obj == Polygon([(5, 10), (10, 10), (10, 5)])
@pytest.mark.parametrize(
"geom",
[
{"type": "MultiLineString", "coordinates": []},
{"type": "MultiLineString", "coordinates": [[]]},
{"type": "MultiLineString", "coordinates": None},
],
)
def test_multilinestring_empty(geom):
assert shape(geom) == MultiLineString()
@pytest.mark.parametrize("coords", [[], [[]], [[], []], None, [[[]]]])
def test_is_coordinates_empty(coords):
assert _is_coordinates_empty(coords)
def test_feature_from_geo_interface():
# https://github.com/shapely/shapely/issues/1814
class Feature:
@property
def __geo_interface__(self):
return {
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [0, 0]},
}
expected = Point([0, 0])
result = shape(Feature())
assert result == expected

View File

@@ -0,0 +1,45 @@
import unittest
import pytest
from shapely.errors import GeometryTypeError
from shapely.geometry import GeometryCollection, LineString, MultiLineString, Point
from shapely.ops import shared_paths
class SharedPaths(unittest.TestCase):
def test_shared_paths_forward(self):
g1 = LineString([(0, 0), (10, 0), (10, 5), (20, 5)])
g2 = LineString([(5, 0), (15, 0)])
result = shared_paths(g1, g2)
assert isinstance(result, GeometryCollection)
assert len(result.geoms) == 2
a, b = result.geoms
assert isinstance(a, MultiLineString)
assert len(a.geoms) == 1
assert a.geoms[0].coords[:] == [(5, 0), (10, 0)]
assert b.is_empty
def test_shared_paths_forward2(self):
g1 = LineString([(0, 0), (10, 0), (10, 5), (20, 5)])
g2 = LineString([(15, 0), (5, 0)])
result = shared_paths(g1, g2)
assert isinstance(result, GeometryCollection)
assert len(result.geoms) == 2
a, b = result.geoms
assert isinstance(b, MultiLineString)
assert len(b.geoms) == 1
assert b.geoms[0].coords[:] == [(5, 0), (10, 0)]
assert a.is_empty
def test_wrong_type(self):
g1 = Point(0, 0)
g2 = LineString([(5, 0), (15, 0)])
with pytest.raises(GeometryTypeError):
shared_paths(g1, g2)
with pytest.raises(GeometryTypeError):
shared_paths(g2, g1)

View File

@@ -0,0 +1,15 @@
import unittest
from shapely.geometry import Polygon
class PolygonTestCase(unittest.TestCase):
def test_polygon_3(self):
p = (1.0, 1.0)
poly = Polygon([p, p, p])
assert poly.bounds == (1.0, 1.0, 1.0, 1.0)
def test_polygon_5(self):
p = (1.0, 1.0)
poly = Polygon([p, p, p, p, p])
assert poly.bounds == (1.0, 1.0, 1.0, 1.0)

View File

@@ -0,0 +1,24 @@
import unittest
from shapely.geometry import LineString, Polygon
from shapely.ops import snap
class Snap(unittest.TestCase):
def test_snap(self):
# input geometries
square = Polygon([(1, 1), (2, 1), (2, 2), (1, 2), (1, 1)])
line = LineString([(0, 0), (0.8, 0.8), (1.8, 0.95), (2.6, 0.5)])
square_coords = square.exterior.coords[:]
line_coords = line.coords[:]
result = snap(line, square, 0.5)
# test result is correct
assert isinstance(result, LineString)
assert result.coords[:] == [(0.0, 0.0), (1.0, 1.0), (2.0, 1.0), (2.6, 0.5)]
# test inputs have not been modified
assert square.exterior.coords[:] == square_coords
assert line.coords[:] == line_coords

View File

@@ -0,0 +1,285 @@
import unittest
import pytest
from shapely.errors import GeometryTypeError
from shapely.geometry import (
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
)
from shapely.ops import linemerge, split, unary_union
class TestSplitGeometry(unittest.TestCase):
# helper class for testing below
def helper(self, geom, splitter, expected_chunks):
s = split(geom, splitter)
assert s.geom_type == "GeometryCollection"
assert len(s.geoms) == expected_chunks
if expected_chunks > 1:
# split --> expected collection that when merged is again equal to original
# geometry
if s.geoms[0].geom_type == "LineString":
self.assertTrue(linemerge(s).simplify(0.000001).equals(geom))
elif s.geoms[0].geom_type == "Polygon":
union = unary_union(s).simplify(0.000001)
assert union.equals(geom)
assert union.area == geom.area
else:
raise ValueError
elif expected_chunks == 1:
# not split --> expected equal to line
assert s.geoms[0].equals(geom)
def test_split_closed_line_with_point(self):
# point at start/end of closed ring -> return equal
# see GH #524
ls = LineString([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)])
splitter = Point(0, 0)
self.helper(ls, splitter, 1)
class TestSplitPolygon(TestSplitGeometry):
poly_simple = Polygon([(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)])
poly_hole = Polygon(
[(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)],
[[(0.5, 0.5), (0.5, 1.5), (1.5, 1.5), (1.5, 0.5), (0.5, 0.5)]],
)
def test_split_poly_with_line(self):
# crossing at 2 points --> return 2 polygons
splitter = LineString([(1, 3), (1, -3)])
self.helper(self.poly_simple, splitter, 2)
self.helper(self.poly_hole, splitter, 2)
# crossing twice with one linestring --> return 3 polygons
splitter = LineString([(1, 3), (1, -3), (1.7, -3), (1.7, 3)])
self.helper(self.poly_simple, splitter, 3)
self.helper(self.poly_hole, splitter, 3)
# touching the boundary --> return equal
splitter = LineString([(0, 2), (5, 2)])
self.helper(self.poly_simple, splitter, 1)
self.helper(self.poly_hole, splitter, 1)
# inside the polygon --> return equal
splitter = LineString([(0.2, 0.2), (1.7, 1.7), (3, 2)])
self.helper(self.poly_simple, splitter, 1)
self.helper(self.poly_hole, splitter, 1)
# outside the polygon --> return equal
splitter = LineString([(0, 3), (3, 3), (3, 0)])
self.helper(self.poly_simple, splitter, 1)
self.helper(self.poly_hole, splitter, 1)
def test_split_poly_with_multiline(self):
# crossing twice with a multilinestring --> return 3 polygons
splitter = MultiLineString([[(0.2, 3), (0.2, -3)], [(1.7, -3), (1.7, 3)]])
self.helper(self.poly_simple, splitter, 3)
self.helper(self.poly_hole, splitter, 3)
# crossing twice with a cross multilinestring --> return 4 polygons
splitter = MultiLineString([[(0.2, 3), (0.2, -3)], [(-3, 1), (3, 1)]])
self.helper(self.poly_simple, splitter, 4)
self.helper(self.poly_hole, splitter, 4)
# cross once, touch the boundary once --> return 2 polygons
splitter = MultiLineString([[(0.2, 3), (0.2, -3)], [(0, 2), (5, 2)]])
self.helper(self.poly_simple, splitter, 2)
self.helper(self.poly_hole, splitter, 2)
# cross once, inside the polygon once --> return 2 polygons
splitter = MultiLineString(
[[(0.2, 3), (0.2, -3)], [(1.2, 1.2), (1.7, 1.7), (3, 2)]]
)
self.helper(self.poly_simple, splitter, 2)
self.helper(self.poly_hole, splitter, 2)
# cross once, outside the polygon once --> return 2 polygons
splitter = MultiLineString([[(0.2, 3), (0.2, -3)], [(0, 3), (3, 3), (3, 0)]])
self.helper(self.poly_simple, splitter, 2)
self.helper(self.poly_hole, splitter, 2)
def test_split_poly_with_other(self):
with pytest.raises(GeometryTypeError):
split(self.poly_simple, Point(1, 1))
with pytest.raises(GeometryTypeError):
split(self.poly_simple, MultiPoint([(1, 1), (3, 4)]))
with pytest.raises(GeometryTypeError):
split(self.poly_simple, self.poly_hole)
class TestSplitLine(TestSplitGeometry):
ls = LineString([(0, 0), (1.5, 1.5), (3.0, 4.0)])
def test_split_line_with_point(self):
# point on line interior --> return 2 segments
splitter = Point(1, 1)
self.helper(self.ls, splitter, 2)
# point on line point --> return 2 segments
splitter = Point(1.5, 1.5)
self.helper(self.ls, splitter, 2)
# point on boundary --> return equal
splitter = Point(3, 4)
self.helper(self.ls, splitter, 1)
# point on exterior of line --> return equal
splitter = Point(2, 2)
self.helper(self.ls, splitter, 1)
def test_split_line_with_multipoint(self):
# points on line interior --> return 4 segments
splitter = MultiPoint([(1, 1), (1.5, 1.5), (0.5, 0.5)])
self.helper(self.ls, splitter, 4)
# points on line interior and boundary -> return 2 segments
splitter = MultiPoint([(1, 1), (3, 4)])
self.helper(self.ls, splitter, 2)
# point on linear interior but twice --> return 2 segments
splitter = MultiPoint([(1, 1), (1.5, 1.5), (1, 1)])
self.helper(self.ls, splitter, 3)
def test_split_line_with_line(self):
# crosses at one point --> return 2 segments
splitter = LineString([(0, 1), (1, 0)])
self.helper(self.ls, splitter, 2)
# crosses at two points --> return 3 segments
splitter = LineString([(0, 1), (1, 0), (1, 2)])
self.helper(self.ls, splitter, 3)
# overlaps --> raise
splitter = LineString([(0, 0), (15, 15)])
with pytest.raises(ValueError):
self.helper(self.ls, splitter, 1)
# does not cross --> return equal
splitter = LineString([(0, 1), (0, 2)])
self.helper(self.ls, splitter, 1)
# is touching the boundary --> return equal
splitter = LineString([(-1, 1), (1, -1)])
assert splitter.touches(self.ls)
self.helper(self.ls, splitter, 1)
# splitter boundary touches interior of line --> return 2 segments
splitter = LineString([(0, 1), (1, 1)]) # touches at (1, 1)
assert splitter.touches(self.ls)
self.helper(self.ls, splitter, 2)
def test_split_line_with_multiline(self):
# crosses at one point --> return 2 segments
splitter = MultiLineString([[(0, 1), (1, 0)], [(0, 0), (2, -2)]])
self.helper(self.ls, splitter, 2)
# crosses at two points --> return 3 segments
splitter = MultiLineString([[(0, 1), (1, 0)], [(0, 2), (2, 0)]])
self.helper(self.ls, splitter, 3)
# crosses at three points --> return 4 segments
splitter = MultiLineString([[(0, 1), (1, 0)], [(0, 2), (2, 0), (2.2, 3.2)]])
self.helper(self.ls, splitter, 4)
# overlaps --> raise
splitter = MultiLineString([[(0, 0), (1.5, 1.5)], [(1.5, 1.5), (3, 4)]])
with pytest.raises(ValueError):
self.helper(self.ls, splitter, 1)
# does not cross --> return equal
splitter = MultiLineString([[(0, 1), (0, 2)], [(1, 0), (2, 0)]])
self.helper(self.ls, splitter, 1)
def test_split_line_with_polygon(self):
# crosses at two points --> return 3 segments
splitter = Polygon([(1, 0), (1, 2), (2, 2), (2, 0), (1, 0)])
self.helper(self.ls, splitter, 3)
# crosses at one point and touches boundary --> return 2 segments
splitter = Polygon([(0, 0), (1, 2), (2, 2), (1, 0), (0, 0)])
self.helper(self.ls, splitter, 2)
# exterior crosses at one point and touches at (0, 0)
# interior crosses at two points
splitter = Polygon(
[(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)],
[[(0.5, 0.5), (0.5, 1.5), (1.5, 1.5), (1.5, 0.5), (0.5, 0.5)]],
)
self.helper(self.ls, splitter, 4)
def test_split_line_with_multipolygon(self):
poly1 = Polygon(
[(0, 0), (2, 0), (2, 2), (0, 2), (0, 0)]
) # crosses at one point and touches at (0, 0)
poly2 = Polygon(
[(0.5, 0.5), (0.5, 1.5), (1.5, 1.5), (1.5, 0.5), (0.5, 0.5)]
) # crosses at two points
poly3 = Polygon([(0, 0), (0, -2), (-2, -2), (-2, 0), (0, 0)]) # not crossing
splitter = MultiPolygon([poly1, poly2, poly3])
self.helper(self.ls, splitter, 4)
class TestSplitClosedRing(TestSplitGeometry):
ls = LineString([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]])
def test_split_closed_ring_with_point(self):
splitter = Point([0.0, 0.0])
self.helper(self.ls, splitter, 1)
splitter = Point([0.0, 0.5])
self.helper(self.ls, splitter, 2)
result = split(self.ls, splitter)
assert result.geoms[0].coords[:] == [(0, 0), (0.0, 0.5)]
assert result.geoms[1].coords[:] == [(0.0, 0.5), (0, 1), (1, 1), (1, 0), (0, 0)]
# previously failed, see GH#585
splitter = Point([0.5, 0.0])
self.helper(self.ls, splitter, 2)
result = split(self.ls, splitter)
assert result.geoms[0].coords[:] == [(0, 0), (0, 1), (1, 1), (1, 0), (0.5, 0)]
assert result.geoms[1].coords[:] == [(0.5, 0), (0, 0)]
splitter = Point([2.0, 2.0])
self.helper(self.ls, splitter, 1)
class TestSplitMulti(TestSplitGeometry):
def test_split_multiline_with_point(self):
# a cross-like multilinestring with a point in the middle --> return 4 line
# segments
l1 = LineString([(0, 1), (2, 1)])
l2 = LineString([(1, 0), (1, 2)])
ml = MultiLineString([l1, l2])
splitter = Point((1, 1))
self.helper(ml, splitter, 4)
def test_split_multiline_with_multipoint(self):
# a cross-like multilinestring with a point in middle, a point on one of the
# lines and a point in the exterior
# --> return 4+1 line segments
l1 = LineString([(0, 1), (3, 1)])
l2 = LineString([(1, 0), (1, 2)])
ml = MultiLineString([l1, l2])
splitter = MultiPoint([(1, 1), (2, 1), (4, 2)])
self.helper(ml, splitter, 5)
def test_split_multipolygon_with_line(self):
# two polygons with a crossing line --> return 4 triangles
poly1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])
poly2 = Polygon([(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)])
mpoly = MultiPolygon([poly1, poly2])
ls = LineString([(-1, -1), (3, 3)])
self.helper(mpoly, ls, 4)
# two polygons away from the crossing line --> return identity
poly1 = Polygon([(10, 10), (10, 11), (11, 11), (11, 10), (10, 10)])
poly2 = Polygon([(-10, -10), (-10, -11), (-11, -11), (-11, -10), (-10, -10)])
mpoly = MultiPolygon([poly1, poly2])
ls = LineString([(-1, -1), (3, 3)])
self.helper(mpoly, ls, 2)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,213 @@
# Tests SVG output and validity
import os
import unittest
from xml.dom.minidom import parseString as parse_xml_string
from shapely.geometry import (
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
)
from shapely.geometry.collection import GeometryCollection
class SvgTestCase(unittest.TestCase):
def assertSVG(self, geom, expected, **kwrds):
"""Helper function to check XML and debug SVG"""
svg_elem = geom.svg(**kwrds)
try:
parse_xml_string(svg_elem)
except Exception:
raise AssertionError("XML is not valid for SVG element: " + str(svg_elem))
svg_doc = geom._repr_svg_()
try:
doc = parse_xml_string(svg_doc)
except Exception:
raise AssertionError("XML is not valid for SVG document: " + str(svg_doc))
svg_output_dir = None
# svg_output_dir = '.' # useful for debugging SVG files
if svg_output_dir:
fname = geom.geom_type
if geom.is_empty:
fname += "_empty"
if not geom.is_valid:
fname += "_invalid"
if kwrds:
fname += "_" + ",".join(str(k) + "=" + str(kwrds[k]) for k in kwrds)
svg_path = os.path.join(svg_output_dir, fname + ".svg")
with open(svg_path, "w") as fp:
fp.write(doc.toprettyxml())
assert svg_elem == expected
def test_point(self):
# Empty
self.assertSVG(Point(), "<g />")
# Valid
g = Point(6, 7)
self.assertSVG(
g,
'<circle cx="6.0" cy="7.0" r="3.0" stroke="#555555" '
'stroke-width="1.0" fill="#66cc99" opacity="0.6" />',
)
self.assertSVG(
g,
'<circle cx="6.0" cy="7.0" r="15.0" stroke="#555555" '
'stroke-width="5.0" fill="#66cc99" opacity="0.6" />',
scale_factor=5,
)
def test_multipoint(self):
# Empty
self.assertSVG(MultiPoint(), "<g />")
# Valid
g = MultiPoint([(6, 7), (3, 4)])
self.assertSVG(
g,
'<g><circle cx="6.0" cy="7.0" r="3.0" stroke="#555555" '
'stroke-width="1.0" fill="#66cc99" opacity="0.6" />'
'<circle cx="3.0" cy="4.0" r="3.0" stroke="#555555" '
'stroke-width="1.0" fill="#66cc99" opacity="0.6" /></g>',
)
self.assertSVG(
g,
'<g><circle cx="6.0" cy="7.0" r="15.0" stroke="#555555" '
'stroke-width="5.0" fill="#66cc99" opacity="0.6" />'
'<circle cx="3.0" cy="4.0" r="15.0" stroke="#555555" '
'stroke-width="5.0" fill="#66cc99" opacity="0.6" /></g>',
scale_factor=5,
)
def test_linestring(self):
# Empty
self.assertSVG(LineString(), "<g />")
# Valid
g = LineString([(5, 8), (496, -6), (530, 20)])
self.assertSVG(
g,
'<polyline fill="none" stroke="#66cc99" stroke-width="2.0" '
'points="5.0,8.0 496.0,-6.0 530.0,20.0" opacity="0.8" />',
)
self.assertSVG(
g,
'<polyline fill="none" stroke="#66cc99" stroke-width="10.0" '
'points="5.0,8.0 496.0,-6.0 530.0,20.0" opacity="0.8" />',
scale_factor=5,
)
# Invalid
self.assertSVG(
LineString([(0, 0), (0, 0)]),
'<polyline fill="none" stroke="#ff3333" stroke-width="2.0" '
'points="0.0,0.0 0.0,0.0" opacity="0.8" />',
)
def test_multilinestring(self):
# Empty
self.assertSVG(MultiLineString(), "<g />")
# Valid
self.assertSVG(
MultiLineString([[(6, 7), (3, 4)], [(2, 8), (9, 1)]]),
'<g><polyline fill="none" stroke="#66cc99" stroke-width="2.0" '
'points="6.0,7.0 3.0,4.0" opacity="0.8" />'
'<polyline fill="none" stroke="#66cc99" stroke-width="2.0" '
'points="2.0,8.0 9.0,1.0" opacity="0.8" /></g>',
)
# Invalid
self.assertSVG(
MultiLineString([[(2, 3), (2, 3)], [(2, 8), (9, 1)]]),
'<g><polyline fill="none" stroke="#ff3333" stroke-width="2.0" '
'points="2.0,3.0 2.0,3.0" opacity="0.8" />'
'<polyline fill="none" stroke="#ff3333" stroke-width="2.0" '
'points="2.0,8.0 9.0,1.0" opacity="0.8" /></g>',
)
def test_polygon(self):
# Empty
self.assertSVG(Polygon(), "<g />")
# Valid
g = Polygon(
[(35, 10), (45, 45), (15, 40), (10, 20), (35, 10)],
[[(20, 30), (35, 35), (30, 20), (20, 30)]],
)
self.assertSVG(
g,
'<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" '
'stroke-width="2.0" opacity="0.6" d="M 35.0,10.0 L 45.0,45.0 L '
"15.0,40.0 L 10.0,20.0 L 35.0,10.0 z M 20.0,30.0 L 35.0,35.0 L "
'30.0,20.0 L 20.0,30.0 z" />',
)
self.assertSVG(
g,
'<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" '
'stroke-width="10.0" opacity="0.6" d="M 35.0,10.0 L 45.0,45.0 L '
"15.0,40.0 L 10.0,20.0 L 35.0,10.0 z M 20.0,30.0 L 35.0,35.0 L "
'30.0,20.0 L 20.0,30.0 z" />',
scale_factor=5,
)
# Invalid
self.assertSVG(
Polygon([(0, 40), (0, 0), (40, 40), (40, 0), (0, 40)]),
'<path fill-rule="evenodd" fill="#ff3333" stroke="#555555" '
'stroke-width="2.0" opacity="0.6" d="M 0.0,40.0 L 0.0,0.0 L '
'40.0,40.0 L 40.0,0.0 L 0.0,40.0 z" />',
)
def test_multipolygon(self):
# Empty
self.assertSVG(MultiPolygon(), "<g />")
# Valid
self.assertSVG(
MultiPolygon(
[
Polygon([(40, 40), (20, 45), (45, 30), (40, 40)]),
Polygon(
[(20, 35), (10, 30), (10, 10), (30, 5), (45, 20), (20, 35)],
[[(30, 20), (20, 15), (20, 25), (30, 20)]],
),
]
),
'<g><path fill-rule="evenodd" fill="#66cc99" stroke="#555555" '
'stroke-width="2.0" opacity="0.6" d="M 40.0,40.0 L 20.0,45.0 L '
'45.0,30.0 L 40.0,40.0 z" />'
'<path fill-rule="evenodd" fill="#66cc99" stroke="#555555" '
'stroke-width="2.0" opacity="0.6" d="M 20.0,35.0 L 10.0,30.0 L '
"10.0,10.0 L 30.0,5.0 L 45.0,20.0 L 20.0,35.0 z M 30.0,20.0 L "
'20.0,15.0 L 20.0,25.0 L 30.0,20.0 z" /></g>',
)
# Invalid
self.assertSVG(
MultiPolygon(
[
Polygon([(140, 140), (120, 145), (145, 130), (140, 140)]),
Polygon([(0, 40), (0, 0), (40, 40), (40, 0), (0, 40)]),
]
),
'<g><path fill-rule="evenodd" fill="#ff3333" stroke="#555555" '
'stroke-width="2.0" opacity="0.6" d="M 140.0,140.0 L '
'120.0,145.0 L 145.0,130.0 L 140.0,140.0 z" />'
'<path fill-rule="evenodd" fill="#ff3333" stroke="#555555" '
'stroke-width="2.0" opacity="0.6" d="M 0.0,40.0 L 0.0,0.0 L '
'40.0,40.0 L 40.0,0.0 L 0.0,40.0 z" /></g>',
)
def test_collection(self):
# Empty
self.assertSVG(GeometryCollection(), "<g />")
# Valid
self.assertSVG(
GeometryCollection([Point(7, 3), LineString([(4, 2), (8, 4)])]),
'<g><circle cx="7.0" cy="3.0" r="3.0" stroke="#555555" '
'stroke-width="1.0" fill="#66cc99" opacity="0.6" />'
'<polyline fill="none" stroke="#66cc99" stroke-width="2.0" '
'points="4.0,2.0 8.0,4.0" opacity="0.8" /></g>',
)
# Invalid
self.assertSVG(
Point(7, 3).union(LineString([(4, 2), (4, 2)])),
'<g><circle cx="7.0" cy="3.0" r="3.0" stroke="#555555" '
'stroke-width="1.0" fill="#ff3333" opacity="0.6" />'
'<polyline fill="none" stroke="#ff3333" stroke-width="2.0" '
'points="4.0,2.0 4.0,2.0" opacity="0.8" /></g>',
)

View File

@@ -0,0 +1,80 @@
import unittest
import pytest
from shapely import geometry
from shapely.ops import transform
class IdentityTestCase(unittest.TestCase):
"""New geometry/coordseq method 'xy' makes numpy interop easier"""
def func(self, x, y, z=None):
return tuple(c for c in [x, y, z] if c)
def test_empty(self):
g = geometry.Point()
h = transform(self.func, g)
assert h.is_empty
def test_point(self):
g = geometry.Point(0, 1)
h = transform(self.func, g)
assert h.geom_type == "Point"
assert list(h.coords) == [(0, 1)]
def test_line(self):
g = geometry.LineString([(0, 1), (2, 3)])
h = transform(self.func, g)
assert h.geom_type == "LineString"
assert list(h.coords) == [(0, 1), (2, 3)]
def test_linearring(self):
g = geometry.LinearRing([(0, 1), (2, 3), (2, 2), (0, 1)])
h = transform(self.func, g)
assert h.geom_type == "LinearRing"
assert list(h.coords) == [(0, 1), (2, 3), (2, 2), (0, 1)]
def test_polygon(self):
g = geometry.Point(0, 1).buffer(1.0)
h = transform(self.func, g)
assert h.geom_type == "Polygon"
assert g.area == pytest.approx(h.area)
def test_multipolygon(self):
g = geometry.MultiPoint([(0, 1), (0, 4)]).buffer(1.0)
h = transform(self.func, g)
assert h.geom_type == "MultiPolygon"
assert g.area == pytest.approx(h.area)
class LambdaTestCase(unittest.TestCase):
"""New geometry/coordseq method 'xy' makes numpy interop easier"""
def test_point(self):
g = geometry.Point(0, 1)
h = transform(lambda x, y, z=None: (x + 1.0, y + 1.0), g)
assert h.geom_type == "Point"
assert list(h.coords) == [(1.0, 2.0)]
def test_line(self):
g = geometry.LineString([(0, 1), (2, 3)])
h = transform(lambda x, y, z=None: (x + 1.0, y + 1.0), g)
assert h.geom_type == "LineString"
assert list(h.coords) == [(1.0, 2.0), (3.0, 4.0)]
def test_polygon(self):
g = geometry.Point(0, 1).buffer(1.0)
h = transform(lambda x, y, z=None: (x + 1.0, y + 1.0), g)
assert h.geom_type == "Polygon"
assert g.area == pytest.approx(h.area)
assert h.centroid.x == pytest.approx(1.0)
assert h.centroid.y == pytest.approx(2.0)
def test_multipolygon(self):
g = geometry.MultiPoint([(0, 1), (0, 4)]).buffer(1.0)
h = transform(lambda x, y, z=None: (x + 1.0, y + 1.0), g)
assert h.geom_type == "MultiPolygon"
assert g.area == pytest.approx(h.area)
assert h.centroid.x == pytest.approx(1.0)
assert h.centroid.y == pytest.approx(3.5)

View File

@@ -0,0 +1,66 @@
import random
import unittest
from functools import partial
from itertools import islice
import pytest
from shapely.geometry import MultiPolygon, Point
from shapely.ops import unary_union
def halton(base):
"""Returns an iterator over an infinite Halton sequence"""
def value(index):
result = 0.0
f = 1.0 / base
i = index
while i > 0:
result += f * (i % base)
i = i // base
f = f / base
return result
i = 1
while i > 0:
yield value(i)
i += 1
class UnionTestCase(unittest.TestCase):
def test_unary_union_partial(self):
# Use a partial function to make 100 points uniformly distributed
# in a 40x40 box centered on 0,0.
r = partial(random.uniform, -20.0, 20.0)
points = [Point(r(), r()) for i in range(100)]
# Buffer the points, producing 100 polygon spots
spots = [p.buffer(2.5) for p in points]
# Perform a cascaded union of the polygon spots, dissolving them
# into a collection of polygon patches
u = unary_union(spots)
assert u.geom_type in ("Polygon", "MultiPolygon")
def setUp(self):
# Instead of random points, use deterministic, pseudo-random Halton
# sequences for repeatability sake.
self.coords = zip(
list(islice(halton(5), 20, 120)),
list(islice(halton(7), 20, 120)),
)
def test_unary_union(self):
patches = [Point(xy).buffer(0.05) for xy in self.coords]
u = unary_union(patches)
assert u.geom_type == "MultiPolygon"
assert u.area == pytest.approx(0.718572540569)
def test_unary_union_multi(self):
# Test of multipart input based on comment by @schwehr at
# https://github.com/shapely/shapely/issues/47#issuecomment-21809308
patches = MultiPolygon([Point(xy).buffer(0.05) for xy in self.coords])
assert unary_union(patches).area == pytest.approx(0.71857254056)
assert unary_union([patches, patches]).area == pytest.approx(0.71857254056)

View File

@@ -0,0 +1,9 @@
import unittest
from shapely.geometry import Point
from shapely.validation import explain_validity
class ValidationTestCase(unittest.TestCase):
def test_valid(self):
assert explain_validity(Point(0, 0)) == "Valid Geometry"

View File

@@ -0,0 +1,108 @@
import unittest
import numpy as np
import pytest
from shapely.geometry import MultiPolygon, Point, box
@pytest.mark.filterwarnings("ignore:The 'shapely.vectorized:")
class VectorizedContainsTestCase(unittest.TestCase):
def assertContainsResults(self, geom, x, y):
from shapely.vectorized import contains
result = contains(geom, x, y)
x = np.asanyarray(x)
y = np.asanyarray(y)
self.assertIsInstance(result, np.ndarray)
self.assertEqual(result.dtype, bool)
result_flat = result.flat
x_flat, y_flat = x.flat, y.flat
# Do the equivalent operation, only slowly, comparing the result
# as we go.
for idx in range(x.size):
assert result_flat[idx] == geom.contains(Point(x_flat[idx], y_flat[idx]))
return result
def construct_torus(self):
point = Point(0, 0)
return point.buffer(5).symmetric_difference(point.buffer(2.5))
def test_contains_poly(self):
y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
self.assertContainsResults(self.construct_torus(), x, y)
def test_contains_point(self):
y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
self.assertContainsResults(Point(x[0], y[0]), x, y)
def test_contains_linestring(self):
y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
self.assertContainsResults(Point(x[0], y[0]), x, y)
def test_contains_multipoly(self):
y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
# Construct a geometry of the torus cut in half vertically.
cut_poly = box(-1, -10, -2.5, 10)
geom = self.construct_torus().difference(cut_poly)
assert isinstance(geom, MultiPolygon)
self.assertContainsResults(geom, x, y)
def test_y_array_order(self):
y, x = np.mgrid[-10:10:5j, -5:15:5j]
y = y.copy("f")
self.assertContainsResults(self.construct_torus(), x, y)
def test_x_array_order(self):
y, x = np.mgrid[-10:10:5j, -5:15:5j]
x = x.copy("f")
self.assertContainsResults(self.construct_torus(), x, y)
def test_xy_array_order(self):
y, x = np.mgrid[-10:10:5j, -5:15:5j]
x = x.copy("f")
y = y.copy("f")
result = self.assertContainsResults(self.construct_torus(), x, y)
# Preserve the order
assert result.flags["F_CONTIGUOUS"]
def test_array_dtype(self):
y, x = np.mgrid[-10:10:5j], np.mgrid[-5:15:5j]
x = x.astype(np.int16)
self.assertContainsResults(self.construct_torus(), x, y)
def test_array_2d(self):
y, x = np.mgrid[-10:10:15j, -5:15:16j]
result = self.assertContainsResults(self.construct_torus(), x, y)
assert result.shape == x.shape
def test_shapely_xy_attr_contains(self):
g = Point(0, 0).buffer(10.0)
self.assertContainsResults(self.construct_torus(), *g.exterior.xy)
@pytest.mark.filterwarnings("ignore:The 'shapely.vectorized:")
class VectorizedTouchesTestCase(unittest.TestCase):
def test_touches(self):
from shapely.vectorized import touches
y, x = np.mgrid[-2:3:6j, -1:3:5j]
geom = box(0, -1, 2, 2)
result = touches(geom, x, y)
expected = np.array(
[
[False, False, False, False, False],
[False, True, True, True, False],
[False, True, False, True, False],
[False, True, False, True, False],
[False, True, True, True, False],
[False, False, False, False, False],
],
dtype=bool,
)
from numpy.testing import assert_array_equal
assert_array_equal(result, expected)

View File

@@ -0,0 +1,129 @@
"""
Test cases for Voronoi Diagram creation.
Overall, I'm trying less to test the correctness of the result
and more to cover input cases and behavior, making sure
that we return a sane result without error or raise a useful one.
"""
import numpy as np
import pytest
from shapely.geometry import MultiPoint
from shapely.ops import voronoi_diagram
from shapely.wkt import loads as load_wkt
def test_no_regions():
mp = MultiPoint(points=[(0.5, 0.5)])
with np.errstate(invalid="ignore"):
regions = voronoi_diagram(mp)
assert len(regions.geoms) == 0
def test_two_regions():
mp = MultiPoint(points=[(0.5, 0.5), (1.0, 1.0)])
regions = voronoi_diagram(mp)
assert len(regions.geoms) == 2
def test_edges():
mp = MultiPoint(points=[(0.5, 0.5), (1.0, 1.0)])
regions = voronoi_diagram(mp, edges=True)
assert len(regions.geoms) == 1
# can be LineString or MultiLineString depending on the GEOS version
assert all(r.geom_type.endswith("LineString") for r in regions.geoms)
def test_smaller_envelope():
mp = MultiPoint(points=[(0.5, 0.5), (1.0, 1.0)])
poly = load_wkt("POLYGON ((0 0, 0.5 0, 0.5 0.5, 0 0.5, 0 0))")
regions = voronoi_diagram(mp, envelope=poly)
assert len(regions.geoms) == 2
assert sum(r.area for r in regions.geoms) > poly.area
def test_larger_envelope():
"""When the envelope we specify is larger than the
area of the input feature, the created regions should
expand to fill that area."""
mp = MultiPoint(points=[(0.5, 0.5), (1.0, 1.0)])
poly = load_wkt("POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))")
regions = voronoi_diagram(mp, envelope=poly)
assert len(regions.geoms) == 2
assert sum(r.area for r in regions.geoms) == poly.area
def test_from_polygon():
poly = load_wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))")
regions = voronoi_diagram(poly)
assert len(regions.geoms) == 4
def test_from_polygon_with_enough_tolerance():
poly = load_wkt("POLYGON ((0 0, 0.5 0, 0.5 0.5, 0 0.5, 0 0))")
regions = voronoi_diagram(poly, tolerance=1.0)
assert len(regions.geoms) == 2
def test_from_polygon_without_enough_tolerance():
poly = load_wkt("POLYGON ((0 0, 0.5 0, 0.5 0.5, 0 0.5, 0 0))")
with pytest.raises(ValueError) as exc:
voronoi_diagram(poly, tolerance=0.6)
assert "Could not create Voronoi Diagram with the specified inputs" in str(
exc.value
)
assert "Try running again with default tolerance value." in str(exc.value)
def test_from_polygon_without_floating_point_coordinates():
poly = load_wkt("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))")
with pytest.raises(ValueError) as exc:
voronoi_diagram(poly, tolerance=0.1)
assert "Could not create Voronoi Diagram with the specified inputs" in str(
exc.value
)
assert "Try running again with default tolerance value." in str(exc.value)
def test_from_multipoint_without_floating_point_coordinates():
"""A Multipoint with the same "shape" as the above Polygon raises the same error."""
mp = load_wkt("MULTIPOINT (0 0, 1 0, 1 1, 0 1)")
with pytest.raises(ValueError) as exc:
voronoi_diagram(mp, tolerance=0.1)
assert "Could not create Voronoi Diagram with the specified inputs" in str(
exc.value
)
assert "Try running again with default tolerance value." in str(exc.value)
def test_from_multipoint_with_tolerace_without_floating_point_coordinates():
"""This multipoint will not work with a tolerance value."""
mp = load_wkt("MULTIPOINT (0 0, 1 0, 1 2, 0 1)")
with pytest.raises(ValueError) as exc:
voronoi_diagram(mp, tolerance=0.1)
assert "Could not create Voronoi Diagram with the specified inputs" in str(
exc.value
)
assert "Try running again with default tolerance value." in str(exc.value)
def test_from_multipoint_without_tolerace_without_floating_point_coordinates():
"""But it's fine without it."""
mp = load_wkt("MULTIPOINT (0 0, 1 0, 1 2, 0 1)")
regions = voronoi_diagram(mp)
assert len(regions.geoms) == 4

View File

@@ -0,0 +1,176 @@
import binascii
import math
import struct
import sys
import pytest
from shapely import wkt
from shapely.geometry import Point
from shapely.tests.legacy.conftest import shapely20_todo
from shapely.wkb import dump, dumps, load, loads
@pytest.fixture(scope="module")
def some_point():
return Point(1.2, 3.4)
def bin2hex(value):
return binascii.b2a_hex(value).upper().decode("utf-8")
def hex2bin(value):
return binascii.a2b_hex(value)
def hostorder(fmt, value):
"""Re-pack a hex WKB value to native endianness if needed
This routine does not understand WKB format, so it must be provided a
struct module format string, without initial indicator character ("@=<>!"),
which will be interpreted as big- or little-endian with standard sizes
depending on the endian flag in the first byte of the value.
"""
if fmt and fmt[0] in "@=<>!":
raise ValueError("Initial indicator character, one of @=<>!, in fmt")
if not fmt or fmt[0] not in "cbB":
raise ValueError("Missing endian flag in fmt")
(hexendian,) = struct.unpack(fmt[0], hex2bin(value[:2]))
hexorder = {0: ">", 1: "<"}[hexendian]
sysorder = {"little": "<", "big": ">"}[sys.byteorder]
if hexorder == sysorder:
return value # Nothing to do
return bin2hex(
struct.pack(
sysorder + fmt,
{">": 0, "<": 1}[sysorder],
*struct.unpack(hexorder + fmt, hex2bin(value))[1:],
)
)
def test_dumps_srid(some_point):
result = dumps(some_point)
assert bin2hex(result) == hostorder(
"BIdd", "0101000000333333333333F33F3333333333330B40"
)
result = dumps(some_point, srid=4326)
assert bin2hex(result) == hostorder(
"BIIdd", "0101000020E6100000333333333333F33F3333333333330B40"
)
def test_dumps_endianness(some_point):
result = dumps(some_point)
assert bin2hex(result) == hostorder(
"BIdd", "0101000000333333333333F33F3333333333330B40"
)
result = dumps(some_point, big_endian=False)
assert bin2hex(result) == "0101000000333333333333F33F3333333333330B40"
result = dumps(some_point, big_endian=True)
assert bin2hex(result) == "00000000013FF3333333333333400B333333333333"
def test_dumps_hex(some_point):
result = dumps(some_point, hex=True)
assert result == hostorder("BIdd", "0101000000333333333333F33F3333333333330B40")
def test_loads_srid():
# load a geometry which includes an srid
geom = loads(hex2bin("0101000020E6100000333333333333F33F3333333333330B40"))
assert isinstance(geom, Point)
assert geom.coords[:] == [(1.2, 3.4)]
# by default srid is not exported
result = dumps(geom)
assert bin2hex(result) == hostorder(
"BIdd", "0101000000333333333333F33F3333333333330B40"
)
# include the srid in the output
result = dumps(geom, include_srid=True)
assert bin2hex(result) == hostorder(
"BIIdd", "0101000020E6100000333333333333F33F3333333333330B40"
)
# replace geometry srid with another
result = dumps(geom, srid=27700)
assert bin2hex(result) == hostorder(
"BIIdd", "0101000020346C0000333333333333F33F3333333333330B40"
)
def test_loads_hex(some_point):
assert loads(dumps(some_point, hex=True), hex=True) == some_point
def test_dump_load_binary(some_point, tmpdir):
file = tmpdir.join("test.wkb")
with open(file, "wb") as file_pointer:
dump(some_point, file_pointer)
with open(file, "rb") as file_pointer:
restored = load(file_pointer)
assert some_point == restored
def test_dump_load_hex(some_point, tmpdir):
file = tmpdir.join("test.wkb")
with open(file, "w") as file_pointer:
dump(some_point, file_pointer, hex=True)
with open(file) as file_pointer:
restored = load(file_pointer, hex=True)
assert some_point == restored
# pygeos handles both bytes and str
@shapely20_todo
def test_dump_hex_load_binary(some_point, tmpdir):
"""Asserts that reading a binary file as text (hex mode) fails."""
file = tmpdir.join("test.wkb")
with open(file, "w") as file_pointer:
dump(some_point, file_pointer, hex=True)
with pytest.raises(TypeError):
with open(file, "rb") as file_pointer:
load(file_pointer)
def test_dump_binary_load_hex(some_point, tmpdir):
"""Asserts that reading a text file (hex mode) as binary fails."""
file = tmpdir.join("test.wkb")
with open(file, "wb") as file_pointer:
dump(some_point, file_pointer)
# TODO(shapely-2.0) on windows this doesn't seem to error with pygeos,
# but you get back a point with garbage coordinates
if sys.platform == "win32":
with open(file) as file_pointer:
restored = load(file_pointer, hex=True)
assert some_point != restored
return
with pytest.raises((UnicodeEncodeError, UnicodeDecodeError)):
with open(file) as file_pointer:
load(file_pointer, hex=True)
def test_point_empty():
g = wkt.loads("POINT EMPTY")
result = dumps(g, big_endian=False)
# Use math.isnan for second part of the WKB representation there are
# many byte representations for NaN)
assert result[: -2 * 8] == b"\x01\x01\x00\x00\x00"
coords = struct.unpack("<2d", result[-2 * 8 :])
assert len(coords) == 2
assert all(math.isnan(val) for val in coords)
def test_point_z_empty():
g = wkt.loads("POINT Z EMPTY")
assert g.wkb_hex == hostorder(
"BIddd", "0101000080000000000000F87F000000000000F87F000000000000F87F"
)

View File

@@ -0,0 +1,61 @@
from math import pi
import pytest
from shapely.geometry import Point
from shapely.wkt import dump, dumps, load, loads
@pytest.fixture(scope="module")
def some_point():
return Point(pi, -pi)
@pytest.fixture(scope="module")
def empty_geometry():
return Point()
def test_wkt(some_point):
""".wkt and wkt.dumps() both do not trim by default."""
assert some_point.wkt == f"POINT ({pi:.15f} {-pi:.15f})"
def test_wkt_null(empty_geometry):
assert empty_geometry.wkt == "POINT EMPTY"
def test_dump_load(some_point, tmpdir):
file = tmpdir.join("test.wkt")
with open(file, "w") as file_pointer:
dump(some_point, file_pointer)
with open(file) as file_pointer:
restored = load(file_pointer)
assert some_point == restored
def test_dump_load_null_geometry(empty_geometry, tmpdir):
file = tmpdir.join("test.wkt")
with open(file, "w") as file_pointer:
dump(empty_geometry, file_pointer)
with open(file) as file_pointer:
restored = load(file_pointer)
# This is does not work with __eq__():
assert empty_geometry.equals(restored)
def test_dumps_loads(some_point):
assert dumps(some_point) == f"POINT ({pi:.16f} {-pi:.16f})"
assert loads(dumps(some_point)) == some_point
def test_dumps_loads_null_geometry(empty_geometry):
assert dumps(empty_geometry) == "POINT EMPTY"
# This is does not work with __eq__():
assert loads(dumps(empty_geometry)).equals(empty_geometry)
def test_dumps_precision(some_point):
assert dumps(some_point, rounding_precision=4) == f"POINT ({pi:.4f} {-pi:.4f})"

View File

@@ -0,0 +1,44 @@
import threading
from binascii import b2a_hex
def main():
num_threads = 10
use_threads = True
if not use_threads:
# Run core code
runShapelyBuilding()
else:
threads = [
threading.Thread(target=runShapelyBuilding, name=str(i), args=(i,))
for i in range(num_threads)
]
for t in threads:
t.start()
for t in threads:
t.join()
def runShapelyBuilding(num):
print(f"{num}: Running shapely tests on wkb")
import shapely.geos
print(f"{num} GEOS Handle: {shapely.geos.lgeos.geos_handle}")
import shapely.wkb
import shapely.wkt
p = shapely.wkt.loads("POINT (0 0)")
print(f"{num} WKT: {shapely.wkt.dumps(p)}")
wkb = shapely.wkb.dumps(p)
print(f"{num} WKB: {b2a_hex(wkb)}")
for i in range(10):
shapely.wkb.loads(wkb)
print(f"{num} GEOS Handle: {shapely.geos.lgeos.geos_handle}")
print(f"Done {num}")
if __name__ == "__main__":
main()