geokdtree 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +121 -0
- data/Rakefile +30 -0
- data/example/usage.rb +65 -0
- data/ext/geokdtree/distance.c +70 -0
- data/ext/geokdtree/distance.h +38 -0
- data/ext/geokdtree/extconf.rb +7 -0
- data/ext/geokdtree/kdtree.c +782 -0
- data/ext/geokdtree/kdtree.h +135 -0
- data/geokdtree.gemspec +23 -0
- data/lib/geokdtree.rb +13 -0
- data/lib/geokdtree/tree.rb +139 -0
- data/lib/geokdtree/tree_ffi.rb +92 -0
- data/lib/geokdtree/version.rb +3 -0
- data/spec/geokdtree/geokdtree_spec.rb +209 -0
- metadata +97 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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
|
+
|
data/example/usage.rb
ADDED
@@ -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,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
|
+
}
|