osgb_wgs84 0.0.2
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.
- data/lib/osgb_wgs84.rb +190 -0
- metadata +51 -0
data/lib/osgb_wgs84.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
class OSGB_WGS84
|
2
|
+
#ellipse parameters
|
3
|
+
@e = { :wgs84 => { :a=> 6378137, :b=> 6356752.3142, :f=> 1 / 298.257223563 },
|
4
|
+
:airy1830 => { :a=> 6377563.396, :b=> 6356256.910, :f=> 1 / 299.3249646 } };
|
5
|
+
|
6
|
+
#helmert transform parameters
|
7
|
+
@h = { :wgs84toOSGB36 => { :tx=> -446.448, :ty=> 125.157, :tz=> -542.060, # m
|
8
|
+
:rx=> -0.1502, :ry=> -0.2470, :rz=> -0.8421, # sec
|
9
|
+
:s=> 20.4894 }, # ppm
|
10
|
+
:osgb36toWGS84 => { :tx=> 446.448, :ty=> -125.157, :tz=> 542.060,
|
11
|
+
:rx=> 0.1502, :ry=> 0.2470, :rz=> 0.8421,
|
12
|
+
:s=> -20.4894 } };
|
13
|
+
|
14
|
+
|
15
|
+
def self.OSGB36_to_WGS84(p1lat, p1lon, p1height)
|
16
|
+
p2 = convert(p1lat, p1lon, p1height, @e[:airy1830], @h[:osgb36toWGS84], @e[:wgs84]);
|
17
|
+
return p2;
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def self.WGS84_to_OSGB36(p1lat, p1lon, p1height)
|
22
|
+
p2 = convert(p1lat, p1lon, p1height, @e[:wgs84], @h[:wgs84toOSGB36], @e[:airy1830]);
|
23
|
+
return p2;
|
24
|
+
end
|
25
|
+
|
26
|
+
# OSGB36 lat lon to OS UK grid eastings & northings
|
27
|
+
# http://www.movable-type.co.uk/scripts/latlong-gridref.html
|
28
|
+
def self.OSGB36_to_OSNG(lat, long)
|
29
|
+
lat = toRad(lat);
|
30
|
+
lon = toRad(long);
|
31
|
+
|
32
|
+
a = 6377563.396; b = 6356256.910 # Airy 1830 major & minor semi-axes
|
33
|
+
f0 = 0.9996012717 # NatGrid scale factor on central meridian
|
34
|
+
lat0 = toRad(49); lon0 = toRad(-2) # NatGrid true origin
|
35
|
+
n0 = -100000; e0 = 400000; # northing & easting of true origin, metres
|
36
|
+
e2 = 1 - (b*b) / (a*a); # eccentricity squared
|
37
|
+
n = (a-b) / (a+b); n2 = n*n; n3 = n*n*n;
|
38
|
+
|
39
|
+
cosLat = Math.cos(lat); sinLat = Math.sin(lat);
|
40
|
+
nu = a*f0/Math.sqrt(1-e2*sinLat*sinLat); # transverse radius of curvature
|
41
|
+
rho = a*f0*(1-e2) / ( (1-e2*sinLat*sinLat) ** 1.5); # meridional radius of curvature
|
42
|
+
eta2 = nu/rho-1;
|
43
|
+
|
44
|
+
ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
|
45
|
+
mb = (3*n + 3*n*n + (21/8)*n3) * Math.sin(lat-lat0) * Math.cos(lat+lat0);
|
46
|
+
mc = ((15/8)*n2 + (15/8)*n3) * Math.sin(2*(lat-lat0)) * Math.cos(2*(lat+lat0));
|
47
|
+
md = (35/24)*n3 * Math.sin(3*(lat-lat0)) * Math.cos(3*(lat+lat0));
|
48
|
+
m = b * f0 * (ma - mb + mc - md); # meridional arc
|
49
|
+
|
50
|
+
cos3lat = cosLat*cosLat*cosLat;
|
51
|
+
cos5lat = cos3lat*cosLat*cosLat;
|
52
|
+
tan2lat = Math.tan(lat)*Math.tan(lat);
|
53
|
+
tan4lat = tan2lat*tan2lat;
|
54
|
+
|
55
|
+
i = m + n0;
|
56
|
+
ii = (nu/2)*sinLat*cosLat;
|
57
|
+
iii = (nu/24)*sinLat*cos3lat*(5-tan2lat+9*eta2);
|
58
|
+
iiiA = (nu/720)*sinLat*cos5lat*(61-58*tan2lat+tan4lat);
|
59
|
+
iv = nu*cosLat;
|
60
|
+
v = (nu/6)*cos3lat*(nu/rho-tan2lat);
|
61
|
+
vi = (nu/120) * cos5lat * (5 - 18*tan2lat + tan4lat + 14*eta2 - 58*tan2lat*eta2);
|
62
|
+
|
63
|
+
dLon = lon-lon0;
|
64
|
+
dLon2 = dLon*dLon
|
65
|
+
dLon3 = dLon2*dLon
|
66
|
+
dLon4 = dLon3*dLon
|
67
|
+
dLon5 = dLon4*dLon
|
68
|
+
dLon6 = dLon5*dLon
|
69
|
+
|
70
|
+
n = i + ii*dLon2 + iii*dLon4 + iiiA*dLon6;
|
71
|
+
e = e0 + iv*dLon + v*dLon3 + vi*dLon5;
|
72
|
+
|
73
|
+
return [ e, n ] #return raw easting and northings instead
|
74
|
+
|
75
|
+
#return gridrefNumToLet( e, n, 8)
|
76
|
+
end
|
77
|
+
|
78
|
+
#convert numeric grid reference (in metres) to standard-form grid ref
|
79
|
+
def self.OSNG_numbers_to_letters(e, n, digits)
|
80
|
+
#get the 100km-grid indices
|
81
|
+
e100k = (e / 100000).floor; n100k = (n / 100000).floor;
|
82
|
+
|
83
|
+
return '' if (e100k<0 or e100k>6 or n100k<0 or n100k>12)
|
84
|
+
|
85
|
+
#translate those into numeric equivalents of the grid letters
|
86
|
+
l1 = (19-n100k) - (19-n100k) % 5 + ((e100k+10) / 5).floor;
|
87
|
+
l2 = (19-n100k) * 5 % 25 + e100k % 5;
|
88
|
+
|
89
|
+
# compensate for skipped 'I' and build grid letter-pairs
|
90
|
+
l1=l1+1 if (l1 > 7)
|
91
|
+
l2=l2+1 if (l2 > 7)
|
92
|
+
# letPair = (l1 +'A'[0]).chr + (l2 +'A'[0]).chr ; #(Old code for Ruby 1.8 replaced with following line for ruby1.9)
|
93
|
+
letPair = (l1 +'A'.unpack('C')[0]).chr + (l2 +'A'.unpack('C')[0]).chr ;
|
94
|
+
|
95
|
+
# strip 100km-grid indices from easting & northing, and reduce precision
|
96
|
+
e = ( (e % 100000) / (10 ** (5 - digits / 2)) ).floor;
|
97
|
+
n = ( (n % 100000) / (10 ** (5 - digits / 2)) ).floor;
|
98
|
+
|
99
|
+
gridRef = letPair + e.to_s.rjust(digits / 2) + n.to_s.rjust(digits / 2)
|
100
|
+
|
101
|
+
return gridRef;
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def self.toRad(degrees)
|
107
|
+
return degrees * Math::PI / 180;
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.toDeg(rads)
|
111
|
+
return rads * 180 / Math::PI
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.convert(p1lat, p1lon, p1height, e1, t, e2)
|
115
|
+
# -- convert polar to cartesian coordinates (using ellipse 1)
|
116
|
+
|
117
|
+
p1lat = toRad(p1lat); p1lon = toRad(p1lon);
|
118
|
+
|
119
|
+
a = e1[:a]; b = e1[:b];
|
120
|
+
|
121
|
+
sinPhi = Math.sin(p1lat); cosPhi = Math.cos(p1lat);
|
122
|
+
sinLambda = Math.sin(p1lon); cosLambda = Math.cos(p1lon);
|
123
|
+
h = p1height;
|
124
|
+
|
125
|
+
eSq = (a*a - b*b) / (a*a);
|
126
|
+
nu = a / Math.sqrt(1 - eSq*sinPhi*sinPhi);
|
127
|
+
|
128
|
+
x1 = (nu+h) * cosPhi * cosLambda;
|
129
|
+
y1 = (nu+h) * cosPhi * sinLambda;
|
130
|
+
z1 = ((1-eSq)*nu + h) * sinPhi;
|
131
|
+
|
132
|
+
|
133
|
+
# -- apply helmert transform using appropriate params
|
134
|
+
|
135
|
+
tx = t[:tx]; ty = t[:ty]; tz = t[:tz];
|
136
|
+
rx = t[:rx] / 3600 * Math::PI/180; #normalise seconds to radians
|
137
|
+
ry = t[:ry] / 3600 * Math::PI/180;
|
138
|
+
rz = t[:rz] / 3600 * Math::PI/180;
|
139
|
+
s1 = t[:s] / 1e6 + 1; #normalise ppm to (s+1)
|
140
|
+
|
141
|
+
#apply transform
|
142
|
+
x2 = tx + x1*s1 - y1*rz + z1*ry;
|
143
|
+
y2 = ty + x1*rz + y1*s1 - z1*rx;
|
144
|
+
z2 = tz - x1*ry + y1*rx + z1*s1;
|
145
|
+
|
146
|
+
|
147
|
+
# -- convert cartesian to polar coordinates (using ellipse 2)
|
148
|
+
|
149
|
+
a = e2[:a]; b = e2[:b];
|
150
|
+
precision = 4 / a; # results accurate to around 4 metres
|
151
|
+
|
152
|
+
eSq = (a*a - b*b) / (a*a);
|
153
|
+
p = Math.sqrt(x2*x2 + y2*y2);
|
154
|
+
phi = Math.atan2(z2, p*(1-eSq)); phiP = 2 * Math::PI;
|
155
|
+
while ( (phi-phiP).abs > precision) do
|
156
|
+
nu = a / Math.sqrt(1 - eSq*Math.sin(phi)*Math.sin(phi));
|
157
|
+
phiP = phi;
|
158
|
+
phi = Math.atan2(z2 + eSq*nu*Math.sin(phi), p);
|
159
|
+
end
|
160
|
+
lambda = Math.atan2(y2, x2);
|
161
|
+
h = p/Math.cos(phi) - nu;
|
162
|
+
|
163
|
+
#return array [lat,lon,height]
|
164
|
+
return [ toDeg(phi), toDeg(lambda), h ];
|
165
|
+
end
|
166
|
+
|
167
|
+
# # Example WGS84 lat/lon to convert
|
168
|
+
# lon = -0.10322
|
169
|
+
# lat = 51.52237
|
170
|
+
|
171
|
+
|
172
|
+
# osgb36point = OSGB_WGS84::WGS84_to_OSGB36(lat,lon, height)
|
173
|
+
# oslat = osgb36point[0]
|
174
|
+
# oslon = osgb36point[1]
|
175
|
+
# osh = osgb36point[2]
|
176
|
+
|
177
|
+
# osUKgridPoint = OSGB_WGS84::OSGB36_to_OSNG(oslat,oslon)
|
178
|
+
# easting = osUKgridPoint[0].round
|
179
|
+
# northing = osUKgridPoint[1].round
|
180
|
+
|
181
|
+
# gridrefLetters = OSGB_WGS84::OSNG_numbers_to_letters(easting,northing, 8)
|
182
|
+
|
183
|
+
# puts "wgs84 lat:" + lat.to_s + ", wgs84 lon:" + lon.to_s
|
184
|
+
# puts "http://www.openstreetmap.org/?mlat=" + lat.to_s + "&mlon=" + lon.to_s + "&zoom=16"
|
185
|
+
# puts "Converts to:";
|
186
|
+
# puts "osgb36 lat:" + oslat.to_s + ", osgb36 lon:" + oslon.to_s
|
187
|
+
# puts "Converts to:";
|
188
|
+
# puts "easting:" + easting.to_s + ", northing:" + northing.to_s + " As a grid ref:" + gridrefLetters
|
189
|
+
# puts "http://streetmap.co.uk/grid/" + easting.to_s + "_" + northing.to_s + "_106"
|
190
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: osgb_wgs84
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chris Veness
|
9
|
+
- Harry Wood
|
10
|
+
- Aaron B. Russell
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2012-09-21 00:00:00.000000000 Z
|
15
|
+
dependencies: []
|
16
|
+
description: This gem converts Ordnance Survey National Grid co-ordinates to and from
|
17
|
+
WGS84 co-ordinates. It is based on a JavaScript implementation originally by Chris
|
18
|
+
Veness, which was then ported to Ruby by Harry Wood and bundled into a gem by Aaron
|
19
|
+
B. Russell.
|
20
|
+
email: aaron@unadopted.co.uk
|
21
|
+
executables: []
|
22
|
+
extensions: []
|
23
|
+
extra_rdoc_files: []
|
24
|
+
files:
|
25
|
+
- lib/osgb_wgs84.rb
|
26
|
+
homepage: https://github.com/arussell/osgb_wgs84
|
27
|
+
licenses:
|
28
|
+
- CC-BY
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 1.8.24
|
48
|
+
signing_key:
|
49
|
+
specification_version: 3
|
50
|
+
summary: Ordnance Survey National Grid to WGS84 co-ordinate converter
|
51
|
+
test_files: []
|