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