geokdtree 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in geokdtree.gemspec
4
+ gemspec
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2013 Colin Surprenant <colin.surprenant@gmail.com>
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,121 @@
1
+ # Ruby/FFI Geokdtree v0.1.0
2
+
3
+ Ruby & JRuby gem with a fast **k-d tree** C implementation using FFI bindings with support for latitude/longitude and **geo distance range search**.
4
+
5
+ A [k-d tree](https://en.wikipedia.org/wiki/K-d_tree) is a space-partitioning data structure for organizing points in a k-dimensional space and are useful for very **fast range searches** and **nearest neighbor searches**. k-d trees are a special case of binary space partitioning trees.
6
+
7
+ ## Installation
8
+
9
+ Tested on OSX 10.8.2 with
10
+ - MRI Ruby 1.9.3 p362
11
+ - MRI Ruby 1.9.3 p385
12
+ - JRuby 1.7.2 (1.9.3 p327)
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'geokdtree'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install geokdtree
25
+
26
+ ## JRuby Notes
27
+
28
+ if the gem installation fails with the following error message, this is because JRuby disabled native extensions by default.
29
+ ```
30
+ Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension
31
+ ```
32
+
33
+ To fix this simply set the following JRuby runtime option
34
+ ``` sh
35
+ $ export JRUBY_OPTS=-Xcext.enabled=true
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ``` ruby
41
+ # simplest 2d tree
42
+ tree = Geokdtree::Tree.new(2)
43
+ tree.insert([1, 0])
44
+ tree.insert([2, 0])
45
+ tree.insert([3, 0])
46
+
47
+ result = tree.nearest([0, 0])
48
+ puts(result.point.inspect) # => [1.0, 0.0]
49
+ puts(result.data.inspect) # => nil
50
+
51
+ # simple 2d tree with point payload.
52
+ # abritary objects can be attached to each inserted point
53
+ tree = Geokdtree::Tree.new(2)
54
+ tree.insert([1, 0], "point 1")
55
+ tree.insert([2, 0], "point 2")
56
+ tree.insert([3, 0], "point 3")
57
+
58
+ # single nearest using standard/Euclidean relative distance
59
+ result = tree.nearest([0, 0])
60
+ puts(result.point.inspect) # => [1.0, 0.0]
61
+ puts(result.data.inspect) # => "point 1"
62
+
63
+ # nearests within range using standard/Euclidean relative distance
64
+ results = tree.nearest_range([0, 0], 2)
65
+ puts(results.size) # => 2
66
+ puts(results[0].point.inspect) # => [2.0, 0.0]
67
+ puts(results[0].data.inspect) # => "point 2"
68
+ puts(results[1].point.inspect) # => [1.0, 0.0]
69
+ puts(results[1].data.inspect) # => "point 1"
70
+
71
+ # 2d tree with lat/lng points
72
+ tree = Geokdtree::Tree.new(2)
73
+ tree.insert([40.7, -74.0], "New York")
74
+ tree.insert([37.77, -122.41], "San Francisco")
75
+ tree.insert([45.50, -73.55], "Montreal")
76
+
77
+ # single nearest using standard/Euclidean relative distance
78
+ result = tree.nearest([34.1, -118.2]) # Los Angeles
79
+ puts(result.point.inspect) # => [37.77, -122.41]
80
+ puts(result.data.inspect) # => "San Francisco"
81
+
82
+
83
+ # nearests within range using miles relative geo distance
84
+ results = tree.nearest_geo_range([47.6, -122.3], 800) # Seattle, within 800 mi
85
+ puts(results.size) # => 1
86
+ puts(results[0].point.inspect) # => [37.77, -122.41]
87
+ puts(results[0].data.inspect) # => "San Francisco"
88
+
89
+ # nearests within range using kilometer relative geo distance
90
+ results = tree.nearest_geo_range([42.35, -71.06], 500, :km) # Boston, within 500 km
91
+ puts(results.size) # => 2
92
+ puts(results[0].point.inspect) # => [45.5, -73.55]
93
+ puts(results[0].data.inspect) # => "Montreal"
94
+ puts(results[1].point.inspect) # => [40.7, -74.0]
95
+ puts(results[1].data.inspect) # => "New York"
96
+
97
+ # compute standard/Euclidean distance between two points
98
+ d = Geokdtree::Tree.distance([-1, 1], [1, 1])
99
+ puts(d) # => 2
100
+
101
+ # compute geo distance between two points (Montreal, Boston)
102
+ d = Geokdtree::Tree.geo_distance([45.5, -73.55], [42.35, -71.06], :km).round(0)
103
+ puts(d.inspect) # => 403
104
+ ```
105
+
106
+ ## Contributing
107
+
108
+ 1. Fork it
109
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
110
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
111
+ 4. Push to the branch (`git push origin my-new-feature`)
112
+ 5. Create new Pull Request
113
+
114
+ ## Credits
115
+ - [John Tsiombikas](http://nuclear.mutantstargoat.com/) for writing the [original C kdtree](https://code.google.com/p/kdtree/).
116
+
117
+ ## Author
118
+ Colin Surprenant, [@colinsurprenant](http://twitter.com/colinsurprenant), [http://github.com/colinsurprenant](http://github.com/colinsurprenant), colin.surprenant@gmail.com
119
+
120
+ ## License
121
+ Ruby/FFI Geokdtree is distributed under the Apache License, Version 2.0.
@@ -0,0 +1,30 @@
1
+ require 'bundler/setup'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'bundler/gem_tasks'
5
+ require 'rspec/core/rake_task'
6
+
7
+ task :default => :spec
8
+
9
+ desc "clean, make and run specsrkae"
10
+ task :spec do
11
+ RSpec::Core::RakeTask.new
12
+ end
13
+
14
+ desc "compile C ext and FFI ext and copy objects into lib"
15
+ task :make => [:clean] do
16
+ Dir.chdir("ext/geokdtree") do
17
+ ruby jruby? ? "-Xcext.enabled=true extconf.rb" : "extconf.rb"
18
+ sh "make"
19
+ end
20
+ cp "ext/geokdtree/geokdtree.bundle", "lib/"
21
+ end
22
+
23
+ CLEAN.include('ext/**/*{.o,.log,.so,.bundle}')
24
+ CLEAN.include('lib/**/*{.o,.log,.so,.bundle}')
25
+ CLEAN.include('ext/**/Makefile')
26
+
27
+ def jruby?
28
+ !!(defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby')
29
+ end
30
+
@@ -0,0 +1,65 @@
1
+ require 'bundler/setup'
2
+ require 'geokdtree'
3
+
4
+ # simplest 2d tree
5
+ tree = Geokdtree::Tree.new(2)
6
+ tree.insert([1, 0])
7
+ tree.insert([2, 0])
8
+ tree.insert([3, 0])
9
+
10
+ result = tree.nearest([0, 0])
11
+ puts(result.point.inspect) # => [1.0, 0.0]
12
+ puts(result.data.inspect) # => nil
13
+
14
+ # simple 2d tree with point payload. abritary objects can be attached to each inserted point
15
+ tree = Geokdtree::Tree.new(2)
16
+ tree.insert([1, 0], "point 1")
17
+ tree.insert([2, 0], "point 2")
18
+ tree.insert([3, 0], "point 3")
19
+
20
+ # single nearest using standard/Euclidean relative distance
21
+ result = tree.nearest([0, 0])
22
+ puts(result.point.inspect) # => [1.0, 0.0]
23
+ puts(result.data.inspect) # => "point 1"
24
+
25
+ # nearests within range using standard/Euclidean relative distance
26
+ results = tree.nearest_range([0, 0], 2)
27
+ puts(results.size) # => 2
28
+ puts(results[0].point.inspect) # => [2.0, 0.0]
29
+ puts(results[0].data.inspect) # => "point 2"
30
+ puts(results[1].point.inspect) # => [1.0, 0.0]
31
+ puts(results[1].data.inspect) # => "point 1"
32
+
33
+ # 2d tree with lat/lng points
34
+ tree = Geokdtree::Tree.new(2)
35
+ tree.insert([40.7, -74.0], "New York")
36
+ tree.insert([37.77, -122.41], "San Francisco")
37
+ tree.insert([45.50, -73.55], "Montreal")
38
+
39
+ # single nearest using standard/Euclidean relative distance
40
+ result = tree.nearest([34.1, -118.2]) # Los Angeles
41
+ puts(result.point.inspect) # => [37.77, -122.41]
42
+ puts(result.data.inspect) # => "San Francisco"
43
+
44
+
45
+ # nearests within range using miles relative geo distance
46
+ results = tree.nearest_geo_range([47.6, -122.3], 800) # Seattle, within 800 miles
47
+ puts(results.size) # => 1
48
+ puts(results[0].point.inspect) # => [37.77, -122.41]
49
+ puts(results[0].data.inspect) # => "San Francisco"
50
+
51
+ # nearests within range using kilometer relative geo distance
52
+ results = tree.nearest_geo_range([42.35, -71.06], 500, :km) # Boston, within 500 kilometer
53
+ puts(results.size) # => 2
54
+ puts(results[0].point.inspect) # => [45.5, -73.55]
55
+ puts(results[0].data.inspect) # => "Montreal"
56
+ puts(results[1].point.inspect) # => [40.7, -74.0]
57
+ puts(results[1].data.inspect) # => "New York"
58
+
59
+ # compute standard/Euclidean distance between two points
60
+ d = Geokdtree::Tree.distance([-1, 1], [1, 1])
61
+ puts(d) # => 2
62
+
63
+ # compute geo distance between two points
64
+ d = Geokdtree::Tree.geo_distance([45.5, -73.55], [42.35, -71.06], :km).round(0) # Montreal, Boston
65
+ puts(d.inspect) # => 403
@@ -0,0 +1,70 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <math.h>
5
+ #include "distance.h"
6
+
7
+ static double RADIAN_PER_DEGREE = M_PI / 180.0;
8
+
9
+ /* return the square Euclidean distance from two points in k dimensions */
10
+ double square_euclidean_distance(const double *a, const double *b, int k) {
11
+ double d = 0, diff;
12
+ while (--k >= 0) {
13
+ diff = (a[k] - b[k]);
14
+ d += diff * diff;
15
+ }
16
+ return d;
17
+ }
18
+
19
+ /* return the Euclidean distance from two points in k dimensions */
20
+ double euclidean_distance(const double *a, const double *b, int k) {
21
+ return sqrt(square_euclidean_distance(a, b, k));
22
+ }
23
+
24
+ /* return the Euclidean distance from two points in two dimensions */
25
+ double euclidean_distance2(double ax, double ay, double bx, double by) {
26
+ double a[2];
27
+ double b[2];
28
+ a[0] = ax;
29
+ a[1] = ay;
30
+ b[0] = bx;
31
+ b[1] = by;
32
+ return sqrt(square_euclidean_distance(a, b, 2));
33
+ }
34
+
35
+ /* return the geo distance from two lat/lng points using the spherical law of cosines */
36
+ double slc_distance(const double *a, const double *b, double radius) {
37
+ double rlat1 = RADIAN_PER_DEGREE * a[0];
38
+ double rlng1 = RADIAN_PER_DEGREE * a[1];
39
+ double rlat2 = RADIAN_PER_DEGREE * b[0];
40
+ double rlng2 = RADIAN_PER_DEGREE * b[1];
41
+ return acos(sin(rlat1) * sin(rlat2) + cos(rlat1) * cos(rlat2) * cos(rlng2 - rlng1)) * radius;
42
+ }
43
+
44
+ /* return the geo distance from two lat/lng points using the spherical law of cosines */
45
+ double slc_distance2(double lat1, double lng1, double lat2, double lng2, double radius) {
46
+ double a[2];
47
+ double b[2];
48
+ a[0] = lat1, a[1] = lng1;
49
+ b[0] = lat2, b[1] = lng2;
50
+ return slc_distance(a, b, radius);
51
+ }
52
+
53
+ /* return the geo distance from two lat/lng points using the haversine formula */
54
+ double haversine_distance2(double lat1, double lng1, double lat2, double lng2, double radius) {
55
+ double rlat1 = RADIAN_PER_DEGREE * lat1;
56
+ double rlat2 = RADIAN_PER_DEGREE * lat2;
57
+ double rdistlat = RADIAN_PER_DEGREE * (lat2 - lat1);
58
+ double rdistlng = RADIAN_PER_DEGREE * (lng2 - lng1);
59
+ double a = SQ(sin(rdistlat / 2.0)) + cos(rlat1) * cos(rlat2) * SQ(sin(rdistlng / 2.0));
60
+ double c = 2.0 * atan2(sqrt(a), sqrt(1 - a));
61
+ return c * radius;
62
+ }
63
+
64
+ int bsq_compare(double a, double b) {
65
+ return (a > SQ(b)) - (a < SQ(b));
66
+ }
67
+
68
+ int std_compare(double a, double b) {
69
+ return (a > b) - (a < b);
70
+ }
@@ -0,0 +1,38 @@
1
+ #ifndef _DISTANCE_H_
2
+ #define _DISTANCE_H_
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ #define RADIUS_MI 3958.760
9
+ #define RADIUS_KM 6371.0
10
+ #define KM_PER_MI 1.609
11
+ #define SQ(x) ((x) * (x))
12
+
13
+ /* return the square Euclidean distance from two points in k dimensions */
14
+ double square_euclidean_distance(const double *a, const double *b, int k);
15
+
16
+ /* return the Euclidean distance from two points in k dimensions */
17
+ double euclidean_distance(const double *a, const double *b, int k);
18
+
19
+ /* return the Euclidean distance from two points in two dimensions */
20
+ double euclidean_distance2(double ax, double ay, double bx, double by);
21
+
22
+ /* return the geo distance from two lat/lng points using the spherical law of cosines */
23
+ double slc_distance(const double *a, const double *b, double radius);
24
+
25
+ /* return the geo distance from two lat/lng points using the spherical law of cosines */
26
+ double slc_distance2(double lat1, double lng1, double lat2, double lng2, double radius);
27
+
28
+ /* return the geo distance from two lat/lng points using the haversine formula */
29
+ double haversine_distance2(double lat1, double lng1, double lat2, double lng2, double radius);
30
+
31
+ int bsq_compare(double a, double b);
32
+ int std_compare(double a, double b);
33
+
34
+ #ifdef __cplusplus
35
+ }
36
+ #endif
37
+
38
+ #endif /* _DISTANCE_H_ */
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS = "-pedantic -Wall -g -O3 -fPIC -g -DUSE_LIST_NODE_ALLOCATOR"
4
+
5
+ have_library("pthread")
6
+
7
+ create_makefile 'geokdtree'
@@ -0,0 +1,782 @@
1
+ /*
2
+ This file is part of ``kdtree'', a library for working with kd-trees.
3
+ Copyright (C) 2007-2011 John Tsiombikas <nuclear@member.fsf.org>
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+ 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+ 3. The name of the author may not be used to endorse or promote products
14
+ derived from this software without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
+ EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
25
+ OF SUCH DAMAGE.
26
+ */
27
+
28
+ /* single nearest neighbor search written by Tamas Nepusz <tamas@cs.rhul.ac.uk> */
29
+
30
+ /*
31
+ Feb 2013 - Colin Surprenant <colin.surprenant@gmail.com>
32
+ - inclusion in the ruby-ffi-kdtree gem
33
+ - added kd_nearest2
34
+ - added kd_nearest_range2
35
+ - added kd_insert2
36
+ - refactored find_nearest to externalize the distance computation and distance comparison
37
+ - refactored kd_nearest_range into kd_generic_nearest_range to externalize the distance computation and distance comparison
38
+ - added kd_nearest_geo_range
39
+ - added distance.[ch]
40
+ - removed all float functions
41
+
42
+ original source code from https://code.google.com/p/kdtree/
43
+ using version 0.5.6 dated Nov 2011
44
+ */
45
+
46
+ #include <stdio.h>
47
+ #include <stdlib.h>
48
+ #include <string.h>
49
+ #include <math.h>
50
+ #include "kdtree.h"
51
+ #include "distance.h"
52
+
53
+ #if defined(WIN32) || defined(__WIN32__)
54
+ #include <malloc.h>
55
+ #endif
56
+
57
+ #ifdef USE_LIST_NODE_ALLOCATOR
58
+
59
+ #ifndef NO_PTHREADS
60
+ #include <pthread.h>
61
+ #else
62
+
63
+ #ifndef I_WANT_THREAD_BUGS
64
+ #error "You are compiling with the fast list node allocator, with pthreads disabled! This WILL break if used from multiple threads."
65
+ #endif /* I want thread bugs */
66
+
67
+ #endif /* pthread support */
68
+ #endif /* use list node allocator */
69
+
70
+ struct kdhyperrect {
71
+ int dim;
72
+ double *min, *max; /* minimum/maximum coords */
73
+ };
74
+
75
+ struct kdnode {
76
+ double *pos;
77
+ int dir;
78
+ void *data;
79
+
80
+ struct kdnode *left, *right; /* negative/positive side */
81
+ };
82
+
83
+ struct res_node {
84
+ struct kdnode *item;
85
+ double dist_sq;
86
+ struct res_node *next;
87
+ };
88
+
89
+ struct kdtree {
90
+ int dim;
91
+ struct kdnode *root;
92
+ struct kdhyperrect *rect;
93
+ void (*destr)(void*);
94
+ };
95
+
96
+ struct kdres {
97
+ struct kdtree *tree;
98
+ struct res_node *rlist, *riter;
99
+ int size;
100
+ };
101
+
102
+ #define SQ(x) ((x) * (x))
103
+
104
+
105
+ static int find_nearest(struct kdnode *node, const double *pos, double range, struct res_node *list, int ordered, int dim, double (*distance_function)(const double *, const double *, int k), int (*compare_function)(double, double));
106
+ static void kd_nearest_i(struct kdnode *node, const double *pos, struct kdnode **result, double *result_dist_sq, struct kdhyperrect* rect);
107
+ static double geo_distance_mi(const double *a, const double *b, int k);
108
+ static double geo_distance_km(const double *a, const double *b, int k);
109
+
110
+ static void clear_rec(struct kdnode *node, void (*destr)(void*));
111
+ static int insert_rec(struct kdnode **node, const double *pos, void *data, int dir, int dim);
112
+ static int rlist_insert(struct res_node *list, struct kdnode *item, double dist_sq);
113
+ static void clear_results(struct kdres *set);
114
+
115
+ static struct kdhyperrect* hyperrect_create(int dim, const double *min, const double *max);
116
+ static void hyperrect_free(struct kdhyperrect *rect);
117
+ static struct kdhyperrect* hyperrect_duplicate(const struct kdhyperrect *rect);
118
+ static void hyperrect_extend(struct kdhyperrect *rect, const double *pos);
119
+ static double hyperrect_dist_sq(struct kdhyperrect *rect, const double *pos);
120
+
121
+ #ifdef USE_LIST_NODE_ALLOCATOR
122
+ static struct res_node *alloc_resnode(void);
123
+ static void free_resnode(struct res_node*);
124
+ #else
125
+ #define alloc_resnode() malloc(sizeof(struct res_node))
126
+ #define free_resnode(n) free(n)
127
+ #endif
128
+
129
+
130
+ struct kdtree *kd_create(int k)
131
+ {
132
+ struct kdtree *tree;
133
+
134
+ if(!(tree = malloc(sizeof *tree))) {
135
+ return 0;
136
+ }
137
+
138
+ tree->dim = k;
139
+ tree->root = 0;
140
+ tree->destr = 0;
141
+ tree->rect = 0;
142
+
143
+ return tree;
144
+ }
145
+
146
+ void kd_free(struct kdtree *tree)
147
+ {
148
+ if(tree) {
149
+ kd_clear(tree);
150
+ free(tree);
151
+ }
152
+ }
153
+
154
+ static void clear_rec(struct kdnode *node, void (*destr)(void*))
155
+ {
156
+ if(!node) return;
157
+
158
+ clear_rec(node->left, destr);
159
+ clear_rec(node->right, destr);
160
+
161
+ if(destr) {
162
+ destr(node->data);
163
+ }
164
+ free(node->pos);
165
+ free(node);
166
+ }
167
+
168
+ void kd_clear(struct kdtree *tree)
169
+ {
170
+ clear_rec(tree->root, tree->destr);
171
+ tree->root = 0;
172
+
173
+ if (tree->rect) {
174
+ hyperrect_free(tree->rect);
175
+ tree->rect = 0;
176
+ }
177
+ }
178
+
179
+ void kd_data_destructor(struct kdtree *tree, void (*destr)(void*))
180
+ {
181
+ tree->destr = destr;
182
+ }
183
+
184
+
185
+ static int insert_rec(struct kdnode **nptr, const double *pos, void *data, int dir, int dim)
186
+ {
187
+ int new_dir;
188
+ struct kdnode *node;
189
+
190
+ if(!*nptr) {
191
+ if(!(node = malloc(sizeof *node))) {
192
+ return -1;
193
+ }
194
+ if(!(node->pos = malloc(dim * sizeof *node->pos))) {
195
+ free(node);
196
+ return -1;
197
+ }
198
+ memcpy(node->pos, pos, dim * sizeof *node->pos);
199
+ node->data = data;
200
+ node->dir = dir;
201
+ node->left = node->right = 0;
202
+ *nptr = node;
203
+ return 0;
204
+ }
205
+
206
+ node = *nptr;
207
+ new_dir = (node->dir + 1) % dim;
208
+ if(pos[node->dir] < node->pos[node->dir]) {
209
+ return insert_rec(&(*nptr)->left, pos, data, new_dir, dim);
210
+ }
211
+ return insert_rec(&(*nptr)->right, pos, data, new_dir, dim);
212
+ }
213
+
214
+ int kd_insert(struct kdtree *tree, const double *pos, void *data)
215
+ {
216
+ if (insert_rec(&tree->root, pos, data, 0, tree->dim)) {
217
+ return -1;
218
+ }
219
+
220
+ if (tree->rect == 0) {
221
+ tree->rect = hyperrect_create(tree->dim, pos, pos);
222
+ } else {
223
+ hyperrect_extend(tree->rect, pos);
224
+ }
225
+
226
+ return 0;
227
+ }
228
+
229
+ int kd_insert2(struct kdtree *tree, double x, double y, void *data)
230
+ {
231
+ double pos[2];
232
+ pos[0] = x;
233
+ pos[1] = y;
234
+ return kd_insert(tree, pos, data);
235
+ }
236
+
237
+ int kd_insert3(struct kdtree *tree, double x, double y, double z, void *data)
238
+ {
239
+ double pos[3];
240
+ pos[0] = x;
241
+ pos[1] = y;
242
+ pos[2] = z;
243
+ return kd_insert(tree, pos, data);
244
+ }
245
+
246
+ /* TODO: add > 16 dimensions handling */
247
+ static int find_nearest(
248
+ struct kdnode *node,
249
+ const double *pos,
250
+ double range,
251
+ struct res_node *list,
252
+ int ordered,
253
+ int dim,
254
+ double (*distance_function)(const double *, const double *, int k),
255
+ int (*compare_function)(double, double)
256
+ )
257
+ {
258
+ double dist, one_dim_dist, one_dim_relative_dist;
259
+ int i, ret, added_res = 0;
260
+ double one_dim_pos[16];
261
+
262
+ if(!node) return 0;
263
+ if(dim > 16) return -1;
264
+
265
+ dist = (*distance_function)(node->pos, pos, dim);
266
+ if((*compare_function)(dist, range) <= 0) {
267
+ if(rlist_insert(list, node, ordered ? dist : -1.0) == -1) {
268
+ return -1;
269
+ }
270
+ added_res = 1;
271
+ }
272
+
273
+ /* isolate single dimension position from node->pos into one_dim_pos for single dimension distance computation */
274
+ for(i = 0; i < dim; i++) {
275
+ one_dim_pos[i] = pos[i];
276
+ }
277
+ one_dim_pos[node->dir] = node->pos[node->dir];
278
+
279
+ one_dim_dist = (*distance_function)(pos, one_dim_pos, dim);
280
+ one_dim_relative_dist = pos[node->dir] - node->pos[node->dir];
281
+
282
+ ret = find_nearest(one_dim_relative_dist <= 0.0 ? node->left : node->right, pos, range, list, ordered, dim, distance_function, compare_function);
283
+ if(ret >= 0 && (*compare_function)(one_dim_dist, range) < 0) {
284
+ added_res += ret;
285
+ ret = find_nearest(one_dim_relative_dist <= 0.0 ? node->right : node->left, pos, range, list, ordered, dim, distance_function, compare_function);
286
+ }
287
+ if(ret == -1) {
288
+ return -1;
289
+ }
290
+ added_res += ret;
291
+
292
+ return added_res;
293
+ }
294
+
295
+ #if 0
296
+ static int find_nearest_n(struct kdnode *node, const double *pos, double range, int num, struct rheap *heap, int dim)
297
+ {
298
+ double dist_sq, dx;
299
+ int i, ret, added_res = 0;
300
+
301
+ if(!node) return 0;
302
+
303
+ /* if the photon is close enough, add it to the result heap */
304
+ dist_sq = 0;
305
+ for(i=0; i<dim; i++) {
306
+ dist_sq += SQ(node->pos[i] - pos[i]);
307
+ }
308
+ if(dist_sq <= range_sq) {
309
+ if(heap->size >= num) {
310
+ /* get furthest element */
311
+ struct res_node *maxelem = rheap_get_max(heap);
312
+
313
+ /* and check if the new one is closer than that */
314
+ if(maxelem->dist_sq > dist_sq) {
315
+ rheap_remove_max(heap);
316
+
317
+ if(rheap_insert(heap, node, dist_sq) == -1) {
318
+ return -1;
319
+ }
320
+ added_res = 1;
321
+
322
+ range_sq = dist_sq;
323
+ }
324
+ } else {
325
+ if(rheap_insert(heap, node, dist_sq) == -1) {
326
+ return =1;
327
+ }
328
+ added_res = 1;
329
+ }
330
+ }
331
+
332
+
333
+ /* find signed distance from the splitting plane */
334
+ dx = pos[node->dir] - node->pos[node->dir];
335
+
336
+ ret = find_nearest_n(dx <= 0.0 ? node->left : node->right, pos, range, num, heap, dim);
337
+ if(ret >= 0 && fabs(dx) < range) {
338
+ added_res += ret;
339
+ ret = find_nearest_n(dx <= 0.0 ? node->right : node->left, pos, range, num, heap, dim);
340
+ }
341
+
342
+ }
343
+ #endif
344
+
345
+ static void kd_nearest_i(struct kdnode *node, const double *pos, struct kdnode **result, double *result_dist_sq, struct kdhyperrect* rect)
346
+ {
347
+ int dir = node->dir;
348
+ int i;
349
+ double dummy, dist_sq;
350
+ struct kdnode *nearer_subtree, *farther_subtree;
351
+ double *nearer_hyperrect_coord, *farther_hyperrect_coord;
352
+
353
+ /* Decide whether to go left or right in the tree */
354
+ dummy = pos[dir] - node->pos[dir];
355
+ if (dummy <= 0) {
356
+ nearer_subtree = node->left;
357
+ farther_subtree = node->right;
358
+ nearer_hyperrect_coord = rect->max + dir;
359
+ farther_hyperrect_coord = rect->min + dir;
360
+ } else {
361
+ nearer_subtree = node->right;
362
+ farther_subtree = node->left;
363
+ nearer_hyperrect_coord = rect->min + dir;
364
+ farther_hyperrect_coord = rect->max + dir;
365
+ }
366
+
367
+ if (nearer_subtree) {
368
+ /* Slice the hyperrect to get the hyperrect of the nearer subtree */
369
+ dummy = *nearer_hyperrect_coord;
370
+ *nearer_hyperrect_coord = node->pos[dir];
371
+ /* Recurse down into nearer subtree */
372
+ kd_nearest_i(nearer_subtree, pos, result, result_dist_sq, rect);
373
+ /* Undo the slice */
374
+ *nearer_hyperrect_coord = dummy;
375
+ }
376
+
377
+ /* Check the distance of the point at the current node, compare it
378
+ * with our best so far */
379
+ dist_sq = 0;
380
+ for(i=0; i < rect->dim; i++) {
381
+ dist_sq += SQ(node->pos[i] - pos[i]);
382
+ }
383
+ if (dist_sq < *result_dist_sq) {
384
+ *result = node;
385
+ *result_dist_sq = dist_sq;
386
+ }
387
+
388
+ if (farther_subtree) {
389
+ /* Get the hyperrect of the farther subtree */
390
+ dummy = *farther_hyperrect_coord;
391
+ *farther_hyperrect_coord = node->pos[dir];
392
+ /* Check if we have to recurse down by calculating the closest
393
+ * point of the hyperrect and see if it's closer than our
394
+ * minimum distance in result_dist_sq. */
395
+ if (hyperrect_dist_sq(rect, pos) < *result_dist_sq) {
396
+ /* Recurse down into farther subtree */
397
+ kd_nearest_i(farther_subtree, pos, result, result_dist_sq, rect);
398
+ }
399
+ /* Undo the slice on the hyperrect */
400
+ *farther_hyperrect_coord = dummy;
401
+ }
402
+ }
403
+
404
+ struct kdres *kd_nearest(struct kdtree *kd, const double *pos)
405
+ {
406
+ struct kdhyperrect *rect;
407
+ struct kdnode *result;
408
+ struct kdres *rset;
409
+ double dist_sq;
410
+ int i;
411
+
412
+ if (!kd) return 0;
413
+ if (!kd->rect) return 0;
414
+
415
+ /* Allocate result set */
416
+ if(!(rset = malloc(sizeof *rset))) {
417
+ return 0;
418
+ }
419
+ if(!(rset->rlist = alloc_resnode())) {
420
+ free(rset);
421
+ return 0;
422
+ }
423
+ rset->rlist->next = 0;
424
+ rset->tree = kd;
425
+
426
+ /* Duplicate the bounding hyperrectangle, we will work on the copy */
427
+ if (!(rect = hyperrect_duplicate(kd->rect))) {
428
+ kd_res_free(rset);
429
+ return 0;
430
+ }
431
+
432
+ /* Our first guesstimate is the root node */
433
+ result = kd->root;
434
+ dist_sq = 0;
435
+ for (i = 0; i < kd->dim; i++)
436
+ dist_sq += SQ(result->pos[i] - pos[i]);
437
+
438
+ /* Search for the nearest neighbour recursively */
439
+ kd_nearest_i(kd->root, pos, &result, &dist_sq, rect);
440
+
441
+ /* Free the copy of the hyperrect */
442
+ hyperrect_free(rect);
443
+
444
+ /* Store the result */
445
+ if (result) {
446
+ if (rlist_insert(rset->rlist, result, -1.0) == -1) {
447
+ kd_res_free(rset);
448
+ return 0;
449
+ }
450
+ rset->size = 1;
451
+ kd_res_rewind(rset);
452
+ return rset;
453
+ } else {
454
+ kd_res_free(rset);
455
+ return 0;
456
+ }
457
+ }
458
+
459
+
460
+ struct kdres *kd_nearest2(struct kdtree *tree, double x, double y)
461
+ {
462
+ double pos[2];
463
+ pos[0] = x;
464
+ pos[1] = y;
465
+ return kd_nearest(tree, pos);
466
+ }
467
+
468
+ struct kdres *kd_nearest3(struct kdtree *tree, double x, double y, double z)
469
+ {
470
+ double pos[3];
471
+ pos[0] = x;
472
+ pos[1] = y;
473
+ pos[2] = z;
474
+ return kd_nearest(tree, pos);
475
+ }
476
+
477
+ /* ---- nearest N search ---- */
478
+ /*
479
+ static kdres *kd_nearest_n(struct kdtree *kd, const double *pos, int num)
480
+ {
481
+ int ret;
482
+ struct kdres *rset;
483
+
484
+ if(!(rset = malloc(sizeof *rset))) {
485
+ return 0;
486
+ }
487
+ if(!(rset->rlist = alloc_resnode())) {
488
+ free(rset);
489
+ return 0;
490
+ }
491
+ rset->rlist->next = 0;
492
+ rset->tree = kd;
493
+
494
+ if((ret = find_nearest_n(kd->root, pos, range, num, rset->rlist, kd->dim)) == -1) {
495
+ kd_res_free(rset);
496
+ return 0;
497
+ }
498
+ rset->size = ret;
499
+ kd_res_rewind(rset);
500
+ return rset;
501
+ }*/
502
+
503
+ struct kdres *kd_generic_nearest_range(
504
+ struct kdtree *kd,
505
+ const double *pos,
506
+ double range,
507
+ double (*distance_function)(const double *, const double *, int k),
508
+ int (*compare_function)(double, double)
509
+ )
510
+ {
511
+ int ret;
512
+ struct kdres *rset;
513
+
514
+ if(!(rset = malloc(sizeof *rset))) {
515
+ return 0;
516
+ }
517
+ if(!(rset->rlist = alloc_resnode())) {
518
+ free(rset);
519
+ return 0;
520
+ }
521
+ rset->rlist->next = 0;
522
+ rset->tree = kd;
523
+
524
+ if((ret = find_nearest(kd->root, pos, range, rset->rlist, 0, kd->dim, distance_function, compare_function)) == -1) {
525
+ kd_res_free(rset);
526
+ return 0;
527
+ }
528
+ rset->size = ret;
529
+ kd_res_rewind(rset);
530
+ return rset;
531
+ }
532
+
533
+ struct kdres *kd_nearest_range(struct kdtree *kd, const double *pos, double range)
534
+ {
535
+ return kd_generic_nearest_range(kd, pos, range, square_euclidean_distance, bsq_compare);
536
+ }
537
+
538
+ struct kdres *kd_nearest_range2(struct kdtree *tree, double x, double y, double range)
539
+ {
540
+ double pos[2];
541
+ pos[0] = x;
542
+ pos[1] = y;
543
+ return kd_nearest_range(tree, pos, range);
544
+ }
545
+
546
+ struct kdres *kd_nearest_range3(struct kdtree *tree, double x, double y, double z, double range)
547
+ {
548
+ double pos[3];
549
+ pos[0] = x;
550
+ pos[1] = y;
551
+ pos[2] = z;
552
+ return kd_nearest_range(tree, pos, range);
553
+ }
554
+
555
+ static double geo_distance_mi(const double *a, const double *b, int k)
556
+ {
557
+ /* just ignore k, we know its 2d but k is required per distance function signature */
558
+ return slc_distance(a, b, RADIUS_MI);
559
+ }
560
+
561
+ static double geo_distance_km(const double *a, const double *b, int k)
562
+ {
563
+ /* just ignore k, we know its 2d but k is required per distance function signature */
564
+ return slc_distance(a, b, RADIUS_KM);
565
+ }
566
+
567
+ struct kdres *kd_nearest_geo_range(struct kdtree *kd, double lat, double lng, double range, int units)
568
+ {
569
+ double pos[2];
570
+ pos[0] = lat;
571
+ pos[1] = lng;
572
+ if(units != GEO_UNITS_MI) {
573
+ return kd_generic_nearest_range(kd, pos, range, geo_distance_km, std_compare);
574
+ }
575
+ return kd_generic_nearest_range(kd, pos, range, geo_distance_mi, std_compare);
576
+ }
577
+
578
+ void kd_res_free(struct kdres *rset)
579
+ {
580
+ clear_results(rset);
581
+ free_resnode(rset->rlist);
582
+ free(rset);
583
+ }
584
+
585
+ int kd_res_size(struct kdres *set)
586
+ {
587
+ return (set->size);
588
+ }
589
+
590
+ void kd_res_rewind(struct kdres *rset)
591
+ {
592
+ rset->riter = rset->rlist->next;
593
+ }
594
+
595
+ int kd_res_end(struct kdres *rset)
596
+ {
597
+ return rset->riter == 0;
598
+ }
599
+
600
+ int kd_res_next(struct kdres *rset)
601
+ {
602
+ rset->riter = rset->riter->next;
603
+ return rset->riter != 0;
604
+ }
605
+
606
+ void *kd_res_item(struct kdres *rset, double *pos)
607
+ {
608
+ if(rset->riter) {
609
+ if(pos) {
610
+ memcpy(pos, rset->riter->item->pos, rset->tree->dim * sizeof *pos);
611
+ }
612
+ return rset->riter->item->data;
613
+ }
614
+ return 0;
615
+ }
616
+
617
+ void *kd_res_item3(struct kdres *rset, double *x, double *y, double *z)
618
+ {
619
+ if(rset->riter) {
620
+ if(*x) *x = rset->riter->item->pos[0];
621
+ if(*y) *y = rset->riter->item->pos[1];
622
+ if(*z) *z = rset->riter->item->pos[2];
623
+ }
624
+ return 0;
625
+ }
626
+
627
+ void *kd_res_item_data(struct kdres *set)
628
+ {
629
+ return kd_res_item(set, 0);
630
+ }
631
+
632
+ /* ---- hyperrectangle helpers ---- */
633
+ static struct kdhyperrect* hyperrect_create(int dim, const double *min, const double *max)
634
+ {
635
+ size_t size = dim * sizeof(double);
636
+ struct kdhyperrect* rect = 0;
637
+
638
+ if (!(rect = malloc(sizeof(struct kdhyperrect)))) {
639
+ return 0;
640
+ }
641
+
642
+ rect->dim = dim;
643
+ if (!(rect->min = malloc(size))) {
644
+ free(rect);
645
+ return 0;
646
+ }
647
+ if (!(rect->max = malloc(size))) {
648
+ free(rect->min);
649
+ free(rect);
650
+ return 0;
651
+ }
652
+ memcpy(rect->min, min, size);
653
+ memcpy(rect->max, max, size);
654
+
655
+ return rect;
656
+ }
657
+
658
+ static void hyperrect_free(struct kdhyperrect *rect)
659
+ {
660
+ free(rect->min);
661
+ free(rect->max);
662
+ free(rect);
663
+ }
664
+
665
+ static struct kdhyperrect* hyperrect_duplicate(const struct kdhyperrect *rect)
666
+ {
667
+ return hyperrect_create(rect->dim, rect->min, rect->max);
668
+ }
669
+
670
+ static void hyperrect_extend(struct kdhyperrect *rect, const double *pos)
671
+ {
672
+ int i;
673
+
674
+ for (i=0; i < rect->dim; i++) {
675
+ if (pos[i] < rect->min[i]) {
676
+ rect->min[i] = pos[i];
677
+ }
678
+ if (pos[i] > rect->max[i]) {
679
+ rect->max[i] = pos[i];
680
+ }
681
+ }
682
+ }
683
+
684
+ static double hyperrect_dist_sq(struct kdhyperrect *rect, const double *pos)
685
+ {
686
+ int i;
687
+ double result = 0;
688
+
689
+ for (i=0; i < rect->dim; i++) {
690
+ if (pos[i] < rect->min[i]) {
691
+ result += SQ(rect->min[i] - pos[i]);
692
+ } else if (pos[i] > rect->max[i]) {
693
+ result += SQ(rect->max[i] - pos[i]);
694
+ }
695
+ }
696
+
697
+ return result;
698
+ }
699
+
700
+ /* ---- static helpers ---- */
701
+
702
+ #ifdef USE_LIST_NODE_ALLOCATOR
703
+ /* special list node allocators. */
704
+ static struct res_node *free_nodes;
705
+
706
+ #ifndef NO_PTHREADS
707
+ static pthread_mutex_t alloc_mutex = PTHREAD_MUTEX_INITIALIZER;
708
+ #endif
709
+
710
+ static struct res_node *alloc_resnode(void)
711
+ {
712
+ struct res_node *node;
713
+
714
+ #ifndef NO_PTHREADS
715
+ pthread_mutex_lock(&alloc_mutex);
716
+ #endif
717
+
718
+ if(!free_nodes) {
719
+ node = malloc(sizeof *node);
720
+ } else {
721
+ node = free_nodes;
722
+ free_nodes = free_nodes->next;
723
+ node->next = 0;
724
+ }
725
+
726
+ #ifndef NO_PTHREADS
727
+ pthread_mutex_unlock(&alloc_mutex);
728
+ #endif
729
+
730
+ return node;
731
+ }
732
+
733
+ static void free_resnode(struct res_node *node)
734
+ {
735
+ #ifndef NO_PTHREADS
736
+ pthread_mutex_lock(&alloc_mutex);
737
+ #endif
738
+
739
+ node->next = free_nodes;
740
+ free_nodes = node;
741
+
742
+ #ifndef NO_PTHREADS
743
+ pthread_mutex_unlock(&alloc_mutex);
744
+ #endif
745
+ }
746
+ #endif /* list node allocator or not */
747
+
748
+
749
+ /* inserts the item. if dist_sq is >= 0, then do an ordered insert */
750
+ /* TODO make the ordering code use heapsort */
751
+ static int rlist_insert(struct res_node *list, struct kdnode *item, double dist_sq)
752
+ {
753
+ struct res_node *rnode;
754
+
755
+ if(!(rnode = alloc_resnode())) {
756
+ return -1;
757
+ }
758
+ rnode->item = item;
759
+ rnode->dist_sq = dist_sq;
760
+
761
+ if(dist_sq >= 0.0) {
762
+ while(list->next && list->next->dist_sq < dist_sq) {
763
+ list = list->next;
764
+ }
765
+ }
766
+ rnode->next = list->next;
767
+ list->next = rnode;
768
+ return 0;
769
+ }
770
+
771
+ static void clear_results(struct kdres *rset)
772
+ {
773
+ struct res_node *tmp, *node = rset->rlist->next;
774
+
775
+ while(node) {
776
+ tmp = node;
777
+ node = node->next;
778
+ free_resnode(tmp);
779
+ }
780
+
781
+ rset->rlist->next = 0;
782
+ }