librtree 0.8.5 → 0.8.9

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rtree/rtree.c +206 -92
  3. data/lib/rtree.rb +184 -11
  4. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9abe4061328dd6a2b8b9f1c00a38f76332a0f0a188af7a5d3e6f1125eab346e
4
- data.tar.gz: 4029ed06803576cc76ec7bd8d3d1eb9a2d628da9fd982d04e80b643177fce244
3
+ metadata.gz: d796bcdad6e023f99009363c9c11a10d8c6dfc7b9c797b487925fa319ace78b9
4
+ data.tar.gz: 83d563781db1c1e1b6bd8b8790cbb68fbbd9a7f997dd49840ff8cba7bfcac3e8
5
5
  SHA512:
6
- metadata.gz: f6b7005f8ec37a45ff8ee894de5642eb969a26af72ad9fa330aeeae1c746447d786b309301e84116de595bed40f70bef8683fbd9a969b2b9c2cabb32e5c4d374
7
- data.tar.gz: c4c6641e2cdfd9f14f21b50047fe2f304ab1446eb643a0f6cd3acd594fbc37469cf71c78434daa82b759bdb2741a1f887add7809d72c7bbc3f39886aee3ecc9b
6
+ metadata.gz: e1a6c56ad9c14ae15c8ed047e980262477f36b672bb08c5cc584eff7b2fba3b20845e8671ee166f8929abbb5a7d11e596cca36da6d89a67cef167773ada20577
7
+ data.tar.gz: b699d85386e8ea991eb2d4e4816635b6a0d550e2dbeb36b670526186d0b72f72f8cf763d22ba6339fe595dc3f70c7d6f9e9bfa5714216ec125e0b1748c146d97
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,102 +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);
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);
425
539
  }
data/lib/rtree.rb CHANGED
@@ -1,5 +1,14 @@
1
- # The Ruby/C interface, which is not documented in YARD.
2
- class RTreeC ; end
1
+ # The internal Ruby/C interface for RTree, not documented in YARD.
2
+ #
3
+ class RTreeBase ; end
4
+
5
+ # The internal Ruby/C interface for RTree::Style, not documented in YARD.
6
+ #
7
+ class RTreeStyleBase ; end
8
+
9
+ require 'rtree/rtree'
10
+ require 'json'
11
+ require 'fcntl'
3
12
 
4
13
  # @author RTree J. J. Green
5
14
  #
@@ -51,7 +60,26 @@ class RTreeC ; end
51
60
  # determined at compile-time (with the RTREE_ID_TYPE variable) and by
52
61
  # default this is a 64-bit unsigned integer.
53
62
  #
54
- class RTree < RTreeC
63
+ class RTree < RTreeBase
64
+
65
+ module IOUtil
66
+
67
+ private
68
+
69
+ # In Ruby 3, the Fibres facility exploits non-blocking IO and so pipes
70
+ # are non-blocking by default, this buggers-up the deserialise method
71
+ # since there is short delay forking here -- so we reinstate blocking
72
+ # with some fairly low-level fcntl)2) magick.
73
+
74
+ def unset_nonblock(fd)
75
+ flags0 = fd.fcntl(Fcntl::F_GETFL, 0)
76
+ flags1 = flags0 & ~Fcntl::O_NONBLOCK
77
+ fd.fcntl(Fcntl::F_SETFL, flags1)
78
+ end
79
+
80
+ end
81
+
82
+ extend IOUtil
55
83
 
56
84
  class << self
57
85
 
@@ -61,6 +89,7 @@ class RTree < RTreeC
61
89
  # @see #json_write
62
90
  # @example Read from file
63
91
  # rtree = File.open('rtree.json', 'r') { |io| RTree.json_read(io) }
92
+ #
64
93
  def json_read(io)
65
94
  super
66
95
  end
@@ -69,6 +98,7 @@ class RTree < RTreeC
69
98
  # @param json [String] a JSON string
70
99
  # @return [RTree] the newly instantiated RTree
71
100
  # @see #to_json
101
+ #
72
102
  def from_json(json)
73
103
  deserialise(json, Encoding::UTF_8) { |io| json_read(io) }
74
104
  end
@@ -80,6 +110,7 @@ class RTree < RTreeC
80
110
  # @see #bsrt_write
81
111
  # @example Read from file
82
112
  # rtree = File.open('rtree.bsrt', 'r') { |io| RTree.bsrt_read(io) }
113
+ #
83
114
  def bsrt_read(io)
84
115
  super
85
116
  end
@@ -89,6 +120,7 @@ class RTree < RTreeC
89
120
  # @param bsrt [String] a binary encoded string
90
121
  # @return [RTree] the newly instantiated RTree
91
122
  # @see #to_bsrt
123
+ #
92
124
  def from_bsrt(bsrt)
93
125
  deserialise(bsrt, Encoding::BINARY) { |io| bsrt_read(io) }
94
126
  end
@@ -96,7 +128,7 @@ class RTree < RTreeC
96
128
  # Build a new RTree instance from CSV stream
97
129
  # @param io [IO] a readable stream object
98
130
  # @param dim [Integer] the dimension of the tree
99
- # @param split [:linear, :quadratic] See {#initialize}
131
+ # @param split [:linear, :quadratic, :greene] See {#initialize}
100
132
  # @param node_page [Integer] See {#initialize}
101
133
  # @return [RTree] the newly built RTree
102
134
  # @note The CSV file (without header) should have the id in the
@@ -104,6 +136,7 @@ class RTree < RTreeC
104
136
  # Extra columns may be present and will be ignored (this
105
137
  # useful feature is the reason that the dimension is a required
106
138
  # argument).
139
+ #
107
140
  def csv_read(io, dim, split: :quadratic, node_page: 0)
108
141
  flags = split_flag(split) | node_page_flag(node_page)
109
142
  super(io, dim, flags)
@@ -114,6 +147,7 @@ class RTree < RTreeC
114
147
  # @param dim [Integer] the dimension of the tree
115
148
  # @param kwarg [Hash] As for {.csv_read}
116
149
  # @return [RTree] the newly built RTree
150
+ #
117
151
  def from_csv(csv, dim, **kwarg)
118
152
  deserialise(csv, Encoding::UTF_8) do |io|
119
153
  csv_read(io, dim, **kwarg)
@@ -121,33 +155,46 @@ class RTree < RTreeC
121
155
  end
122
156
 
123
157
  # @return [Array<Integer>] version of librtree
158
+ #
124
159
  def version
125
160
  @version ||= super.split('.').map(&:to_i)
126
161
  end
127
162
 
128
163
  # @return [String] email address for librtree bug reports
164
+ #
129
165
  def bugreport
130
166
  super
131
167
  end
132
168
 
133
169
  # @return [String] librtree homepage
170
+ #
134
171
  def url
135
172
  super
136
173
  end
137
174
 
138
175
  # @!visibility private
176
+ # This seems to need the self qualification on the constants,
177
+ # without them we get a NameError, not sure why so asked on SO
178
+ # https://stackoverflow.com/questions/68122256/ It's still not
179
+ # clear to me why this qualification is needed, but that's a
180
+ # problem with my understanding of the Ruby Eigenclass, it is
181
+ # the expected behaviour
182
+ #
139
183
  def split_flag(split)
140
184
  case split
141
185
  when :quadratic
142
- 0
186
+ self::SPLIT_QUADRATIC
143
187
  when :linear
144
- 1
188
+ self::SPLIT_LINEAR
189
+ when :greene
190
+ self::SPLIT_GREENE
145
191
  else
146
192
  raise ArgumentError, "bad split value: #{split}"
147
193
  end
148
194
  end
149
195
 
150
196
  # @!visibility private
197
+ #
151
198
  def node_page_flag(node_page)
152
199
  node_page << 2
153
200
  end
@@ -157,6 +204,7 @@ class RTree < RTreeC
157
204
  def deserialise(string, encoding)
158
205
  raise TypeError unless string.is_a? String
159
206
  rd, wr = IO.pipe(encoding)
207
+ unset_nonblock(rd)
160
208
  if fork then
161
209
  wr.close
162
210
  begin
@@ -180,9 +228,10 @@ class RTree < RTreeC
180
228
 
181
229
  # Initialize a new (empty) RTree
182
230
  # @param dim [Integer] the dimension of the tree
183
- # @param split [:linear, :quadratic] determines the splitting strategy,
184
- # the linear strategy is faster to build, the quadratic produces a
185
- # better-quality R-tree which is faster to query.
231
+ # @param split [:linear, :quadratic, :greene] determines the splitting
232
+ # strategy, the linear strategy is faster to build, the quadratic
233
+ # and greene strategies produce better-quality R-trees which are
234
+ # faster to query.
186
235
  # @param node_page [Integer] the nodes-per-page value. This value can
187
236
  # affect performance quite dramatically, particularly build time. A
188
237
  # value which is too large would result in an infeasible branching
@@ -192,6 +241,7 @@ class RTree < RTreeC
192
241
  # You may get better performance for your use-case by manual
193
242
  # experimentation, but zero is a good place to start.
194
243
  # @return [RTree] the newly instantiated RTree
244
+ #
195
245
  def initialize(dim, split: :quadratic, node_page: 0)
196
246
  @split = split
197
247
  @node_page = node_page
@@ -200,6 +250,7 @@ class RTree < RTreeC
200
250
 
201
251
  # Create a deep copy
202
252
  # @return [RTree] a deep copy of the RTree
253
+ #
203
254
  def clone
204
255
  super
205
256
  end
@@ -216,6 +267,7 @@ class RTree < RTreeC
216
267
  # @example Add a rectangle to a dimension two RTree
217
268
  # rtree = RTree.new(2)
218
269
  # rtree.add_rect(7, [0, 0, 1, 1])
270
+ #
219
271
  def add_rect(id, coords)
220
272
  super
221
273
  end
@@ -225,6 +277,7 @@ class RTree < RTreeC
225
277
  # @return [Array<Integer>] the ids of all rectangles which intersect
226
278
  # the search rectangle. If a block is given then these values will
227
279
  # be yielded to the block (one at a time).
280
+ #
228
281
  def search(coords)
229
282
  if block_given? then
230
283
  super
@@ -250,18 +303,21 @@ class RTree < RTreeC
250
303
  # block and then convert the returned Ruby Floats to C; so we would
251
304
  # expect that it loses much of its competitive advantage when
252
305
  # compared to an R-tree rebuild.
306
+ #
253
307
  def update!
254
308
  super
255
309
  end
256
310
 
257
311
  # The height to the tree in the usual mathematical sense
258
312
  # @return [Integer] the tree height
313
+ #
259
314
  def height
260
315
  super
261
316
  end
262
317
 
263
318
  # Whether the RTree has any rectangles or not
264
319
  # @return [Boolean] true if the RTree is empty
320
+ #
265
321
  def empty?
266
322
  height == 0
267
323
  end
@@ -272,6 +328,7 @@ class RTree < RTreeC
272
328
  # @see .json_read
273
329
  # @example Write to file
274
330
  # File.open('rtree.json', 'w') { |io| rtree.json_write(io) }
331
+ #
275
332
  def json_write(io)
276
333
  super
277
334
  end
@@ -282,6 +339,7 @@ class RTree < RTreeC
282
339
  # @see .bsrt_read
283
340
  # @example Write to file
284
341
  # File.open('rtree.bsrt', 'w') { |io| rtree.bsrt_write(io) }
342
+ #
285
343
  def bsrt_write(io)
286
344
  super
287
345
  end
@@ -289,6 +347,7 @@ class RTree < RTreeC
289
347
  # Serialise to JSON string
290
348
  # @return [String] the UTF-8 encoded JSON
291
349
  # @see .from_json
350
+ #
292
351
  def to_json
293
352
  serialise(Encoding::UTF_8) { |io| json_write(io) }
294
353
  end
@@ -296,12 +355,14 @@ class RTree < RTreeC
296
355
  # Serialise to BSRT string
297
356
  # @return [String] the binary encoded BSRT
298
357
  # @see .from_bsrt
358
+ #
299
359
  def to_bsrt
300
360
  serialise(Encoding::BINARY) { |io| bsrt_write(io) }
301
361
  end
302
362
 
303
363
  # The RTree structure in hash form
304
364
  # @return [Hash]
365
+ #
305
366
  def to_h
306
367
  JSON.parse(to_json, symbolize_names: true)
307
368
  end
@@ -311,6 +372,7 @@ class RTree < RTreeC
311
372
  # must be in the same order. Certainly {#clone} will produce
312
373
  # an instance which is equal in this sense.
313
374
  # @return [Boolean] true if the instances are identical
375
+ #
314
376
  def eq?(other)
315
377
  super
316
378
  end
@@ -318,6 +380,7 @@ class RTree < RTreeC
318
380
  alias_method(:==, :eq?)
319
381
 
320
382
  # @return [Integer] the dimension of the R-tree
383
+ #
321
384
  def dim
322
385
  super
323
386
  end
@@ -328,41 +391,74 @@ class RTree < RTreeC
328
391
  # running count). Performance-minded users may wish to
329
392
  # cache this value (invalidating the cache when calling
330
393
  # {#add_rect} of course).
394
+ #
331
395
  def size
332
396
  super
333
397
  end
334
398
 
335
399
  # @return [Integer] the bytes in a page of memory
400
+ #
336
401
  def page_size
337
402
  super
338
403
  end
339
404
 
340
405
  # @return [Integer] the size in bytes of a node
406
+ #
341
407
  def node_size
342
408
  super
343
409
  end
344
410
 
345
411
  # @return [Integer] the size in bytes of a rectangle
412
+ #
346
413
  def rect_size
347
414
  super
348
415
  end
349
416
 
350
417
  # @return [Integer] the size in bytes of a branch
418
+ #
351
419
  def branch_size
352
420
  super
353
421
  end
354
422
 
355
423
  # @return [Integer] the number of branches from each node
424
+ #
356
425
  def branching_factor
357
426
  super
358
427
  end
359
428
 
360
429
  # @return [Float] the volume of the unit sphere in the R-tree's
361
430
  # dimension
431
+ #
362
432
  def unit_sphere_volume
363
433
  super
364
434
  end
365
435
 
436
+ # Create a PostScript plot of the RTree
437
+ # @param io [IO] a writeable stream object
438
+ # @param style [RTree::Style] a style object describing the fill
439
+ # colour and stroke width and colour for each level of the tree.
440
+ # @param height [Float] the height of the plot in units of PostScript
441
+ # point (1/72 inch)
442
+ # @param width [Float] the width of the plot in units of PostScript
443
+ # point (1/72 inch), if neither height nor width is given then a
444
+ # width of 216 (3 inches) will be taken as default
445
+ # @param margin [Float] extra space around the plot in units of
446
+ # PostScript point (1/72 inch), default zero
447
+ #
448
+ def postscript(io, style, height: nil, width: nil, margin: 0)
449
+ if height && width then
450
+ raise ArgumentError, 'cannot specify both height and width'
451
+ end
452
+ if height then
453
+ axis = AXIS_HEIGHT
454
+ extent = height
455
+ else
456
+ axis = AXIS_WIDTH
457
+ extent = width || 216
458
+ end
459
+ super(style, axis, extent, margin, io)
460
+ end
461
+
366
462
  private
367
463
 
368
464
  attr_reader :split, :node_page
@@ -394,8 +490,85 @@ class RTree < RTreeC
394
490
  end
395
491
  result
396
492
  end
493
+ end
494
+
495
+ # @author RTree::Style J. J. Green
496
+ #
497
+ # A Ruby wrapper around RTree styles, used in PostScript plotting.
498
+ # in particular by {RTree#postscript}.
499
+ #
500
+ class RTree::Style < RTreeStyleBase
397
501
 
502
+ extend RTree::IOUtil
398
503
 
399
- end
504
+ class << self
400
505
 
401
- require 'rtree/rtree'
506
+ # Create a new Style instance from JSON stream
507
+ # @param io [IO] a readable stream object
508
+ # @return [RTree::Style] the newly instantiated Style
509
+ # @example Read from file
510
+ # style = File.open('some.style', 'r') do |io|
511
+ # RTree::Style.json_read(io)
512
+ # end
513
+ #
514
+ def json_read(io)
515
+ super
516
+ end
517
+
518
+ # Create a new Style instance from JSON string
519
+ # @param json [String] a JSON string
520
+ # @return [RTree::Style] the newly instantiated Style
521
+ # @see .json_read
522
+ #
523
+ def from_json(json)
524
+ raise TypeError unless json.is_a? String
525
+ rd, wr = IO.pipe(Encoding::UTF_8)
526
+ unset_nonblock(rd)
527
+ if fork then
528
+ wr.close
529
+ begin
530
+ result = json_read(rd)
531
+ ensure
532
+ rd.close
533
+ Process.wait
534
+ end
535
+ else
536
+ rd.close
537
+ begin
538
+ wr.write(json)
539
+ ensure
540
+ wr.close
541
+ exit!
542
+ end
543
+ end
544
+ result
545
+ end
546
+
547
+ # Create a new Style instance from array of Hash
548
+ # @param array [Array] an array of hash, this will be converted
549
+ # to JSON and read by .from_json
550
+ # @return [RTree::Style] the newly instantiated Style
551
+ # @see .from_json
552
+ # @example Create a simple one-level style:
553
+ # style = RTree::Style.from_a(
554
+ # [
555
+ # {
556
+ # fill: {
557
+ # rgb: [0.5, 0.5, 0.5]
558
+ # },
559
+ # stroke: {
560
+ # rgb: [0, 0, 1],
561
+ # width: 3
562
+ # }
563
+ # }
564
+ # ]
565
+ # )
566
+ #
567
+ def from_a(array)
568
+ from_json(array.to_json)
569
+ end
570
+
571
+ alias_method :from_array, :from_a
572
+
573
+ end
574
+ 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.5
4
+ version: 0.8.9
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-13 00:00:00.000000000 Z
11
+ date: 2021-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -126,8 +126,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
126
  - !ruby/object:Gem::Version
127
127
  version: '0'
128
128
  requirements:
129
- - The librtree library (1.0.5 or later)
130
- rubygems_version: 3.1.2
129
+ - The librtree library (1.0.6 or later)
130
+ rubygems_version: 3.2.3
131
131
  signing_key:
132
132
  specification_version: 4
133
133
  summary: R-tree spatial index