# 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