fast_nearest_latlng 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/LICENSE +51 -0
  2. data/README +11 -0
  3. data/ext/extconf.rb +4 -0
  4. data/ext/fastloc.c +224 -0
  5. data/ext/test.rb +49 -0
  6. metadata +73 -0
data/LICENSE ADDED
@@ -0,0 +1,51 @@
1
+ Copyright (c) 2012 Todd Fisher (todd.fisher@gmail.com).
2
+ Fast Nearest latlng is free software licensed under the following terms:
3
+
4
+ 1. You may make and give away verbatim copies of the source form of the
5
+ software without restriction, provided that you duplicate all of the
6
+ original copyright notices and associated disclaimers.
7
+
8
+ 2. You may modify your copy of the software in any way, provided that
9
+ you do at least ONE of the following:
10
+
11
+ a) place your modifications in the Public Domain or otherwise
12
+ make them Freely Available, such as by posting said
13
+ modifications to Usenet or an equivalent medium, or by allowing
14
+ the author to include your modifications in the software.
15
+
16
+ b) use the modified software only within your corporation or
17
+ organization.
18
+
19
+ c) give non-standard binaries non-standard names, with
20
+ instructions on where to get the original software distribution.
21
+
22
+ d) make other distribution arrangements with the author.
23
+
24
+ 3. You may distribute the software in object code or binary form,
25
+ provided that you do at least ONE of the following:
26
+
27
+ a) distribute the binaries and library files of the software,
28
+ together with instructions (in the manual page or equivalent)
29
+ on where to get the original distribution.
30
+
31
+ b) accompany the distribution with the machine-readable source of
32
+ the software.
33
+
34
+ c) give non-standard binaries non-standard names, with
35
+ instructions on where to get the original software distribution.
36
+
37
+ d) make other distribution arrangements with the author.
38
+
39
+ 4. You may modify and include the part of the software into any other
40
+ software (possibly commercial).
41
+
42
+ 5. The scripts and library files supplied as input to or produced as
43
+ output from the software do not automatically fall under the
44
+ copyright of the software, but belong to whomever generated them,
45
+ and may be sold commercially, and may be aggregated with this
46
+ software.
47
+
48
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
49
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
50
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
51
+ PURPOSE.
data/README ADDED
@@ -0,0 +1,11 @@
1
+ find the nearest locations to a given point
2
+
3
+
4
+ floc = FastLoc.new(:unit => :miles) # :kilometers or :nautical_miles
5
+
6
+ floc.add(latdeg,lngdeg,3)
7
+ floc.add(latdeg,lngdeg,4)
8
+ floc.add(latdeg,lngdeg,5)
9
+ floc.add(latdeg,lngdeg,6)
10
+
11
+ floc.nearest(latdeg,lngdeg) -> [5,3,4,6]
@@ -0,0 +1,4 @@
1
+ require 'mkmf'
2
+
3
+ dir_config('fastloc')
4
+ create_makefile('fastloc')
@@ -0,0 +1,224 @@
1
+ #include <ruby.h>
2
+ #include <stdlib.h>
3
+ #include <math.h>
4
+
5
+ VALUE cFastLoc;
6
+ VALUE cFastLoc;
7
+
8
+ #define pi 3.14159265358979323846
9
+
10
+ typedef struct {
11
+ int id;
12
+ double lat, lng;
13
+ } Location;
14
+
15
+ typedef struct {
16
+ int id;
17
+ double dist;
18
+ } ldat;
19
+
20
+ typedef struct {
21
+ Location **locations;
22
+ size_t count;
23
+ char unit;
24
+ } ruby_fast_loc;
25
+
26
+ static double deg2rad(double deg) {
27
+ return (deg * pi / 180);
28
+ }
29
+
30
+ static double rad2deg(double rad) {
31
+ return (rad * 180 / pi);
32
+ }
33
+
34
+ // see: http://www.zipcodeworld.com/samples/distance.c.html
35
+ static double distance(double lat1, double lon1, double lat2, double lon2, char unit) {
36
+ double theta, dist;
37
+ theta = lon1 - lon2;
38
+ dist = sin(deg2rad(lat1)) * sin(deg2rad(lat2)) + cos(deg2rad(lat1)) * cos(deg2rad(lat2)) * cos(deg2rad(theta));
39
+ dist = acos(dist);
40
+ dist = rad2deg(dist);
41
+ dist = dist * 60 * 1.1515;
42
+ switch(unit) {
43
+ case 'M':
44
+ break;
45
+ case 'K':
46
+ dist = dist * 1.609344;
47
+ break;
48
+ case 'N':
49
+ dist = dist * 0.8684;
50
+ break;
51
+ }
52
+ return (dist);
53
+ }
54
+
55
+ static VALUE ruby_fast_loc_add(VALUE self, VALUE latdeg, VALUE lngdeg, VALUE id) {
56
+ int i = 0, count = 0;
57
+ VALUE add;
58
+ ruby_fast_loc *floc;
59
+
60
+ Data_Get_Struct(self, ruby_fast_loc, floc);
61
+
62
+ floc->count++;
63
+
64
+ Location **locations = (Location**)malloc(sizeof(Location*)*floc->count);
65
+
66
+ Location *location = (Location*)malloc(sizeof(Location));
67
+
68
+ location->lat = NUM2DBL(latdeg);
69
+ location->lng = NUM2DBL(lngdeg);
70
+ location->id = FIX2INT(id);
71
+ locations[floc->count-1] = location;
72
+
73
+ if (floc->locations) {
74
+ count = floc->count-1;
75
+ for (i = 0; i < count; ++i) {
76
+ locations[i] = floc->locations[i];
77
+ }
78
+ free(floc->locations);
79
+ }
80
+ floc->locations = locations;
81
+
82
+ return self;
83
+ }
84
+ static int distance_compare(const void *a, const void *b) {
85
+ ldat* v1 = ((ldat*)a);
86
+ ldat* v2 = ((ldat*)b);
87
+ if (v1->dist < v2->dist) { return -1; }
88
+ else if (v1->dist == v2->dist) { return 0; }
89
+ return 1;
90
+ }
91
+
92
+ static VALUE ruby_fast_loc_nearest(VALUE self, VALUE latdeg, VALUE lngdeg) {
93
+ VALUE sorted = rb_ary_new();
94
+ ruby_fast_loc *floc;
95
+
96
+ Data_Get_Struct(self, ruby_fast_loc, floc);
97
+ int i = 0;
98
+ double lat = NUM2DBL(latdeg);
99
+ double lng = NUM2DBL(lngdeg);
100
+ ldat distances[floc->count];
101
+
102
+ for (i = 0; i < floc->count; ++i) {
103
+ distances[i].id = floc->locations[i]->id;
104
+ distances[i].dist = distance(lat, lng, floc->locations[i]->lat, floc->locations[i]->lng, floc->unit);
105
+ }
106
+
107
+ // void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *));
108
+ qsort(distances, floc->count, sizeof(ldat), distance_compare);
109
+
110
+ for (i = 0; i < floc->count; ++i) {
111
+ VALUE result = rb_ary_new2(2);
112
+ rb_ary_push(result, rb_float_new(distances[i].dist));
113
+ rb_ary_push(result, rb_int_new(distances[i].id));
114
+ rb_ary_push(sorted, result);
115
+ }
116
+ return sorted;
117
+ }
118
+
119
+ static VALUE ruby_fast_loc_locations(VALUE self) {
120
+ ruby_fast_loc *floc;
121
+
122
+ Data_Get_Struct(self, ruby_fast_loc, floc);
123
+
124
+ if (!floc->count) {
125
+ return rb_ary_new();
126
+ }
127
+
128
+ VALUE add = Qnil;
129
+ VALUE locations = rb_ary_new();
130
+ int i;
131
+ Location *location;
132
+
133
+ for (i = 0; i < floc->count && floc->locations; ++i) {
134
+ location = floc->locations[i];
135
+
136
+ add = rb_ary_new2(3);
137
+ rb_ary_push(add, rb_float_new(location->lat));
138
+ rb_ary_push(add, rb_float_new(location->lng));
139
+ rb_ary_push(add, rb_int_new(location->id));
140
+ rb_ary_push(locations, add);
141
+ }
142
+
143
+ return locations;
144
+ }
145
+ static VALUE ruby_fast_loc_get_unit(VALUE self) {
146
+ ruby_fast_loc *floc;
147
+
148
+ Data_Get_Struct(self, ruby_fast_loc, floc);
149
+
150
+ return rb_str_new(&floc->unit,1);
151
+ }
152
+
153
+ static VALUE ruby_fast_loc_set_unit(VALUE self, VALUE unit_sym) {
154
+ ruby_fast_loc *floc;
155
+
156
+ Data_Get_Struct(self, ruby_fast_loc, floc);
157
+
158
+ if (unit_sym == ID2SYM(rb_intern("miles"))) {
159
+ floc->unit = 'M';
160
+ } else if (unit_sym == ID2SYM(rb_intern("kilometers"))) {
161
+ floc->unit = 'K';
162
+ } else if (unit_sym == ID2SYM(rb_intern("nautical_miles"))) {
163
+ floc->unit = 'N';
164
+ }
165
+
166
+ return rb_str_new(&floc->unit,1);
167
+ }
168
+
169
+ void fast_loc_mark(ruby_fast_loc *floc) {
170
+ }
171
+
172
+ static void fast_loc_clear(ruby_fast_loc *floc) {
173
+ while(floc->count) {
174
+ free(floc->locations[--floc->count]);
175
+ }
176
+ free(floc->locations);
177
+ floc->locations = NULL;
178
+ }
179
+ static void fast_loc_free(ruby_fast_loc *floc) {
180
+ fast_loc_clear(floc);
181
+ free(floc);
182
+ }
183
+
184
+
185
+ static VALUE ruby_fast_loc_new(int argc, VALUE *argv, VALUE klass) {
186
+ VALUE new_floc, options, unit_sym;
187
+ ruby_fast_loc *floc;
188
+
189
+ rb_scan_args(argc, argv, "01", &options);
190
+
191
+ floc = ALLOC(ruby_fast_loc);
192
+ floc->unit = 'M';
193
+ floc->locations = NULL;
194
+ floc->count = 0;
195
+
196
+ new_floc = Data_Wrap_Struct(klass, fast_loc_mark, fast_loc_free, floc);
197
+
198
+ if (!NIL_P(options) && TYPE(options) == T_HASH) {
199
+ unit_sym = rb_hash_aref(options, ID2SYM(rb_intern("unit")));
200
+ ruby_fast_loc_set_unit(new_floc, unit_sym);
201
+ }
202
+
203
+ return new_floc;
204
+ }
205
+ static VALUE ruby_fast_loc_reset(VALUE self) {
206
+ ruby_fast_loc *floc;
207
+
208
+ Data_Get_Struct(self, ruby_fast_loc, floc);
209
+
210
+ fast_loc_clear(floc);
211
+
212
+ return self;
213
+ }
214
+
215
+ void Init_fastloc() {
216
+ cFastLoc = rb_define_class("FastLoc", rb_cObject);
217
+ rb_define_singleton_method(cFastLoc, "new", ruby_fast_loc_new, -1);
218
+ rb_define_method(cFastLoc, "add", ruby_fast_loc_add, 3);
219
+ rb_define_method(cFastLoc, "unit", ruby_fast_loc_get_unit, 0);
220
+ rb_define_method(cFastLoc, "unit=", ruby_fast_loc_set_unit, 1);
221
+ rb_define_method(cFastLoc, "locations", ruby_fast_loc_locations, 0);
222
+ rb_define_method(cFastLoc, "nearest", ruby_fast_loc_nearest, 2);
223
+ rb_define_method(cFastLoc, "reset", ruby_fast_loc_reset, 0);
224
+ }
@@ -0,0 +1,49 @@
1
+ $:.unshift '.'
2
+ require 'fastloc'
3
+
4
+ 10.times do
5
+
6
+ floc = FastLoc.new(:unit => :miles)
7
+ puts floc.unit
8
+ floc.unit = :kilometers
9
+ puts floc.unit
10
+ floc.unit = :nautical_miles
11
+ puts floc.unit
12
+ floc.unit = :miles
13
+ puts floc.unit
14
+
15
+ floc.add(39.0566, -76.5352, 1)
16
+ floc.add(39.07, -76.5398, 2)
17
+ floc.add(39.0566, -76.5352, 3)
18
+ floc.add(39.07, -76.5398, 4)
19
+ floc.add(39.0566, -76.5352, 5)
20
+ floc.add(39.07, -76.5398, 6)
21
+ floc.add(39.0566, -76.5352, 7)
22
+ floc.add(39.07, -76.5398, 8)
23
+ floc.add(39.0566, -76.5352, 9)
24
+ floc.add(39.07, -76.5398, 10)
25
+ floc.add(39.0566, -76.5352, 11)
26
+ floc.add(39.07, -76.5398, 12)
27
+ floc.add(39.0566, -76.5352, 13)
28
+ floc.add(39.07, -76.5398, 14)
29
+ floc.add(39.0566, -76.5352, 15)
30
+ floc.add(39.07, -76.5398, 16)
31
+ floc.add(39.0566, -76.5352, 17)
32
+ floc.add(39.07, -76.5398, 18)
33
+ floc.add(39.0566, -76.5352, 19)
34
+ floc.add(39.07, -76.5398, 20)
35
+
36
+ #puts floc.locations.inspect
37
+ t = Time.now
38
+ n = 1000
39
+ n.times do
40
+ floc.nearest(39.0735, -76.5654)
41
+ end
42
+ dur = Time.now - t
43
+ rate = n / dur
44
+ puts "processed: #{dur} seconds #{rate} per second with 20 possible"
45
+
46
+ floc.reset
47
+ floc=nil
48
+ end
49
+ GC.start
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fast_nearest_latlng
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Todd A. Fisher
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-15 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Locate the nearest location within a set to a given location
23
+ email: todd.fisher@gmail.com
24
+ executables: []
25
+
26
+ extensions:
27
+ - ext/extconf.rb
28
+ extra_rdoc_files:
29
+ - LICENSE
30
+ - README
31
+ files:
32
+ - ext/fastloc.c
33
+ - ext/extconf.rb
34
+ - ext/test.rb
35
+ - LICENSE
36
+ - README
37
+ has_rdoc: true
38
+ homepage: http://github.com/
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --main
44
+ - README
45
+ require_paths:
46
+ - ext
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ hash: 3
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ requirements: []
66
+
67
+ rubyforge_project: fast_nearest_latlng
68
+ rubygems_version: 1.5.2
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Nearest lat lng lookup
72
+ test_files:
73
+ - ext/test.rb