neartree 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +34 -0
- data/ext/neartree/NearTree.c +316 -0
- data/ext/neartree/extconf.rb +12 -0
- data/neartree.gemspec +21 -0
- metadata +54 -0
data/README.rdoc
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
= neartree
|
2
|
+
|
3
|
+
== Ruby bindings for the NearTree C library
|
4
|
+
|
5
|
+
This project implements Ruby bindings for the NearTree C library. NearTree
|
6
|
+
provides an R-Tree structure with fast nearest k-neighbour searches.
|
7
|
+
|
8
|
+
The NearTree C library is at:
|
9
|
+
|
10
|
+
http://neartree.sourceforge.net/
|
11
|
+
|
12
|
+
The bindings have only been tested on NearTree 2.3.2.
|
13
|
+
|
14
|
+
|
15
|
+
== Usage
|
16
|
+
|
17
|
+
require 'neartree'
|
18
|
+
|
19
|
+
rtree = NearTree::RTree.new(2) # number of dimensions
|
20
|
+
|
21
|
+
# insert coordinates/value pair
|
22
|
+
# coordinates must be an array of Numeric objects
|
23
|
+
# The value can be any ruby object
|
24
|
+
rtree.insert([ 0.0, 0.0], 10.0) # coordinates, value
|
25
|
+
rtree.insert([ 2.0, 0.0], 2.0)
|
26
|
+
rtree.insert([-2.0, -2.0], 0.0)
|
27
|
+
|
28
|
+
rtree.find_nearest([ 0.5, 0.5]) # => [[0.0, 0.0], 10.0]
|
29
|
+
rtree.find_nearest([100.0, 0.0]) # => [[2.0, 0.0], 2.0]
|
30
|
+
|
31
|
+
# coordinates, number_of_neighbours, radius
|
32
|
+
rtree.find_k_nearest([1.2, 0.5], 3, 2.0) # => [[[2.0, 0.0], 2.0], [[0.0, 0.0], 10.0]]
|
33
|
+
rtree.find_k_nearest([1.2, 0.5], 1, 2.0) # => [[[2.0, 0.0], 2.0]]
|
34
|
+
|
@@ -0,0 +1,316 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <CNearTree.h>
|
3
|
+
#include <limits.h>
|
4
|
+
#include <stdbool.h>
|
5
|
+
|
6
|
+
VALUE neartree_module;
|
7
|
+
VALUE neartree_class;
|
8
|
+
|
9
|
+
typedef struct {
|
10
|
+
int dimension; /* dimensionality of the space */
|
11
|
+
// bool has_radius; /* is radius used for searchs ? */
|
12
|
+
// double radius; /* radius for the searchs */
|
13
|
+
CNearTreeHandle* tree_handle; /* pointer to the actual CNearTree struct */
|
14
|
+
VALUE points; /* ruby array of the points inserted */
|
15
|
+
VALUE values; /* ruby array of the values inserted */
|
16
|
+
} NearTreeRTree;
|
17
|
+
|
18
|
+
/* aux: error handling */
|
19
|
+
void check_error_code(int error_code) {
|
20
|
+
if (error_code != CNEARTREE_SUCCESS) {
|
21
|
+
char* message = "NearTree error: %s"; /* message */
|
22
|
+
char* error_string = "unkown error"; /* default error */
|
23
|
+
VALUE exception = rb_eRuntimeError; /* default exception */
|
24
|
+
|
25
|
+
switch(error_code) {
|
26
|
+
case CNEARTREE_MALLOC_FAILED:
|
27
|
+
error_string = "malloc failed";
|
28
|
+
break;
|
29
|
+
case CNEARTREE_BAD_ARGUMENT:
|
30
|
+
error_string = "bad argument";
|
31
|
+
exception = rb_eArgError;
|
32
|
+
break;
|
33
|
+
case CNEARTREE_NOT_FOUND:
|
34
|
+
error_string = "not found";
|
35
|
+
exception = rb_eKeyError;
|
36
|
+
break;
|
37
|
+
case CNEARTREE_FREE_FAILED:
|
38
|
+
error_string = "free failed";
|
39
|
+
break;
|
40
|
+
case CNEARTREE_CVECTOR_FAILED:
|
41
|
+
error_string = "CVector failed";
|
42
|
+
break;
|
43
|
+
}
|
44
|
+
|
45
|
+
rb_raise(exception, message, error_string);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
/* aux: check CVector error */
|
50
|
+
void check_cvector_error(int error_code) {
|
51
|
+
if (error_code < 0) {
|
52
|
+
rb_raise(rb_eRuntimeError, "CVector error %d", error_code);
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
/* aux: get struct handle for an object */
|
57
|
+
NearTreeRTree* get_object_handle(VALUE self) {
|
58
|
+
NearTreeRTree* handle;
|
59
|
+
Data_Get_Struct(self, NearTreeRTree, handle);
|
60
|
+
return handle;
|
61
|
+
}
|
62
|
+
|
63
|
+
/* aux: get C dobule array from ruby index array, must be xfree()d when done with */
|
64
|
+
double* get_array_from_index(int dimension, VALUE index) {
|
65
|
+
/* check index is Array */
|
66
|
+
if (TYPE(index) != T_ARRAY) {
|
67
|
+
rb_raise(rb_eTypeError, "Index is not an Array");
|
68
|
+
}
|
69
|
+
|
70
|
+
/* check index array length */
|
71
|
+
int index_length = RARRAY_LEN(index);
|
72
|
+
if (index_length != dimension) {
|
73
|
+
rb_raise(rb_eArgError,
|
74
|
+
"Invalid index array, should be %i, is %i", dimension, index_length);
|
75
|
+
}
|
76
|
+
|
77
|
+
/* copy to double array */
|
78
|
+
VALUE* index_ptr = RARRAY_PTR(index);
|
79
|
+
double* array_index = ALLOC_N(double, dimension);
|
80
|
+
for(int i = 0; i != dimension; ++i) { array_index[i] = NUM2DBL(index_ptr[i]); }
|
81
|
+
|
82
|
+
return array_index;
|
83
|
+
}
|
84
|
+
|
85
|
+
/* aux: extract the radius parameter */
|
86
|
+
double get_radius_from_value(int argc, VALUE* argv, int index) {
|
87
|
+
if (index < argc && argv[index] != Qnil) {
|
88
|
+
double radius = NUM2DBL(argv[index]);
|
89
|
+
if (radius < 0.0) {
|
90
|
+
rb_raise(rb_eArgError, "Radius must not be negative.");
|
91
|
+
}
|
92
|
+
|
93
|
+
return radius;
|
94
|
+
} else { // no radius parameter : default
|
95
|
+
return DBL_MAX;
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
/* NearTree#initialize */
|
100
|
+
// FEATURE add support for other types of vectors (integer, string)
|
101
|
+
// FEATURE add support for NearTree norms
|
102
|
+
VALUE method_initialize(VALUE self, VALUE dimension) {
|
103
|
+
NearTreeRTree* handle = get_object_handle(self); /* handle */
|
104
|
+
|
105
|
+
/* dimension */
|
106
|
+
if (!FIXNUM_P(dimension)) {
|
107
|
+
rb_raise(rb_eTypeError, "Invalid dimension");
|
108
|
+
}
|
109
|
+
int d = NUM2INT(dimension);
|
110
|
+
if (d <= 0) { rb_raise(rb_eArgError, "Invalid dimension %i", d); }
|
111
|
+
handle->dimension = d;
|
112
|
+
|
113
|
+
/* create CNearTree */
|
114
|
+
check_error_code(CNearTreeCreate(handle->tree_handle, d, CNEARTREE_TYPE_DOUBLE));
|
115
|
+
|
116
|
+
return self;
|
117
|
+
}
|
118
|
+
|
119
|
+
/* NearTree#insert */
|
120
|
+
VALUE method_insert(VALUE self, VALUE index, VALUE value) {
|
121
|
+
NearTreeRTree* handle = get_object_handle(self); /* handle */
|
122
|
+
|
123
|
+
/* array index, check for bad parameter */
|
124
|
+
double* array_index = get_array_from_index(handle->dimension, index);
|
125
|
+
|
126
|
+
/* insert point/value */
|
127
|
+
rb_ary_push(handle->points, index);
|
128
|
+
rb_ary_push(handle->values, value);
|
129
|
+
|
130
|
+
/* insert element */
|
131
|
+
check_error_code(
|
132
|
+
CNearTreeImmediateInsert(*(handle->tree_handle), (void*)array_index, (void*)value));
|
133
|
+
|
134
|
+
xfree(array_index); /* free array index */
|
135
|
+
|
136
|
+
return Qnil;
|
137
|
+
}
|
138
|
+
|
139
|
+
/* NearTree#find_nearest(index, radius = nil) */
|
140
|
+
VALUE method_find_nearest(int argc, VALUE* argv, VALUE self) {
|
141
|
+
if (!(argc == 1 || argc == 2)) {
|
142
|
+
rb_raise(rb_eArgError, "Invalid parameter number: %d", argc);
|
143
|
+
}
|
144
|
+
|
145
|
+
NearTreeRTree* handle = get_object_handle(self); /* handle */
|
146
|
+
|
147
|
+
/* array index */
|
148
|
+
double* array_index = get_array_from_index(handle->dimension, argv[0]);
|
149
|
+
|
150
|
+
/* radius */
|
151
|
+
double radius = get_radius_from_value(argc, argv, 1);
|
152
|
+
|
153
|
+
VALUE* value; /* nearest value */
|
154
|
+
double* nearest_point; /* nearest point */
|
155
|
+
|
156
|
+
check_error_code(CNearTreeNearestNeighbor(*(handle->tree_handle),
|
157
|
+
radius, (void*)&nearest_point, (void*)&value, array_index));
|
158
|
+
|
159
|
+
xfree(array_index); /* free array index */
|
160
|
+
|
161
|
+
/* nearest_point to ruby array */
|
162
|
+
VALUE point_array = rb_ary_new();
|
163
|
+
for(int i = 0; i != handle->dimension; ++i) {
|
164
|
+
rb_ary_push(point_array, DBL2NUM(nearest_point[i]));
|
165
|
+
}
|
166
|
+
|
167
|
+
/* result array */
|
168
|
+
VALUE result_array = rb_ary_new();
|
169
|
+
rb_ary_push(result_array, point_array);
|
170
|
+
rb_ary_push(result_array, *value);
|
171
|
+
|
172
|
+
return result_array;
|
173
|
+
}
|
174
|
+
|
175
|
+
/* NearTree#find_k_nearest(index, k, radius = nil) */
|
176
|
+
VALUE method_find_k_nearest(int argc, VALUE* argv, VALUE self) {
|
177
|
+
if (!(argc == 2 || argc == 3)) {
|
178
|
+
rb_raise(rb_eArgError, "Invalid parameter number: %d", argc);
|
179
|
+
}
|
180
|
+
|
181
|
+
NearTreeRTree* handle = get_object_handle(self); /* handle */
|
182
|
+
|
183
|
+
/* array index */
|
184
|
+
double* array_index = get_array_from_index(handle->dimension, argv[0]);
|
185
|
+
|
186
|
+
/* k */
|
187
|
+
if (!FIXNUM_P(argv[1])) {
|
188
|
+
rb_raise(rb_eTypeError, "Invalid k");
|
189
|
+
}
|
190
|
+
long k = FIX2INT(argv[1]);
|
191
|
+
// if (INT2FIX((int)k) != argv[1]) {
|
192
|
+
// }
|
193
|
+
if (k <= 0) {
|
194
|
+
rb_raise(rb_eArgError, "k must be superior to 0");
|
195
|
+
}
|
196
|
+
|
197
|
+
/* radius */
|
198
|
+
double radius = get_radius_from_value(argc, argv, 2);
|
199
|
+
|
200
|
+
CVectorHandle nearest_points;
|
201
|
+
CVectorHandle nearest_values;
|
202
|
+
check_cvector_error(CVectorCreate(&nearest_points, sizeof(void*), k));
|
203
|
+
check_cvector_error(CVectorCreate(&nearest_values, sizeof(void*), k));
|
204
|
+
|
205
|
+
check_error_code(CNearTreeFindKNearest(*(handle->tree_handle), (size_t)k, radius,
|
206
|
+
nearest_points, nearest_values, array_index, 0));
|
207
|
+
|
208
|
+
xfree(array_index); /* free array index */
|
209
|
+
|
210
|
+
/* result array */
|
211
|
+
VALUE result_array = rb_ary_new();
|
212
|
+
size_t points_size;
|
213
|
+
size_t values_size;
|
214
|
+
check_cvector_error(CVectorGetSize(nearest_points, &points_size));
|
215
|
+
check_cvector_error(CVectorGetSize(nearest_values, &values_size));
|
216
|
+
|
217
|
+
if (points_size != values_size) {
|
218
|
+
rb_raise(rb_eRuntimeError, "Vector sizes error");
|
219
|
+
}
|
220
|
+
|
221
|
+
for(int i = 0; i != points_size; ++i) {
|
222
|
+
VALUE result = rb_ary_new();
|
223
|
+
/* coord */
|
224
|
+
VALUE result_coord = rb_ary_new();
|
225
|
+
for(int j = 0; j != handle->dimension; ++j) {
|
226
|
+
rb_ary_push(result_coord,
|
227
|
+
DBL2NUM(((double**)(nearest_points->array))[i][j]));
|
228
|
+
}
|
229
|
+
rb_ary_push(result, result_coord);
|
230
|
+
rb_ary_push(result, *(((VALUE**)nearest_values->array)[i]));
|
231
|
+
|
232
|
+
rb_ary_push(result_array, result);
|
233
|
+
}
|
234
|
+
|
235
|
+
return result_array;
|
236
|
+
}
|
237
|
+
|
238
|
+
/* writer : radius */
|
239
|
+
/*VALUE method_radius_equal(VALUE self, VALUE radius) {
|
240
|
+
NearTreeRTree* handle = get_object_handle(self);
|
241
|
+
if (radius == Qnil) {
|
242
|
+
handle->has_radius = false;
|
243
|
+
handle->radius = DBL_MAX;
|
244
|
+
} else {
|
245
|
+
handle->has_radius = true;
|
246
|
+
handle->radius = NUM2DBL(radius);
|
247
|
+
}
|
248
|
+
return DBL2NUM(handle->radius);
|
249
|
+
} */
|
250
|
+
|
251
|
+
/* readers: dimension/points/radius/values */
|
252
|
+
VALUE method_dimension(VALUE self) { return INT2NUM(get_object_handle(self)->dimension); }
|
253
|
+
VALUE method_points( VALUE self) { return get_object_handle(self)->points; }
|
254
|
+
VALUE method_values( VALUE self) { return get_object_handle(self)->values; }
|
255
|
+
|
256
|
+
VALUE method_empty(VALUE self) {
|
257
|
+
return RARRAY_LEN(get_object_handle(self)->points) == 0 ? Qtrue : Qfalse;
|
258
|
+
}
|
259
|
+
|
260
|
+
/*VALUE method_radius(VALUE self) {
|
261
|
+
NearTreeRTree* handle = get_object_handle(self);
|
262
|
+
if (handle->has_radius) {
|
263
|
+
return DBL2NUM(handle->radius);
|
264
|
+
} else {
|
265
|
+
return Qnil;
|
266
|
+
}
|
267
|
+
} */
|
268
|
+
|
269
|
+
/* called by GC on marking of the object */
|
270
|
+
void neartree_gc_mark(NearTreeRTree* ptr) {
|
271
|
+
rb_gc_mark(ptr->points); /* points */
|
272
|
+
rb_gc_mark(ptr->values); /* values */
|
273
|
+
}
|
274
|
+
|
275
|
+
/* called by GC on collection of the object */
|
276
|
+
void neartree_gc_free(NearTreeRTree* ptr) {
|
277
|
+
xfree(ptr->tree_handle); /* free CNearTreeHandle */
|
278
|
+
xfree(ptr); /* free NearTreeRTree */
|
279
|
+
}
|
280
|
+
|
281
|
+
/* allocate */
|
282
|
+
VALUE neartree_allocate(VALUE class) {
|
283
|
+
NearTreeRTree* handle = ALLOC(NearTreeRTree); /* alloc NearTreeRTree */
|
284
|
+
handle->tree_handle = ALLOC(CNearTreeHandle); /* alloc CNearTreeHandle */
|
285
|
+
// handle->has_radius = false; /* by default, don't use radius */
|
286
|
+
// handle->radius = DBL_MAX;
|
287
|
+
handle->points = rb_ary_new(); /* points */
|
288
|
+
handle->values = rb_ary_new(); /* values */
|
289
|
+
|
290
|
+
return Data_Wrap_Struct(class, neartree_gc_mark, neartree_gc_free, handle);
|
291
|
+
}
|
292
|
+
|
293
|
+
/* extension init */
|
294
|
+
void Init_neartree() {
|
295
|
+
/* module/class */
|
296
|
+
neartree_module = rb_define_module("NearTree");
|
297
|
+
neartree_class = rb_define_class_under(neartree_module, "RTree", rb_cObject);
|
298
|
+
|
299
|
+
/* methods */
|
300
|
+
rb_define_method(neartree_class, "initialize" , method_initialize , 1);
|
301
|
+
rb_define_method(neartree_class, "insert" , method_insert , 2);
|
302
|
+
rb_define_method(neartree_class, "find_nearest" , method_find_nearest , -1);
|
303
|
+
rb_define_method(neartree_class, "find_k_nearest", method_find_k_nearest, -1);
|
304
|
+
|
305
|
+
rb_define_method(neartree_class, "dimension", method_dimension, 0);
|
306
|
+
rb_define_method(neartree_class, "points" , method_points , 0);
|
307
|
+
rb_define_method(neartree_class, "values" , method_values , 0);
|
308
|
+
rb_define_method(neartree_class, "empty?" , method_empty , 0);
|
309
|
+
// rb_define_method(neartree_class, "radius", method_radius, 0);
|
310
|
+
|
311
|
+
// rb_define_method(neartree_class, "radius=", method_radius_equal, 1);
|
312
|
+
|
313
|
+
|
314
|
+
rb_define_alloc_func(neartree_class, neartree_allocate); /* register allocate */
|
315
|
+
}
|
316
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
extension_name = 'neartree'
|
4
|
+
|
5
|
+
abort 'need CNearTree.h' unless have_header('CNearTree.h')
|
6
|
+
|
7
|
+
$CFLAGS = '-std=c99 -g -Wall ' + $CFLAGS
|
8
|
+
|
9
|
+
dir_config(extension_name + '/src')
|
10
|
+
have_library('CNearTree')
|
11
|
+
create_makefile(extension_name)
|
12
|
+
|
data/neartree.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = 'neartree'
|
4
|
+
s.date = '2010-12-19'
|
5
|
+
s.version = '0.2'
|
6
|
+
s.summary = 'Ruby NearTree bindings.'
|
7
|
+
s.description = 'neartree is a Ruby library binding to the NearTree C library for storing point/value
|
8
|
+
pairs in an R-Tree structure and searching it for the nearest neighbour for any given point.'
|
9
|
+
s.authors = ['Jean Krohn']
|
10
|
+
s.email = 'jb.krohn@gmail.com'
|
11
|
+
s.homepage = 'http://github.com/susano/ruby-neartree'
|
12
|
+
s.extensions = 'ext/neartree/extconf.rb'
|
13
|
+
s.rubyforge_project = 'neartree'
|
14
|
+
s.files = %w[
|
15
|
+
README.rdoc
|
16
|
+
neartree.gemspec
|
17
|
+
ext/neartree/extconf.rb
|
18
|
+
ext/neartree/NearTree.c
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: neartree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.2'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jean Krohn
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2010-12-19 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! 'neartree is a Ruby library binding to the NearTree C library for storing
|
15
|
+
point/value
|
16
|
+
|
17
|
+
pairs in an R-Tree structure and searching it for the nearest neighbour for any
|
18
|
+
given point.'
|
19
|
+
email: jb.krohn@gmail.com
|
20
|
+
executables: []
|
21
|
+
extensions:
|
22
|
+
- ext/neartree/extconf.rb
|
23
|
+
extra_rdoc_files: []
|
24
|
+
files:
|
25
|
+
- README.rdoc
|
26
|
+
- neartree.gemspec
|
27
|
+
- ext/neartree/extconf.rb
|
28
|
+
- ext/neartree/NearTree.c
|
29
|
+
homepage: http://github.com/susano/ruby-neartree
|
30
|
+
licenses: []
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project: neartree
|
49
|
+
rubygems_version: 1.8.24
|
50
|
+
signing_key:
|
51
|
+
specification_version: 3
|
52
|
+
summary: Ruby NearTree bindings.
|
53
|
+
test_files: []
|
54
|
+
has_rdoc:
|