librtree 0.8.6 → 0.9.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.
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.