source: inundation/ga/storm_surge/pyvolution/test_util.py @ 1027

Last change on this file since 1027 was 1024, checked in by ole, 20 years ago

Added function outside_polygon and a test

File size: 30.0 KB
Line 
1#!/usr/bin/env python
2
3
4import unittest
5from Numeric import zeros, array, allclose
6from math import sqrt, pi
7
8from util import *
9from config import epsilon
10
11
12def test_function(x, y):
13    return x+y
14
15class Test_Util(unittest.TestCase):
16    def setUp(self):
17        pass
18
19    def tearDown(self):
20        pass
21
22
23    def test_gradient(self):
24        x0 = 0.0; y0 = 0.0; z0 = 0.0
25        x1 = 1.0; y1 = 0.0; z1 = -1.0
26        x2 = 0.0; y2 = 1.0; z2 = 0.0
27
28        zx, zy = gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2)
29
30        assert zx == -1.0
31        assert zy == 0.0
32
33
34
35
36    def test_gradient2(self):
37
38        from util import gradient
39        x0 = 2.0/3; y0 = 2.0/3
40        x1=  8.0/3; y1 = 2.0/3
41        x2 = 2.0/3; y2 = 8.0/3
42
43        q0 = 2.0+2.0/3
44        q1 = 8.0+2.0/3
45        q2 = 2.0+8.0/3
46
47        #Gradient of fitted pwl surface
48        a, b = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
49
50        assert abs(a - 3.0) < epsilon
51        assert abs(b - 1.0) < epsilon
52
53
54
55    def test_that_C_extension_compiles(self):
56        FN = 'util_ext.c'
57        try:
58            import util_ext
59        except:
60            from compile import compile
61
62            try:
63                compile(FN)
64            except:
65                raise 'Could not compile %s' %FN
66            else:
67                import util_ext
68
69
70    def test_gradient_C_extension(self):
71        from util_ext import gradient as gradient_c
72
73        x0 = 2.0/3; y0 = 2.0/3
74        x1=  8.0/3; y1 = 2.0/3
75        x2 = 2.0/3; y2 = 8.0/3
76
77        q0 = 2.0+2.0/3
78        q1 = 8.0+2.0/3
79        q2 = 2.0+8.0/3
80
81        #Gradient of fitted pwl surface
82        a, b = gradient_c(x0, y0, x1, y1, x2, y2, q0, q1, q2)
83
84        assert abs(a - 3.0) < epsilon
85        assert abs(b - 1.0) < epsilon
86
87
88    def test_gradient_C_extension3(self):
89        from util_ext import gradient as gradient_c
90
91        from RandomArray import uniform, seed
92        seed(17, 53)
93
94        x0, x1, x2, y0, y1, y2 = uniform(0.0,3.0,6)
95
96        q0 = uniform(0.0, 10.0, 4)
97        q1 = uniform(1.0, 3.0, 4)
98        q2 = uniform(7.0, 20.0, 4)
99
100
101        for i in range(4):
102            #Gradient of fitted pwl surface
103            from util import gradient_python
104            a_ref, b_ref = gradient(x0, y0, x1, y1, x2, y2,
105                                    q0[i], q1[i], q2[i])
106
107            #print a_ref, b_ref
108            a, b = gradient_c(x0, y0, x1, y1, x2, y2,
109                              q0[i], q1[i], q2[i])
110
111            #print a, a_ref, b, b_ref
112            assert abs(a - a_ref) < epsilon
113            assert abs(b - b_ref) < epsilon
114
115
116
117    #Geometric
118    #def test_distance(self):
119    #    from util import distance#
120    #
121    #    self.failUnless( distance([4,2],[7,6]) == 5.0,
122    #                     'Distance is wrong!')
123    #    self.failUnless( allclose(distance([7,6],[9,8]), 2.82842712475),
124    #                    'distance is wrong!')
125    #    self.failUnless( allclose(distance([9,8],[4,2]), 7.81024967591),
126    #                    'distance is wrong!')
127    #
128    #    self.failUnless( distance([9,8],[4,2]) == distance([4,2],[9,8]),
129    #                    'distance is wrong!')
130
131    def test_angle(self):
132        from util import angle
133
134        assert allclose(angle([1.0, 1.0])/pi*180, 45.0)
135
136
137    def test_anglediff(self):
138        from util import anglediff
139
140        assert allclose(anglediff([0.0, 1.], [1.0, 1.0])/pi*180, 45.0)
141
142
143
144
145    def test_file_function_time(self):
146        """Test that File function interpolates correctly
147        between given times. No x,y dependency here.
148        """
149
150        #Write file
151        import os, time
152        from config import time_format
153        from math import sin, pi
154
155        finaltime = 1200
156        filename = 'test_file_function.txt'
157        fid = open(filename, 'w')
158        start = time.mktime(time.strptime('2000', '%Y'))
159        dt = 60  #One minute intervals
160        t = 0.0
161        while t <= finaltime:
162            t_string = time.strftime(time_format, time.gmtime(t+start))
163            fid.write('%s, %f %f %f\n' %(t_string, 2*t, t**2, sin(t*pi/600)))
164            t += dt
165
166        fid.close()
167
168        F = file_function(filename)
169
170        #Now try interpolation
171        for i in range(20):
172            t = i*10
173            q = F(t)
174
175            #Exact linear intpolation
176            assert allclose(q[0], 2*t)
177            if i%6 == 0:
178                assert allclose(q[1], t**2)
179                assert allclose(q[2], sin(t*pi/600))
180
181        #Check non-exact
182
183        t = 90 #Halfway between 60 and 120
184        q = F(t)
185        assert allclose( (120**2 + 60**2)/2, q[1] )
186        assert allclose( (sin(120*pi/600) + sin(60*pi/600))/2, q[2] )
187
188
189        t = 100 #Two thirds of the way between between 60 and 120
190        q = F(t)
191        assert allclose( 2*120**2/3 + 60**2/3, q[1] )
192        assert allclose( 2*sin(120*pi/600)/3 + sin(60*pi/600)/3, q[2] )
193
194        os.remove(filename)
195
196
197
198
199    def test_spatio_temporal_file_function_time(self):
200        """Test that File function interpolates correctly
201        between given times.
202        NetCDF version (x,y,t dependency)
203        """
204
205        #Create NetCDF (sww) file to be read
206        # x: 0, 5, 10, 15
207        # y: -20, -10, 0, 10
208        # t: 0, 60, 120, ...., 1200
209        #
210        # test quantities (arbitrary but non-trivial expressions):
211        #
212        #   stage     = 3*x - y**2 + 2*t
213        #   xmomentum = exp( -((x-7)**2 + (y+5)**2)/20 ) * t**2
214        #   ymomentum = x**2 + y**2 * sin(t*pi/600)
215
216        #Nice test that may render some of the others redundant.
217
218        import os, time
219        from config import time_format
220        from Numeric import sin, pi, exp
221        from mesh_factory import rectangular
222        from shallow_water import Domain
223        import data_manager
224
225        finaltime = 1200
226        filename = 'test_file_function'
227
228        #Create a domain to hold test grid
229
230        points, vertices, boundary =\
231                rectangular(4, 4, 15, 30, origin = (0, -20))
232
233
234        #print 'Number of elements', len(vertices)
235        domain = Domain(points, vertices, boundary)
236        domain.smooth = False
237        domain.default_order = 2
238        domain.set_datadir('.')
239        domain.set_name(filename)
240        domain.store = True
241        domain.format = 'sww'   #Native netcdf visualisation format
242
243        #print 'E', domain.get_extent()
244        #print points
245        start = time.mktime(time.strptime('2000', '%Y'))
246        domain.starttime = start
247
248
249        #Store structure
250        domain.initialise_storage()
251
252        #Compute artificial time steps and store
253        dt = 60  #One minute intervals
254        t = 0.0
255        while t <= finaltime:
256            #Compute quantities
257            f1 = lambda x,y: 3*x - y**2 + 2*t + 4
258            domain.set_quantity('stage', f1)
259
260            f2 = lambda x,y: x+y+t**2
261            domain.set_quantity('xmomentum', f2)
262
263            f3 = lambda x,y: x**2 + y**2 * sin(t*pi/600)
264            domain.set_quantity('ymomentum', f3)
265
266            #Store and advance time
267            domain.time = t
268            domain.store_timestep(domain.conserved_quantities)
269            t += dt
270
271
272        interpolation_points = [[0,-20], [1,0], [0,1], [1.1, 3.14], [10,-12.5]]
273
274
275
276        #Set domain.starttime to too early
277        domain.starttime = start - 1
278
279        #Create file function
280        F = file_function(filename + '.sww', domain,
281                          quantities = domain.conserved_quantities,
282                          interpolation_points = interpolation_points)
283
284        #Check that FF updates fixes domain starttime
285        assert allclose(domain.starttime, start)
286
287        #Check that domain.starttime isn't updated if later
288        domain.starttime = start + 1
289        F = file_function(filename + '.sww', domain,
290                          quantities = domain.conserved_quantities,
291                          interpolation_points = interpolation_points)
292        assert allclose(domain.starttime, start+1)
293
294        domain.starttime = start
295
296
297
298        #Check linear interpolation in time
299        #for id in range(len(interpolation_points)):
300        for id in [1]:
301            x = interpolation_points[id][0]
302            y = interpolation_points[id][1]
303
304            for i in range(20):
305                t = i*10
306                k = i%6
307
308                if k == 0:
309                    q0 = F(t, point_id=id)
310                    q1 = F(t+60, point_id=id)
311
312
313                q = F(t, point_id=id)
314                assert allclose(q, (k*q1 + (6-k)*q0)/6)
315
316
317        #Another check of linear interpolation in time
318        for id in range(len(interpolation_points)):
319            q60 = F(60, point_id=id)
320            q120 = F(120, point_id=id)
321
322            t = 90 #Halfway between 60 and 120
323            q = F(t,point_id=id)
324            assert allclose( (q120+q60)/2, q )
325
326            t = 100 #Two thirds of the way between between 60 and 120
327            q = F(t, point_id=id)
328            assert allclose(q60/3 + 2*q120/3, q)
329
330
331
332        #Check that domain.starttime isn't updated if later than file starttime but earlier
333        #than file end time
334        delta = 23
335        domain.starttime = start + delta
336        F = file_function(filename + '.sww', domain,
337                          quantities = domain.conserved_quantities,
338                          interpolation_points = interpolation_points)
339        assert allclose(domain.starttime, start+delta)
340
341
342
343
344        #Now try interpolation with delta offset
345        for id in [1]:
346            x = interpolation_points[id][0]
347            y = interpolation_points[id][1]
348
349            for i in range(20):
350                t = i*10
351                k = i%6
352
353                if k == 0:
354                    q0 = F(t-delta, point_id=id)
355                    q1 = F(t+60-delta, point_id=id)
356
357                q = F(t-delta, point_id=id)
358                assert allclose(q, (k*q1 + (6-k)*q0)/6)
359
360
361        os.remove(filename + '.sww')
362
363
364
365    def test_file_function_time_with_domain(self):
366        """Test that File function interpolates correctly
367        between given times. No x,y dependency here.
368        Use domain with starttime
369        """
370
371        #Write file
372        import os, time, calendar
373        from config import time_format
374        from math import sin, pi
375        from domain import Domain
376
377        finaltime = 1200
378        filename = 'test_file_function.txt'
379        fid = open(filename, 'w')
380        start = time.mktime(time.strptime('2000', '%Y'))
381        dt = 60  #One minute intervals
382        t = 0.0
383        while t <= finaltime:
384            t_string = time.strftime(time_format, time.gmtime(t+start))
385            fid.write('%s, %f %f %f\n' %(t_string, 2*t, t**2, sin(t*pi/600)))
386            t += dt
387
388        fid.close()
389
390        a = [0.0, 0.0]
391        b = [4.0, 0.0]
392        c = [0.0, 3.0]
393
394        points = [a, b, c]
395        vertices = [[0,1,2]]
396        domain = Domain(points, vertices)
397
398        #Check that domain.starttime is updated if non-existing
399        F = file_function(filename, domain)
400        assert allclose(domain.starttime, start)
401
402        #Check that domain.starttime is updated if too early
403        domain.starttime = start - 1
404        F = file_function(filename, domain)
405        assert allclose(domain.starttime, start)
406
407        #Check that domain.starttime isn't updated if later
408        domain.starttime = start + 1
409        F = file_function(filename, domain)
410        assert allclose(domain.starttime, start+1)
411
412        domain.starttime = start
413
414
415        #Now try interpolation
416        for i in range(20):
417            t = i*10
418            q = F(t)
419
420            #Exact linear intpolation
421            assert allclose(q[0], 2*t)
422            if i%6 == 0:
423                assert allclose(q[1], t**2)
424                assert allclose(q[2], sin(t*pi/600))
425
426        #Check non-exact
427
428        t = 90 #Halfway between 60 and 120
429        q = F(t)
430        assert allclose( (120**2 + 60**2)/2, q[1] )
431        assert allclose( (sin(120*pi/600) + sin(60*pi/600))/2, q[2] )
432
433
434        t = 100 #Two thirds of the way between between 60 and 120
435        q = F(t)
436        assert allclose( 2*120**2/3 + 60**2/3, q[1] )
437        assert allclose( 2*sin(120*pi/600)/3 + sin(60*pi/600)/3, q[2] )
438
439        os.remove(filename)
440
441
442    def test_file_function_time_with_domain_different_start(self):
443        """Test that File function interpolates correctly
444        between given times. No x,y dependency here.
445        Use domain with a starttime later than that of file
446
447        ASCII version
448        """
449
450        #Write file
451        import os, time, calendar
452        from config import time_format
453        from math import sin, pi
454        from domain import Domain
455
456        finaltime = 1200
457        filename = 'test_file_function.txt'
458        fid = open(filename, 'w')
459        start = time.mktime(time.strptime('2000', '%Y'))
460        dt = 60  #One minute intervals
461        t = 0.0
462        while t <= finaltime:
463            t_string = time.strftime(time_format, time.gmtime(t+start))
464            fid.write('%s, %f %f %f\n' %(t_string, 2*t, t**2, sin(t*pi/600)))
465            t += dt
466
467        fid.close()
468
469        a = [0.0, 0.0]
470        b = [4.0, 0.0]
471        c = [0.0, 3.0]
472
473        points = [a, b, c]
474        vertices = [[0,1,2]]
475        domain = Domain(points, vertices)
476
477        #Check that domain.starttime isn't updated if later than file starttime but earlier
478        #than file end time
479        delta = 23
480        domain.starttime = start + delta
481        F = file_function(filename, domain)
482        assert allclose(domain.starttime, start+delta)
483
484
485
486
487        #Now try interpolation with delta offset
488        for i in range(20):
489            t = i*10
490            q = F(t-delta)
491
492            #Exact linear intpolation
493            assert allclose(q[0], 2*t)
494            if i%6 == 0:
495                assert allclose(q[1], t**2)
496                assert allclose(q[2], sin(t*pi/600))
497
498        #Check non-exact
499
500        t = 90 #Halfway between 60 and 120
501        q = F(t-delta)
502        assert allclose( (120**2 + 60**2)/2, q[1] )
503        assert allclose( (sin(120*pi/600) + sin(60*pi/600))/2, q[2] )
504
505
506        t = 100 #Two thirds of the way between between 60 and 120
507        q = F(t-delta)
508        assert allclose( 2*120**2/3 + 60**2/3, q[1] )
509        assert allclose( 2*sin(120*pi/600)/3 + sin(60*pi/600)/3, q[2] )
510
511        os.remove(filename)
512
513
514    def test_spatio_temporal_file_function(self):
515        """Test that spatio temporal file function performs the correct
516        interpolations in both time and space
517        """
518        import time
519
520        #Create sww file of simple propagation from left to right
521        #through rectangular domain
522        from shallow_water import Domain, Dirichlet_boundary
523        from mesh_factory import rectangular
524        from Numeric import take, concatenate, reshape
525
526        #Create basic mesh and shallow water domain
527        points, vertices, boundary = rectangular(3, 3)
528        domain1 = Domain(points, vertices, boundary)
529
530        from util import mean
531        domain1.reduction = mean
532        domain1.smooth = True #NOTE: Mimic sww output where each vertex has
533                              # only one value.
534
535        domain1.default_order = 2
536        domain1.store = True
537        domain1.set_datadir('.')
538        domain1.set_name('spatio_temporal_boundary_source_%d' %(id(self)))
539        domain1.quantities_to_be_stored = ['stage', 'xmomentum', 'ymomentum']
540
541        #Bed-slope, friction and IC at vertices (and interpolated elsewhere)
542        domain1.set_quantity('elevation', 0)
543        domain1.set_quantity('friction', 0)
544        domain1.set_quantity('stage', 0)
545
546        # Boundary conditions
547        B0 = Dirichlet_boundary([0,0,0])
548        B6 = Dirichlet_boundary([0.6,0,0])
549        domain1.set_boundary({'left': B6, 'top': B6, 'right': B0, 'bottom': B0})
550        domain1.check_integrity()
551
552        finaltime = 8
553        #Evolution
554        for t in domain1.evolve(yieldstep = 0.1, finaltime = finaltime):
555            pass
556            #domain1.write_time()
557
558
559        #Now read data from sww and check
560        from Scientific.IO.NetCDF import NetCDFFile
561        filename = domain1.get_name() + '.' + domain1.format
562        fid = NetCDFFile(filename)
563
564        x = fid.variables['x'][:]
565        y = fid.variables['y'][:]
566        stage = fid.variables['stage'][:]
567        xmomentum = fid.variables['xmomentum'][:]
568        ymomentum = fid.variables['ymomentum'][:]
569        time = fid.variables['time'][:]
570
571        #Take stage vertex values at last timestep on diagonal
572        #Diagonal is identified by vertices: 0, 5, 10, 15
573
574        timestep = len(time)-1 #Last timestep
575        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
576        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
577        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
578        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
579
580        #Reference interpolated values at midpoints on diagonal at
581        #this timestep are
582        r0 = (D[0] + D[1])/2
583        r1 = (D[1] + D[2])/2
584        r2 = (D[2] + D[3])/2
585
586        #And the midpoints are found now
587        Dx = take(reshape(x, (16,1)), [0,5,10,15])
588        Dy = take(reshape(y, (16,1)), [0,5,10,15])
589
590        diag = concatenate( (Dx, Dy), axis=1)
591        d_midpoints = (diag[1:] + diag[:-1])/2
592
593        #Let us see if the file function can find the correct
594        #values at the midpoints at the last timestep:
595        f = file_function(filename, domain1,
596                          interpolation_points = d_midpoints)
597
598        q = f(timestep/10., point_id=0); assert allclose(r0, q)
599        q = f(timestep/10., point_id=1); assert allclose(r1, q)
600        q = f(timestep/10., point_id=2); assert allclose(r2, q)
601
602
603        ##################
604        #Now do the same for the first timestep
605
606        timestep = 0 #First timestep
607        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
608        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
609        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
610        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
611
612        #Reference interpolated values at midpoints on diagonal at
613        #this timestep are
614        r0 = (D[0] + D[1])/2
615        r1 = (D[1] + D[2])/2
616        r2 = (D[2] + D[3])/2
617
618        #Let us see if the file function can find the correct
619        #values
620        q = f(0, point_id=0); assert allclose(r0, q)
621        q = f(0, point_id=1); assert allclose(r1, q)
622        q = f(0, point_id=2); assert allclose(r2, q)
623
624
625        ##################
626        #Now do it again for a timestep in the middle
627
628        timestep = 33
629        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
630        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
631        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
632        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
633
634        #Reference interpolated values at midpoints on diagonal at
635        #this timestep are
636        r0 = (D[0] + D[1])/2
637        r1 = (D[1] + D[2])/2
638        r2 = (D[2] + D[3])/2
639
640        q = f(timestep/10., point_id=0); assert allclose(r0, q)
641        q = f(timestep/10., point_id=1); assert allclose(r1, q)
642        q = f(timestep/10., point_id=2); assert allclose(r2, q)
643
644
645        ##################
646        #Now check temporal interpolation
647        #Halfway between timestep 15 and 16
648
649        timestep = 15
650        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
651        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
652        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
653        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
654
655        #Reference interpolated values at midpoints on diagonal at
656        #this timestep are
657        r0_0 = (D[0] + D[1])/2
658        r1_0 = (D[1] + D[2])/2
659        r2_0 = (D[2] + D[3])/2
660
661        #
662        timestep = 16
663        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
664        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
665        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
666        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
667
668        #Reference interpolated values at midpoints on diagonal at
669        #this timestep are
670        r0_1 = (D[0] + D[1])/2
671        r1_1 = (D[1] + D[2])/2
672        r2_1 = (D[2] + D[3])/2
673
674        # The reference values are
675        r0 = (r0_0 + r0_1)/2
676        r1 = (r1_0 + r1_1)/2
677        r2 = (r2_0 + r2_1)/2
678
679        q = f((timestep - 0.5)/10., point_id=0); assert allclose(r0, q)
680        q = f((timestep - 0.5)/10., point_id=1); assert allclose(r1, q)
681        q = f((timestep - 0.5)/10., point_id=2); assert allclose(r2, q)
682
683        ##################
684        #Finally check interpolation 2 thirds of the way
685        #between timestep 15 and 16
686
687        # The reference values are
688        r0 = (r0_0 + 2*r0_1)/3
689        r1 = (r1_0 + 2*r1_1)/3
690        r2 = (r2_0 + 2*r2_1)/3
691
692        #And the file function gives
693        q = f((timestep - 1.0/3)/10., point_id=0); assert allclose(r0, q)
694        q = f((timestep - 1.0/3)/10., point_id=1); assert allclose(r1, q)
695        q = f((timestep - 1.0/3)/10., point_id=2); assert allclose(r2, q)
696
697        fid.close()
698        import os
699        os.remove(filename)
700
701
702    def test_xya_ascii(self):
703        import time, os
704        FN = 'xyatest' + str(time.time()) + '.xya'
705        fid = open(FN, 'w')
706        fid.write('      %s %s %s\n' %('a1', 'a2', 'a3') )
707        fid.write('%f %f %f %f %f\n' %(0,1,10,20,30) )
708        fid.write('%f %f %f %f %f\n' %(1,0,30,20,10) )
709        fid.write('%f %f %f %f %f\n' %(1,1,40.2,40.3,40.4) )
710        fid.close()
711
712        points, attributes = read_xya(FN, format = 'asc')
713
714        assert allclose(points, [ [0,1], [1,0], [1,1] ])
715        assert allclose(attributes['a1'], [10,30,40.2])
716        assert allclose(attributes['a2'], [20,20,40.3])
717        assert allclose(attributes['a3'], [30,10,40.4])
718
719        os.remove(FN)
720
721    def test_xya_ascii_w_names(self):
722        import time, os
723        FN = 'xyatest' + str(time.time()) + '.xya'
724        fid = open(FN, 'w')
725        fid.write('      %s %s %s\n' %('a1', 'a2', 'a3') )
726        fid.write('%f %f %f %f %f\n' %(0,1,10,20,30) )
727        fid.write('%f %f %f %f %f\n' %(1,0,30,20,10) )
728        fid.write('%f %f %f %f %f\n' %(1,1,40.2,40.3,40.4) )
729        fid.close()
730
731        points, attributes = read_xya(FN, format = 'asc')
732
733        assert allclose(points, [ [0,1], [1,0], [1,1] ])
734
735        assert allclose(attributes['a1'], [10,30,40.2])
736        assert allclose(attributes['a2'], [20,20,40.3])
737        assert allclose(attributes['a3'], [30,10,40.4])
738
739
740        os.remove(FN)
741
742
743
744
745    #Polygon stuff
746    def test_polygon_function_constants(self):
747        p1 = [[0,0], [10,0], [10,10], [0,10]]
748        p2 = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
749
750        f = Polygon_function( [(p1, 1.0)] )
751        z = f([5, 5, 27, 35], [5, 9, 8, -5]) #Two first inside p1
752        assert allclose(z, [1,1,0,0])
753
754
755        f = Polygon_function( [(p2, 2.0)] )
756        z = f([5, 5, 27, 35], [5, 9, 8, -5]) #First and last inside p2
757        assert allclose(z, [2,0,0,2])
758
759
760        #Combined
761        f = Polygon_function( [(p1, 1.0), (p2, 2.0)] )
762        z = f([5, 5, 27, 35], [5, 9, 8, -5])
763        assert allclose(z, [2,1,0,2])
764
765
766    def test_polygon_function_callable(self):
767        """Check that values passed into Polygon_function can be callable
768        themselves.
769        """
770        p1 = [[0,0], [10,0], [10,10], [0,10]]
771        p2 = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
772
773        f = Polygon_function( [(p1, test_function)] )
774        z = f([5, 5, 27, 35], [5, 9, 8, -5]) #Two first inside p1
775        assert allclose(z, [10,14,0,0])
776
777        #Combined
778        f = Polygon_function( [(p1, test_function), (p2, 2.0)] )
779        z = f([5, 5, 27, 35], [5, 9, 8, -5])
780        assert allclose(z, [2,14,0,2])
781
782
783        #Combined w default
784        f = Polygon_function( [(p1, test_function), (p2, 2.0)], default = 3.14)
785        z = f([5, 5, 27, 35], [5, 9, 8, -5])
786        assert allclose(z, [2,14,3.14,2])
787
788
789        #Combined w default func
790        f = Polygon_function( [(p1, test_function), (p2, 2.0)],
791                              default = test_function)
792        z = f([5, 5, 27, 35], [5, 9, 8, -5])
793        assert allclose(z, [2,14,35,2])
794
795
796    def test_point_on_line(self):
797
798        #Endpoints first
799        assert point_on_line( 0, 0, 0,0, 1,0 )
800        assert point_on_line( 1, 0, 0,0, 1,0 )
801
802        #Then points on line
803        assert point_on_line( 0.5, 0, 0,0, 1,0 )
804        assert point_on_line( 0, 0.5, 0,1, 0,0 )
805        assert point_on_line( 1, 0.5, 1,1, 1,0 )
806        assert point_on_line( 0.5, 0.5, 0,0, 1,1 )
807
808        #Then points not on line
809        assert not point_on_line( 0.5, 0, 0,1, 1,1 )
810        assert not point_on_line( 0, 0.5, 0,0, 1,1 )
811
812        #From real example that failed
813        assert not point_on_line( 40,50, 40,20, 40,40 )
814
815
816        #From real example that failed
817        assert not point_on_line( 40,19, 40,20, 40,40 )
818
819
820
821
822    def test_inside_polygon_main(self):
823
824
825        #Simplest case: Polygon is the unit square
826        polygon = [[0,0], [1,0], [1,1], [0,1]]
827
828        assert inside_polygon( (0.5, 0.5), polygon )
829        assert not inside_polygon( (0.5, 1.5), polygon )
830        assert not inside_polygon( (0.5, -0.5), polygon )
831        assert not inside_polygon( (-0.5, 0.5), polygon )
832        assert not inside_polygon( (1.5, 0.5), polygon )
833
834        #Try point on borders
835        assert inside_polygon( (1., 0.5), polygon, closed=True)
836        assert inside_polygon( (0.5, 1), polygon, closed=True)
837        assert inside_polygon( (0., 0.5), polygon, closed=True)
838        assert inside_polygon( (0.5, 0.), polygon, closed=True)
839
840        assert not inside_polygon( (0.5, 1), polygon, closed=False)
841        assert not inside_polygon( (0., 0.5), polygon, closed=False)
842        assert not inside_polygon( (0.5, 0.), polygon, closed=False)
843        assert not inside_polygon( (1., 0.5), polygon, closed=False)
844
845
846
847        #From real example (that failed)
848        polygon = [[20,20], [40,20], [40,40], [20,40]]
849        points = [ [40, 50] ]
850        res = inside_polygon(points, polygon)
851        assert len(res) == 0
852
853        polygon = [[20,20], [40,20], [40,40], [20,40]]
854        points = [ [25, 25], [30, 20], [40, 50], [90, 20], [40, 90] ]
855        res = inside_polygon(points, polygon)
856        assert len(res) == 2
857        assert allclose(res, [0,1])
858
859
860
861        #More convoluted and non convex polygon
862        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
863        assert inside_polygon( (0.5, 0.5), polygon )
864        assert inside_polygon( (1, -0.5), polygon )
865        assert inside_polygon( (1.5, 0), polygon )
866
867        assert not inside_polygon( (0.5, 1.5), polygon )
868        assert not inside_polygon( (0.5, -0.5), polygon )
869
870
871        #Very convoluted polygon
872        polygon = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
873        assert inside_polygon( (5, 5), polygon )
874        assert inside_polygon( (17, 7), polygon )
875        assert inside_polygon( (27, 2), polygon )
876        assert inside_polygon( (35, -5), polygon )
877        assert not inside_polygon( (15, 7), polygon )
878        assert not inside_polygon( (24, 3), polygon )
879        assert not inside_polygon( (25, -10), polygon )
880
881
882
883        #Another combination (that failed)
884        polygon = [[0,0], [10,0], [10,10], [0,10]]
885        assert inside_polygon( (5, 5), polygon )
886        assert inside_polygon( (7, 7), polygon )
887        assert not inside_polygon( (-17, 7), polygon )
888        assert not inside_polygon( (7, 17), polygon )
889        assert not inside_polygon( (17, 7), polygon )
890        assert not inside_polygon( (27, 8), polygon )
891        assert not inside_polygon( (35, -5), polygon )
892
893
894
895
896        #Multiple polygons
897
898        polygon = [[0,0], [1,0], [1,1], [0,1], [0,0],
899                   [10,10], [11,10], [11,11], [10,11], [10,10]]
900        assert inside_polygon( (0.5, 0.5), polygon )
901        assert inside_polygon( (10.5, 10.5), polygon )
902
903        #FIXME: Fails if point is 5.5, 5.5
904        assert not inside_polygon( (0, 5.5), polygon )
905
906        #Polygon with a hole
907        polygon = [[-1,-1], [2,-1], [2,2], [-1,2], [-1,-1],
908                   [0,0], [1,0], [1,1], [0,1], [0,0]]
909
910        assert inside_polygon( (0, -0.5), polygon )
911        assert not inside_polygon( (0.5, 0.5), polygon )
912
913    def test_inside_polygon_vector_version(self):
914        #Now try the vector formulation returning indices
915        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
916        points = [ [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
917        res = inside_polygon( points, polygon )
918
919        assert allclose( res, [0,1,2] )
920
921    def test_outside_polygon(self):
922
923        U = [[0,0], [1,0], [1,1], [0,1]] #Unit square   
924        assert not outside_polygon( [0.5, 0.5], U )
925        #evaluate to False as the point 0.5, 0.5 is inside the unit square
926
927        assert outside_polygon( [1.5, 0.5], U )
928        #evaluate to True as the point 1.5, 0.5 is outside the unit square
929       
930        indices = outside_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U )
931        assert allclose( indices, [1] )
932       
933        #One more test of vector formulation returning indices
934        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
935        points = [ [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
936        res = outside_polygon( points, polygon )
937
938        assert allclose( res, [3,4] )
939
940
941
942        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
943        points = [ [0.5, 1.4], [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
944        res = outside_polygon( points, polygon )
945
946        assert allclose( res, [0, 4, 5] )       
947       
948
949    def test_populate_polygon(self):
950
951        polygon = [[0,0], [1,0], [1,1], [0,1]]
952        points = populate_polygon(polygon, 5)
953
954        assert len(points) == 5
955        for point in points:
956            assert inside_polygon(point, polygon)
957
958
959        #Very convoluted polygon
960        polygon = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
961
962        points = populate_polygon(polygon, 5)
963
964        assert len(points) == 5
965        for point in points:
966            assert inside_polygon(point, polygon)
967
968
969
970#-------------------------------------------------------------
971if __name__ == "__main__":
972    #suite = unittest.makeSuite(TestCase,'test_inside_polygon_main')
973    suite = unittest.makeSuite(Test_Util,'test')
974    runner = unittest.TextTestRunner()
975    runner.run(suite)
976
977
978
979
Note: See TracBrowser for help on using the repository browser.