|
|
@@ -0,0 +1,181 @@ |
|
|
|
# https://raw.github.com/LambethCouncil/OSGB36_Converter/master/OSGB36.rb |
|
|
|
module OSGB36 |
|
|
|
|
|
|
|
extend self |
|
|
|
|
|
|
|
# Takes OSGB36 Easting/Northing coords |
|
|
|
# and returns WGS84 Latitude and Longitude |
|
|
|
def en_to_ll(easting, northing) |
|
|
|
@OSGB36_ll = to_OSGB36(easting, northing) |
|
|
|
to_WGS84(@OSGB36_ll[:latitude], @OSGB36_ll[:longitude]) |
|
|
|
end |
|
|
|
|
|
|
|
# Takes OSGB36 Easting/Northing coords and returns |
|
|
|
# OSGB36 Latitude and Longitude |
|
|
|
def to_OSGB36(easting, northing) |
|
|
|
|
|
|
|
@OSGB_F0 = 0.9996012717 |
|
|
|
@N0 = -100000.0 |
|
|
|
@E0 = 400000.0 |
|
|
|
@phi0 = deg_to_rad(49.0) |
|
|
|
@lambda0 = deg_to_rad(-2.0) |
|
|
|
@a = 6377563.396 |
|
|
|
@b = 6356256.909 |
|
|
|
@eSquared = ((@a * @a) - (@b * @b)) / (@a * @a) |
|
|
|
@phi = 0.0 |
|
|
|
@lambda = 0.0 |
|
|
|
@E = easting |
|
|
|
@N = northing |
|
|
|
@n = (@a - @b) / (@a + @b) |
|
|
|
@M = 0.0 |
|
|
|
@phiPrime = ((@N - @N0) / (@a * @OSGB_F0)) + @phi0 |
|
|
|
|
|
|
|
begin |
|
|
|
|
|
|
|
@M = |
|
|
|
(@b * @OSGB_F0)\ |
|
|
|
* (((1 + @n + ((5.0 / 4.0) * @n * @n) + ((5.0 / 4.0) * @n * @n * @n))\ |
|
|
|
* (@phiPrime - @phi0))\ |
|
|
|
- (((3 * @n) + (3 * @n * @n) + ((21.0 / 8.0) * @n * @n * @n))\ |
|
|
|
* Math.sin(@phiPrime - @phi0)\ |
|
|
|
* Math.cos(@phiPrime + @phi0))\ |
|
|
|
+ ((((15.0 / 8.0) * @n * @n) + ((15.0 / 8.0) * @n * @n * @n))\ |
|
|
|
* Math.sin(2.0 * (@phiPrime - @phi0))\ |
|
|
|
* Math.cos(2.0 * (@phiPrime + @phi0)))\ |
|
|
|
- (((35.0 / 24.0) * @n * @n * @n)\ |
|
|
|
* Math.sin(3.0 * (@phiPrime - @phi0))\ |
|
|
|
* Math.cos(3.0 * (@phiPrime + @phi0)))) |
|
|
|
|
|
|
|
@phiPrime += (@N - @N0 - @M) / (@a * @OSGB_F0) |
|
|
|
|
|
|
|
end while ((@N - @N0 - @M) >= 0.001) |
|
|
|
|
|
|
|
@v = @a * @OSGB_F0 * ((1.0 - @eSquared * sin_pow_2(@phiPrime)) ** -0.5) |
|
|
|
@rho = |
|
|
|
@a\ |
|
|
|
* @OSGB_F0\ |
|
|
|
* (1.0 - @eSquared)\ |
|
|
|
* ((1.0 - @eSquared * sin_pow_2(@phiPrime)) ** -1.5) |
|
|
|
@etaSquared = (@v / @rho) - 1.0 |
|
|
|
@VII = Math.tan(@phiPrime) / (2 * @rho * @v) |
|
|
|
@VIII = |
|
|
|
(Math.tan(@phiPrime) / (24.0 * @rho * (@v ** 3.0)))\ |
|
|
|
* (5.0\ |
|
|
|
+ (3.0 * tan_pow_2(@phiPrime))\ |
|
|
|
+ @etaSquared\ |
|
|
|
- (9.0 * tan_pow_2(@phiPrime) * @etaSquared)) |
|
|
|
@IX = |
|
|
|
(Math.tan(@phiPrime) / (720.0 * @rho * (@v ** 5.0)))\ |
|
|
|
* (61.0\ |
|
|
|
+ (90.0 * tan_pow_2(@phiPrime))\ |
|
|
|
+ (45.0 * tan_pow_2(@phiPrime) * tan_pow_2(@phiPrime))) |
|
|
|
@X = sec(@phiPrime) / @v |
|
|
|
@XI = |
|
|
|
(sec(@phiPrime) / (6.0 * @v * @v * @v))\ |
|
|
|
* ((@v / @rho) + (2 * tan_pow_2(@phiPrime))) |
|
|
|
@XII = |
|
|
|
(sec(@phiPrime) / (120.0 * (@v ** 5.0)))\ |
|
|
|
* (5.0\ |
|
|
|
+ (28.0 * tan_pow_2(@phiPrime))\ |
|
|
|
+ (24.0 * tan_pow_2(@phiPrime) * tan_pow_2(@phiPrime))) |
|
|
|
@XIIA = |
|
|
|
(sec(@phiPrime) / (5040.0 * (@v ** 7.0)))\ |
|
|
|
* (61.0\ |
|
|
|
+ (662.0 * tan_pow_2(@phiPrime))\ |
|
|
|
+ (1320.0 * tan_pow_2(@phiPrime) * tan_pow_2(@phiPrime))\ |
|
|
|
+ (720.0\ |
|
|
|
* tan_pow_2(@phiPrime)\ |
|
|
|
* tan_pow_2(@phiPrime)\ |
|
|
|
* tan_pow_2(@phiPrime))) |
|
|
|
@phi = |
|
|
|
@phiPrime\ |
|
|
|
- (@VII * ((@E - @E0) ** 2.0))\ |
|
|
|
+ (@VIII * ((@E - @E0) ** 4.0))\ |
|
|
|
- (@IX * ((@E - @E0) ** 6.0)) |
|
|
|
@lambda = |
|
|
|
@lambda0\ |
|
|
|
+ (@X * (@E - @E0))\ |
|
|
|
- (@XI * ((@E - @E0) ** 3.0))\ |
|
|
|
+ (@XII * ((@E - @E0) ** 5.0))\ |
|
|
|
- (@XIIA * ((@E - @E0) ** 7.0)) |
|
|
|
|
|
|
|
{ :latitude => rad_to_deg(@phi), :longitude => rad_to_deg(@lambda) } |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
# Takes OSGB36 Latitude and Longitude coords |
|
|
|
# and returns WGS84 Latitude and Longitude |
|
|
|
def to_WGS84(latitude,longitude) |
|
|
|
|
|
|
|
@a = 6377563.396 |
|
|
|
@b = 6356256.909 |
|
|
|
@eSquared = ((@a * @a) - (@b * @b)) / (@a * @a) |
|
|
|
|
|
|
|
@phi = deg_to_rad(latitude) |
|
|
|
@lambda = deg_to_rad(longitude) |
|
|
|
@v = @a / (Math.sqrt(1 - @eSquared * sin_pow_2(@phi))) |
|
|
|
@H = 0 |
|
|
|
@x = (@v + @H) * Math.cos(@phi) * Math.cos(@lambda) |
|
|
|
@y = (@v + @H) * Math.cos(@phi) * Math.sin(@lambda) |
|
|
|
@z = ((1 - @eSquared) * @v + @H) * Math.sin(@phi) |
|
|
|
|
|
|
|
@tx = 446.448 |
|
|
|
@ty = -124.157 |
|
|
|
@tz = 542.060 |
|
|
|
|
|
|
|
@s = -0.0000204894 |
|
|
|
@rx = deg_to_rad( 0.00004172222) |
|
|
|
@ry = deg_to_rad( 0.00006861111) |
|
|
|
@rz = deg_to_rad( 0.00023391666) |
|
|
|
|
|
|
|
@xB = @tx + (@x * (1 + @s)) + (-@rx * @y) + (@ry * @z) |
|
|
|
@yB = @ty + (@rz * @x) + (@y * (1 + @s)) + (-@rx * @z) |
|
|
|
@zB = @tz + (-@ry * @x) + (@rx * @y) + (@z * (1 + @s)) |
|
|
|
|
|
|
|
@a = 6378137.000 |
|
|
|
@b = 6356752.3141 |
|
|
|
@eSquared = ((@a * @a) - (@b * @b)) / (@a * @a) |
|
|
|
|
|
|
|
@lambdaB = rad_to_deg(Math.atan(@yB / @xB)) |
|
|
|
@p = Math.sqrt((@xB * @xB) + (@yB * @yB)) |
|
|
|
@phiN = Math.atan(@zB / (@p * (1 - @eSquared))) |
|
|
|
|
|
|
|
(1..10).each do |i| |
|
|
|
@v = @a / (Math.sqrt(1 - @eSquared * sin_pow_2(@phiN))) |
|
|
|
@phiN1 = Math.atan((@zB + (@eSquared * @v * Math.sin(@phiN))) / @p) |
|
|
|
@phiN = @phiN1 |
|
|
|
end |
|
|
|
|
|
|
|
@phiB = rad_to_deg(@phiN) |
|
|
|
|
|
|
|
{ :latitude => @phiB, :longitude => @lambdaB } |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
private # Some Math |
|
|
|
|
|
|
|
def deg_to_rad(degrees) |
|
|
|
degrees / 180.0 * Math::PI |
|
|
|
end |
|
|
|
|
|
|
|
def rad_to_deg(r) |
|
|
|
(r/Math::PI)*180 |
|
|
|
end |
|
|
|
|
|
|
|
def sin_pow_2(x) |
|
|
|
Math.sin(x) * Math.sin(x) |
|
|
|
end |
|
|
|
|
|
|
|
def cos_pow_2(x) |
|
|
|
Math.cos(x) * Math.cos(x) |
|
|
|
end |
|
|
|
|
|
|
|
def tan_pow_2(x) |
|
|
|
Math.tan(x) * Math.tan(x) |
|
|
|
end |
|
|
|
|
|
|
|
def sec(x) |
|
|
|
1.0 / Math.cos(x) |
|
|
|
end |
|
|
|
|
|
|
|
end |