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 +3 -0
- data/Manifest.txt +8 -0
- data/README +75 -0
- data/ext/extconf.rb +3 -0
- data/ext/vincenty.c +161 -0
- data/lib/georuby-extras.rb +27 -0
- data/test/test_vincenty.rb +42 -0
- metadata +69 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
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
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
|