librtree 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rtree/rtree.c +206 -95
  3. data/lib/rtree.rb +224 -42
  4. metadata +12 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad62e4e446f6fb4571d33f6498bf9df26d5b360a0325cde40ead187ebcb0be04
4
- data.tar.gz: d462f690013c36933961206659026af7fe9b50ea6d23198f15a0965247390192
3
+ metadata.gz: 91ab938275be940bbe12869b5b95266bdd32a503c5aae74ef84e242db8b65cd2
4
+ data.tar.gz: 981f9d121d65cb94f3ca42cd09c3841471d9c9c6c4176416d8de639f2ffe9922
5
5
  SHA512:
6
- metadata.gz: 19e997efe8519f2db02e10f69f8d60af57c376e9a6de7f38bc8f3613cbd8287800e096816f1c37361c122c0a7c99e41bd97f7842ddebdf0357395e31a666739b
7
- data.tar.gz: bf1911bade3fcba763aee1f4a451dd9638f8e9c3b06577482b09bc134942282d031538d8a436ee161ec4334c7f2dfdadfcf8ac1349dc3d32b053c69390e5eb7f
6
+ metadata.gz: 42764d0851e8ddab67468847fc08f8d148ad332a7b8e3d81f26a2eb50d8a79b0e2ac2cfb6e805c7f3d1bd2b17a5ed1bd52c89ed0001c88fb3c9c209fb879fe0f
7
+ data.tar.gz: a394efa120788677dbda6d409b07e1a1d6d234692f08ba6371dfbc99c0f82420e2f720214793c6944f0b652cc81739096b06ff5e8822ac5f18d8a0b3f70388ba
data/ext/rtree/rtree.c CHANGED
@@ -4,48 +4,33 @@
4
4
 
5
5
  #include <rtree.h>
6
6
  #include <rtree/package.h>
7
+ #include <rtree/postscript.h>
7
8
 
8
9
  #include <errno.h>
9
10
  #include <stdint.h>
10
11
 
12
+ static rb_data_type_t style_type;
13
+ static rb_data_type_t rtree_type;
11
14
 
12
- static void lrt_dfree(void *p)
15
+ static void rt_dfree(void *p)
13
16
  {
14
17
  rtree_destroy((rtree_t*)p);
15
18
  }
16
19
 
17
- static size_t lrt_dsize(const void *p)
20
+ static size_t rt_dsize(const void *p)
18
21
  {
19
22
  return rtree_bytes((const rtree_t*)p);
20
23
  }
21
24
 
22
- static rb_data_type_t type = {
23
- .wrap_struct_name = "rtree-wrap",
24
- .function = {
25
- .dmark = NULL,
26
- .dfree = lrt_dfree,
27
- .dsize = lrt_dsize,
28
- #if RUBY_API_VERSION_CODE < 20700
29
- .reserved = { NULL, NULL }
30
- #else
31
- .dcompact = NULL,
32
- .reserved = { NULL }
33
- #endif
34
- },
35
- .parent = NULL,
36
- .data = NULL,
37
- .flags = RUBY_TYPED_FREE_IMMEDIATELY
38
- };
39
-
40
- static VALUE lrt_alloc(VALUE cls)
25
+ static VALUE rt_alloc(VALUE cls)
41
26
  {
42
27
  rtree_t *rtree;;
43
28
  if ((rtree = rtree_alloc()) == NULL)
44
29
  rb_raise(rb_eNoMemError, "failed to alloc rtree");
45
- return TypedData_Wrap_Struct(cls, &type, rtree);
30
+ return TypedData_Wrap_Struct(cls, &rtree_type, rtree);
46
31
  }
47
32
 
48
- static VALUE lrt_init(VALUE self, VALUE dim_obj, VALUE flags_obj)
33
+ static VALUE rt_init(VALUE self, VALUE dim_obj, VALUE flags_obj)
49
34
  {
50
35
  Check_Type(dim_obj, T_FIXNUM);
51
36
  size_t dim = FIX2ULONG(dim_obj);
@@ -54,7 +39,7 @@ static VALUE lrt_init(VALUE self, VALUE dim_obj, VALUE flags_obj)
54
39
  state_flags_t flags = FIX2UINT(flags_obj);
55
40
 
56
41
  rtree_t *rtree;
57
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
42
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
58
43
 
59
44
  if ((rtree_init(rtree, dim, flags)) != 0)
60
45
  rb_raise(rb_eNoMemError, "failed to init rtree");
@@ -62,28 +47,28 @@ static VALUE lrt_init(VALUE self, VALUE dim_obj, VALUE flags_obj)
62
47
  return self;
63
48
  }
64
49
 
65
- static VALUE lrt_release(VALUE self)
50
+ static VALUE rt_release(VALUE self)
66
51
  {
67
52
  rtree_t *rtree;
68
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
53
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
69
54
  rtree_destroy(rtree);
70
55
  return self;
71
56
  }
72
57
 
73
- static VALUE lrt_height(VALUE self)
58
+ static VALUE rt_height(VALUE self)
74
59
  {
75
60
  rtree_t *rtree;
76
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
61
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
77
62
  return INT2NUM(rtree_height(rtree));
78
63
  }
79
64
 
80
- static VALUE lrt_add_rect(VALUE self, VALUE id_obj, VALUE coord_obj)
65
+ static VALUE rt_add_rect(VALUE self, VALUE id_obj, VALUE coord_obj)
81
66
  {
82
67
  Check_Type(coord_obj, T_ARRAY);
83
68
  Check_Type(id_obj, T_FIXNUM);
84
69
 
85
70
  rtree_t *rtree;
86
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
71
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
87
72
 
88
73
  rtree_id_t
89
74
  id = FIX2ULONG(id_obj);
@@ -135,13 +120,13 @@ static int update_cb(rtree_id_t id, rtree_coord_t *coord, void *context)
135
120
  return 0;
136
121
  }
137
122
 
138
- static VALUE lrt_update(VALUE self)
123
+ static VALUE rt_update(VALUE self)
139
124
  {
140
125
  if (!rb_block_given_p())
141
126
  rb_raise(rb_eArgError, "Expected block");
142
127
 
143
128
  rtree_t *rtree;
144
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
129
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
145
130
 
146
131
  size_t len = 2 * state_dims(rtree->state);
147
132
  int err = rtree_update(rtree, update_cb, &len);
@@ -154,10 +139,10 @@ static VALUE lrt_update(VALUE self)
154
139
 
155
140
  /*
156
141
  The librtree API expects the search callback to return zero to
157
- continue the search, non-zero to terminate. I was expecting to
142
+ continue the search, non-zero to terminate. I was expecting to
158
143
  need to wrap the 'yield' in 'rescue's and 'ensure's to handle
159
144
  exceptions and breaks, but find that doing nothing actually does
160
- the right thing (see specs). The search implemetation is just
145
+ the right thing (see specs). The search implementation is just
161
146
  a recursive search through the tree (as you would expect) so
162
147
  there is no end-of-search cleanup to do, so I think this is all
163
148
  just fine ... wild
@@ -169,7 +154,7 @@ static int search_cb(rtree_id_t id, void *context)
169
154
  return 0;
170
155
  }
171
156
 
172
- static VALUE lrt_search(VALUE self, VALUE coord_obj)
157
+ static VALUE rt_search(VALUE self, VALUE coord_obj)
173
158
  {
174
159
  if (!rb_block_given_p())
175
160
  rb_raise(rb_eArgError, "Expected block");
@@ -177,7 +162,7 @@ static VALUE lrt_search(VALUE self, VALUE coord_obj)
177
162
  Check_Type(coord_obj, T_ARRAY);
178
163
 
179
164
  rtree_t *rtree;
180
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
165
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
181
166
 
182
167
  size_t
183
168
  len = RARRAY_LEN(coord_obj),
@@ -220,21 +205,21 @@ static VALUE deserialise(VALUE cls, VALUE io_obj, deserialise_t *f)
220
205
  rb_raise(rb_eRuntimeError, "Failed read from stream");
221
206
  }
222
207
 
223
- return TypedData_Wrap_Struct(cls, &type, rtree);
208
+ return TypedData_Wrap_Struct(cls, &rtree_type, rtree);
224
209
  }
225
210
 
226
- static VALUE lrt_json_read(VALUE cls, VALUE io_obj)
211
+ static VALUE rt_json_read(VALUE cls, VALUE io_obj)
227
212
  {
228
213
  return deserialise(cls, io_obj, rtree_json_read);
229
214
  }
230
215
 
231
- static VALUE lrt_bsrt_read(VALUE cls, VALUE io_obj)
216
+ static VALUE rt_bsrt_read(VALUE cls, VALUE io_obj)
232
217
  {
233
218
  return deserialise(cls, io_obj, rtree_bsrt_read);
234
219
  }
235
220
 
236
- static VALUE lrt_csv_read(VALUE cls,
237
- VALUE io_obj, VALUE dim_obj, VALUE flags_obj)
221
+ static VALUE rt_csv_read(VALUE cls,
222
+ VALUE io_obj, VALUE dim_obj, VALUE flags_obj)
238
223
  {
239
224
  Check_Type(io_obj, T_FILE);
240
225
  rb_io_t *io;
@@ -259,7 +244,7 @@ static VALUE lrt_csv_read(VALUE cls,
259
244
  rb_raise(rb_eRuntimeError, "Failed read from stream");
260
245
  }
261
246
 
262
- return TypedData_Wrap_Struct(cls, &type, rtree);
247
+ return TypedData_Wrap_Struct(cls, &rtree_type, rtree);
263
248
  }
264
249
 
265
250
  /* serialisation */
@@ -277,7 +262,7 @@ static VALUE serialise(VALUE self, VALUE io_obj, serialise_t *f)
277
262
  FILE *fp = rb_io_stdio_file(io);
278
263
 
279
264
  rtree_t *rtree;
280
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
265
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
281
266
 
282
267
  int err = f(rtree, fp);
283
268
  if (err != 0)
@@ -286,22 +271,22 @@ static VALUE serialise(VALUE self, VALUE io_obj, serialise_t *f)
286
271
  return self;
287
272
  }
288
273
 
289
- static VALUE lrt_json_write(VALUE self, VALUE io_obj)
274
+ static VALUE rt_json_write(VALUE self, VALUE io_obj)
290
275
  {
291
276
  return serialise(self, io_obj, rtree_json_write);
292
277
  }
293
278
 
294
- static VALUE lrt_bsrt_write(VALUE self, VALUE io_obj)
279
+ static VALUE rt_bsrt_write(VALUE self, VALUE io_obj)
295
280
  {
296
281
  return serialise(self, io_obj, rtree_bsrt_write);
297
282
  }
298
283
 
299
- static VALUE lrt_identical(VALUE self, VALUE other)
284
+ static VALUE rt_identical(VALUE self, VALUE other)
300
285
  {
301
286
  rtree_t *rtree_self, *rtree_other;
302
287
 
303
- TypedData_Get_Struct(self, rtree_t, &type, rtree_self);
304
- TypedData_Get_Struct(other, rtree_t, &type, rtree_other);
288
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree_self);
289
+ TypedData_Get_Struct(other, rtree_t, &rtree_type, rtree_other);
305
290
 
306
291
  if (rtree_identical(rtree_self, rtree_other))
307
292
  return Qtrue;
@@ -309,10 +294,10 @@ static VALUE lrt_identical(VALUE self, VALUE other)
309
294
  return Qfalse;
310
295
  }
311
296
 
312
- static VALUE lrt_clone(VALUE self)
297
+ static VALUE rt_clone(VALUE self)
313
298
  {
314
299
  rtree_t *rtree;
315
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
300
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
316
301
 
317
302
  rtree_t *clone;
318
303
  errno = 0;
@@ -324,105 +309,231 @@ static VALUE lrt_clone(VALUE self)
324
309
  rb_raise(rb_eRuntimeError, "Failed clone");
325
310
  }
326
311
 
327
- return TypedData_Wrap_Struct(CLASS_OF(self), &type, clone);
312
+ return TypedData_Wrap_Struct(CLASS_OF(self), &rtree_type, clone);
328
313
  }
329
314
 
330
315
  static VALUE state_size_access(VALUE self, size_t (*f)(const state_t*))
331
316
  {
332
317
  rtree_t *rtree;
333
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
318
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
334
319
  return INT2NUM(f(rtree->state));
335
320
  }
336
321
 
337
- static VALUE lrt_dim(VALUE self)
322
+ static VALUE rt_dim(VALUE self)
338
323
  {
339
324
  return state_size_access(self, state_dims);
340
325
  }
341
326
 
342
- static VALUE lrt_page_size(VALUE self)
327
+ static VALUE rt_page_size(VALUE self)
343
328
  {
344
329
  return state_size_access(self, state_page_size);
345
330
  }
346
331
 
347
- static VALUE lrt_node_size(VALUE self)
332
+ static VALUE rt_node_size(VALUE self)
348
333
  {
349
334
  return state_size_access(self, state_node_size);
350
335
  }
351
336
 
352
- static VALUE lrt_rect_size(VALUE self)
337
+ static VALUE rt_rect_size(VALUE self)
353
338
  {
354
339
  return state_size_access(self, state_rect_size);
355
340
  }
356
341
 
357
- static VALUE lrt_branch_size(VALUE self)
342
+ static VALUE rt_branch_size(VALUE self)
358
343
  {
359
344
  return state_size_access(self, state_branch_size);
360
345
  }
361
346
 
362
- static VALUE lrt_branching_factor(VALUE self)
347
+ static VALUE rt_branching_factor(VALUE self)
363
348
  {
364
349
  return state_size_access(self, state_branching_factor);
365
350
  }
366
351
 
367
- static VALUE lrt_unit_sphere_volume(VALUE self)
352
+ static VALUE rt_unit_sphere_volume(VALUE self)
368
353
  {
369
354
  rtree_t *rtree;
370
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
355
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
371
356
  return DBL2NUM(state_unit_sphere_volume(rtree->state));
372
357
  }
373
358
 
374
- static VALUE lrt_size(VALUE self)
359
+ static VALUE rt_size(VALUE self)
375
360
  {
376
361
  rtree_t *rtree;
377
- TypedData_Get_Struct(self, rtree_t, &type, rtree);
362
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
378
363
  return INT2NUM(rtree_bytes(rtree));
379
364
  }
380
365
 
381
- static VALUE lrt_version(VALUE self)
366
+ static VALUE rt_version(VALUE self)
382
367
  {
383
368
  return rb_str_new_cstr(rtree_package_version);
384
369
  }
385
370
 
386
- static VALUE lrt_bugreport(VALUE self)
371
+ static VALUE rt_bugreport(VALUE self)
387
372
  {
388
373
  return rb_str_new_cstr(rtree_package_bugreport);
389
374
  }
390
375
 
391
- static VALUE lrt_url(VALUE self)
376
+ static VALUE rt_url(VALUE self)
392
377
  {
393
378
  return rb_str_new_cstr(rtree_package_url);
394
379
  }
395
380
 
381
+ static VALUE rt_postscript(VALUE self,
382
+ VALUE style_obj,
383
+ VALUE axis_obj,
384
+ VALUE extent_obj,
385
+ VALUE margin_obj,
386
+ VALUE io_obj)
387
+ {
388
+ rtree_t *rtree;
389
+ TypedData_Get_Struct(self, rtree_t, &rtree_type, rtree);
390
+
391
+ style_t *style;
392
+ TypedData_Get_Struct(style_obj, style_t, &style_type, style);
393
+
394
+ Check_Type(io_obj, T_FILE);
395
+ rb_io_t *io;
396
+ GetOpenFile(io_obj, io);
397
+ rb_io_check_initialized(io);
398
+ rb_io_check_writable(io);
399
+ FILE *fp = rb_io_stdio_file(io);
400
+
401
+ float
402
+ extent = NUM2DBL(extent_obj),
403
+ margin = NUM2DBL(margin_obj);
404
+
405
+ Check_Type(axis_obj, T_FIXNUM);
406
+ extent_axis_t axis = FIX2UINT(axis_obj);
407
+
408
+ rtree_postscript_t opt =
409
+ {
410
+ .style = style,
411
+ .axis = axis,
412
+ .extent = extent,
413
+ .margin = margin,
414
+ .title = "librtree-ruby output"
415
+ };
416
+
417
+ int err;
418
+ if ((err = rtree_postscript(rtree, &opt, fp)) != 0)
419
+ rb_raise(rb_eRuntimeError, "librtree: %s", rtree_strerror(err));
420
+
421
+ return Qnil;
422
+ }
423
+
424
+ static rb_data_type_t rtree_type = {
425
+ .wrap_struct_name = "rtree-wrap",
426
+ .function = {
427
+ .dmark = NULL,
428
+ .dfree = rt_dfree,
429
+ .dsize = rt_dsize,
430
+ #if RUBY_API_VERSION_CODE < 20700
431
+ .reserved = { NULL, NULL }
432
+ #else
433
+ .dcompact = NULL,
434
+ .reserved = { NULL }
435
+ #endif
436
+ },
437
+ .parent = NULL,
438
+ .data = NULL,
439
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
440
+ };
441
+
442
+ /* RTree::Style */
443
+
444
+ static void st_dfree(void *p)
445
+ {
446
+ postscript_style_destroy((style_t*)p);
447
+ }
448
+
449
+ static VALUE st_release(VALUE self)
450
+ {
451
+ style_t *style;
452
+ TypedData_Get_Struct(self, style_t, &style_type, style);
453
+ postscript_style_destroy(style);
454
+ return self;
455
+ }
456
+
457
+ static VALUE st_json_read(VALUE cls, VALUE io_obj)
458
+ {
459
+ Check_Type(io_obj, T_FILE);
460
+
461
+ rb_io_t *io;
462
+ GetOpenFile(io_obj, io);
463
+ rb_io_check_initialized(io);
464
+ rb_io_check_readable(io);
465
+ FILE *fp = rb_io_stdio_file(io);
466
+
467
+ style_t *style;
468
+ errno = 0;
469
+
470
+ if ((style = postscript_style_read(fp)) == NULL)
471
+ {
472
+ if (errno)
473
+ rb_sys_fail(__func__);
474
+ else
475
+ rb_raise(rb_eRuntimeError, "Failed read from stream");
476
+ }
477
+
478
+ return TypedData_Wrap_Struct(cls, &style_type, style);
479
+ }
480
+
481
+ static rb_data_type_t style_type = {
482
+ .wrap_struct_name = "style-wrap",
483
+ .function = {
484
+ .dmark = NULL,
485
+ .dfree = st_dfree,
486
+ .dsize = NULL,
487
+ #if RUBY_API_VERSION_CODE < 20700
488
+ .reserved = { NULL, NULL }
489
+ #else
490
+ .dcompact = NULL,
491
+ .reserved = { NULL }
492
+ #endif
493
+ },
494
+ .parent = NULL,
495
+ .data = NULL,
496
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
497
+ };
498
+
396
499
  void Init_rtree(void)
397
500
  {
398
- VALUE cls = rb_const_get(rb_cObject, rb_intern("RTreeC"));
399
-
400
- rb_define_alloc_func(cls, lrt_alloc);
401
- rb_define_method(cls, "initialize", lrt_init, 2);
402
- rb_define_method(cls, "free", lrt_release, 0);
403
- rb_define_method(cls, "clone", lrt_clone, 0);
404
- rb_define_method(cls, "update!", lrt_update, 0);
405
- rb_define_method(cls, "height", lrt_height, 0);
406
- rb_define_method(cls, "add_rect", lrt_add_rect, 2);
407
- rb_define_method(cls, "search", lrt_search, 1);
408
- rb_define_method(cls, "json_write", lrt_json_write, 1);
409
- rb_define_method(cls, "bsrt_write", lrt_bsrt_write, 1);
410
- rb_define_method(cls, "eq?", lrt_identical, 1);
411
- rb_define_method(cls, "dim", lrt_dim, 0);
412
- rb_define_method(cls, "size", lrt_size, 0);
413
- rb_define_method(cls, "page_size", lrt_page_size, 0);
414
- rb_define_method(cls, "node_size", lrt_node_size, 0);
415
- rb_define_method(cls, "rect_size", lrt_rect_size, 0);
416
- rb_define_method(cls, "branch_size", lrt_branch_size, 0);
417
- rb_define_method(cls, "branching_factor", lrt_branching_factor, 0);
418
- rb_define_method(cls, "unit_sphere_volume", lrt_unit_sphere_volume, 0);
419
- rb_define_singleton_method(cls, "version", lrt_version, 0);
420
- rb_define_singleton_method(cls, "bugreport", lrt_bugreport, 0);
421
- rb_define_singleton_method(cls, "url", lrt_url, 0);
422
- rb_define_singleton_method(cls, "json_read", lrt_json_read, 1);
423
- rb_define_singleton_method(cls, "bsrt_read", lrt_bsrt_read, 1);
424
- rb_define_singleton_method(cls, "csv_read", lrt_csv_read, 3);
425
- rb_define_const(cls, "SPLIT_QUADRATIC", INT2NUM(RTREE_SPLIT_QUADRATIC));
426
- rb_define_const(cls, "SPLIT_LINEAR", INT2NUM(RTREE_SPLIT_LINEAR));
427
- rb_define_const(cls, "SPLIT_GREENE", INT2NUM(RTREE_SPLIT_GREENE));
501
+ VALUE cRTreeBase = rb_const_get(rb_cObject, rb_intern("RTreeBase"));
502
+
503
+ rb_define_alloc_func(cRTreeBase, rt_alloc);
504
+ rb_define_method(cRTreeBase, "initialize", rt_init, 2);
505
+ rb_define_method(cRTreeBase, "free", rt_release, 0);
506
+ rb_define_method(cRTreeBase, "clone", rt_clone, 0);
507
+ rb_define_method(cRTreeBase, "update!", rt_update, 0);
508
+ rb_define_method(cRTreeBase, "height", rt_height, 0);
509
+ rb_define_method(cRTreeBase, "add_rect", rt_add_rect, 2);
510
+ rb_define_method(cRTreeBase, "search", rt_search, 1);
511
+ rb_define_method(cRTreeBase, "json_write", rt_json_write, 1);
512
+ rb_define_method(cRTreeBase, "bsrt_write", rt_bsrt_write, 1);
513
+ rb_define_method(cRTreeBase, "eq?", rt_identical, 1);
514
+ rb_define_method(cRTreeBase, "dim", rt_dim, 0);
515
+ rb_define_method(cRTreeBase, "size", rt_size, 0);
516
+ rb_define_method(cRTreeBase, "page_size", rt_page_size, 0);
517
+ rb_define_method(cRTreeBase, "node_size", rt_node_size, 0);
518
+ rb_define_method(cRTreeBase, "rect_size", rt_rect_size, 0);
519
+ rb_define_method(cRTreeBase, "branch_size", rt_branch_size, 0);
520
+ rb_define_method(cRTreeBase, "branching_factor", rt_branching_factor, 0);
521
+ rb_define_method(cRTreeBase, "unit_sphere_volume", rt_unit_sphere_volume, 0);
522
+ rb_define_method(cRTreeBase, "postscript", rt_postscript, 5);
523
+ rb_define_singleton_method(cRTreeBase, "version", rt_version, 0);
524
+ rb_define_singleton_method(cRTreeBase, "bugreport", rt_bugreport, 0);
525
+ rb_define_singleton_method(cRTreeBase, "url", rt_url, 0);
526
+ rb_define_singleton_method(cRTreeBase, "json_read", rt_json_read, 1);
527
+ rb_define_singleton_method(cRTreeBase, "bsrt_read", rt_bsrt_read, 1);
528
+ rb_define_singleton_method(cRTreeBase, "csv_read", rt_csv_read, 3);
529
+ rb_define_const(cRTreeBase, "SPLIT_QUADRATIC", INT2NUM(RTREE_SPLIT_QUADRATIC));
530
+ rb_define_const(cRTreeBase, "SPLIT_LINEAR", INT2NUM(RTREE_SPLIT_LINEAR));
531
+ rb_define_const(cRTreeBase, "SPLIT_GREENE", INT2NUM(RTREE_SPLIT_GREENE));
532
+ rb_define_const(cRTreeBase, "AXIS_HEIGHT", INT2NUM(axis_height));
533
+ rb_define_const(cRTreeBase, "AXIS_WIDTH", INT2NUM(axis_width));
534
+
535
+ VALUE cRTreeStyleBase = rb_const_get(rb_cObject, rb_intern("RTreeStyleBase"));
536
+
537
+ rb_define_method(cRTreeStyleBase, "free", st_release, 0);
538
+ rb_define_singleton_method(cRTreeStyleBase, "json_read", st_json_read, 1);
428
539
  }
data/lib/rtree.rb CHANGED
@@ -1,5 +1,16 @@
1
- # The Ruby/C interface, which is not documented in YARD.
2
- class RTreeC ; end
1
+ # frozen_string_literal: true
2
+
3
+ # @!visibility private
4
+ #
5
+ class RTreeBase ; end
6
+
7
+ # @!visibility private
8
+ #
9
+ class RTreeStyleBase ; end
10
+
11
+ require 'rtree/rtree'
12
+ require 'json'
13
+ require 'fcntl'
3
14
 
4
15
  # @author RTree J. J. Green
5
16
  #
@@ -51,7 +62,79 @@ class RTreeC ; end
51
62
  # determined at compile-time (with the RTREE_ID_TYPE variable) and by
52
63
  # default this is a 64-bit unsigned integer.
53
64
  #
54
- class RTree < RTreeC
65
+ class RTree < RTreeBase
66
+
67
+ # @!visibility private
68
+ #
69
+ module IOUtil
70
+ extend self
71
+
72
+ # @!visibility private
73
+ #
74
+ def deserialise(string, encoding)
75
+ raise TypeError unless string.is_a? String
76
+ rd, wr = IO.pipe(encoding)
77
+ if fork then
78
+ wr.close
79
+ unset_nonblock(rd)
80
+ begin
81
+ result = yield(rd)
82
+ ensure
83
+ rd.close
84
+ Process.wait
85
+ end
86
+ else
87
+ rd.close
88
+ begin
89
+ wr.write(string)
90
+ ensure
91
+ wr.close
92
+ exit!
93
+ end
94
+ end
95
+ result
96
+ end
97
+
98
+ # @!visibility private
99
+ #
100
+ def serialise(encoding)
101
+ rd, wr = IO.pipe(encoding)
102
+ if fork then
103
+ wr.close
104
+ unset_nonblock(rd)
105
+ begin
106
+ result = rd.read
107
+ ensure
108
+ rd.close
109
+ Process.wait
110
+ end
111
+ else
112
+ rd.close
113
+ begin
114
+ yield(wr)
115
+ ensure
116
+ wr.close
117
+ exit!
118
+ end
119
+ end
120
+ result
121
+ end
122
+
123
+ private
124
+
125
+ # In Ruby 3, the Fibres facility exploits non-blocking IO and so pipes
126
+ # are non-blocking by default, this buggers-up the deserialise method
127
+ # since there is short delay forking here -- so we reinstate blocking
128
+ # with some fairly low-level fcntl(2) magic.
129
+
130
+ def unset_nonblock(fd)
131
+ fd.fcntl(
132
+ Fcntl::F_SETFL,
133
+ fd.fcntl(Fcntl::F_GETFL, 0) & ~Fcntl::O_NONBLOCK
134
+ )
135
+ end
136
+
137
+ end
55
138
 
56
139
  class << self
57
140
 
@@ -61,6 +144,7 @@ class RTree < RTreeC
61
144
  # @see #json_write
62
145
  # @example Read from file
63
146
  # rtree = File.open('rtree.json', 'r') { |io| RTree.json_read(io) }
147
+ #
64
148
  def json_read(io)
65
149
  super
66
150
  end
@@ -69,6 +153,7 @@ class RTree < RTreeC
69
153
  # @param json [String] a JSON string
70
154
  # @return [RTree] the newly instantiated RTree
71
155
  # @see #to_json
156
+ #
72
157
  def from_json(json)
73
158
  deserialise(json, Encoding::UTF_8) { |io| json_read(io) }
74
159
  end
@@ -80,6 +165,7 @@ class RTree < RTreeC
80
165
  # @see #bsrt_write
81
166
  # @example Read from file
82
167
  # rtree = File.open('rtree.bsrt', 'r') { |io| RTree.bsrt_read(io) }
168
+ #
83
169
  def bsrt_read(io)
84
170
  super
85
171
  end
@@ -89,6 +175,7 @@ class RTree < RTreeC
89
175
  # @param bsrt [String] a binary encoded string
90
176
  # @return [RTree] the newly instantiated RTree
91
177
  # @see #to_bsrt
178
+ #
92
179
  def from_bsrt(bsrt)
93
180
  deserialise(bsrt, Encoding::BINARY) { |io| bsrt_read(io) }
94
181
  end
@@ -104,6 +191,7 @@ class RTree < RTreeC
104
191
  # Extra columns may be present and will be ignored (this
105
192
  # useful feature is the reason that the dimension is a required
106
193
  # argument).
194
+ #
107
195
  def csv_read(io, dim, split: :quadratic, node_page: 0)
108
196
  flags = split_flag(split) | node_page_flag(node_page)
109
197
  super(io, dim, flags)
@@ -114,6 +202,7 @@ class RTree < RTreeC
114
202
  # @param dim [Integer] the dimension of the tree
115
203
  # @param kwarg [Hash] As for {.csv_read}
116
204
  # @return [RTree] the newly built RTree
205
+ #
117
206
  def from_csv(csv, dim, **kwarg)
118
207
  deserialise(csv, Encoding::UTF_8) do |io|
119
208
  csv_read(io, dim, **kwarg)
@@ -121,21 +210,31 @@ class RTree < RTreeC
121
210
  end
122
211
 
123
212
  # @return [Array<Integer>] version of librtree
213
+ #
124
214
  def version
125
215
  @version ||= super.split('.').map(&:to_i)
126
216
  end
127
217
 
128
218
  # @return [String] email address for librtree bug reports
219
+ #
129
220
  def bugreport
130
221
  super
131
222
  end
132
223
 
133
224
  # @return [String] librtree homepage
225
+ #
134
226
  def url
135
227
  super
136
228
  end
137
229
 
138
230
  # @!visibility private
231
+ # This seems to need the self qualification on the constants,
232
+ # without them we get a NameError, not sure why so asked on SO
233
+ # https://stackoverflow.com/questions/68122256/ It's still not
234
+ # clear to me why this qualification is needed, but that's a
235
+ # problem with my understanding of the Ruby Eigenclass, it is
236
+ # the expected behaviour
237
+ #
139
238
  def split_flag(split)
140
239
  case split
141
240
  when :quadratic
@@ -150,41 +249,24 @@ class RTree < RTreeC
150
249
  end
151
250
 
152
251
  # @!visibility private
252
+ #
153
253
  def node_page_flag(node_page)
154
254
  node_page << 2
155
255
  end
156
256
 
157
257
  private
158
258
 
159
- def deserialise(string, encoding)
160
- raise TypeError unless string.is_a? String
161
- rd, wr = IO.pipe(encoding)
162
- if fork then
163
- wr.close
164
- begin
165
- result = yield(rd)
166
- ensure
167
- rd.close
168
- Process.wait
169
- end
170
- else
171
- rd.close
172
- begin
173
- wr.write(string)
174
- ensure
175
- wr.close
176
- exit!
177
- end
178
- end
179
- result
259
+ def deserialise(*args, &block)
260
+ RTree::IOUtil.deserialise(*args, &block)
180
261
  end
262
+
181
263
  end
182
264
 
183
265
  # Initialize a new (empty) RTree
184
266
  # @param dim [Integer] the dimension of the tree
185
267
  # @param split [:linear, :quadratic, :greene] determines the splitting
186
268
  # strategy, the linear strategy is faster to build, the quadratic
187
- # and greene strategies produces a better-quality R-trees which is
269
+ # and greene strategies produce better-quality R-trees which are
188
270
  # faster to query.
189
271
  # @param node_page [Integer] the nodes-per-page value. This value can
190
272
  # affect performance quite dramatically, particularly build time. A
@@ -195,6 +277,7 @@ class RTree < RTreeC
195
277
  # You may get better performance for your use-case by manual
196
278
  # experimentation, but zero is a good place to start.
197
279
  # @return [RTree] the newly instantiated RTree
280
+ #
198
281
  def initialize(dim, split: :quadratic, node_page: 0)
199
282
  @split = split
200
283
  @node_page = node_page
@@ -203,6 +286,7 @@ class RTree < RTreeC
203
286
 
204
287
  # Create a deep copy
205
288
  # @return [RTree] a deep copy of the RTree
289
+ #
206
290
  def clone
207
291
  super
208
292
  end
@@ -219,6 +303,7 @@ class RTree < RTreeC
219
303
  # @example Add a rectangle to a dimension two RTree
220
304
  # rtree = RTree.new(2)
221
305
  # rtree.add_rect(7, [0, 0, 1, 1])
306
+ #
222
307
  def add_rect(id, coords)
223
308
  super
224
309
  end
@@ -228,6 +313,7 @@ class RTree < RTreeC
228
313
  # @return [Array<Integer>] the ids of all rectangles which intersect
229
314
  # the search rectangle. If a block is given then these values will
230
315
  # be yielded to the block (one at a time).
316
+ #
231
317
  def search(coords)
232
318
  if block_given? then
233
319
  super
@@ -253,20 +339,23 @@ class RTree < RTreeC
253
339
  # block and then convert the returned Ruby Floats to C; so we would
254
340
  # expect that it loses much of its competitive advantage when
255
341
  # compared to an R-tree rebuild.
342
+ #
256
343
  def update!
257
344
  super
258
345
  end
259
346
 
260
347
  # The height to the tree in the usual mathematical sense
261
348
  # @return [Integer] the tree height
349
+ #
262
350
  def height
263
351
  super
264
352
  end
265
353
 
266
354
  # Whether the RTree has any rectangles or not
267
355
  # @return [Boolean] true if the RTree is empty
356
+ #
268
357
  def empty?
269
- height == 0
358
+ height.zero?
270
359
  end
271
360
 
272
361
  # Serialise to JSON stream
@@ -275,6 +364,7 @@ class RTree < RTreeC
275
364
  # @see .json_read
276
365
  # @example Write to file
277
366
  # File.open('rtree.json', 'w') { |io| rtree.json_write(io) }
367
+ #
278
368
  def json_write(io)
279
369
  super
280
370
  end
@@ -285,6 +375,7 @@ class RTree < RTreeC
285
375
  # @see .bsrt_read
286
376
  # @example Write to file
287
377
  # File.open('rtree.bsrt', 'w') { |io| rtree.bsrt_write(io) }
378
+ #
288
379
  def bsrt_write(io)
289
380
  super
290
381
  end
@@ -292,6 +383,7 @@ class RTree < RTreeC
292
383
  # Serialise to JSON string
293
384
  # @return [String] the UTF-8 encoded JSON
294
385
  # @see .from_json
386
+ #
295
387
  def to_json
296
388
  serialise(Encoding::UTF_8) { |io| json_write(io) }
297
389
  end
@@ -299,12 +391,14 @@ class RTree < RTreeC
299
391
  # Serialise to BSRT string
300
392
  # @return [String] the binary encoded BSRT
301
393
  # @see .from_bsrt
394
+ #
302
395
  def to_bsrt
303
396
  serialise(Encoding::BINARY) { |io| bsrt_write(io) }
304
397
  end
305
398
 
306
399
  # The RTree structure in hash form
307
400
  # @return [Hash]
401
+ #
308
402
  def to_h
309
403
  JSON.parse(to_json, symbolize_names: true)
310
404
  end
@@ -314,6 +408,7 @@ class RTree < RTreeC
314
408
  # must be in the same order. Certainly {#clone} will produce
315
409
  # an instance which is equal in this sense.
316
410
  # @return [Boolean] true if the instances are identical
411
+ #
317
412
  def eq?(other)
318
413
  super
319
414
  end
@@ -321,6 +416,7 @@ class RTree < RTreeC
321
416
  alias_method(:==, :eq?)
322
417
 
323
418
  # @return [Integer] the dimension of the R-tree
419
+ #
324
420
  def dim
325
421
  super
326
422
  end
@@ -331,41 +427,77 @@ class RTree < RTreeC
331
427
  # running count). Performance-minded users may wish to
332
428
  # cache this value (invalidating the cache when calling
333
429
  # {#add_rect} of course).
430
+ #
334
431
  def size
335
432
  super
336
433
  end
337
434
 
338
435
  # @return [Integer] the bytes in a page of memory
436
+ #
339
437
  def page_size
340
438
  super
341
439
  end
342
440
 
343
441
  # @return [Integer] the size in bytes of a node
442
+ #
344
443
  def node_size
345
444
  super
346
445
  end
347
446
 
348
447
  # @return [Integer] the size in bytes of a rectangle
448
+ #
349
449
  def rect_size
350
450
  super
351
451
  end
352
452
 
353
453
  # @return [Integer] the size in bytes of a branch
454
+ #
354
455
  def branch_size
355
456
  super
356
457
  end
357
458
 
358
459
  # @return [Integer] the number of branches from each node
460
+ #
359
461
  def branching_factor
360
462
  super
361
463
  end
362
464
 
363
465
  # @return [Float] the volume of the unit sphere in the R-tree's
364
466
  # dimension
467
+ #
365
468
  def unit_sphere_volume
366
469
  super
367
470
  end
368
471
 
472
+ # Create a PostScript plot of the RTree
473
+ #
474
+ # @param io [IO] a writeable stream object
475
+ # @param style [RTree::Style] a style object describing the fill
476
+ # colour and stroke width and colour for each level of the tree.
477
+ # @param height [Float] the height of the plot in units of PostScript
478
+ # point (1/72 inch)
479
+ # @param width [Float] the width of the plot in units of PostScript
480
+ # point (1/72 inch), if neither height nor width is given then a
481
+ # width of 216 (3 inches) will be taken as default
482
+ # @param margin [Float] extra space around the plot in units of
483
+ # PostScript point (1/72 inch), default zero
484
+ #
485
+ # rdoc-image:img/cb-bupu3.png
486
+ #
487
+ def postscript(io, style, height: nil, width: nil, margin: 0)
488
+ if height && width then
489
+ raise ArgumentError, 'cannot specify both height and width'
490
+ end
491
+ if height then
492
+ axis = AXIS_HEIGHT
493
+ extent = height
494
+ else
495
+ axis = AXIS_WIDTH
496
+ extent = width || 216
497
+ end
498
+ super(style, axis, extent, margin, io)
499
+ end
500
+
369
501
  private
370
502
 
371
503
  attr_reader :split, :node_page
@@ -382,23 +514,73 @@ class RTree < RTreeC
382
514
  node_page_flag | split_flag
383
515
  end
384
516
 
385
- def serialise(encoding)
386
- rd, wr = IO.pipe(encoding)
387
- if fork then
388
- wr.close
389
- result = rd.read
390
- rd.close
391
- Process.wait
392
- else
393
- rd.close
394
- yield(wr)
395
- wr.close
396
- exit!
397
- end
398
- result
517
+ def serialise(*args, &block)
518
+ RTree::IOUtil.serialise(*args, &block)
399
519
  end
400
520
 
401
-
402
521
  end
403
522
 
404
- require 'rtree/rtree'
523
+ # @author RTree::Style J. J. Green
524
+ #
525
+ # A Ruby wrapper around RTree styles, used in PostScript plotting.
526
+ # in particular by {RTree#postscript}.
527
+ #
528
+ class RTree::Style < RTreeStyleBase
529
+
530
+ class << self
531
+
532
+ # Create a new Style instance from JSON stream
533
+ # @param io [IO] a readable stream object
534
+ # @return [RTree::Style] the newly instantiated Style
535
+ # @example Read from file
536
+ # style = File.open('some.style', 'r') do |io|
537
+ # RTree::Style.json_read(io)
538
+ # end
539
+ #
540
+ def json_read(io)
541
+ super
542
+ end
543
+
544
+ # Create a new Style instance from JSON string
545
+ # @param json [String] a JSON string
546
+ # @return [RTree::Style] the newly instantiated Style
547
+ # @see .json_read
548
+ #
549
+ def from_json(json)
550
+ deserialise(json, Encoding::UTF_8) { |io| json_read(io) }
551
+ end
552
+
553
+ # Create a new Style instance from array of Hash
554
+ # @param array [Array] an array of hash, this will be converted
555
+ # to JSON and read by .from_json
556
+ # @return [RTree::Style] the newly instantiated Style
557
+ # @see .from_json
558
+ # @example Create a simple one-level style:
559
+ # style = RTree::Style.from_a(
560
+ # [
561
+ # {
562
+ # fill: {
563
+ # rgb: [0.5, 0.5, 0.5]
564
+ # },
565
+ # stroke: {
566
+ # rgb: [0, 0, 1],
567
+ # width: 3
568
+ # }
569
+ # }
570
+ # ]
571
+ # )
572
+ #
573
+ def from_a(array)
574
+ from_json(array.to_json)
575
+ end
576
+
577
+ alias_method :from_array, :from_a
578
+
579
+ private
580
+
581
+ def deserialise(*args, &block)
582
+ RTree::IOUtil.deserialise(*args, &block)
583
+ end
584
+
585
+ end
586
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: librtree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.6
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - J.J. Green
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-17 00:00:00.000000000 Z
11
+ date: 2021-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2'
19
+ version: '2.1'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2'
26
+ version: '2.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.6'
47
+ version: '3.10'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.6'
54
+ version: '3.10'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: yard
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -70,30 +70,30 @@ dependencies:
70
70
  name: rubygems-tasks
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - "<="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.2.4
75
+ version: '0.2'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - "<="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.2.4
82
+ version: '0.2'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rake-compiler
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '1'
89
+ version: '1.1'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '1'
96
+ version: '1.1'
97
97
  description: |
98
98
  A Ruby extension implementing the R-tree spatial-index of
99
99
  Guttman-Green.