File: test_affinity.py

package info (click to toggle)
python-shapely 2.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 2,564 kB
  • sloc: python: 18,650; ansic: 6,615; makefile: 88; sh: 62
file content (308 lines) | stat: -rw-r--r-- 12,483 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
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)