logo  Convert GB Grid Easting/Northing to Lat/Long
This Chapter
GPS & OS Ref
GPS to OS (GB)
OS(GB) to GPS
OS(GB) Format
GPS Format
Chapters
Home Page
Colours, RGB
Computer Specifications
Dates&Times
Disk Drives
Files
Folders
GPS and OS Ref
VB.Net Forms
Image Files
If & Select
List/Array
Mathematics
NuGet
Sound
String Functions
Sun and Moon
User Controls
Validation
DigitalDan Sites
My Other Sites
Contact Site

Note
Some pages
may contain
inaccuracies
Hits=5
Convert OS Grid References to Latitude/Longitude
Notes

Public Function OsXyToLatLon(EN As EastNorth) As LatLon
 Dim ret As LatLon
 Dim a, b, n, eSq, f0, lon0, lat0, n0, e0, v, p, netaSq, m, no, ea, latBar, vii, viii, ix, x, xi, xii, xiia, lat, lon As Double
 ea = EN.East
 no = EN.North
 Dim adj As OSTN = ArrOSTN(ea, no)
 ea = EN.East - adj.X
 no = EN.North - adj.Y
 ' ea and no within a few mm at this point - OSTN15 flexi sheet accuracy will improve if we adjrect at adjusted point
 adj = ArrOSTN(ea, no)
 ea = EN.East - adj.X
 no = EN.North - adj.Y
 ' ea and no now within about 1mm - very close to limit accuracy on OSTN15
 a = 6378137.0
 b = 6356752.31414036
 f0 = 0.9996012717
 lat0 = DegToRad(49)
 lon0 = DegToRad(-2)
 e0 = 400000
 n0 = -100000
 eSq = (Pow2(a) - Pow2(b)) / Pow2(a)
 n = (a - b) / (a + b)
 latBar = ((no - n0) / (a * f0)) + lat0
 m = (b * f0) * ((1 + n + ((5 / 4) * Pow2(n)) + ((5 / 4) * Pow3(n))) * (latBar - lat0) - (((3 * n) + (3 * Pow2(n)) + ((21 / 8) * Pow3(n))) * Math.Sin(latBar - lat0) * Math.Cos(latBar + lat0)) + (((15 / 8) * Pow2(n)) + ((15 / 8) * Pow3(n))) * Math.Sin(2 * (latBar - lat0)) * Math.Cos(2 * (latBar + lat0)) - (35 / 24) * Pow3(n) * Math.Sin(3 * (latBar - lat0)) * Math.Cos(3 * (latBar + lat0)))
 While (Math.Abs(no - n0 - m) >= 0.000001)
  latBar = ((no - n0 - m) / (a * f0)) + latBar
  m = (b * f0) * ((1 + n + ((5 / 4) * Pow2(n)) + ((5 / 4) * Pow3(n))) * (latBar - lat0) - (((3 * n) + (3 * Pow2(n)) + ((21 / 8) * Pow3(n))) * Math.Sin(latBar - lat0) * Math.Cos(latBar + lat0)) + (((15 / 8) * Pow2(n)) + ((15 / 8) * Pow3(n))) * Math.Sin(2 * (latBar - lat0)) * Math.Cos(2 * (latBar + lat0)) - (35 / 24) * Pow3(n) * Math.Sin(3 * (latBar - lat0)) * Math.Cos(3 * (latBar + lat0)))
 End While
 latBar = ((no - n0 - m) / (a * f0)) + latBar
 v = (a * f0) * Pow_Min0_5((1 - eSq * Pow2(Math.Sin(latBar))))
 p = (a * f0) * (1 - eSq) * Pow_Min1_5((1 - (eSq * Pow2(Math.Sin(latBar)))))
 netaSq = (v / p) - 1
 vii = (Math.Tan(latBar) / (2 * p * v))
 viii = (Math.Tan(latBar) / (24 * p * Pow3(v))) * (5 + (3 * Pow2(Math.Tan(latBar))) + netaSq - (9 * Pow2(Math.Tan(latBar)) * netaSq))
 ix = (Math.Tan(latBar) / (720 * p * (v ^ 5))) * (61 + (90 * (Math.Tan(latBar) ^ 2)) + (45 * (Math.Tan(latBar) ^ 4)))
 x = Sec(latBar) / v
 xi = (Sec(latBar) / (6 * (v ^ 3))) * ((v / p) + (2 * (Math.Tan(latBar) ^ 2)))
 xii = (Sec(latBar) / (120 * (v ^ 5))) * (5 + (28 * (Math.Tan(latBar) ^ 2)) + (24 * (Math.Tan(latBar) ^ 4)))
 xiia = (Sec(latBar) / (5040 * (v ^ 7))) * (61 + (662 * (Math.Tan(latBar) ^ 2)) + (1320 * (Math.Tan(latBar) ^ 4)) + (720 * (Math.Tan(latBar) ^ 6)))
 lat = latBar - (vii * ((ea - e0) ^ 2)) + (viii * ((ea - e0) ^ 4)) - (ix * ((ea - e0) ^ 6))
 lon = lon0 + (x * (ea - e0)) - (xi * ((ea - e0) ^ 3)) + (xii * ((ea - e0) ^ 5)) - (xiia * ((ea - e0) ^ 7))
 lat = RadToDeg(lat)
 lon = RadToDeg(lon)
 ret.Lat = lat
 ret.lon = lon
 Return ret
End Function
'
Private Function DegToRad(Deg As Double) As Double
 Return Deg * Math.PI / 180
End Function
'
Private Function RadToDeg(Rad As Double) As Double
 Return Rad * 180 / Math.PI
End Function
'
Private Function Sec(angle As Double) As Double
 Return Math.Sqrt(1 + (Math.Tan(angle) * Math.Tan(angle)))
End Function
'
Private Function Pow2(n As Double) As Double
 Return n * n
End Function
'
Private Function Pow3(n As Double) As Double
 Return n * n * n
End Function
'
Private Function Pow_Min0_5(n As Double) As Double
 Return 1 / (Math.Sqrt(n))
End Function
'
Private Function Pow_Min1_5(n As Double) As Double
 Return 1 / (n * Math.Sqrt(n))
End Function
'
Private Function OstnXY(east As Double, north As Double) As OSTN
 Dim adj As New OSTN
 Dim e1, n1, x0, y0 As Integer
 If east <= 0 Or east >= 700000 Or north <= 0 Or north >= 1250000 Then
  adj.X = 0
  adj.Y = 0
  Return adj
 End If
 If Not OstnIsLoaded() Then
  Dim pat As String = Application.StartupPath
  If Mid(pat, pat.Length, 1) <> "\" Then pat &= "\"
  LoadOSTN(pat & "ostn15.dat")
 End If
 e1 = Math.Floor(east / 1000) : x0 = e1 * 1000
 n1 = Math.Floor(north / 1000) : y0 = n1 * 1000
 Dim se0, se1, se2, se3, sn0, sn1, sn2, sn3, sz0, sz1, sz2, sz3, dx, dy, t, u, se, sn, sz As Double
 se0 = ArrOSTN(e1, n1).X
 sn0 = ArrOSTN(e1, n1).Y
 se1 = ArrOSTN(e1 + 1, n1).X
 sn1 = ArrOSTN(e1 + 1, n1).Y
 se2 = ArrOSTN(e1 + 1, n1 + 1).X
 sn2 = ArrOSTN(e1 + 1, n1 + 1).Y
 se3 = ArrOSTN(e1, n1 + 1).X
 sn3 = ArrOSTN(e1, n1 + 1).Y
 dx = east Mod 1000
 dy = north Mod 1000
 t = dx / 1000
 u = dy / 1000
 se = ((1 - t) * (1 - u) * se0) + (t * (1 - u) * se1) + (t * u * se2) + ((1 - t) * u * se3)
 sn = ((1 - t) * (1 - u) * sn0) + (t * (1 - u) * sn1) + (t * u * sn2) + ((1 - t) * u * sn3)
 sz = ((1 - t) * (1 - u) * sz0) + (t * (1 - u) * sz1) + (t * u * sz2) + ((1 - t) * u * sz3)
 adj.X = se
 adj.Y = sn
 Return adj
End Function
'
Private Function OstnIsLoaded() As Boolean
 If ArrOSTN.Length < 700 Then Return False
 If ArrOSTN(350, 625).X = 0 Then Return False
  Return True
 End Function
'
Public Function LoadOSTN(OstnFile As String) As String
 ' returns empty string if successful or an error message
 ' expects to find 701x1251 entries
 ' each entry is one line containing OSTN-X and OSTN-y Adjustment
 ' entries sorted ascending on Northing_thenBy_Easting
 If OstnIsLoaded() Then Return String.Empty
 Dim ListOSTNpairs As List(Of String) = IO.File.ReadAllLines(OstnFile).ToList
 If ListOSTNpairs.Count <> 701 * 1251 Then Return "Unexpected number of entries in OSTN file"
 Dim parts() As String
 For yy As Integer = 0 To 1250
  For xx As Integer = 0 To 700
   parts = ListOSTNpairs.Item((yy * 701) + xx).Split(","c)
   If parts.Length <> 2 Then Return "Unpaired OSTN entry encountered"
   If Not Double.TryParse(parts(0), ArrOSTN(xx, yy).X) Then Return ("Non numeric data encountered")
   If Not Double.TryParse(parts(1), ArrOSTN(xx, yy).Y) Then Return ("Non numeric data encountered")
  Next
 Next
 ListOSTNpairs.Clear()
 Return String.Empty
End Function
'
Public Structure LatLon
 Dim Lat As Double
 Dim lon As Double
End Structure
'
Public Structure OSTN
 Dim X As Double
 Dim Y As Double
End Structure
'
Public Structure EastNorth
 Dim East As Double
 Dim North As Double
End Structure
  
Acknowledgement
This page references material Copyright Ordnance Survey 2018 from their publication A Guide to Coordinate Systems in Great Britain

DigitalDan.co.uk