source: branches/numpy/anuga/utilities/test_polygon.py @ 7092

Last change on this file since 7092 was 7038, checked in by rwilson, 15 years ago

Fixed bugs in the back-merge from the Numpy trunk.

File size: 66.6 KB
Line 
1#!/usr/bin/env python
2
3import unittest
4import numpy as num
5from math import sqrt, pi
6from anuga.utilities.numerical_tools import ensure_numeric
7from anuga.utilities.system_tools import get_pathname_from_package
8
9from polygon import *
10from anuga.coordinate_transforms.geo_reference import Geo_reference
11from anuga.geospatial_data.geospatial_data import Geospatial_data
12
13
14def test_function(x, y):
15    return x+y
16
17
18class Test_Polygon(unittest.TestCase):
19    def setUp(self):
20        pass
21
22    def tearDown(self):
23        pass
24
25    def test_that_C_extension_compiles(self):
26        FN = 'polygon_ext.c'
27        try:
28            import polygon_ext
29        except:
30            from compile import compile
31
32            try:
33                compile(FN)
34            except:
35                raise Exception, 'Could not compile %s' %FN
36            else:
37                import polygon_ext
38
39    # Polygon stuff
40    def test_polygon_function_constants(self):
41        p1 = [[0,0], [10,0], [10,10], [0,10]]
42        p2 = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
43
44        f = Polygon_function([(p1, 1.0)])
45        z = f([5, 5, 27, 35], [5, 9, 8, -5])    # Two first inside p1
46        assert num.allclose(z, [1, 1, 0, 0])
47
48        f = Polygon_function([(p2, 2.0)])
49        z = f([5, 5, 27, 35], [5, 9, 8, -5])    # First and last inside p2
50        assert num.allclose(z, [2, 0, 0, 2])
51
52        # Combined
53        f = Polygon_function([(p1, 1.0), (p2, 2.0)])
54        z = f([5, 5, 27, 35], [5, 9, 8, -5])
55        assert num.allclose(z, [2, 1, 0, 2])
56
57    def test_polygon_function_csvfile(self):
58        from os import sep, getenv
59
60        # Get path where this test is run
61        path = get_pathname_from_package('anuga.utilities')
62
63        # Form absolute filename and read
64        filename = path + sep + 'mainland_only.csv'
65        p1 = read_polygon(filename)
66
67        f = Polygon_function([(p1, 10.0)])
68        z = f([430000, 480000], [490000, 7720000]) # 1st outside, 2nd inside
69
70        assert num.allclose(z, [0, 10])
71
72    def test_polygon_function_georef(self):
73        """Check that georeferencing works"""
74
75        from anuga.coordinate_transforms.geo_reference import Geo_reference
76
77        geo = Geo_reference(56, 200, 1000)
78
79        # Make points 'absolute'
80        p1 = [[200,1000], [210,1000], [210,1010], [200,1010]]
81        p2 = [[200,1000], [210,1010], [215,1005], [220, 1010], [225,1000],
82              [230,1010], [240,990]]
83
84        f = Polygon_function([(p1, 1.0)], geo_reference=geo)
85        z = f([5, 5, 27, 35], [5, 9, 8, -5]) # Two first inside p1
86
87        assert num.allclose(z, [1, 1, 0, 0])
88
89        f = Polygon_function([(p2, 2.0)], geo_reference=geo)
90        z = f([5, 5, 27, 35], [5, 9, 8, -5]) # First and last inside p2
91        assert num.allclose(z, [2, 0, 0, 2])
92
93        # Combined
94        f = Polygon_function([(p1, 1.0), (p2, 2.0)], geo_reference=geo)
95        z = f([5, 5, 27, 35], [5, 9, 8, -5])
96        assert num.allclose(z, [2, 1, 0, 2])
97
98        # Check that it would fail without geo
99        f = Polygon_function([(p1, 1.0), (p2, 2.0)])
100        z = f([5, 5, 27, 35], [5, 9, 8, -5])
101        assert not num.allclose(z, [2, 1, 0, 2])
102
103    def test_polygon_function_callable(self):
104        """Check that values passed into Polygon_function can be callable."""
105
106        p1 = [[0,0], [10,0], [10,10], [0,10]]
107        p2 = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
108
109        f = Polygon_function([(p1, test_function)])
110        z = f([5, 5, 27, 35], [5, 9, 8, -5]) # Two first inside p1
111        assert num.allclose(z, [10, 14, 0, 0])
112
113        # Combined
114        f = Polygon_function([(p1, test_function), (p2, 2.0)])
115        z = f([5, 5, 27, 35], [5, 9, 8, -5])
116        assert num.allclose(z, [2, 14, 0, 2])
117
118        # Combined w default
119        f = Polygon_function([(p1, test_function), (p2, 2.0)], default = 3.14)
120        z = f([5, 5, 27, 35], [5, 9, 8, -5])
121        assert num.allclose(z, [2, 14, 3.14, 2])
122
123        # Combined w default func
124        f = Polygon_function([(p1, test_function), (p2, 2.0)],
125                             default=test_function)
126        z = f([5, 5, 27, 35], [5, 9, 8, -5])
127        assert num.allclose(z, [2, 14, 35, 2])
128
129    def test_point_on_line(self):
130        # Endpoints first
131        assert point_on_line([0,0], [[0,0], [1,0]])
132        assert point_on_line([1,0], [[0,0], [1,0]])
133
134        # Endpoints first - non-simple
135        assert point_on_line([-0.1,0.0], [[-0.1,0.0], [1.5,0.6]])
136        assert point_on_line([1.5,0.6], [[-0.1,0.0], [1.5,0.6]])
137
138        # Then points on line
139        assert point_on_line([0.5,0], [[0,0], [1,0]])
140        assert point_on_line([0,0.5], [[0,1], [0,0]])
141        assert point_on_line([1,0.5], [[1,1], [1,0]])
142        assert point_on_line([0.5,0.5], [[0,0], [1,1]])
143
144        # Then points not on line
145        assert not point_on_line([0.5,0], [[0,1], [1,1]])
146        assert not point_on_line([0,0.5], [[0,0], [1,1]])
147
148        # From real example that failed
149        assert not point_on_line([40,50], [[40,20], [40,40]])
150
151        # From real example that failed
152        assert not point_on_line([40,19], [[40,20], [40,40]])
153
154        # Degenerate line
155        assert point_on_line([40,19], [[40,19], [40,19]])
156        assert not point_on_line([40,19], [[40,40], [40,40]])
157
158    def test_is_inside_polygon_main(self):
159        # Simplest case: Polygon is the unit square
160        polygon = [[0,0], [1,0], [1,1], [0,1]]
161
162        assert is_inside_polygon((0.5, 0.5), polygon)
163        assert not is_inside_polygon((0.5, 1.5), polygon)
164        assert not is_inside_polygon((0.5, -0.5), polygon)
165        assert not is_inside_polygon((-0.5, 0.5), polygon)
166        assert not is_inside_polygon((1.5, 0.5), polygon)
167
168        # Try point on borders
169        assert is_inside_polygon((1., 0.5), polygon, closed=True)
170        assert is_inside_polygon((0.5, 1.), polygon, closed=True)
171        assert is_inside_polygon((0., 0.5), polygon, closed=True)
172        assert is_inside_polygon((0.5, 0.), polygon, closed=True)
173
174        assert not is_inside_polygon((0.5, 1.), polygon, closed=False)
175        assert not is_inside_polygon((0., 0.5), polygon, closed=False)
176        assert not is_inside_polygon((0.5, 0.), polygon, closed=False)
177        assert not is_inside_polygon((1., 0.5), polygon, closed=False)
178
179    def test_inside_polygon_main(self):
180        # Simplest case: Polygon is the unit square
181        polygon = [[0,0], [1,0], [1,1], [0,1]]
182
183        assert is_inside_polygon_quick( (0.5, 0.5), polygon )
184        assert not is_inside_polygon_quick( (0.5, 1.5), polygon )
185        assert not is_inside_polygon_quick( (0.5, -0.5), polygon )
186        assert not is_inside_polygon_quick( (-0.5, 0.5), polygon )
187        assert not is_inside_polygon_quick( (1.5, 0.5), polygon )
188
189        # Try point on borders
190        assert is_inside_polygon_quick( (1., 0.5), polygon, closed=True)
191        assert is_inside_polygon_quick( (0.5, 1), polygon, closed=True)
192        assert is_inside_polygon_quick( (0., 0.5), polygon, closed=True)
193        assert is_inside_polygon_quick( (0.5, 0.), polygon, closed=True)
194
195        assert not is_inside_polygon_quick( (0.5, 1), polygon, closed=False)
196        assert not is_inside_polygon_quick( (0., 0.5), polygon, closed=False)
197        assert not is_inside_polygon_quick( (0.5, 0.), polygon, closed=False)
198        assert not is_inside_polygon_quick( (1., 0.5), polygon, closed=False)
199
200
201    def test_inside_polygon_main(self):
202        # Simplest case: Polygon is the unit square
203        polygon = [[0,0], [1,0], [1,1], [0,1]]
204
205        # From real example (that failed)
206        polygon = [[20,20], [40,20], [40,40], [20,40]]
207        points = [[40, 50]]
208        res = inside_polygon(points, polygon)
209        assert len(res) == 0
210
211        polygon = [[20,20], [40,20], [40,40], [20,40]]
212        points = [[25, 25], [30, 20], [40, 50], [90, 20], [40, 90]]
213        res = inside_polygon(points, polygon)
214        assert len(res) == 2
215        assert num.allclose(res, [0,1])
216
217        # More convoluted and non convex polygon
218        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
219        assert is_inside_polygon( (0.5, 0.5), polygon )
220        assert is_inside_polygon( (1, -0.5), polygon )
221        assert is_inside_polygon( (1.5, 0), polygon )
222
223        assert not is_inside_polygon( (0.5, 1.5), polygon )
224        assert not is_inside_polygon( (0.5, -0.5), polygon )
225
226        assert is_inside_polygon_quick( (0.5, 0.5), polygon )
227        assert is_inside_polygon_quick( (1, -0.5), polygon )
228        assert is_inside_polygon_quick( (1.5, 0), polygon )
229
230        assert not is_inside_polygon_quick( (0.5, 1.5), polygon )
231        assert not is_inside_polygon_quick( (0.5, -0.5), polygon )
232
233        # Very convoluted polygon
234        polygon = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
235        assert is_inside_polygon((5, 5), polygon)
236        assert is_inside_polygon((17, 7), polygon)
237        assert is_inside_polygon((27, 2), polygon)
238        assert is_inside_polygon((35, -5), polygon)
239        assert not is_inside_polygon((15, 7), polygon)
240        assert not is_inside_polygon((24, 3), polygon)
241        assert not is_inside_polygon((25, -10), polygon)
242
243        # Another combination (that failed)
244        polygon = [[0,0], [10,0], [10,10], [0,10]]
245        assert is_inside_polygon((5, 5), polygon)
246        assert is_inside_polygon((7, 7), polygon)
247        assert not is_inside_polygon((-17, 7), polygon)
248        assert not is_inside_polygon((7, 17), polygon)
249        assert not is_inside_polygon((17, 7), polygon)
250        assert not is_inside_polygon((27, 8), polygon)
251        assert not is_inside_polygon((35, -5), polygon)
252
253        # Multiple polygons
254        polygon = [[0,0], [1,0], [1,1], [0,1], [0,0], [10,10],
255                   [11,10], [11,11], [10,11], [10,10]]
256        assert is_inside_polygon((0.5, 0.5), polygon)
257        assert is_inside_polygon((10.5, 10.5), polygon)
258
259        # FIXME: Fails if point is 5.5, 5.5
260        assert not is_inside_polygon((0, 5.5), polygon)
261
262        # Polygon with a hole
263        polygon = [[-1,-1], [2,-1], [2,2], [-1,2], [-1,-1],
264                   [0,0], [1,0], [1,1], [0,1], [0,0]]
265
266        assert is_inside_polygon((0, -0.5), polygon)
267        assert not is_inside_polygon((0.5, 0.5), polygon)
268
269    def test_duplicate_points_being_ok(self):
270        # Simplest case: Polygon is the unit square
271        polygon = [[0,0], [1,0], [1,0], [1,0], [1,1], [0,1], [0,0]]
272
273        assert is_inside_polygon((0.5, 0.5), polygon)
274        assert not is_inside_polygon((0.5, 1.5), polygon)
275        assert not is_inside_polygon((0.5, -0.5), polygon)
276        assert not is_inside_polygon((-0.5, 0.5), polygon)
277        assert not is_inside_polygon((1.5, 0.5), polygon)
278
279        # Try point on borders
280        assert is_inside_polygon((1., 0.5), polygon, closed=True)
281        assert is_inside_polygon((0.5, 1), polygon, closed=True)
282        assert is_inside_polygon((0., 0.5), polygon, closed=True)
283        assert is_inside_polygon((0.5, 0.), polygon, closed=True)
284
285        assert not is_inside_polygon((0.5, 1), polygon, closed=False)
286        assert not is_inside_polygon((0., 0.5), polygon, closed=False)
287        assert not is_inside_polygon((0.5, 0.), polygon, closed=False)
288        assert not is_inside_polygon((1., 0.5), polygon, closed=False)
289
290        # From real example
291        polygon = [[20,20], [40,20], [40,40], [20,40]]
292        points = [[40, 50]]
293        res = inside_polygon(points, polygon)
294        assert len(res) == 0
295
296    def test_inside_polygon_vector_version(self):
297        # Now try the vector formulation returning indices
298        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
299        points = [[0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
300        res = inside_polygon( points, polygon, verbose=False )
301        assert num.allclose( res, [0,1,2] )
302
303    def test_outside_polygon(self):
304        # unit square
305        U = [[0,0], [1,0], [1,1], [0,1]]
306
307        # evaluate to False as the point 0.5, 0.5 is inside the unit square
308        assert not is_outside_polygon([0.5, 0.5], U)
309
310        # evaluate to True as the point 1.5, 0.5 is outside the unit square
311        assert is_outside_polygon([1.5, 0.5], U)
312
313        indices = outside_polygon([[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U)
314        assert num.allclose(indices, [1])
315
316        # One more test of vector formulation returning indices
317        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
318        points = [[0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
319        res = outside_polygon(points, polygon)
320        assert num.allclose(res, [3, 4])
321
322        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
323        points = [[0.5, 1.4], [0.5, 0.5], [1, -0.5], [1.5, 0],
324                  [0.5, 1.5], [0.5, -0.5]]
325        res = outside_polygon(points, polygon)
326
327        assert num.allclose(res, [0, 4, 5])
328
329    def test_outside_polygon2(self):
330        # unit square
331        U = [[0,0], [1,0], [1,1], [0,1]]
332
333        # evaluate to False as the point 0.5, 1.0 is inside the unit square
334        assert not outside_polygon([0.5, 1.0], U, closed = True)
335
336        # evaluate to True as the point 0.5, 1.0 is outside the unit square
337        assert is_outside_polygon([0.5, 1.0], U, closed = False)
338
339    def test_all_outside_polygon(self):
340        """Test case where all points are outside poly"""
341
342        # unit square
343        U = [[0,0], [1,0], [1,1], [0,1]]
344
345        points = [[2,2], [1,3], [-1,1], [0,2]]      # All outside
346
347        indices, count = separate_points_by_polygon(points, U)
348        assert count == 0                           # None inside
349        assert num.allclose(indices, [3, 2, 1, 0])
350
351        indices = outside_polygon(points, U, closed = True)
352        assert num.allclose(indices, [0, 1, 2, 3])
353
354        indices = inside_polygon(points, U, closed = True)
355        assert num.allclose(indices, [])
356
357    def test_all_inside_polygon(self):
358        """Test case where all points are inside poly"""
359
360        # unit square
361        U = [[0,0], [1,0], [1,1], [0,1]]
362
363        points = [[0.5,0.5], [0.2,0.3], [0,0.5]]    # All inside (or on edge)
364
365        indices, count = separate_points_by_polygon(points, U)
366        assert count == 3       # All inside
367        assert num.allclose(indices, [0, 1, 2])
368
369        indices = outside_polygon(points, U, closed=True)
370        assert num.allclose(indices, [])
371
372        indices = inside_polygon(points, U, closed=True)
373        assert num.allclose(indices, [0, 1, 2])
374
375
376    def test_separate_points_by_polygon(self):
377        # unit square
378        U = [[0,0], [1,0], [1,1], [0,1]]
379
380        indices, count = separate_points_by_polygon([[0.5, 0.5],
381                                                     [1, -0.5],
382                                                     [0.3, 0.2]],
383                                                    U)
384        assert num.allclose( indices, [0, 2, 1] )
385        assert count == 2
386
387        # One more test of vector formulation returning indices
388        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
389        points = [[0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
390        res, count = separate_points_by_polygon(points, polygon)
391
392        assert num.allclose(res, [0, 1, 2, 4, 3])
393        assert count == 3
394
395        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
396        points = [ [0.5, 1.4], [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
397        res, count = separate_points_by_polygon( points, polygon )
398
399        assert num.allclose( res, [1,2,3,5,4,0] )
400        assert count == 3
401
402
403    def test_is_inside_triangle(self):
404        # Simplest case:
405        triangle = [[0, 0], [1, 0], [0.5, 1]]
406
407        assert is_inside_triangle((0.5, 0.5), triangle)
408        assert is_inside_triangle((0.9, 0.1), triangle)
409        assert not is_inside_triangle((0.5, 1.5), triangle)
410        assert not is_inside_triangle((0.5, -0.5), triangle)
411        assert not is_inside_triangle((-0.5, 0.5), triangle)
412        assert not is_inside_triangle((1.5, 0.5), triangle)
413
414        # Try point on borders
415        assert is_inside_triangle((0.5, 0), triangle, closed=True)
416        assert is_inside_triangle((1, 0), triangle, closed=True)
417
418        assert not is_inside_triangle((0.5, 0), triangle, closed=False)
419        assert not is_inside_triangle((1, 0), triangle, closed=False)
420
421        # Try vertices
422        for P in triangle:
423            assert is_inside_triangle(P, triangle, closed=True)
424            assert not is_inside_triangle(P, triangle, closed=False)
425
426        # Slightly different
427        triangle = [[0, 0.1], [1, -0.2], [0.5, 1]]
428        assert is_inside_triangle((0.5, 0.5), triangle)
429        assert is_inside_triangle((0.4, 0.1), triangle)
430        assert not is_inside_triangle((1, 1), triangle)
431
432        # Try vertices
433        for P in triangle:
434            assert is_inside_triangle(P, triangle, closed=True)
435            assert not is_inside_triangle(P, triangle, closed=False)
436
437
438    def test_populate_polygon(self):
439        polygon = [[0,0], [1,0], [1,1], [0,1]]
440        points = populate_polygon(polygon, 5)
441
442        assert len(points) == 5
443        for point in points:
444            assert is_inside_polygon(point, polygon)
445
446            assert is_inside_polygon_quick(point, polygon)
447
448
449        # Very convoluted polygon
450        polygon = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
451        points = populate_polygon(polygon, 5)
452        assert len(points) == 5
453        for point in points:
454            assert is_inside_polygon(point, polygon)
455            assert is_inside_polygon_quick(point, polygon)
456
457
458    def test_populate_polygon_with_exclude(self):
459        polygon = [[0,0], [1,0], [1,1], [0,1]]
460        ex_poly = [[0,0], [0.5,0], [0.5, 0.5], [0,0.5]]     # SW quarter
461        points = populate_polygon(polygon, 5, exclude=[ex_poly])
462
463        assert len(points) == 5
464        for point in points:
465            assert is_inside_polygon(point, polygon)
466            assert not is_inside_polygon(point, ex_poly)
467
468        # overlap
469        polygon = [[0,0], [1,0], [1,1], [0,1]]
470        ex_poly = [[-1,-1], [0.5,0], [0.5, 0.5], [-1,0.5]]
471        points = populate_polygon(polygon, 5, exclude=[ex_poly])
472
473        assert len(points) == 5
474        for point in points:
475            assert is_inside_polygon(point, polygon)
476            assert not is_inside_polygon(point, ex_poly)
477
478        # Multiple
479        polygon = [[0,0], [1,0], [1,1], [0,1]]
480        ex_poly1 = [[0,0], [0.5,0], [0.5, 0.5], [0,0.5]]    # SW quarter
481        ex_poly2 = [[0.5,0.5], [0.5,1], [1, 1], [1,0.5]]    # NE quarter
482
483        points = populate_polygon(polygon, 20, exclude=[ex_poly1, ex_poly2])
484
485        assert len(points) == 20
486        for point in points:
487            assert is_inside_polygon(point, polygon)
488            assert not is_inside_polygon(point, ex_poly1)
489            assert not is_inside_polygon(point, ex_poly2)
490
491        # Very convoluted polygon
492        polygon = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
493        ex_poly = [[-1,-1], [5,0], [5, 5], [-1,5]]
494        points = populate_polygon(polygon, 20, exclude=[ex_poly])
495
496        assert len(points) == 20
497        for point in points:
498            assert is_inside_polygon(point, polygon)
499            assert not is_inside_polygon(point, ex_poly), '%s' % str(point)
500
501    def test_populate_polygon_with_exclude2(self):
502        min_outer = 0
503        max_outer = 1000
504        polygon_outer = [[min_outer,min_outer], [max_outer,min_outer],
505                         [max_outer,max_outer], [min_outer,max_outer]]
506
507        delta = 10
508        min_inner1 = min_outer + delta
509        max_inner1 = max_outer - delta
510        inner1_polygon = [[min_inner1,min_inner1], [max_inner1,min_inner1],
511                          [max_inner1,max_inner1], [min_inner1,max_inner1]]
512
513        density_inner2 = 1000
514        min_inner2 = min_outer + 2*delta
515        max_inner2 = max_outer - 2*delta
516        inner2_polygon = [[min_inner2,min_inner2], [max_inner2,min_inner2],
517                          [max_inner2,max_inner2], [min_inner2,max_inner2]]
518
519        points = populate_polygon(polygon_outer, 20,
520                                  exclude=[inner1_polygon, inner2_polygon])
521
522        assert len(points) == 20
523        for point in points:
524            assert is_inside_polygon(point, polygon_outer)
525            assert not is_inside_polygon(point, inner1_polygon)
526            assert not is_inside_polygon(point, inner2_polygon)
527
528        # Very convoluted polygon
529        polygon = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
530        ex_poly = [[-1,-1], [5,0], [5, 5], [-1,5]]
531        points = populate_polygon(polygon, 20, exclude=[ex_poly])
532
533        assert len(points) == 20
534        for point in points:
535            assert is_inside_polygon(point, polygon)
536            assert not is_inside_polygon(point, ex_poly), '%s' % str(point)
537
538    def test_point_in_polygon(self):
539        polygon = [[0,0], [1,0], [1,1], [0,1]]
540        point = point_in_polygon(polygon)
541        assert is_inside_polygon(point, polygon)
542
543        # this may get into a vicious loop
544        # polygon = [[1e32,1e54], [1,0], [1,1], [0,1]]
545
546        polygon = [[1e15,1e7], [1,0], [1,1], [0,1]]
547        point = point_in_polygon(polygon)
548        assert is_inside_polygon(point, polygon)
549
550        polygon = [[0,0], [1,0], [1,1], [1e8,1e8]]
551        point = point_in_polygon(polygon)
552        assert is_inside_polygon(point, polygon)
553
554        polygon = [[1e32,1e54], [-1e32,1e54], [1e32,-1e54]]
555        point = point_in_polygon(polygon)
556        assert is_inside_polygon(point, polygon)
557
558        polygon = [[1e18,1e15], [1,0], [0,1]]
559        point = point_in_polygon(polygon)
560        assert is_inside_polygon(point, polygon)
561
562    def test_in_and_outside_polygon_main(self):
563        # Simplest case: Polygon is the unit square
564        polygon = [[0,0], [1,0], [1,1], [0,1]]
565
566        inside, outside = in_and_outside_polygon((0.5, 0.5), polygon)
567        assert inside[0] == 0
568        assert len(outside) == 0
569
570        inside, outside = in_and_outside_polygon((1., 0.5), polygon,
571                                                 closed=True)
572        assert inside[0] == 0
573        assert len(outside) == 0
574
575        inside, outside = in_and_outside_polygon((1., 0.5), polygon,
576                                                 closed=False)
577        assert len(inside) == 0
578        assert outside[0] == 0
579
580        points = [(1., 0.25),(1., 0.75)]
581        inside, outside = in_and_outside_polygon(points, polygon, closed=True)
582        assert (inside, [0,1])
583        assert len(outside) == 0
584
585        inside, outside = in_and_outside_polygon(points, polygon, closed=False)
586        assert len(inside) == 0
587        assert (outside, [0,1])
588
589        points = [(100., 0.25), (0.5, 0.5) ]
590        inside, outside = in_and_outside_polygon(points, polygon)
591        assert (inside, [1])
592        assert outside[0] == 0
593
594        points = [(100., 0.25),(0.5, 0.5), (39,20), (0.6,0.7),(56,43),(67,90)]
595        inside, outside = in_and_outside_polygon(points, polygon)
596        assert (inside, [1, 3])
597        assert (outside, [0, 2, 4, 5])
598
599    def test_intersection1(self):
600        line0 = [[-1,0], [1,0]]
601        line1 = [[0,-1], [0,1]]
602
603        status, value = intersection(line0, line1)
604        assert status == 1
605        assert num.allclose(value, [0.0, 0.0])
606
607    def test_intersection2(self):
608        line0 = [[0,0], [24,12]]
609        line1 = [[0,12], [24,0]]
610
611        status, value = intersection(line0, line1)
612        assert status == 1
613        assert num.allclose(value, [12.0, 6.0])
614
615        # Swap direction of one line
616        line1 = [[24,0], [0,12]]
617
618        status, value = intersection(line0, line1)
619        assert status == 1
620        assert num.allclose(value, [12.0, 6.0])
621
622        # Swap order of lines
623        status, value = intersection(line1, line0)
624        assert status == 1
625        assert num.allclose(value, [12.0, 6.0])
626
627    def test_intersection3(self):
628        line0 = [[0,0], [24,12]]
629        line1 = [[0,17], [24,0]]
630
631        status, value = intersection(line0, line1)
632        assert status == 1
633        assert num.allclose(value, [14.068965517, 7.0344827586])
634
635        # Swap direction of one line
636        line1 = [[24,0], [0,17]]
637
638        status, value = intersection(line0, line1)
639        assert status == 1
640        assert num.allclose(value, [14.068965517, 7.0344827586])
641
642        # Swap order of lines
643        status, value = intersection(line1, line0)
644        assert status == 1
645        assert num.allclose(value, [14.068965517, 7.0344827586])
646
647    def test_intersection_endpoints(self):
648        """test_intersection_endpoints(self):
649
650        Test that coinciding endpoints are picked up
651        """
652
653        line0 = [[0,0], [1,1]]
654        line1 = [[1,1], [2,1]]
655
656        status, value = intersection(line0, line1)
657        assert status == 1
658        assert num.allclose(value, [1.0, 1.0])
659
660        line0 = [[1,1], [2,0]]
661        line1 = [[1,1], [2,1]]
662
663        status, value = intersection(line0, line1)
664        assert status == 1
665        assert num.allclose(value, [1.0, 1.0])
666
667    # This function is a helper function for
668    # the test_intersection_bug_20081110_?() set of tests.
669    # This function tests all parallel line cases for 4 collinear points.
670    # This function should never be run directly by the unittest code.
671    def helper_test_parallel_intersection_code(self, P1, P2, P3, P4):
672        # lines in same direction, no overlap
673        # 0:         ---->----
674        # 1:                     --------->-----------
675        line0 = [P1, P2]
676        line1 = [P3, P4]
677        status, value = intersection(line0, line1)
678        self.failIf(status!=3, 'Expected status 3, got status=%s, value=%s' %
679                               (str(status), str(value)))
680        self.failUnless(value is None, 'Expected value of None, got %s' %
681                                       str(value))
682
683        # lines in same direction, no overlap
684        # 0:         ----<----
685        # 1:                     ---------<-----------
686        line0 = [P2, P1]
687        line1 = [P4, P3]
688        status, value = intersection(line0, line1)
689        self.failIf(status!=3, 'Expected status 3, got status=%s, value=%s' %
690                               (str(status), str(value)))
691        self.failUnless(value is None, 'Expected value of None, got %s' %
692                                       str(value))
693
694        # lines in opposite direction, no overlap
695        # 0:         ----<----
696        # 1:                     --------->-----------
697        line0 = [P2, P1]
698        line1 = [P3, P4]
699        status, value = intersection(line0, line1)
700        self.failIf(status!=3, 'Expected status 3, got status=%s, value=%s' %
701                               (str(status), str(value)))
702        self.failUnless(value is None, 'Expected value of None, got %s' %
703                                       str(value))
704
705        # lines in opposite direction, no overlap
706        # 0:         ---->----
707        # 1:                     ---------<-----------
708        line0 = [P1, P2]
709        line1 = [P4, P3]
710        status, value = intersection(line0, line1)
711        self.failIf(status!=3, 'Expected status 3, got status=%s, value=%s' %
712                               (str(status), str(value)))
713        self.failUnless(value is None, 'Expected value of None, got %s' %
714                                       str(value))
715
716        # ----------------------------------------------------------------------
717
718        # line0 fully within line1, same direction
719        # 0:         ---->----
720        # 1:    --------->-----------
721        # value should be line0:
722        #            ---->----
723        line0 = [P2, P3]
724        line1 = [P1, P4]
725        status, value = intersection(line0, line1)
726        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
727                               (str(status), str(value)))
728        self.failUnless(num.allclose(value, line0))
729
730        # line0 fully within line1, same direction
731        # 0:         ----<----
732        # 1:    ---------<-----------
733        # value should be line0:
734        #            ----<----
735        line0 = [P3, P2]
736        line1 = [P4, P1]
737        status, value = intersection(line0, line1)
738        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
739                               (str(status), str(value)))
740        self.failUnless(num.allclose(value, line0))
741
742        # line0 fully within line1, opposite direction
743        # 0:         ----<----
744        # 1:    --------->-----------
745        # value should be line0:
746        #            ----<----
747        line0 = [P3, P2]
748        line1 = [P1, P4]
749        status, value = intersection(line0, line1)
750        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
751                               (str(status), str(value)))
752        self.failUnless(num.allclose(value, line0))
753
754        # line0 fully within line1, opposite direction
755        # 0:         ---->----
756        # 1:    ---------<-----------
757        # value should be line0:
758        #            ---->----
759        line0 = [P2, P3]
760        line1 = [P4, P1]
761        status, value = intersection(line0, line1)
762        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
763                               (str(status), str(value)))
764        self.failUnless(num.allclose(value, line0))
765
766        # ----------------------------------------------------------------------
767
768        # line1 fully within line0, same direction
769        # 0:    --------->-----------
770        # 1:         ---->----
771        # value should be line1:
772        #            ---->----
773        line0 = [P1, P4]
774        line1 = [P2, P3]
775        status, value = intersection(line0, line1)
776        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
777                               (str(status), str(value)))
778        self.failUnless(num.allclose(value, line1))
779
780        # line1 fully within line0, same direction
781        # 0:    ---------<-----------
782        # 1:         ----<----
783        # value should be line1:
784        #            ----<----
785        line0 = [P4, P1]
786        line1 = [P3, P2]
787        status, value = intersection(line0, line1)
788        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
789                               (str(status), str(value)))
790        self.failUnless(num.allclose(value, line1))
791
792        # line1 fully within line0, opposite direction
793        # 0:    ---------<-----------
794        # 1:         ---->----
795        # value should be line1:
796        #            ---->----
797        line0 = [P4, P1]
798        line1 = [P2, P3]
799        status, value = intersection(line0, line1)
800        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
801                               (str(status), str(value)))
802        self.failUnless(num.allclose(value, line1))
803
804        # line1 fully within line0, opposite direction
805        # 0:    --------->-----------
806        # 1:         ----<----
807        # value should be line1:
808        #            ----<----
809        line0 = [P1, P4]
810        line1 = [P3, P2]
811        status, value = intersection(line0, line1)
812        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
813                               (str(status), str(value)))
814        self.failUnless(num.allclose(value, line1))
815
816        # ----------------------------------------------------------------------
817
818        # line in same direction, partial overlap
819        # 0:    ----->-----
820        # 1:       ------->--------
821        # value should be segment line1_start->line0_end:
822        #          --->----
823        line0 = [P1, P3]
824        line1 = [P2, P4]
825        status, value = intersection(line0, line1)
826        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
827                               (str(status), str(value)))
828        self.failUnless(num.allclose(value, [line1[0],line0[1]]))
829
830        # line in same direction, partial overlap
831        # 0:    -----<-----
832        # 1:       -------<--------
833        # value should be segment line0_start->line1_end:
834        #          ---<----
835        line0 = [P3, P1]
836        line1 = [P4, P2]
837        status, value = intersection(line0, line1)
838        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
839                               (str(status), str(value)))
840        self.failUnless(num.allclose(value, [line0[0],line1[1]]))
841
842        # line in opposite direction, partial overlap
843        # 0:    -----<-----
844        # 1:       ------->--------
845        # value should be segment line0_start->line1_start:
846        #          ---<----
847        line0 = [P3, P1]
848        line1 = [P2, P4]
849        status, value = intersection(line0, line1)
850        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
851                               (str(status), str(value)))
852        self.failUnless(num.allclose(value, [line0[0],line1[0]]))
853
854        # line in opposite direction, partial overlap
855        # 0:    ----->-----
856        # 1:       -------<--------
857        # value should be segment line1_end->line0_end:
858        #          --->----
859        line0 = [P1, P3]
860        line1 = [P4, P2]
861        status, value = intersection(line0, line1)
862        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
863                               (str(status), str(value)))
864        self.failUnless(num.allclose(value, [line1[1],line0[1]]))
865
866        # ----------------------------------------------------------------------
867
868        # line in same direction, partial overlap
869        # 0:       ------>------
870        # 1:    ------>------
871        # value should be segment line0_start->line1_end:
872        #          --->----
873        line0 = [P2, P4]
874        line1 = [P1, P3]
875        status, value = intersection(line0, line1)
876        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
877                               (str(status), str(value)))
878        self.failUnless(num.allclose(value, [line0[0],line1[1]]))
879
880        # line in same direction, partial overlap
881        # 0:       ------<------
882        # 1:    ------<------
883        # value should be segment line1_start->line0_end:
884        #          ----<-----
885        line0 = [P4, P2]
886        line1 = [P3, P1]
887        status, value = intersection(line0, line1)
888        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
889                               (str(status), str(value)))
890        self.failUnless(num.allclose(value, [line1[0],line0[1]]))
891
892        # line in opposite direction, partial overlap
893        # 0:       ------<------
894        # 1:    ----->------
895        # value should be segment line1_end->line0_end:
896        #          --->----
897        line0 = [P4, P2]
898        line1 = [P1, P3]
899        status, value = intersection(line0, line1)
900        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
901                               (str(status), str(value)))
902        self.failUnless(num.allclose(value, [line1[1],line0[1]]))
903
904        # line in opposite direction, partial overlap
905        # 0:       ------>------
906        # 1:    -----<------
907        # value should be segment line0_start->line1_start:
908        #          ---<----
909        line0 = [P2, P4]
910        line1 = [P3, P1]
911        status, value = intersection(line0, line1)
912        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
913                               (str(status), str(value)))
914        self.failUnless(num.allclose(value, [line0[0],line1[0]]))
915
916        # ----------------------------------------------------------------------
917
918        # line in same direction, same left point, line1 longer
919        # 0:    ----->------
920        # 1:    ------->--------
921        # value should be line0:
922        #       ----->------
923        line0 = [P1, P3]
924        line1 = [P1, P4]
925        status, value = intersection(line0, line1)
926        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
927                               (str(status), str(value)))
928        self.failUnless(num.allclose(value, line0))
929
930        # line in same direction, same left point, line1 longer
931        # 0:    -----<------
932        # 1:    -------<--------
933        # value should be line0:
934        #       -----<------
935        line0 = [P3, P1]
936        line1 = [P4, P1]
937        status, value = intersection(line0, line1)
938        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
939                               (str(status), str(value)))
940        self.failUnless(num.allclose(value, line0))
941
942        # line in opposite direction, same left point, line1 longer
943        # 0:    ----->------
944        # 1:    -------<--------
945        # value should be line0:
946        #       ----->------
947        line0 = [P1, P3]
948        line1 = [P4, P1]
949        status, value = intersection(line0, line1)
950        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
951                               (str(status), str(value)))
952        self.failUnless(num.allclose(value, line0))
953
954        # line in opposite direction, same start point, line1 longer
955        # 0:    -----<------
956        # 1:    ------->--------
957        # value should be line0:
958        #       -----<------
959        line0 = [P3, P1]
960        line1 = [P1, P4]
961        status, value = intersection(line0, line1)
962        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
963                               (str(status), str(value)))
964        self.failUnless(num.allclose(value, line0))
965
966        # ----------------------------------------------------------------------
967
968        # line in same direction, same left point, same right point
969        # 0:    ------->--------
970        # 1:    ------->--------
971        # value should be line0 or line1:
972        #       ------->--------
973        line0 = [P1, P3]
974        line1 = [P1, P3]
975        status, value = intersection(line0, line1)
976        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
977                               (str(status), str(value)))
978        self.failUnless(num.allclose(value, line0))
979
980        # line in same direction, same left point, same right point
981        # 0:    -------<--------
982        # 1:    -------<--------
983        # value should be line0 (or line1):
984        #       -------<--------
985        line0 = [P3, P1]
986        line1 = [P3, P1]
987        status, value = intersection(line0, line1)
988        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
989                               (str(status), str(value)))
990        self.failUnless(num.allclose(value, line0))
991
992        # line in opposite direction, same left point, same right point
993        # 0:    ------->--------
994        # 1:    -------<--------
995        # value should be line0:
996        #       ------->--------
997        line0 = [P1, P3]
998        line1 = [P3, P1]
999        status, value = intersection(line0, line1)
1000        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1001                               (str(status), str(value)))
1002        self.failUnless(num.allclose(value, line0))
1003
1004        # line in opposite direction, same left point, same right point
1005        # 0:    -------<--------
1006        # 1:    ------->--------
1007        # value should be line0:
1008        #       -------<--------
1009        line0 = [P3, P1]
1010        line1 = [P1, P3]
1011        status, value = intersection(line0, line1)
1012        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1013                               (str(status), str(value)))
1014        self.failUnless(num.allclose(value, line0))
1015
1016        # ----------------------------------------------------------------------
1017
1018        # line in same direction, same right point, line1 longer
1019        # 0:        ----->------
1020        # 1:    ------->--------
1021        # value should be line0:
1022        #           ----->------
1023        line0 = [P2, P4]
1024        line1 = [P1, P4]
1025        status, value = intersection(line0, line1)
1026        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1027                               (str(status), str(value)))
1028        self.failUnless(num.allclose(value, line0))
1029
1030        # line in same direction, same right point, line1 longer
1031        # 0:        -----<------
1032        # 1:    -------<--------
1033        # value should be line0:
1034        #           -----<------
1035        line0 = [P4, P2]
1036        line1 = [P4, P1]
1037        status, value = intersection(line0, line1)
1038        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1039                               (str(status), str(value)))
1040        self.failUnless(num.allclose(value, line0))
1041
1042        # line in opposite direction, same right point, line1 longer
1043        # 0:        ----->------
1044        # 1:    -------<--------
1045        # value should be line0:
1046        #           ----->------
1047        line0 = [P2, P4]
1048        line1 = [P4, P1]
1049        status, value = intersection(line0, line1)
1050        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1051                               (str(status), str(value)))
1052        self.failUnless(num.allclose(value, line0))
1053
1054        # line in opposite direction, same right point, line1 longer
1055        # 0:        -----<------
1056        # 1:    ------->--------
1057        # value should be line0:
1058        #           -----<------
1059        line0 = [P4, P2]
1060        line1 = [P1, P4]
1061        status, value = intersection(line0, line1)
1062        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1063                               (str(status), str(value)))
1064        self.failUnless(num.allclose(value, line0))
1065
1066        # ----------------------------------------------------------------------
1067
1068        # line in same direction, same left point, line0 longer
1069        # 0:    ------->--------
1070        # 1:    ----->------
1071        # value should be line1:
1072        #       ----->------
1073        line0 = [P1, P4]
1074        line1 = [P1, P3]
1075        status, value = intersection(line0, line1)
1076        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1077                               (str(status), str(value)))
1078        self.failUnless(num.allclose(value, line1))
1079
1080        # line in same direction, same left point, line0 longer
1081        # 0:    -------<--------
1082        # 1:    -----<------
1083        # value should be line1:
1084        #       -----<------
1085        line0 = [P4, P1]
1086        line1 = [P3, P1]
1087        status, value = intersection(line0, line1)
1088        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1089                               (str(status), str(value)))
1090        self.failUnless(num.allclose(value, line1))
1091
1092        # line in opposite direction, same left point, line0 longer
1093        # 0:    ------->--------
1094        # 1:    -----<------
1095        # value should be line1:
1096        #       -----<------
1097        line0 = [P1, P4]
1098        line1 = [P3, P1]
1099        status, value = intersection(line0, line1)
1100        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1101                               (str(status), str(value)))
1102        self.failUnless(num.allclose(value, line1))
1103
1104        # line in opposite direction, same left point, line0 longer
1105        # 0:    -------<--------
1106        # 1:    ----->------
1107        # value should be line1:
1108        #       ----->------
1109        line0 = [P4, P1]
1110        line1 = [P1, P3]
1111        status, value = intersection(line0, line1)
1112        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1113                               (str(status), str(value)))
1114        self.failUnless(num.allclose(value, line1))
1115
1116        # ----------------------------------------------------------------------
1117
1118        # line in same direction, same right point, line0 longer
1119        # 0:    ------->--------
1120        # 1:        ----->------
1121        # value should be line1:
1122        #           ----->------
1123        line0 = [P1, P4]
1124        line1 = [P2, P4]
1125        status, value = intersection(line0, line1)
1126        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1127                               (str(status), str(value)))
1128        self.failUnless(num.allclose(value, line1))
1129
1130        # line in same direction, same right point, line0 longer
1131        # 0:    -------<--------
1132        # 1:        -----<------
1133        # value should be line1:
1134        #           -----<------
1135        line0 = [P4, P1]
1136        line1 = [P4, P2]
1137        status, value = intersection(line0, line1)
1138        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1139                               (str(status), str(value)))
1140        self.failUnless(num.allclose(value, line1))
1141
1142        # line in opposite direction, same right point, line0 longer
1143        # 0:    ------->--------
1144        # 1:        -----<------
1145        # value should be line1:
1146        #           -----<------
1147        line0 = [P1, P4]
1148        line1 = [P4, P2]
1149        status, value = intersection(line0, line1)
1150        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1151                               (str(status), str(value)))
1152        self.failUnless(num.allclose(value, line1))
1153
1154        # line in opposite direction, same right point, line0 longer
1155        # 0:    -------<--------
1156        # 1:        ----->------
1157        # value should be line1:
1158        #           ----->------
1159        line0 = [P4, P1]
1160        line1 = [P2, P4]
1161        status, value = intersection(line0, line1)
1162        self.failIf(status!=2, 'Expected status 2, got status=%s, value=%s' %
1163                               (str(status), str(value)))
1164        self.failUnless(num.allclose(value, line1))
1165
1166
1167    def test_intersection_bug_20081110_TR(self):
1168        """test_intersection_bug_20081110(self)
1169
1170        Test all cases in top-right quadrant
1171        """
1172
1173        # define 4 collinear points in top-right quadrant
1174        #    P1---P2---P3---P4
1175        P1 = [1.0, 1.0]
1176        P2 = [2.0, 2.0]
1177        P3 = [3.0, 3.0]
1178        P4 = [4.0, 4.0]
1179
1180        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1181        P1 = [1.0, 1.0+1.0e-9]
1182        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1183        P1 = [1.0, 1.0]
1184        P2 = [2.0, 2.0+1.0e-9]
1185        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1186        P2 = [2.0, 2.0]
1187        P3 = [3.0, 3.0+1.0e-9]
1188        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1189        P3 = [3.0, 3.0]
1190        P4 = [4.0, 4.0+1.0e-9]
1191        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1192
1193    def test_intersection_bug_20081110_TL(self):
1194        """test_intersection_bug_20081110(self)
1195
1196        Test all cases in top-left quadrant
1197        """
1198
1199        # define 4 collinear points in top-left quadrant
1200        #    P1---P2---P3---P4
1201        P1 = [-1.0, 1.0]
1202        P2 = [-2.0, 2.0]
1203        P3 = [-3.0, 3.0]
1204        P4 = [-4.0, 4.0]
1205
1206        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1207        P1 = [-1.0, 1.0+1.0e-9]
1208        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1209        P1 = [-1.0, 1.0]
1210        P2 = [-2.0, 2.0+1.0e-9]
1211        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1212        P2 = [-2.0, 2.0]
1213        P3 = [-3.0, 3.0+1.0e-9]
1214        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1215        P3 = [-3.0, 3.0]
1216        P4 = [-4.0, 4.0+1.0e-9]
1217        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1218
1219    def test_intersection_bug_20081110_BL(self):
1220        """test_intersection_bug_20081110(self)
1221
1222        Test all cases in bottom-left quadrant
1223        """
1224
1225        # define 4 collinear points in bottom-left quadrant
1226        #    P1---P2---P3---P4
1227        P1 = [-1.0, -1.0]
1228        P2 = [-2.0, -2.0]
1229        P3 = [-3.0, -3.0]
1230        P4 = [-4.0, -4.0]
1231
1232        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1233        P1 = [-1.0, -1.0+1.0e-9]
1234        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1235        P1 = [-1.0, -1.0]
1236        P2 = [-2.0, -2.0+1.0e-9]
1237        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1238        P2 = [-2.0, -2.0]
1239        P3 = [-3.0, -3.0+1.0e-9]
1240        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1241        P3 = [-3.0, -3.0]
1242        P4 = [-4.0, -4.0+1.0e-9]
1243        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1244
1245    def test_intersection_bug_20081110_BR(self):
1246        """test_intersection_bug_20081110(self)
1247
1248        Test all cases in bottom-right quadrant
1249        """
1250
1251        # define 4 collinear points in bottom-right quadrant
1252        #    P1---P2---P3---P4
1253        P1 = [1.0, -1.0]
1254        P2 = [2.0, -2.0]
1255        P3 = [3.0, -3.0]
1256        P4 = [4.0, -4.0]
1257
1258        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1259        P1 = [1.0, -1.0+1.0e-9]
1260        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1261        P1 = [1.0, -1.0]
1262        P2 = [2.0, -2.0+1.0e-9]
1263        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1264        P2 = [2.0, -2.0]
1265        P3 = [3.0, -3.0+1.0e-9]
1266        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1267        P3 = [3.0, -3.0]
1268        P4 = [4.0, -4.0+1.0e-9]
1269        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1270
1271    def test_intersection_bug_20081110_TR_TL(self):
1272        """test_intersection_bug_20081110(self)
1273
1274        Test all cases in top-right & top-left quadrant
1275        """
1276
1277        # define 4 collinear points, 1 in TL, 3 in TR
1278        #    P1-+-P2---P3---P4
1279        P1 = [-3.0, 1.0]
1280        P2 = [ 1.0, 5.0]
1281        P3 = [ 2.0, 6.0]
1282        P4 = [ 3.0, 7.0]
1283        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1284
1285        # define 4 collinear points, 2 in TL, 2 in TR
1286        #    P1---P2-+-P3---P4
1287        P1 = [-3.0, 1.0]
1288        P2 = [-2.0, 2.0]
1289        P3 = [ 2.0, 6.0]
1290        P4 = [ 3.0, 7.0]
1291        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1292
1293        # define 4 collinear points, 3 in TL, 1 in TR
1294        #    P1---P2---P3-+-P4
1295        P1 = [-3.0, 1.0]
1296        P2 = [-2.0, 2.0]
1297        P3 = [-1.0, 3.0]
1298        P4 = [ 3.0, 7.0]
1299        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1300
1301    def test_intersection_bug_20081110_TR_BL(self):
1302        """test_intersection_bug_20081110(self)
1303
1304        Test all cases in top-right & bottom-left quadrant
1305        """
1306
1307        # define 4 collinear points, 1 in BL, 3 in TR
1308        #    P1-+-P2---P3---P4
1309        P1 = [-4.0, -3.0]
1310        P2 = [ 1.0,  2.0]
1311        P3 = [ 2.0,  3.0]
1312        P4 = [ 3.0,  4.0]
1313        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1314
1315        # define 4 collinear points, 2 in TL, 2 in TR
1316        #    P1---P2-+-P3---P4
1317        P1 = [-4.0, -3.0]
1318        P2 = [-3.0, -2.0]
1319        P3 = [ 2.0,  3.0]
1320        P4 = [ 3.0,  4.0]
1321        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1322
1323        # define 4 collinear points, 3 in TL, 1 in TR
1324        #    P1---P2---P3-+-P4
1325        P1 = [-4.0, -3.0]
1326        P2 = [-3.0, -2.0]
1327        P3 = [-2.0, -1.0]
1328        P4 = [ 3.0,  4.0]
1329        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1330
1331    def test_intersection_bug_20081110_TR_BR(self):
1332        """test_intersection_bug_20081110(self)
1333
1334        Test all cases in top-right & bottom-right quadrant
1335        """
1336
1337        # define 4 collinear points, 1 in BR, 3 in TR
1338        #    P1-+-P2---P3---P4
1339        P1 = [ 1.0, -3.0]
1340        P2 = [ 5.0,  1.0]
1341        P3 = [ 6.0,  2.0]
1342        P4 = [ 7.0,  3.0]
1343        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1344
1345        # define 4 collinear points, 2 in BR, 2 in TR
1346        #    P1---P2-+-P3---P4
1347        P1 = [ 1.0, -3.0]
1348        P2 = [ 2.0, -2.0]
1349        P3 = [ 6.0,  2.0]
1350        P4 = [ 7.0,  3.0]
1351        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1352
1353        # define 4 collinear points, 3 in BR, 1 in TR
1354        #    P1---P2---P3-+-P4
1355        P1 = [ 1.0, -3.0]
1356        P2 = [ 2.0, -2.0]
1357        P3 = [ 3.0, -1.0]
1358        P4 = [ 7.0,  3.0]
1359        self.helper_test_parallel_intersection_code(P1, P2, P3, P4)
1360
1361
1362    def test_intersection_direction_invariance(self):
1363        """This runs through a number of examples and checks that
1364        direction of lines don't matter.
1365        """
1366
1367        line0 = [[0,0], [100,100]]
1368
1369        common_end_point = [20, 150]
1370
1371        for i in range(100):
1372            x = 20 + i * 1.0/100
1373
1374            line1 = [[x,0], common_end_point]
1375            status, p1 = intersection(line0, line1)
1376            assert status == 1
1377
1378            # Swap direction of line1
1379            line1 = [common_end_point, [x,0]]
1380            status, p2 = intersection(line0, line1)
1381            assert status == 1
1382
1383            msg = ('Orientation of line should not matter.\n'
1384                   'However, segment [%f,%f], [%f, %f]' %
1385                   (x, 0, common_end_point[0], common_end_point[1]))
1386            msg += ' gave %s, \nbut when reversed we got %s' % (p1, p2)
1387            assert num.allclose(p1, p2), msg
1388
1389            # Swap order of lines
1390            status, p3 = intersection(line1, line0)
1391            assert status == 1
1392            msg = 'Order of lines gave different results'
1393            assert num.allclose(p1, p3), msg
1394
1395    def test_no_intersection(self):
1396        line0 = [[-1,1], [1,1]]
1397        line1 = [[0,-1], [0,0]]
1398
1399        status, value = intersection(line0, line1)
1400        assert status == 0
1401        assert value is None
1402
1403    def test_intersection_parallel(self):
1404        line0 = [[-1,1], [1,1]]
1405        line1 = [[-1,0], [5,0]]
1406
1407        status, value = intersection(line0, line1)
1408        assert status == 4
1409        assert value is None
1410
1411        line0 = [[0,0], [10,100]]
1412        line1 = [[-10,5], [0,105]]
1413
1414        status, value = intersection(line0, line1)
1415        assert status == 4
1416        assert value is None
1417
1418    def test_intersection_coincide(self):
1419        """Test what happens when two lines partly coincide"""
1420
1421        # Overlap 1
1422        line0 = [[0,0], [5,0]]
1423        line1 = [[-3,0], [3,0]]
1424
1425        status, value = intersection(line0, line1)
1426        assert status == 2
1427        assert num.allclose(value, [[0,0], [3,0]])
1428
1429        # Overlap 2
1430        line0 = [[-10,0], [5,0]]
1431        line1 = [[-3,0], [10,0]]
1432
1433        status, value = intersection(line0, line1)
1434        assert status == 2
1435        assert num.allclose(value, [[-3, 0], [5,0]])
1436
1437        # Inclusion 1
1438        line0 = [[0,0], [5,0]]
1439        line1 = [[2,0], [3,0]]
1440
1441        status, value = intersection(line0, line1)
1442        assert status == 2
1443        assert num.allclose(value, line1)
1444
1445        # Inclusion 2
1446        line0 = [[1,0], [5,0]]
1447        line1 = [[-10,0], [15,0]]
1448
1449        status, value = intersection(line0, line1)
1450        assert status == 2
1451        assert num.allclose(value, line0)
1452
1453        # Exclusion (no intersection)
1454        line0 = [[-10,0], [1,0]]
1455        line1 = [[3,0], [15,0]]
1456
1457        status, value = intersection(line0, line1)
1458        assert status == 3
1459        assert value is None
1460
1461        # Try examples with some slope (y=2*x+5)
1462
1463        # Overlap
1464        line0 = [[0,5], [7,19]]
1465        line1 = [[1,7], [10,25]]
1466        status, value = intersection(line0, line1)
1467        assert status == 2
1468        assert num.allclose(value, [[1, 7], [7, 19]])
1469
1470        status, value = intersection(line1, line0)
1471        assert status == 2
1472        assert num.allclose(value, [[1, 7], [7, 19]])
1473
1474        # Swap direction
1475        line0 = [[7,19], [0,5]]
1476        line1 = [[1,7], [10,25]]
1477        status, value = intersection(line0, line1)
1478        assert status == 2
1479        assert num.allclose(value, [[7, 19], [1, 7]])
1480
1481        line0 = [[0,5], [7,19]]
1482        line1 = [[10,25], [1,7]]
1483        status, value = intersection(line0, line1)
1484        assert status == 2
1485        assert num.allclose(value, [[1, 7], [7, 19]])
1486
1487        # Inclusion
1488        line0 = [[1,7], [7,19]]
1489        line1 = [[0,5], [10,25]]
1490        status, value = intersection(line0, line1)
1491        assert status == 2
1492        assert num.allclose(value, [[1,7], [7, 19]])
1493
1494        line0 = [[0,5], [10,25]]
1495        line1 = [[1,7], [7,19]]
1496        status, value = intersection(line0, line1)
1497        assert status == 2
1498        assert num.allclose(value, [[1,7], [7, 19]])
1499
1500        line0 = [[0,5], [10,25]]
1501        line1 = [[7,19], [1,7]]
1502        status, value = intersection(line0, line1)
1503        assert status == 2
1504        assert num.allclose(value, [[7, 19], [1, 7]])
1505
1506
1507    def NOtest_inside_polygon_main(self):
1508        # FIXME (Ole): Why is this disabled?
1509        #print "inside",inside
1510        #print "outside",outside
1511
1512        assert not inside_polygon((0.5, 1.5), polygon)
1513        assert not inside_polygon((0.5, -0.5), polygon)
1514        assert not inside_polygon((-0.5, 0.5), polygon)
1515        assert not inside_polygon((1.5, 0.5), polygon)
1516
1517        # Try point on borders
1518        assert inside_polygon((1., 0.5), polygon, closed=True)
1519        assert inside_polygon((0.5, 1), polygon, closed=True)
1520        assert inside_polygon((0., 0.5), polygon, closed=True)
1521        assert inside_polygon((0.5, 0.), polygon, closed=True)
1522
1523        assert not inside_polygon((0.5, 1), polygon, closed=False)
1524        assert not inside_polygon((0., 0.5), polygon, closed=False)
1525        assert not inside_polygon((0.5, 0.), polygon, closed=False)
1526        assert not inside_polygon((1., 0.5), polygon, closed=False)
1527
1528        # From real example (that failed)
1529        polygon = [[20,20], [40,20], [40,40], [20,40]]
1530        points = [[40, 50]]
1531        res = inside_polygon(points, polygon)
1532        assert len(res) == 0
1533
1534        polygon = [[20,20], [40,20], [40,40], [20,40]]
1535        points = [[25, 25], [30, 20], [40, 50], [90, 20], [40, 90]]
1536        res = inside_polygon(points, polygon)
1537        assert len(res) == 2
1538        assert num.allclose(res, [0,1])
1539
1540    def test_polygon_area(self):
1541        # Simplest case: Polygon is the unit square
1542        polygon = [[0,0], [1,0], [1,1], [0,1]]
1543        assert polygon_area(polygon) == 1
1544
1545        # Simple case: Polygon is a rectangle
1546        polygon = [[0,0], [1,0], [1,4], [0,4]]
1547        assert polygon_area(polygon) == 4
1548
1549        # Simple case: Polygon is a unit triangle
1550        polygon = [[0,0], [1,0], [0,1]]
1551        assert polygon_area(polygon) == 0.5
1552
1553        # Simple case: Polygon is a diamond
1554        polygon = [[0,0], [1,1], [2,0], [1, -1]]
1555        assert polygon_area(polygon) == 2.0
1556
1557        # Complex case where numerical errors might occur
1558        polygon = [[314037.58727982, 6224952.2960092],
1559                   [314038.58727982, 6224952.2960092],
1560                   [314038.58727982, 6224953.2960092],
1561                   [314037.58727982, 6224953.2960092]]
1562        assert polygon_area(polygon) == 1.0
1563
1564    def test_poly_xy(self):
1565        # Simplest case: Polygon is the unit square
1566        polygon = [[0,0], [1,0], [1,1], [0,1]]
1567        x, y = poly_xy(polygon)
1568        assert len(x) == len(polygon)+1
1569        assert len(y) == len(polygon)+1
1570        assert x[0] == 0
1571        assert x[1] == 1
1572        assert x[2] == 1
1573        assert x[3] == 0
1574        assert y[0] == 0
1575        assert y[1] == 0
1576        assert y[2] == 1
1577        assert y[3] == 1
1578
1579    # Arbitrary polygon
1580        polygon = [[1,5], [1,1], [100,10], [1,10], [3,6]]
1581        x, y = poly_xy(polygon)
1582        assert len(x) == len(polygon)+1
1583        assert len(y) == len(polygon)+1
1584        assert x[0] == 1
1585        assert x[1] == 1
1586        assert x[2] == 100
1587        assert x[3] == 1
1588        assert x[4] == 3
1589        assert y[0] == 5
1590        assert y[1] == 1
1591        assert y[2] == 10
1592        assert y[3] == 10
1593        assert y[4] == 6
1594
1595    # Disabled
1596    def xtest_plot_polygons(self):
1597        import os
1598
1599        # Simplest case: Polygon is the unit square
1600        polygon1 = [[0,0], [1,0], [1,1], [0,1]]
1601        polygon2 = [[1,1], [2,1], [3,2], [2,2]]
1602        v = plot_polygons([polygon1, polygon2], 'test1')
1603        assert len(v) == 4
1604        assert v[0] == 0
1605        assert v[1] == 3
1606        assert v[2] == 0
1607        assert v[3] == 2
1608
1609        # Another case
1610        polygon3 = [[1,5], [10,1], [100,10], [50,10], [3,6]]
1611        v = plot_polygons([polygon2,polygon3], 'test2')
1612        assert len(v) == 4
1613        assert v[0] == 1
1614        assert v[1] == 100
1615        assert v[2] == 1
1616        assert v[3] == 10
1617
1618        os.remove('test1.png')
1619        os.remove('test2.png')
1620
1621    def test_inside_polygon_geospatial(self):
1622        #Simplest case: Polygon is the unit square
1623        polygon_absolute = [[0,0], [1,0], [1,1], [0,1]]
1624        poly_geo_ref = Geo_reference(57, 100, 100)
1625        polygon = poly_geo_ref.change_points_geo_ref(polygon_absolute)
1626        poly_spatial = Geospatial_data(polygon, geo_reference=poly_geo_ref)
1627
1628        points_absolute = (0.5, 0.5)
1629        points_geo_ref = Geo_reference(57, 78, -56)
1630        points = points_geo_ref.change_points_geo_ref(points_absolute)
1631        points_spatial = Geospatial_data(points, geo_reference=points_geo_ref)
1632
1633        assert is_inside_polygon(points_absolute, polygon_absolute)
1634        assert is_inside_polygon(ensure_numeric(points_absolute),
1635                                 ensure_numeric(polygon_absolute))
1636        assert is_inside_polygon(points_absolute, poly_spatial)
1637        assert is_inside_polygon(points_spatial, poly_spatial)
1638        assert is_inside_polygon(points_spatial, polygon_absolute)
1639
1640        assert is_inside_polygon(points_absolute, polygon_absolute)
1641
1642    def NOtest_decimate_polygon(self):
1643        polygon = [[0,0], [10,10], [15,5], [20, 10],
1644                   [25,0], [30,10], [40,-10], [35, -5]]
1645
1646        dpoly = decimate_polygon(polygon, factor=2)
1647
1648        print dpoly
1649
1650        assert len(dpoly)*2==len(polygon)
1651
1652        minx = maxx = polygon[0][0]
1653        miny = maxy = polygon[0][1]
1654        for point in polygon[1:]:
1655            x, y = point
1656
1657            if x < minx: minx = x
1658            if x > maxx: maxx = x
1659            if y < miny: miny = y
1660            if y > maxy: maxy = y
1661
1662        assert [minx, miny] in polygon
1663        print minx, maxy
1664        assert [minx, maxy] in polygon
1665        assert [maxx, miny] in polygon
1666        assert [maxx, maxy] in polygon
1667
1668    def test_interpolate_polyline(self):
1669        """test_interpolate_polyline(self):
1670
1671        This test is added under the assumption that the function
1672        interpolate_polyline implemented by John Jakeman works.
1673        It has been exercised somewhat by tests of sts boundary,
1674        but never before separately.
1675        """
1676
1677        f = num.array([58.06150614, 58.06150614, 58.06150614])
1678        vertex_coordinates = num.array([[0., 0., ],
1679                                        [4.04092634, 1106.11074699],
1680                                        [8.08836552, 2212.16910609]])
1681        gauge_neighbour_id = [1, 2, -1]
1682        point_coordinates = num.array([[2.21870766e+03, 1.09802864e+03],
1683                                       [1.62739645e+03, 2.20626983e+03],
1684                                       [5.20084967e+02, 2.21030386e+03],
1685                                       [6.06464546e+00, 1.65913993e+03],
1686                                       [1.61934862e+03, -5.88143836e+00],
1687                                       [5.11996623e+02, -1.85956061e+00],
1688                                       [2.02046270e+00, 5.53055373e+02]])
1689
1690        z_ref = [0., 0., 0., 58.06150614, 0., 0., 58.06150614]
1691
1692        z = interpolate_polyline(f, vertex_coordinates,
1693                                 gauge_neighbour_id, point_coordinates)
1694        assert num.allclose(z, z_ref)
1695
1696        # Another f
1697        f = num.array([58.06150614, 158.06150614, 258.06150614])
1698        z_ref = [0., 0., 0., 208.06150645, 0., 0., 108.0615061]
1699        z = interpolate_polyline(f, vertex_coordinates,
1700                                 gauge_neighbour_id, point_coordinates)
1701        assert num.allclose(z, z_ref)
1702
1703        # Other and simpler numbers
1704        f = num.array([1, 5, 13])
1705        vertex_coordinates = num.array([[0., 0., ],
1706                                        [4., 4.],
1707                                        [8., 8.]])
1708        point_coordinates = num.array([[0.1, 0.1],
1709                                       [3.5, 3.5],
1710                                       [4.0, 4.0],
1711                                       [5.2, 5.2],
1712                                       [7.0, 7.0],
1713                                       [8.3, 8.3]])
1714        gauge_neighbour_id = [1, 2, -1]
1715
1716        z = interpolate_polyline(f, vertex_coordinates,
1717                                 gauge_neighbour_id, point_coordinates)
1718        z_ref = [1.1, 4.5, 5., 7.4, 11., 0.]
1719        assert num.allclose(z, z_ref)
1720
1721        # Test exception thrown for one point
1722        f = num.array([5])
1723        vertex_coordinates = num.array([[4., 4.]])
1724        try:
1725            z = interpolate_polyline(f, vertex_coordinates,
1726                                     gauge_neighbour_id, point_coordinates)
1727        except Exception:
1728            pass
1729        else:
1730            raise Exception, 'One point should have raised exception'
1731
1732        # More polyline nodes
1733        data = num.array([1, 5, 13, 12, 6, 29])
1734        polyline_nodes = num.array([[0., 0.],
1735                                    [4., 4.],
1736                                    [8., 8.],
1737                                    [10., 10.],
1738                                    [10., 5.],
1739                                    [10., 0.]])
1740        point_coordinates = num.array([[0.1, 0.1],
1741                                       [3.5, 3.5],
1742                                       [4.0, 4.0],
1743                                       [5.2, 5.2],
1744                                       [7.0, 7.0],
1745                                       [8.3, 8.3],
1746                                       [10., 10.],
1747                                       [10., 9.],
1748                                       [10., 7.1],
1749                                       [10., 4.3],
1750                                       [10., 1.0]])
1751        gauge_neighbour_id = [1, 2, 3, 4, 5, -1]
1752        z = interpolate_polyline(data, polyline_nodes,
1753                                 gauge_neighbour_id, point_coordinates)
1754        z_ref = [1.1, 4.5, 5., 7.4, 11., 12.85, 12., 10.8, 8.52, 9.22, 24.4]
1755        assert num.allclose(z, z_ref)
1756
1757
1758    def test_is_inside_triangle_more(self):
1759
1760        res = is_inside_triangle([0.5, 0.5], [[ 0.5,  0. ],
1761                                              [ 0.5,  0.5],
1762                                              [ 0.,   0. ]])
1763        assert res is True
1764
1765        res = is_inside_triangle([0.59999999999999998, 0.29999999999999999],
1766                                 [[ 0.5,  0. ], [ 0.5, 0.5], [0., 0.]])
1767        assert res is False
1768
1769        res = is_inside_triangle([0.59999999999999998, 0.29999999999999999],
1770                                 [[1., 0.], [1., 0.5], [0.5, 0.]])
1771        assert res is False
1772
1773
1774        res = is_inside_triangle([0.59999999999999998, 0.29999999999999999],
1775                                 [[0.5, 0.5], [0.5, 0.], [1., 0.5]])
1776        assert res is True
1777
1778
1779        res = is_inside_triangle([0.10000000000000001, 0.20000000000000001],
1780                                 [[0.5, 0.], [0.5, 0.5], [0., 0.]])
1781        assert res is False
1782
1783
1784        res = is_inside_triangle([0.10000000000000001, 0.20000000000000001],
1785                                 [[0., 0.5], [0., 0.], [0.5, 0.5]])
1786        assert res is True
1787
1788        res = is_inside_triangle([0.69999999999999996, 0.69999999999999996],
1789                                 [[0.5, 0.], [0.5, 0.5], [0., 0.]])
1790        assert res is False
1791
1792        res = is_inside_triangle([0.59999999999999998, 0.29999999999999999],
1793                                 [[0.25, 0.5], [0.25, 0.25], [0.5, 0.5]])
1794        assert res is False
1795
1796
1797        res = is_inside_triangle([10, 3],
1798                                 [[0.1, 0.],
1799                                  [0.1, 0.08333333],
1800                                  [0., 0.]])
1801        assert res is False
1802
1803################################################################################
1804
1805if __name__ == "__main__":
1806    suite = unittest.makeSuite(Test_Polygon,'test')
1807    runner = unittest.TextTestRunner()
1808    runner.run(suite)
Note: See TracBrowser for help on using the repository browser.