davetroy-georuby-extras 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ === 0.5.0 / 2008-12-3
2
+
3
+ * Released by David Troy (C) Under the MIT License
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ README
3
+ ext/extconf.rb
4
+ ext/vincenty.c
5
+ lib/georuby-extras.rb
6
+ test/test_vincenty.rb
7
+ test/extras-benchmark.rb
8
+
data/README ADDED
@@ -0,0 +1,75 @@
1
+ = georuby-extras
2
+
3
+ * http://github.com/davetroy/georuby-extras
4
+
5
+ == DESCRIPTION:
6
+
7
+ GeoRuby-Extras Gem for Ruby (c) 2008 David Troy
8
+ dave@roundhousetech.com
9
+
10
+ GeoRuby-Extras offers some native C enhancements and extras to the popular GeoRuby gem.
11
+
12
+ GeoRuby offers OGC-compliant simple features for Ruby as well as the ability to
13
+ process SHP files and do other cool GIS hacks. GeoRuby is often used in conjunction
14
+ with GIS extensions for popular databases including Postgres and MySQL.
15
+
16
+ == FEATURES/PROBLEMS:
17
+
18
+ * Provides native C implementation of Vincenty distance and direct algorithms.
19
+ * 25 times faster than GeoRuby 'ellipsoidal_distance' function.
20
+ * Adds the Vincenty 'direct' algorithm (point_at_bearing_and_distance).
21
+ * No known problems; won't work with JRuby or non-MRI Ruby implementations.
22
+
23
+ == SYNOPSIS:
24
+
25
+ require 'rubygems'
26
+ require 'geo_ruby'
27
+ require 'georuby-extras'
28
+
29
+ @home = Point.from_lon_lat(-76.511,39.024)
30
+ @nyc = Point.from_lon_lat(-74,40.6)
31
+
32
+ >> @home.ellipsoidal_distance(@nyc)
33
+ => 277195.510448391
34
+
35
+ >> @home.point_at_bearing_and_distance(91.0, 130000.0).kml_representation
36
+ => "<Point>\n<coordinates>-75.0105920700835,38.9939120193168</coordinates>\n</Point>\n"
37
+
38
+ == REQUIREMENTS:
39
+
40
+ * GCC and a Gnu-ish build environment (for native extensions)
41
+
42
+ == INSTALLATION
43
+
44
+ 1) Enable gems from github, if you haven't already done so (rubygems >= 1.2):
45
+ > sudo gem sources -a http://gems.github.com
46
+
47
+ 2) Install gem
48
+ > sudo gem install davetroy-georuby-extras
49
+
50
+ 3) Profit!
51
+
52
+ == LICENSE:
53
+
54
+ (The MIT License)
55
+
56
+ Copyright (c) 2008 David Troy, Roundhouse Technologies LLC
57
+
58
+ Permission is hereby granted, free of charge, to any person obtaining
59
+ a copy of this software and associated documentation files (the
60
+ 'Software'), to deal in the Software without restriction, including
61
+ without limitation the rights to use, copy, modify, merge, publish,
62
+ distribute, sublicense, and/or sell copies of the Software, and to
63
+ permit persons to whom the Software is furnished to do so, subject to
64
+ the following conditions:
65
+
66
+ The above copyright notice and this permission notice shall be
67
+ included in all copies or substantial portions of the Software.
68
+
69
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
70
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
71
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
72
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
73
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
74
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
75
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/ext/extconf.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+ dir_config("vincenty")
3
+ create_makefile("vincenty")
data/ext/vincenty.c ADDED
@@ -0,0 +1,161 @@
1
+ // geohash-native.c
2
+ // (c) 2008 David Troy
3
+ // dave@roundhousetech.com
4
+ //
5
+ // Based on Javascript code (c) by Chris Veness
6
+ // http://www.movable-type.co.uk/scripts/latlong-vincenty.html
7
+ // http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html
8
+ //
9
+ // Derived from algorithms by T. Vincenty, 1975
10
+ //
11
+ // (The MIT License)
12
+ //
13
+ // Copyright (c) 2008 David Troy, Roundhouse Technologies LLC
14
+ //
15
+ // Permission is hereby granted, free of charge, to any person obtaining
16
+ // a copy of this software and associated documentation files (the
17
+ // 'Software'), to deal in the Software without restriction, including
18
+ // without limitation the rights to use, copy, modify, merge, publish,
19
+ // distribute, sublicense, and/or sell copies of the Software, and to
20
+ // permit persons to whom the Software is furnished to do so, subject to
21
+ // the following conditions:
22
+ //
23
+ // The above copyright notice and this permission notice shall be
24
+ // included in all copies or substantial portions of the Software.
25
+ //
26
+ // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
27
+ // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
29
+ // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
30
+ // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31
+ // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
+
34
+ #include "ruby.h"
35
+ #include <math.h>
36
+ #include <ctype.h>
37
+
38
+ #define DEG_TO_RAD 0.0174532925199433
39
+ #define PI 3.14159265358979
40
+
41
+ static VALUE rb_mVincenty;
42
+
43
+ static VALUE distance(VALUE self, VALUE rb_lon1, VALUE rb_lat1, VALUE rb_lon2, VALUE rb_lat2, VALUE rb_a, VALUE rb_b) {
44
+
45
+ Check_Type(rb_lon1, T_FLOAT);
46
+ Check_Type(rb_lat1, T_FLOAT);
47
+ Check_Type(rb_lon2, T_FLOAT);
48
+ Check_Type(rb_lat2, T_FLOAT);
49
+ Check_Type(rb_a, T_FLOAT);
50
+ Check_Type(rb_b, T_FLOAT);
51
+
52
+ double lon1 = RFLOAT(rb_lon1)->value;
53
+ double lat1 = RFLOAT(rb_lat1)->value;
54
+ double lon2 = RFLOAT(rb_lon2)->value;
55
+ double lat2 = RFLOAT(rb_lat2)->value;
56
+ double a = RFLOAT(rb_a)->value;
57
+ double b = RFLOAT(rb_b)->value;
58
+
59
+ double sinLambda, cosLambda, sinSigma, cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM, C;
60
+
61
+ double f = (a-b) / a;
62
+ double L = (lon2-lon1) * DEG_TO_RAD;
63
+ double U1 = atan((1-f) * tan(lat1 * DEG_TO_RAD));
64
+ double U2 = atan((1-f) * tan(lat2 * DEG_TO_RAD));
65
+ double sinU1 = sin(U1), cosU1 = cos(U1);
66
+ double sinU2 = sin(U2), cosU2 = cos(U2);
67
+
68
+ int iterLimit = 20;
69
+ double lambda = L, lambdaP=2*PI;
70
+ while ((fabs(lambda-lambdaP) > 1e-12 && --iterLimit>0)) {
71
+ sinLambda = sin(lambda); cosLambda = cos(lambda);
72
+ sinSigma = sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
73
+ if (sinSigma==0) return rb_float_new(0); // co-incident points
74
+ cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
75
+ sigma = atan2(sinSigma, cosSigma);
76
+ sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
77
+ cosSqAlpha = 1 - sinAlpha*sinAlpha;
78
+ cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
79
+ if (isnan(cos2SigmaM)) cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6)
80
+ C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
81
+ lambdaP = lambda;
82
+ lambda = L + (1-C) * f * sinAlpha * (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
83
+ }
84
+
85
+ if (iterLimit==0) return Qnil; // formula failed to converge
86
+
87
+ double uSq = cosSqAlpha * (a*a - b*b) / (b*b);
88
+ double A = 1 + uSq/16384.0*(4096+uSq*(-768+uSq*(320-175*uSq)));
89
+ double B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
90
+ double deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM) -
91
+ B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
92
+
93
+ double s = b*A*(sigma-deltaSigma);
94
+
95
+ return rb_float_new(s);
96
+ }
97
+
98
+ static VALUE point_from_lon_lat(VALUE self, VALUE rb_lon1, VALUE rb_lat1, VALUE rb_bearing, VALUE rb_distance, VALUE rb_a, VALUE rb_b) {
99
+ Check_Type(rb_lon1, T_FLOAT);
100
+ Check_Type(rb_lat1, T_FLOAT);
101
+ Check_Type(rb_bearing, T_FLOAT);
102
+ Check_Type(rb_distance, T_FLOAT);
103
+ Check_Type(rb_a, T_FLOAT);
104
+ Check_Type(rb_b, T_FLOAT);
105
+
106
+ VALUE ret;
107
+
108
+ double lon1 = RFLOAT(rb_lon1)->value;
109
+ double lat1 = RFLOAT(rb_lat1)->value;
110
+ double brng = RFLOAT(rb_bearing)->value;
111
+ double s = RFLOAT(rb_distance)->value;
112
+ double a = RFLOAT(rb_a)->value;
113
+ double b = RFLOAT(rb_b)->value;
114
+
115
+ double f = (a-b) / a;
116
+ double alpha1 = brng * DEG_TO_RAD;
117
+ double sinAlpha1 = sin(alpha1), cosAlpha1 = cos(alpha1);
118
+
119
+ double tanU1 = (1-f) * tan(lat1 * DEG_TO_RAD);
120
+ double cosU1 = 1 / sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1;
121
+ double sigma1 = atan2(tanU1, cosAlpha1);
122
+ double sinAlpha = cosU1 * sinAlpha1;
123
+ double cosSqAlpha = 1 - sinAlpha*sinAlpha;
124
+ double uSq = cosSqAlpha * (a*a - b*b) / (b*b);
125
+ double A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
126
+ double B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
127
+
128
+ double sigma = s / (b*A), sigmaP = 2*PI;
129
+ double cos2SigmaM, sinSigma, deltaSigma, cosSigma;
130
+ while (fabs(sigma-sigmaP) > 1e-12) {
131
+ cos2SigmaM = cos(2*sigma1 + sigma);
132
+ sinSigma = sin(sigma), cosSigma = cos(sigma);
133
+ deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
134
+ B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
135
+ sigmaP = sigma;
136
+ sigma = s / (b*A) + deltaSigma;
137
+ }
138
+
139
+ double tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1;
140
+ double lat2 = atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1, (1-f)*sqrt(sinAlpha*sinAlpha + tmp*tmp));
141
+ double lambda = atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1);
142
+ double C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
143
+ double L = lambda - (1-C) * f * sinAlpha * (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
144
+
145
+ double revAz = atan2(sinAlpha, -tmp); // final bearing
146
+
147
+ ret = rb_ary_new2(2); /* [lon, lat] */
148
+ rb_ary_store(ret, 0, rb_float_new(lon1+L/DEG_TO_RAD));
149
+ rb_ary_store(ret, 1, rb_float_new(lat2/DEG_TO_RAD));
150
+
151
+ return ret;
152
+ }
153
+
154
+ void Init_vincenty()
155
+ {
156
+ rb_mVincenty = rb_define_module("Vincenty");
157
+ rb_define_module_function(rb_mVincenty, "distance", distance, 6);
158
+ rb_define_module_function(rb_mVincenty, "point_from_lon_lat", point_from_lon_lat, 6);
159
+ }
160
+
161
+ // end
@@ -0,0 +1,27 @@
1
+ require 'geo_ruby'
2
+ require "#{File.dirname(__FILE__)}/../ext/vincenty"
3
+
4
+ module GeoRuby
5
+ module SimpleFeatures
6
+ class Point < Geometry
7
+
8
+ alias_method :orig_ellipsoidal_distance, :ellipsoidal_distance
9
+
10
+ # Implementation of the Vincenty 'distance' formula to find the ellipsoidal distance between points
11
+ # As per implementation by Chris Veness (http://www.movable-type.co.uk/scripts/latlong-vincenty.html)
12
+ # Default a/b values are for the WGS84 ellipsoid - other values may be specified
13
+ def ellipsoidal_distance(point, a = 6378137.0, b = 6356752.3142)
14
+ Vincenty.distance(x.to_f, y.to_f, point.x.to_f, point.y.to_f, a.to_f, b.to_f)
15
+ end
16
+
17
+ # Implementation of the Vincenty 'direct' formula to find a point based on a bearing and distance
18
+ # As per implementation by Chris Veness (http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html)
19
+ # Default a/b values are for the WGS84 ellipsoid - other values may be specified
20
+ def point_at_bearing_and_distance(bearing=0.0, distance=0.0, a = 6378137.0, b = 6356752.3142)
21
+ Point.from_coordinates(Vincenty.point_from_lon_lat(lon.to_f,lat.to_f,bearing.to_f,distance.to_f,a.to_f,b.to_f))
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'test/unit'
4
+ require 'geo_ruby'
5
+ require "#{File.dirname(__FILE__)}/../lib/georuby-extras"
6
+
7
+ class GeoHashNativeTest < Test::Unit::TestCase
8
+
9
+ include GeoRuby::SimpleFeatures
10
+
11
+ def setup
12
+ @home = Point.from_lon_lat(-76.511,39.024)
13
+ @nyc = Point.from_lon_lat(-74,40.6)
14
+ end
15
+
16
+ def test_distance
17
+ assert_in_delta 277195.5104, @home.ellipsoidal_distance(@nyc), 0.001
18
+ end
19
+
20
+ def test_orig_distance
21
+ assert_in_delta 277195.5104, @home.orig_ellipsoidal_distance(@nyc), 0.001
22
+ end
23
+
24
+ def test_bearing_from_point
25
+ dest = @home.point_at_bearing_and_distance(91.0, 130000.0)
26
+ assert_in_delta -75.0106, dest.x, 0.0001
27
+ assert_in_delta 38.9939, dest.y, 0.0001
28
+ end
29
+
30
+ # This test is basically identical to those in georuby; to confirm compatibility
31
+ def test_point_distance
32
+ point1 = Point.from_x_y(0,0)
33
+ point2 = Point.from_x_y(3,4)
34
+ assert_equal(5,point1.euclidian_distance(point2))
35
+
36
+ assert_in_delta(554058.924,point1.ellipsoidal_distance(point2),0.001)
37
+ assert_in_delta(554058.924,point1.orig_ellipsoidal_distance(point2),0.001)
38
+
39
+ assert_in_delta(555811.68,point1.spherical_distance(point2),0.01)
40
+ end
41
+
42
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: davetroy-georuby-extras
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - David Troy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-03 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: GeoRuby
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.0
23
+ version:
24
+ description: Provides native implementations of Vincenty ellipsoidal functions; more to come.
25
+ email: dave@roundhousetech.com
26
+ executables: []
27
+
28
+ extensions:
29
+ - ext/extconf.rb
30
+ extra_rdoc_files:
31
+ - Manifest.txt
32
+ - README
33
+ - History.txt
34
+ files:
35
+ - ext/extconf.rb
36
+ - ext/vincenty.c
37
+ - lib/georuby-extras.rb
38
+ - Manifest.txt
39
+ - README
40
+ - History.txt
41
+ has_rdoc: true
42
+ homepage: http://github.com/davetroy/georuby-extras
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --main
46
+ - README
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.2.0
65
+ signing_key:
66
+ specification_version: 2
67
+ summary: Native extensions and extra functions for the GeoRuby library.
68
+ test_files:
69
+ - test/test_vincenty.rb