1from . import unittest, numpy, shapely20_deprecated
2import pytest
3
4from shapely.coords import CoordinateSequence
5from shapely.errors import ShapelyDeprecationWarning
6from shapely.geometry import LineString, asLineString, Point, LinearRing
7
8
9def test_from_coordinate_sequence():
10    # From coordinate tuples
11    line = LineString(((1.0, 2.0), (3.0, 4.0)))
12    assert len(line.coords) == 2
13    assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
14
15    line = LineString([(1.0, 2.0), (3.0, 4.0)])
16    assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
17
18
19def test_from_coordinate_sequence_3D():
20    line = LineString(((1.0, 2.0, 3.0), (3.0, 4.0, 5.0)))
21    assert line.has_z
22    assert line.coords[:] == [(1.0, 2.0, 3.0), (3.0, 4.0, 5.0)]
23
24
25def test_from_points():
26    # From Points
27    line = LineString((Point(1.0, 2.0), Point(3.0, 4.0)))
28    assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
29
30    line = LineString([Point(1.0, 2.0), Point(3.0, 4.0)])
31    assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
32
33
34def test_from_mix():
35    # From mix of tuples and Points
36    line = LineString((Point(1.0, 2.0), (2.0, 3.0), Point(3.0, 4.0)))
37    assert line.coords[:] == [(1.0, 2.0), (2.0, 3.0), (3.0, 4.0)]
38
39
40def test_from_linestring():
41    # From another linestring
42    line = LineString(((1.0, 2.0), (3.0, 4.0)))
43    copy = LineString(line)
44    assert copy.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
45    assert copy.geom_type == 'LineString'
46
47
48def test_from_linearring():
49    coords = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]
50    ring = LinearRing(coords)
51    copy = LineString(ring)
52    assert copy.coords[:] == coords
53    assert copy.geom_type == 'LineString'
54
55
56def test_from_linestring_z():
57    coords = [(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]
58    line = LineString(coords)
59    copy = LineString(line)
60    assert copy.coords[:] == coords
61    assert copy.geom_type == 'LineString'
62
63
64def test_from_generator():
65    gen = (coord for coord in [(1.0, 2.0), (3.0, 4.0)])
66    line = LineString(gen)
67    assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
68
69
70def test_from_empty():
71    line = LineString()
72    assert line.is_empty
73    assert isinstance(line.coords, CoordinateSequence)
74    assert line.coords[:] == []
75
76    line = LineString([])
77    assert line.is_empty
78    assert isinstance(line.coords, CoordinateSequence)
79    assert line.coords[:] == []
80
81
82def test_from_numpy():
83    # Construct from a numpy array
84    np = pytest.importorskip("numpy")
85
86    line = LineString(np.array([[1.0, 2.0], [3.0, 4.0]]))
87    assert line.coords[:] == [(1.0, 2.0), (3.0, 4.0)]
88
89
90@pytest.mark.filterwarnings("error:An exception was ignored")  # NumPy 1.21
91def test_numpy_empty_linestring_coords():
92    np = pytest.importorskip("numpy")
93
94    # Check empty
95    line = LineString([])
96    la = np.asarray(line.coords)
97
98    assert la.shape == (0,)
99
100
101@shapely20_deprecated
102@pytest.mark.filterwarnings("error:An exception was ignored")  # NumPy 1.21
103def test_numpy_object_array():
104    np = pytest.importorskip("numpy")
105
106    geom = LineString([(0.0, 0.0), (0.0, 1.0)])
107    ar = np.empty(1, object)
108    ar[:] = [geom]
109    assert ar[0] == geom
110
111
112def test_from_invalid_dim():
113    with pytest.raises(ValueError, match="at least 2 coordinate tuples"):
114        LineString([(1, 2)])
115
116    with pytest.raises(ValueError, match="Inconsistent coordinate dimensionality"):
117        LineString([(1, 2, 3), (4, 5)])
118
119    # TODO this does not fail
120    # with pytest.raises(ValueError, match="Inconsistent coordinate dimensionality"):
121    #     LineString([(1, 2), (3, 4, 5)]))
122
123    # TODO better error, right now raises AssertionError
124    with pytest.raises(Exception):
125        LineString([(1, 2, 3, 4), (4, 5, 6, 7)])
126
127
128def test_from_single_coordinate():
129    """Test for issue #486"""
130    coords = [[-122.185933073564, 37.3629353839073]]
131    with pytest.raises(ValueError):
132        ls = LineString(coords)
133        ls.geom_type  # caused segfault before fix
134
135
136class LineStringTestCase(unittest.TestCase):
137
138    def test_linestring(self):
139
140        # From coordinate tuples
141        line = LineString(((1.0, 2.0), (3.0, 4.0)))
142        self.assertEqual(len(line.coords), 2)
143        self.assertEqual(line.coords[:], [(1.0, 2.0), (3.0, 4.0)])
144
145        # Bounds
146        self.assertEqual(line.bounds, (1.0, 2.0, 3.0, 4.0))
147
148        # Coordinate access
149        self.assertEqual(tuple(line.coords), ((1.0, 2.0), (3.0, 4.0)))
150        self.assertEqual(line.coords[0], (1.0, 2.0))
151        self.assertEqual(line.coords[1], (3.0, 4.0))
152        with self.assertRaises(IndexError):
153            line.coords[2]  # index out of range
154
155        # Geo interface
156        self.assertEqual(line.__geo_interface__,
157                         {'type': 'LineString',
158                          'coordinates': ((1.0, 2.0), (3.0, 4.0))})
159
160    @shapely20_deprecated
161    def test_linestring_mutate(self):
162        line = LineString(((1.0, 2.0), (3.0, 4.0)))
163
164        # Coordinate modification
165        line.coords = ((-1.0, -1.0), (1.0, 1.0))
166        self.assertEqual(line.__geo_interface__,
167                         {'type': 'LineString',
168                          'coordinates': ((-1.0, -1.0), (1.0, 1.0))})
169
170    @shapely20_deprecated
171    def test_linestring_adapter(self):
172        # Adapt a coordinate list to a line string
173        coords = [[5.0, 6.0], [7.0, 8.0]]
174        la = asLineString(coords)
175        self.assertEqual(la.coords[:], [(5.0, 6.0), (7.0, 8.0)])
176
177    def test_linestring_empty(self):
178        # Test Non-operability of Null geometry
179        l_null = LineString()
180        self.assertEqual(l_null.wkt, 'GEOMETRYCOLLECTION EMPTY')
181        self.assertEqual(l_null.length, 0.0)
182
183    @shapely20_deprecated
184    def test_linestring_empty_mutate(self):
185        # Check that we can set coordinates of a null geometry
186        l_null = LineString()
187        l_null.coords = [(0, 0), (1, 1)]
188        self.assertAlmostEqual(l_null.length, 1.4142135623730951)
189
190    def test_equals_argument_order(self):
191        """
192        Test equals predicate functions correctly regardless of the order
193        of the inputs. See issue #317.
194        """
195        coords = ((0, 0), (1, 0), (1, 1), (0, 0))
196        ls = LineString(coords)
197        lr = LinearRing(coords)
198
199        self.assertFalse(ls.__eq__(lr))  # previously incorrectly returned True
200        self.assertFalse(lr.__eq__(ls))
201        self.assertFalse(ls == lr)
202        self.assertFalse(lr == ls)
203
204        ls_clone = LineString(coords)
205        lr_clone = LinearRing(coords)
206
207        self.assertTrue(ls.__eq__(ls_clone))
208        self.assertTrue(lr.__eq__(lr_clone))
209        self.assertTrue(ls == ls_clone)
210        self.assertTrue(lr == lr_clone)
211
212    @shapely20_deprecated
213    @unittest.skipIf(not numpy, 'Numpy required')
214    def test_numpy(self):
215
216        from numpy import array, asarray
217        from numpy.testing import assert_array_equal
218
219        # Construct from a numpy array
220        line = LineString(array([[0.0, 0.0], [1.0, 2.0]]))
221        self.assertEqual(len(line.coords), 2)
222        self.assertEqual(line.coords[:], [(0.0, 0.0), (1.0, 2.0)])
223
224        line = LineString(((1.0, 2.0), (3.0, 4.0)))
225        la = asarray(line)
226        expected = array([[1.0, 2.0], [3.0, 4.0]])
227        assert_array_equal(la, expected)
228
229    @unittest.skipIf(not numpy, 'Numpy required')
230    def test_numpy_linestring_coords(self):
231        from numpy.testing import assert_array_equal
232
233        line = LineString(((1.0, 2.0), (3.0, 4.0)))
234        expected = numpy.array([[1.0, 2.0], [3.0, 4.0]])
235
236        # Coordinate sequences can be adapted as well
237        la = numpy.asarray(line.coords)
238        assert_array_equal(la, expected)
239
240    @shapely20_deprecated
241    @unittest.skipIf(not numpy, 'Numpy required')
242    def test_numpy_adapter(self):
243        from numpy import array, asarray
244        from numpy.testing import assert_array_equal
245
246        # Adapt a Numpy array to a line string
247        a = array([[1.0, 2.0], [3.0, 4.0]])
248        la = asLineString(a)
249        assert_array_equal(la.context, a)
250        self.assertEqual(la.coords[:], [(1.0, 2.0), (3.0, 4.0)])
251
252        # Now, the inverse
253        self.assertEqual(la.__array_interface__,
254                         la.context.__array_interface__)
255
256        pas = asarray(la)
257        assert_array_equal(pas, array([[1.0, 2.0], [3.0, 4.0]]))
258
259    @shapely20_deprecated
260    @unittest.skipIf(not numpy, 'Numpy required')
261    def test_numpy_asarray(self):
262        from numpy import array, asarray
263        from numpy.testing import assert_array_equal
264
265        # From Array.txt
266        a = asarray([[0.0, 0.0], [2.0, 2.0], [1.0, 1.0]])
267        line = LineString(a)
268        self.assertEqual(line.coords[:], [(0.0, 0.0), (2.0, 2.0), (1.0, 1.0)])
269
270        data = line.ctypes
271        self.assertEqual(data[0], 0.0)
272        self.assertEqual(data[5], 1.0)
273
274        b = asarray(line)
275        assert_array_equal(b, array([[0., 0.], [2., 2.], [1., 1.]]))
276
277    @shapely20_deprecated
278    @unittest.skipIf(not numpy, 'Numpy required')
279    def test_numpy_empty(self):
280        from numpy import array, asarray
281        from numpy.testing import assert_array_equal
282
283        # Test array interface of empty linestring
284        le = LineString()
285        a = asarray(le)
286        self.assertEqual(a.shape[0], 0)
287
288
289def test_linestring_mutability_deprecated():
290    line = LineString(((1.0, 2.0), (3.0, 4.0)))
291    with pytest.warns(ShapelyDeprecationWarning, match="Setting"):
292        line.coords = ((-1.0, -1.0), (1.0, 1.0))
293
294
295def test_linestring_adapter_deprecated():
296    coords = [[5.0, 6.0], [7.0, 8.0]]
297    with pytest.warns(ShapelyDeprecationWarning, match="proxy geometries"):
298        asLineString(coords)
299
300
301def test_linestring_ctypes_deprecated():
302    line = LineString(((1.0, 2.0), (3.0, 4.0)))
303    with pytest.warns(ShapelyDeprecationWarning, match="ctypes"):
304        line.ctypes
305
306
307def test_linestring_array_interface_deprecated():
308    line = LineString(((1.0, 2.0), (3.0, 4.0)))
309    with pytest.warns(ShapelyDeprecationWarning, match="array_interface"):
310        line.array_interface()
311
312
313@unittest.skipIf(not numpy, 'Numpy required')
314def test_linestring_array_interface_numpy_deprecated():
315    import numpy as np
316
317    line = LineString(((1.0, 2.0), (3.0, 4.0)))
318    with pytest.warns(ShapelyDeprecationWarning, match="array interface"):
319        np.array(line)
320