1from . import unittest 2from math import pi 3from shapely import affinity 4from shapely.wkt import loads as load_wkt 5from shapely.geometry import Point 6 7try: 8 import numpy 9except ImportError: 10 numpy = False 11 12 13class AffineTestCase(unittest.TestCase): 14 15 def test_affine_params(self): 16 g = load_wkt('LINESTRING(2.4 4.1, 2.4 3, 3 3)') 17 self.assertRaises( 18 TypeError, affinity.affine_transform, g, None) 19 self.assertRaises( 20 TypeError, affinity.affine_transform, g, '123456') 21 self.assertRaises(ValueError, affinity.affine_transform, g, 22 [1, 2, 3, 4, 5, 6, 7, 8, 9]) 23 self.assertRaises(AttributeError, affinity.affine_transform, None, 24 [1, 2, 3, 4, 5, 6]) 25 26 def test_affine_geom_types(self): 27 28 # identity matrices, which should result with no transformation 29 matrix2d = (1, 0, 30 0, 1, 31 0, 0) 32 matrix3d = (1, 0, 0, 33 0, 1, 0, 34 0, 0, 1, 35 0, 0, 0) 36 37 # empty in, empty out 38 empty2d = load_wkt('MULTIPOLYGON EMPTY') 39 self.assertTrue(affinity.affine_transform(empty2d, matrix2d).is_empty) 40 41 def test_geom(g2, g3=None): 42 self.assertFalse(g2.has_z) 43 a2 = affinity.affine_transform(g2, matrix2d) 44 self.assertFalse(a2.has_z) 45 self.assertTrue(g2.equals(a2)) 46 if g3 is not None: 47 self.assertTrue(g3.has_z) 48 a3 = affinity.affine_transform(g3, matrix3d) 49 self.assertTrue(a3.has_z) 50 self.assertTrue(g3.equals(a3)) 51 return 52 53 pt2d = load_wkt('POINT(12.3 45.6)') 54 pt3d = load_wkt('POINT(12.3 45.6 7.89)') 55 test_geom(pt2d, pt3d) 56 ls2d = load_wkt('LINESTRING(0.9 3.4, 0.7 2, 2.5 2.7)') 57 ls3d = load_wkt('LINESTRING(0.9 3.4 3.3, 0.7 2 2.3, 2.5 2.7 5.5)') 58 test_geom(ls2d, ls3d) 59 lr2d = load_wkt('LINEARRING(0.9 3.4, 0.7 2, 2.5 2.7, 0.9 3.4)') 60 lr3d = load_wkt( 61 'LINEARRING(0.9 3.4 3.3, 0.7 2 2.3, 2.5 2.7 5.5, 0.9 3.4 3.3)') 62 test_geom(lr2d, lr3d) 63 test_geom(load_wkt('POLYGON((0.9 2.3, 0.5 1.1, 2.4 0.8, 0.9 2.3), ' 64 '(1.1 1.7, 0.9 1.3, 1.4 1.2, 1.1 1.7), ' 65 '(1.6 1.3, 1.7 1, 1.9 1.1, 1.6 1.3))')) 66 test_geom(load_wkt( 67 'MULTIPOINT ((-300 300), (700 300), (-800 -1100), (200 -300))')) 68 test_geom(load_wkt( 69 'MULTILINESTRING((0 0, -0.7 -0.7, 0.6 -1), ' 70 '(-0.5 0.5, 0.7 0.6, 0 -0.6))')) 71 test_geom(load_wkt( 72 'MULTIPOLYGON(((900 4300, -1100 -400, 900 -800, 900 4300)), ' 73 '((1200 4300, 2300 4400, 1900 1000, 1200 4300)))')) 74 test_geom(load_wkt('GEOMETRYCOLLECTION(POINT(20 70),' 75 ' POLYGON((60 70, 13 35, 60 -30, 60 70)),' 76 ' LINESTRING(60 70, 50 100, 80 100))')) 77 78 def test_affine_2d(self): 79 g = load_wkt('LINESTRING(2.4 4.1, 2.4 3, 3 3)') 80 # custom scale and translate 81 expected2d = load_wkt('LINESTRING(-0.2 14.35, -0.2 11.6, 1 11.6)') 82 matrix2d = (2, 0, 83 0, 2.5, 84 -5, 4.1) 85 a2 = affinity.affine_transform(g, matrix2d) 86 self.assertTrue(a2.equals_exact(expected2d, 1e-6)) 87 self.assertFalse(a2.has_z) 88 # Make sure a 3D matrix does not make a 3D shape from a 2D input 89 matrix3d = (2, 0, 0, 90 0, 2.5, 0, 91 0, 0, 10, 92 -5, 4.1, 100) 93 a3 = affinity.affine_transform(g, matrix3d) 94 self.assertTrue(a3.equals_exact(expected2d, 1e-6)) 95 self.assertFalse(a3.has_z) 96 97 def test_affine_3d(self): 98 g2 = load_wkt('LINESTRING(2.4 4.1, 2.4 3, 3 3)') 99 g3 = load_wkt('LINESTRING(2.4 4.1 100.2, 2.4 3 132.8, 3 3 128.6)') 100 # custom scale and translate 101 matrix2d = (2, 0, 102 0, 2.5, 103 -5, 4.1) 104 matrix3d = (2, 0, 0, 105 0, 2.5, 0, 106 0, 0, 0.3048, 107 -5, 4.1, 100) 108 # Combinations of 2D and 3D geometries and matrices 109 a22 = affinity.affine_transform(g2, matrix2d) 110 a23 = affinity.affine_transform(g2, matrix3d) 111 a32 = affinity.affine_transform(g3, matrix2d) 112 a33 = affinity.affine_transform(g3, matrix3d) 113 # Check dimensions 114 self.assertFalse(a22.has_z) 115 self.assertFalse(a23.has_z) 116 self.assertTrue(a32.has_z) 117 self.assertTrue(a33.has_z) 118 # 2D equality checks 119 expected2d = load_wkt('LINESTRING(-0.2 14.35, -0.2 11.6, 1 11.6)') 120 expected3d = load_wkt('LINESTRING(-0.2 14.35 130.54096, ' 121 '-0.2 11.6 140.47744, 1 11.6 139.19728)') 122 expected32 = load_wkt('LINESTRING(-0.2 14.35 100.2, ' 123 '-0.2 11.6 132.8, 1 11.6 128.6)') 124 self.assertTrue(a22.equals_exact(expected2d, 1e-6)) 125 self.assertTrue(a23.equals_exact(expected2d, 1e-6)) 126 # Do explicit 3D check of coordinate values 127 for a, e in zip(a32.coords, expected32.coords): 128 for ap, ep in zip(a, e): 129 self.assertAlmostEqual(ap, ep) 130 for a, e in zip(a33.coords, expected3d.coords): 131 for ap, ep in zip(a, e): 132 self.assertAlmostEqual(ap, ep) 133 134 135class TransformOpsTestCase(unittest.TestCase): 136 137 def test_rotate(self): 138 ls = load_wkt('LINESTRING(240 400, 240 300, 300 300)') 139 # counter-clockwise degrees 140 rls = affinity.rotate(ls, 90) 141 els = load_wkt('LINESTRING(220 320, 320 320, 320 380)') 142 self.assertTrue(rls.equals(els)) 143 # retest with named parameters for the same result 144 rls = affinity.rotate(geom=ls, angle=90, origin='center') 145 self.assertTrue(rls.equals(els)) 146 # clockwise radians 147 rls = affinity.rotate(ls, -pi/2, use_radians=True) 148 els = load_wkt('LINESTRING(320 380, 220 380, 220 320)') 149 self.assertTrue(rls.equals(els)) 150 ## other `origin` parameters 151 # around the centroid 152 rls = affinity.rotate(ls, 90, origin='centroid') 153 els = load_wkt('LINESTRING(182.5 320, 282.5 320, 282.5 380)') 154 self.assertTrue(rls.equals(els)) 155 # around the second coordinate tuple 156 rls = affinity.rotate(ls, 90, origin=ls.coords[1]) 157 els = load_wkt('LINESTRING(140 300, 240 300, 240 360)') 158 self.assertTrue(rls.equals(els)) 159 # around the absolute Point of origin 160 rls = affinity.rotate(ls, 90, origin=Point(0, 0)) 161 els = load_wkt('LINESTRING(-400 240, -300 240, -300 300)') 162 self.assertTrue(rls.equals(els)) 163 164 def test_rotate_empty(self): 165 rls = affinity.rotate(load_wkt('LINESTRING EMPTY'), 90) 166 els = load_wkt('LINESTRING EMPTY') 167 self.assertTrue(rls.equals(els)) 168 169 @unittest.skipIf(not numpy, 'numpy not installed') 170 def test_rotate_angle_array(self): 171 ls = load_wkt('LINESTRING(240 400, 240 300, 300 300)') 172 els = load_wkt('LINESTRING(220 320, 320 320, 320 380)') 173 # check with degrees 174 theta = numpy.array([90.0]) 175 rls = affinity.rotate(ls, theta) 176 self.assertEqual(theta[0], 90.0) 177 self.assertTrue(rls.equals(els)) 178 # check with radians 179 theta = numpy.array([pi/2]) 180 rls = affinity.rotate(ls, theta, use_radians=True) 181 self.assertEqual(theta[0], pi/2) 182 self.assertTrue(rls.equals(els)) 183 184 def test_scale(self): 185 ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)') 186 # test defaults of 1.0 187 sls = affinity.scale(ls) 188 self.assertTrue(sls.equals(ls)) 189 # different scaling in different dimensions 190 sls = affinity.scale(ls, 2, 3, 0.5) 191 els = load_wkt('LINESTRING(210 500 5, 210 200 15, 330 200 10)') 192 self.assertTrue(sls.equals(els)) 193 # Do explicit 3D check of coordinate values 194 for a, b in zip(sls.coords, els.coords): 195 for ap, bp in zip(a, b): 196 self.assertEqual(ap, bp) 197 # retest with named parameters for the same result 198 sls = affinity.scale(geom=ls, xfact=2, yfact=3, zfact=0.5, 199 origin='center') 200 self.assertTrue(sls.equals(els)) 201 ## other `origin` parameters 202 # around the centroid 203 sls = affinity.scale(ls, 2, 3, 0.5, origin='centroid') 204 els = load_wkt('LINESTRING(228.75 537.5, 228.75 237.5, 348.75 237.5)') 205 self.assertTrue(sls.equals(els)) 206 # around the second coordinate tuple 207 sls = affinity.scale(ls, 2, 3, 0.5, origin=ls.coords[1]) 208 els = load_wkt('LINESTRING(240 600, 240 300, 360 300)') 209 self.assertTrue(sls.equals(els)) 210 # around some other 3D Point of origin 211 sls = affinity.scale(ls, 2, 3, 0.5, origin=Point(100, 200, 1000)) 212 els = load_wkt('LINESTRING(380 800 505, 380 500 515, 500 500 510)') 213 self.assertTrue(sls.equals(els)) 214 # Do explicit 3D check of coordinate values 215 for a, b in zip(sls.coords, els.coords): 216 for ap, bp in zip(a, b): 217 self.assertEqual(ap, bp) 218 219 def test_scale_empty(self): 220 sls = affinity.scale(load_wkt('LINESTRING EMPTY')) 221 els = load_wkt('LINESTRING EMPTY') 222 self.assertTrue(sls.equals(els)) 223 224 def test_skew(self): 225 ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)') 226 # test default shear angles of 0.0 227 sls = affinity.skew(ls) 228 self.assertTrue(sls.equals(ls)) 229 # different shearing in x- and y-directions 230 sls = affinity.skew(ls, 15, -30) 231 els = load_wkt('LINESTRING (253.39745962155615 417.3205080756888, ' 232 '226.60254037844385 317.3205080756888, ' 233 '286.60254037844385 282.67949192431126)') 234 self.assertTrue(sls.equals_exact(els, 1e-6)) 235 # retest with radians for the same result 236 sls = affinity.skew(ls, pi/12, -pi/6, use_radians=True) 237 self.assertTrue(sls.equals_exact(els, 1e-6)) 238 # retest with named parameters for the same result 239 sls = affinity.skew(geom=ls, xs=15, ys=-30, 240 origin='center', use_radians=False) 241 self.assertTrue(sls.equals_exact(els, 1e-6)) 242 ## other `origin` parameters 243 # around the centroid 244 sls = affinity.skew(ls, 15, -30, origin='centroid') 245 els = load_wkt('LINESTRING(258.42150697963973 406.49519052838332, ' 246 '231.6265877365273980 306.4951905283833185, ' 247 '291.6265877365274264 271.8541743770057337)') 248 self.assertTrue(sls.equals_exact(els, 1e-6)) 249 # around the second coordinate tuple 250 sls = affinity.skew(ls, 15, -30, origin=ls.coords[1]) 251 els = load_wkt('LINESTRING(266.7949192431123038 400, 240 300, ' 252 '300 265.3589838486224153)') 253 self.assertTrue(sls.equals_exact(els, 1e-6)) 254 # around the absolute Point of origin 255 sls = affinity.skew(ls, 15, -30, origin=Point(0, 0)) 256 els = load_wkt('LINESTRING(347.179676972449101 261.435935394489832, ' 257 '320.3847577293367976 161.4359353944898317, ' 258 '380.3847577293367976 126.7949192431122754)') 259 self.assertTrue(sls.equals_exact(els, 1e-6)) 260 261 def test_skew_empty(self): 262 sls = affinity.skew(load_wkt('LINESTRING EMPTY')) 263 els = load_wkt('LINESTRING EMPTY') 264 self.assertTrue(sls.equals(els)) 265 266 @unittest.skipIf(not numpy, 'numpy not installed') 267 def test_skew_xs_ys_array(self): 268 ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)') 269 els = load_wkt('LINESTRING (253.39745962155615 417.3205080756888, ' 270 '226.60254037844385 317.3205080756888, ' 271 '286.60254037844385 282.67949192431126)') 272 # check with degrees 273 xs_ys = numpy.array([15.0, -30.0]) 274 sls = affinity.skew(ls, xs_ys[0:1], xs_ys[1:2]) 275 self.assertEqual(xs_ys[0], 15.0) 276 self.assertEqual(xs_ys[1], -30.0) 277 self.assertTrue(sls.equals_exact(els, 1e-6)) 278 # check with radians 279 xs_ys = numpy.array([pi/12, -pi/6]) 280 sls = affinity.skew(ls, xs_ys[0:1], xs_ys[1:2], use_radians=True) 281 self.assertEqual(xs_ys[0], pi/12) 282 self.assertEqual(xs_ys[1], -pi/6) 283 self.assertTrue(sls.equals_exact(els, 1e-6)) 284 285 def test_translate(self): 286 ls = load_wkt('LINESTRING(240 400 10, 240 300 30, 300 300 20)') 287 # test default offset of 0.0 288 tls = affinity.translate(ls) 289 self.assertTrue(tls.equals(ls)) 290 # test all offsets 291 tls = affinity.translate(ls, 100, 400, -10) 292 els = load_wkt('LINESTRING(340 800 0, 340 700 20, 400 700 10)') 293 self.assertTrue(tls.equals(els)) 294 # Do explicit 3D check of coordinate values 295 for a, b in zip(tls.coords, els.coords): 296 for ap, bp in zip(a, b): 297 self.assertEqual(ap, bp) 298 # retest with named parameters for the same result 299 tls = affinity.translate(geom=ls, xoff=100, yoff=400, zoff=-10) 300 self.assertTrue(tls.equals(els)) 301 302 def test_translate_empty(self): 303 tls = affinity.translate(load_wkt('LINESTRING EMPTY')) 304 els = load_wkt('LINESTRING EMPTY') 305 self.assertTrue(tls.equals(els)) 306