geotree 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,153 @@
1
+ require 'test/unit'
2
+
3
+ require_relative '../lib/geotree/tools.rb'
4
+ req('blockfile diskblockfile')
5
+
6
+
7
+ #SINGLETEST = "test_200_recover_with_incorrect_password"
8
+ if defined? SINGLETEST
9
+ if main?(__FILE__)
10
+ ARGV.concat("-n #{SINGLETEST}".split)
11
+ end
12
+ end
13
+
14
+ class TestBlockFile < Test::Unit::TestCase #< MyTestSuite
15
+
16
+
17
+ # include BlockFile
18
+ # include LEnc
19
+
20
+ # Make a directory, if it doesn't already exist
21
+ def mkdir(name)
22
+ if !File.directory?(name)
23
+ Dir::mkdir(name)
24
+ end
25
+ end
26
+
27
+ def suite_setup
28
+ # pr("\n\n>>>> TestBlockFile setup\n\n")
29
+
30
+ # Make current directory = the one containing this script
31
+ main?(__FILE__)
32
+
33
+ @@testDir = "__temp_dirs__"
34
+ mkdir(@@testDir)
35
+
36
+
37
+ clean()
38
+ end
39
+
40
+
41
+ def clean
42
+ end
43
+
44
+ def build_bf
45
+ if !@bf
46
+ @bf = BlockFile.new(64)
47
+ end
48
+ end
49
+
50
+ def suite_teardown
51
+ # pr("\n\n<<<< TestBlockFile teardown\n\n")
52
+ end
53
+
54
+ def method_setup
55
+ # pr("\n\n\n")
56
+ end
57
+
58
+ def method_teardown
59
+ @bf = nil
60
+ end
61
+
62
+
63
+
64
+ def ex(args)
65
+ if args.is_a? String
66
+ args = args.split
67
+ end
68
+ args.concat(["-w", @@sourceDir, "-q"])
69
+ LEncApp.new().run(args)
70
+ end
71
+
72
+
73
+ # --------------- tests --------------------------
74
+
75
+ def test_100_create_block_file
76
+ build_bf
77
+ assert(@bf && !@bf.open?)
78
+ end
79
+ def test_110_create_and_open_block_file
80
+ build_bf
81
+ @bf.open
82
+ assert(@bf && @bf.open?)
83
+ end
84
+
85
+ def test_120_user_values
86
+ build_bf
87
+ @bf.open
88
+ k = 42
89
+ @bf.write_user(2,k)
90
+ @bf.write_user(1,k/2)
91
+ assert(@bf.read_user(2) == k)
92
+ assert(@bf.read_user(1) == k/2)
93
+ end
94
+
95
+ def test_130_read_when_not_open
96
+ assert_raise(IllegalStateException) do
97
+ build_bf
98
+ @bf.write_user(2,42)
99
+ end
100
+ end
101
+
102
+ def test_140_private_constant_access
103
+ assert_raise(NameError) do
104
+ BlockFile::BLOCKTYPE_RECYCLE
105
+ end
106
+ end
107
+
108
+ # def test_140_phony
109
+ # assert_raise(IllegalStateException) do
110
+ # puts "nothing"
111
+ # end
112
+ # end
113
+
114
+ end
115
+
116
+ if true && __FILE__ == $0
117
+
118
+ pth = '__foo__.bin'
119
+ remove_file_or_dir(pth)
120
+
121
+ bf = DiskBlockFile.new(64,pth)
122
+ bf.open
123
+
124
+ bnames = []
125
+ (20..40).each do |i|
126
+ by = bf.alloc_buffer
127
+ bf.block_size.times do |x|
128
+ by[x] = i.chr
129
+ end
130
+ # bf.write(n,by)
131
+ n = bf.alloc(by)
132
+ bnames << n
133
+ pr("i=#{i} n=#{n} block_size=%d\n",bf.block_size)
134
+ pr("alloc'd block #{n}:\n%s\n",n,bf.dump(n))
135
+ end
136
+
137
+ 5.times do
138
+ q = bnames.pop
139
+
140
+ bf.free q
141
+ pr("\n\n\nafter freeing %d: %s\n",q,d(bf))
142
+
143
+ if (q % 3 == 2)
144
+ q2 = bf.alloc
145
+ pr(" alloc'd another page #{q2}\n")
146
+ end
147
+
148
+ end
149
+
150
+ puts bf
151
+ bf.close
152
+ end
153
+
@@ -0,0 +1,139 @@
1
+ require 'test/unit'
2
+
3
+ require_relative '../lib/geotree/tools.rb'
4
+ req('externalsort')
5
+
6
+ include ExternalSortModule
7
+
8
+ class TestExternalSort < MyTestSuite
9
+ include ExternalSortModule
10
+
11
+ ELEM_SIZE = 16
12
+
13
+ # Make a directory, if it doesn't already exist
14
+ def mkdir(name)
15
+ if !File.directory?(name)
16
+ Dir::mkdir(name)
17
+ end
18
+ end
19
+
20
+ def suite_setup
21
+
22
+ # Make current directory = the one containing this script
23
+ main?(__FILE__)
24
+
25
+ @@test_dir = "workdir"
26
+ mkdir(@@test_dir)
27
+
28
+ @@path = File.join(@@test_dir,"_input_.dat")
29
+ raise Exception,"no test file found" if !File.file?(@@path)
30
+
31
+ @@path3 = File.join(@@test_dir,"_largefile_.dat")
32
+ end
33
+
34
+ def prepare_unsorted
35
+ @@path2 = File.join(@@test_dir,"_output_.dat")
36
+ remove_file_or_dir(@@path2)
37
+ FileUtils.cp(@@path,@@path2)
38
+
39
+ fs = File.size(@@path2)
40
+ File.truncate(@@path2, fs - fs % ELEM_SIZE)
41
+ end
42
+
43
+ def suite_teardown
44
+ # remove_file_or_dir(@@path3)
45
+ end
46
+
47
+ def method_setup
48
+ end
49
+
50
+ def method_teardown
51
+ end
52
+
53
+ def test_chunk
54
+ db = false
55
+
56
+ prepare_unsorted
57
+
58
+ f = File.open(@@path,"rb")
59
+ f2 = File.open(@@path2,"wb")
60
+
61
+ fs = File.size(@@path)
62
+ fs -= fs % ELEM_SIZE
63
+
64
+ ch = ChunkReader.new(f, 0, fs, ELEM_SIZE, 73 )
65
+
66
+ ch2 = ChunkWriter.new(f2,0,fs,ELEM_SIZE, 300 )
67
+
68
+ while !ch.done
69
+ !db|| puts(ch.peek_dump)
70
+
71
+ buff, offset = ch.read()
72
+ !db||pr("Read: %s",hex_dump_to_string(buff[offset,ch.element_size]))
73
+ ch2.write(buff,offset)
74
+ end
75
+ !db || pr("%s\n%s\n",d(ch),d(ch2))
76
+
77
+ end
78
+
79
+ def test_sort
80
+
81
+ db = false
82
+
83
+ prepare_unsorted
84
+
85
+ !db|| pr("before sorting:\n")
86
+ !db|| puts(hex_dump(read_text_file(@@path2)))
87
+ sr = Sorter.new(@@path2, ELEM_SIZE, nil, 90, 8)
88
+
89
+ sr.sort
90
+ !db|| pr("after sorting:\n")
91
+ !db|| puts(hex_dump(read_text_file(@@path2)))
92
+ end
93
+
94
+
95
+ def test_sort_large
96
+
97
+ elem_size = 16
98
+
99
+ if !File.file?(@@path3)
100
+ pr("Constructing LARGE file for sort test...\n")
101
+ f = File.open(@@path3,"wb+")
102
+ srand(1965)
103
+
104
+ a = 'abcdefghijklmnopABCDEFGHIJKLMNOP'.chars
105
+ a = a[0...elem_size]
106
+
107
+ tot = 10_000_000 / 20
108
+
109
+ tot.times do |i|
110
+ s = a.shuffle.join
111
+ f.write(s)
112
+ end
113
+ end
114
+
115
+ c1 = Proc.new do |x,y|
116
+ bx,ox = x
117
+ by,oy = y
118
+ bx[ox,elem_size] <=> by[oy,elem_size]
119
+ end
120
+ c2 = Proc.new do |x,y|
121
+ bx,ox = x
122
+ by,oy = y
123
+ bx[ox+1,elem_size-1] <=> by[oy+1,elem_size-1]
124
+ end
125
+
126
+ pr("Sorting file in one way...\n")
127
+
128
+ sr = Sorter.new(@@path3, elem_size, c2)
129
+ sr.sort
130
+
131
+ pr("Sorting file in another...\n")
132
+
133
+ sr = Sorter.new(@@path3, elem_size, c1)
134
+ sr.sort
135
+ pr("...done sorting\n")
136
+
137
+ end
138
+
139
+ end
@@ -0,0 +1,432 @@
1
+ require 'test/unit'
2
+
3
+ require_relative '../lib/geotree/tools.rb'
4
+ req('geotree pswriter multitree')
5
+
6
+ include GeoTreeModule
7
+
8
+ #SINGLETEST = "test_ps_output_multi"
9
+ if defined? SINGLETEST
10
+ if main?(__FILE__)
11
+ ARGV.concat("-n #{SINGLETEST}".split)
12
+ end
13
+ end
14
+
15
+ class TestGeoTree < MyTestSuite
16
+ # Make a directory, if it doesn't already exist
17
+ def mkdir(name)
18
+ if !File.directory?(name)
19
+ Dir::mkdir(name)
20
+ end
21
+ end
22
+
23
+ def rnd_subset(pts, frac = 0.3)
24
+ raise ArgumentError if pts.empty?
25
+
26
+ a = pts.dup
27
+ num = [(a.size * frac).to_i.ceil,a.size].min
28
+ srand(1977)
29
+ a.shuffle!
30
+ del = a.slice!(0..num)
31
+ rem = a
32
+ [del,rem]
33
+ end
34
+
35
+ def pt_set(set = 0)
36
+ assert(set < @@pts.size )
37
+ @@pts[set]
38
+ end
39
+
40
+ def rel_path(path)
41
+ File.join(@@test_dir,path)
42
+ end
43
+
44
+ def suite_setup
45
+ n_pts = 3000
46
+
47
+ # Make current directory = the one containing this script
48
+ main?(__FILE__)
49
+
50
+ @@test_dir = "workdir"
51
+ mkdir(@@test_dir)
52
+ @@tree_path = rel_path("_mytree_.dat")
53
+
54
+ clean()
55
+
56
+ @@pts = []
57
+ srand(7)
58
+
59
+ @@pts << DataPoint.rnd_many(n_pts)
60
+
61
+ p2 = DataPoint.rnd_many(n_pts/4)
62
+ i = 0
63
+ while i < p2.size
64
+ n = [p2.size-i, 200].min
65
+ pi = p2[i]
66
+ (i...i+n).each do |j|
67
+ pj = p2[j]
68
+ pj.loc.set_to(pi.loc)
69
+ end
70
+ i += n
71
+ end
72
+ p2.shuffle!
73
+
74
+ @@pts << p2
75
+
76
+ p3 = DataPoint.rnd_many(n_pts)
77
+ p3.each_with_index do |pt,i|
78
+ pt.loc=Loc.new(i*5,i*5)
79
+ end
80
+ @@pts << p3
81
+
82
+ srand(2000)
83
+ @@rects = Bounds.rnd_many(20)
84
+ end
85
+
86
+ def tree
87
+ @@t ||= GeoTree.open(@@tree_path)
88
+ end
89
+
90
+ def add_pts(set = 0, max_pts = 0)
91
+ ps = @@pts[set]
92
+ if max_pts > 0
93
+ ps = ps[0,max_pts]
94
+ end
95
+ ps.each{|dp| tree.add(dp)}
96
+ tree.buffering = false
97
+ end
98
+
99
+ def clean(delete_tree_file = true)
100
+ @@t = nil
101
+ return if !delete_tree_file
102
+ remove_file_or_dir(@@tree_path)
103
+ end
104
+
105
+ def suite_teardown
106
+ end
107
+
108
+ def method_setup
109
+ end
110
+
111
+ def method_teardown
112
+ end
113
+
114
+ # Construct list of data points lying within a rectangle
115
+ def pts_within_rect(pts,r)
116
+ names = pts.select{|pt| r.contains_point(pt.loc)}
117
+ end
118
+
119
+ def names(pt_list)
120
+ DataPoint.name_list(pt_list)
121
+ end
122
+
123
+ def query(tree, b, pts = nil)
124
+ pts ||= pt_set
125
+
126
+ db = false
127
+ !db || pr("query rect= #{b}\n")
128
+
129
+ !db || pr("tree find=#{tree.find(b)}\n")
130
+
131
+ f1 = names(tree.find(b))
132
+ f2 = names(pts_within_rect(pts,b))
133
+ !db || pr("Find #{b} returned:\n #{d(f1)}\n #{d(f2)}\n")
134
+
135
+ if (!(f1 == f2))
136
+ raise IllegalStateException, "Query tree, bounds #{b}, expected #{d(f2)}, got #{d(f1)}"
137
+ end
138
+ end
139
+
140
+ def test_create_tree
141
+ clean
142
+ tree
143
+ end
144
+
145
+ def test_add_points
146
+ clean
147
+ add_pts
148
+ # puts(tree.dump)
149
+ # puts("Tree stats:\n#{d2(tree.statistics)}\n")
150
+ end
151
+
152
+ def test_queries
153
+ clean
154
+ add_pts
155
+ bn = @@rects
156
+ bn.each{|b| query(tree,b)}
157
+ end
158
+
159
+ def test_remove
160
+ db = false
161
+ # db = true
162
+
163
+ @@pts.each_with_index do |pset,i|
164
+ clean
165
+
166
+ # Use buffering, since some point sets are very unbalanced
167
+ tree.buffering = true
168
+ add_pts(i)
169
+
170
+ pts = pset
171
+ while !pts.empty?
172
+
173
+ del, rem = rnd_subset(pts)
174
+ !db || pr("\nChose subset size #{del.size} of points #{pts.size}...\n")
175
+
176
+ # !db || puts(tree.dump)
177
+
178
+ if !del.empty?
179
+ # construct a copy of the first point to be removed, one with a slightly
180
+ # different location, to verify that it won't get removed
181
+ pt = del[0]
182
+ loc = pt.loc
183
+ while true
184
+ x = loc.x + rand(3)-1
185
+ y = loc.y + rand(3) - 1
186
+ break if x!=loc.x || y != loc.y
187
+ end
188
+
189
+ pt = DataPoint.new(pt.name, pt.weight, Loc.new(x,y))
190
+ !db || pr("Attempting to remove perturbed point #{pt}")
191
+ pt2 = tree.remove(pt)
192
+ assert(!pt2)
193
+ # !db || puts(tree.dump)
194
+
195
+ end
196
+
197
+ del.each do |pt|
198
+ !db || pr(" attempting to remove #{pt}\n")
199
+ # !db || puts(tree.dump)
200
+
201
+ dp = tree.remove(pt)
202
+ assert(dp,"failed to remove #{pt}")
203
+ end
204
+
205
+ # try removing each point again to verify we can't
206
+ del.each do |pt|
207
+ !db || pr(" attempting to remove #{pt} again\n")
208
+ dp = tree.remove(pt)
209
+ assert(!dp)
210
+ end
211
+ pts = rem
212
+
213
+ # !db || puts(tree.dump)
214
+ end
215
+
216
+ end
217
+ end
218
+
219
+ def test_buffering
220
+ db = false
221
+ # db = true
222
+
223
+ return if !db
224
+
225
+ !db || pr("test buffering\n")
226
+
227
+ clean
228
+ tree
229
+
230
+ tree.buffering = true
231
+ add_pts(2) #2,2000)
232
+ stat1 = tree.statistics
233
+ assert(stat1['leaf_depth (avg)'] < 2.6)
234
+ end
235
+
236
+ # Test using points expressed in terms of longitude(x) and latitude(y)
237
+ def test_latlong
238
+ db = false
239
+
240
+ clean
241
+ t = tree
242
+
243
+ pts = []
244
+
245
+ pts << Loc.new(57.9,-2.9) # Aberdeen
246
+ pts << Loc.new(19.26,-99.7) # Mexico City
247
+ pts << Loc.new(-26.12,28.4) # Johannesburg
248
+
249
+ pts.each_with_index do |pt,i|
250
+ t.add(DataPoint.new(1+i,0,pt))
251
+ end
252
+
253
+ !db||pr("pts:\n%s\n",d2(pts))
254
+
255
+ pts.each_with_index do |lc,i|
256
+ y,x = lc.latit, lc.longit
257
+ !db|| pr("y,x=#{y},#{x}\n")
258
+ b = Bounds.new(x-1,y-1,2,2)
259
+
260
+ r = t.find(b)
261
+ assert(r.size == 1)
262
+ end
263
+
264
+ end
265
+
266
+ def test_latlong_range_error
267
+ assert_raise(ArgumentError) do
268
+ b = Bounds.new(175.0,50,10,10)
269
+ end
270
+ end
271
+
272
+ def test_open_and_close
273
+
274
+ clean
275
+ t = tree
276
+ ps = pt_set(0)
277
+ npts = ps.size
278
+
279
+ ps.each do |dp|
280
+ t.add(dp)
281
+ end
282
+
283
+ t.close
284
+ @@t = nil
285
+
286
+ t = tree
287
+ ps2 = t.find(GeoTree.max_bounds)
288
+
289
+ assert(ps2.size == ps.size)
290
+ end
291
+
292
+ def plot_pts(w,pts,gray=0)
293
+ w.push(w.set_gray(gray))
294
+ plot_pts_colored(w,pts)
295
+ w.pop
296
+ end
297
+
298
+ def plot_pts_colored(w,pts,scale=1)
299
+ pts.each do |pt|
300
+ w.draw_disc(pt.loc.x,pt.loc.y,scale*0.3*(pt.weight+4))
301
+ end
302
+
303
+ end
304
+
305
+ def pt_on_circle(cx,cy,ang,rad)
306
+
307
+ x = cx + Math.cos(ang) * rad
308
+ y = cy + Math.sin(ang) * rad
309
+ Loc.new(x.to_i,y.to_i)
310
+ end
311
+
312
+ def prepare_tree(tree)
313
+
314
+ ps1 = pt_set(0)
315
+ ps1 = ps1[0,120]
316
+
317
+ srand(42)
318
+ ps2 = DataPoint.rnd_many(400)
319
+ ps2.each do |pt|
320
+ rs = rand*rand*500
321
+ pc = pt_on_circle(820,620,rand * 2*3.1415,rs)
322
+ pt.loc = pc
323
+ end
324
+ ps1.concat(ps2)
325
+
326
+ ps1.each{|x| tree.add(x)}
327
+ ps1
328
+ end
329
+
330
+ def prepare_ws(path)
331
+ b = Bounds.new(0,0,1000,1000)
332
+
333
+ w = PSWriter.new(rel_path(path))
334
+
335
+ w.set_logical_page_size(b.w,b.h)
336
+
337
+ w
338
+ end
339
+
340
+ def test_ps_output
341
+
342
+ tree = GeoTree.new
343
+ ps1 = prepare_tree(tree)
344
+ w = prepare_ws("tree.ps")
345
+ # ps1,w = prepare_ws(tree,"tree.ps")
346
+
347
+ 50.times do |i|
348
+ w.new_page("GeoTree")
349
+
350
+ a = i * 3.1415/18
351
+ rad = 30+i*8
352
+
353
+ pp = pt_on_circle(500,450,a,rad)
354
+ x = pp.x
355
+ y = pp.y
356
+
357
+ width = (200 * (20+i)) / 25
358
+ height = (width * 2)/3
359
+ r = Bounds.new(x-width/2,y-height/2,width,height)
360
+ pts = tree.find(r)
361
+ w.push(w.set_gray(0))
362
+ w.draw_rect(r.x,r.y,r.w,r.h )
363
+ w.pop
364
+
365
+ w.push(w.set_rgb(0.4,0.4,0.9))
366
+ plot_pts_colored(w,ps1)
367
+ w.pop
368
+
369
+ w.push(w.set_rgb(0.75,0,0))
370
+ plot_pts_colored(w,pts,1.5)
371
+ w.pop
372
+
373
+ end
374
+
375
+ w.close();
376
+ end
377
+
378
+ def test_ps_output_multi
379
+
380
+ tree_path = rel_path("_multitree_")
381
+
382
+ # Perform two passes. On the first,
383
+ # create the multitree and the points;
384
+ # on the second, open the tree and
385
+ # construct a plot.
386
+
387
+ ps1 = nil
388
+
389
+ [0,1].each do |pass|
390
+
391
+ if pass == 0
392
+ remove_file_or_dir(tree_path)
393
+ else
394
+ assert(File.directory?(tree_path))
395
+ end
396
+
397
+ ndetails = 5
398
+ tree = MultiTree.new(tree_path,ndetails)
399
+
400
+ if pass == 0
401
+ ps1 = prepare_tree(tree)
402
+ tree.close
403
+ else
404
+ w = prepare_ws("multi_tree.ps")
405
+
406
+ steps = 4
407
+ (ndetails*steps).times do |i|
408
+ dt = i/steps
409
+ w.new_page("MultiTree detail=#{dt}")
410
+
411
+ r = Bounds.new(10+i*16,190+i*10,700,600)
412
+
413
+ pts = tree.find(r,dt)
414
+
415
+ w.push(w.set_gray(0))
416
+ w.draw_rect(r.x,r.y,r.w,r.h )
417
+ w.pop
418
+
419
+ plot_pts(w,ps1,0.8)
420
+
421
+ w.push(w.set_rgb(0.75,0,0))
422
+ plot_pts_colored(w,pts,1.2)
423
+ w.pop
424
+
425
+ end
426
+ w.close();
427
+ end
428
+ end
429
+ end
430
+
431
+ end
432
+