OSGB function reference

Convert

Conversion between latitude/longitude coordinates and OSGB grid references.

This module provides the core routines that implement the OS conversion formulae.

osgb.convert.grid_to_ll(easting, northing, model=u'WGS84')

Convert OSGB (easting, northing) to latitude and longitude.

Input
an (easting, northing) pair in metres from the false point of origin of the grid. Note that if you are starting with a grid reference string like TQ124095 you will need to use the parse_grid functions in osgb.gridder to parse it into an easting, northing pair before you can call this function.
Output
a (latitude, longitude) pair in degrees, postive East/North negative West/South

An optional argument ‘model’ defines the graticule model to use. The default is WGS84, the standard model used for the GPS network and for references given on Google Earth or Wikipedia, etc. The only other valid value is ‘OSGB36’ which is the traditional model used in the UK before GPS. Latitude and longitude marked around the edges of OS maps published before 2015 are given in the OSGB36 model.

Accuracy: Grid references rounded to whole metres will give lat/lon that are accurate to about 5 decimal places. In the UK, 0.00001 of a degree of latitude is about 70cm, 0.00001 of a degree of longitude is about 1m.

For example:

>>> # Glendessary, the graticule marker on Sheet 33
>>> lat, lon = grid_to_ll(197575, 794790, model='OSGB36')
>>> (round(lat, 5), round(lon, 5))
(56.99998, -5.3333)
>>> # Scorriton
>>> lat, lon = grid_to_ll(269995, 68361, model='OSGB36')
>>> (round(lat, 5), round(lon, 5))
(50.5, -3.83333)

But Grid references in millimetres will give results accurate to 8 decimal places.

>>> # Cranbourne Chase, on the central meridian
>>> lat, lon = grid_to_ll(400000, 122350.044, model='OSGB36')
>>> (round(lat, 8), round(lon, 8))
(51.0, -2.0)
>>> # The example from the OSGB documentation
>>> lat, lon = grid_to_ll(651409.903, 313177.27, model='OSGB36')
>>> (round(lat, 8), round(lon, 8))
(52.6575703, 1.71792158)

The routines will produce lots more decimal places, so that you can choose what rounding you want, although they aren’t really meaningful beyond nine places, since the conversion routines supplied by the OS are only designed to be accurate to about 1mm (8 places).

>>> # Hoy (Orkney)
>>> grid_to_ll(323223, 1004000, model='OSGB36')
(58.91680150461385, -3.3333320035568224)
>>> # Glen Achcall
>>> grid_to_ll(217380, 896060, model='OSGB36')
(57.91671633292687, -5.083330213971718)

Finally here is an example of how to use the optional keyword arguments:

>>> # Keyword arguments for Glen Achcall
>>> grid_to_ll(easting=217380, northing=896060, model='OSGB36')
(57.91671633292687, -5.083330213971718)

Converting traditional grid references: To convert a grid reference string like TQ183506, you need to parse it into a full (easting, northing) pair first, using osgb.gridder

osgb.convert.ll_to_grid(lat, lon, model=u'WGS84', rounding=-1)

Convert a (latitude, longitude) pair to an OSGB grid (easting, northing) pair.

Output
a tuple containing (easting, northing) in metres from the grid origin.
Input

The arguments should be supplied as real numbers representing decimal degrees, like this:

>>> ll_to_grid(51.5, -2.1)
(393154.813, 177900.607)

Following the normal convention, positive arguments mean North or East, negative South or West.

If you have data with degrees, minutes and seconds, you can convert them to decimals like this:

>>> ll_to_grid(51+25/60, 0-5/60-2/3600)
(533338.156, 170369.238)
>>> ll_to_grid(52 + 39/60 + 27.2531/3600, 1 + 43/60 + 4.5177/3600, model='OSGB36')
(651409.903, 313177.27)

But if you are still using python2 then be sure to import division so that you get the correct semantics for division when both numerator and denominator are integers.

If you have trouble remembering the order of the arguments, or the returned values, note that latitude comes before longitude in the alphabet too, as easting comes before northing. However since reasonable latitudes for the OSGB are in the range 49 to 61, and reasonable longitudes in the range -9 to +2, the ll_to_grid function accepts argument in either order. If your longitude is larger than your latitude, then the values of the arguments will be silently swapped:

>>> ll_to_grid(-2.1, 51.5)
(393154.813, 177900.607)

But you can always give the arguments as named keywords if you prefer:

>>> ll_to_grid(lon=-2.1, lat=51.5)
(393154.813, 177900.607)

The easting and northing will be returned as the distance in metres from the ‘false point of origin’ of the British Grid (which is a point some way to the south-west of the Scilly Isles). If you want the result presented in a more traditional grid reference format you should pass the results to osgb.format_grid()

If the coordinates you supply are in the area covered by the OSTN transformation data, then the results will be rounded to 3 decimal places, which corresponds to the nearest millimetre. If they are outside the coverage then the conversion is automagically done using a Helmert transformation instead of the OSTN data. The results will be rounded to the nearest metre in this case, although you probably should not rely on the results being more accurate than about 5m.

>>> # Somewhere in London
>>> ll_to_grid(51.3, 0)
(539524.836, 157551.913)
>>> # Far north
>>> ll_to_grid(61.3, 0)
(507242.0, 1270342.0)

The coverage extends quite a long way off shore.

>>> # A point in the sea, to the north-west of Coll
>>> ll_to_grid(56.75, -7)
(94469.613, 773209.471)

The numbers returned may be negative if your latitude and longitude are far enough south and west, but beware that the transformation is less and less accurate or useful the further you get from the British Isles.

>>> ll_to_grid(51.3, -10)
(-157250.0, 186110.0)

ll_to_grid also takes an optional argument that sets the ellipsoid model to use. This defaults to WGS84, the name of the normal model for working with normal GPS coordinates, but if you want to work with the traditional latitude and longitude values printed on OS maps then you should add an optional model argument

>>> ll_to_grid(49, -2, model='OSGB36')
(400000.0, -100000.0)

Incidentally, the grid coordinates returned by this call are the coordinates of the ‘true point of origin’ of the British grid. You should get back an easting of 400000.0 for any point with longitude 2W since this is the central meridian used for the OSGB projection. However you will get a slightly different value unless you specify model='OSGB36' since the WGS84 meridians are not quite the same as OSGB36.

>>> ll_to_grid(52, -2, model='OSGB36')
(400000.0, 233553.731)
>>> ll_to_grid(52, -2, model='WGS84')
(400096.274, 233505.403)

If the model is not OSGB36 or WGS84 you will get an UndefinedModelError exception:

>>> ll_to_grid(52, -2, model='EDM50') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
    ...
UndefinedModelError: EDM50

You can also control the rounding directly if you need to, but be aware that asking for more decimal places does not make the conversion any more accurate; the formulae used are only designed to be accurate to 1mm.

>>> ll_to_grid(52, -2, rounding=4)
(400096.2738, 233505.4033)

Gridder

Parse and format OSGB grid reference strings.

This module provides functions to parse and format grid references, and to tell you which maps include a given reference.

osgb.gridder.format_grid(easting, northing=None, form=u'SS EEE NNN')

Formats an (easting, northing) pair into traditional grid reference.

This routine formats an (easting, northing) pair into a traditional grid reference with two letters and two sets of three numbers, like this: SU 387 147.

>>> print(format_grid(438710.908, 114792.248))
SU 387 147

If you want the individual components, apply split() to it.

>>> print('-'.join(format_grid(438710.908, 114792.248).split()))
SU-387-147

and note that the results are strings not integers. Note also that rather than being rounded, the easting and northing are truncated (as the OS system demands), so the grid reference refers to the lower left corner of the relevant square. The system is described below the legend on all OS Landranger maps.

The format_grid routine takes an optional keyword argument form, that controls the form of grid reference returned.

>>> print(format_grid(438710.908, 114792.248, form='SS EEE NNN'))
SU 387 147
>>> print(format_grid(438710.908, 114792.248, form='SS EEEEE NNNNN'))
SU 38710 14792
>>> print(format_grid(438710.908, 114792.248, form='SS'))
SU
>>> print(format_grid(438710.908, 114792.248, form='SSEN'))
SU31
>>> print(format_grid(438710.908, 114792.248, form='SSEENN'))
SU3814
>>> print(format_grid(438710.908, 114792.248, form='SSEEENNN'))
SU387147
>>> print(format_grid(438710.908, 114792.248, form='SSEEEENNNN'))
SU38711479
>>> print(format_grid(438710.908, 114792.248, form='SSEEEEENNNNN'))
SU3871014792
>>> print(format_grid(438710.908, 114792.248, form='SS EN'))
SU 31
>>> print(format_grid(438710.908, 114792.248, form='SS EE NN'))
SU 38 14
>>> print(format_grid(438710.908, 114792.248, form='SS EEE NNN'))
SU 387 147
>>> print(format_grid(438710.908, 114792.248, form='SS EEEE NNNN'))
SU 3871 1479
>>> print(format_grid(400010.908, 114792.248, form='SS EEEEE NNNNN'))
SU 00010 14792

You can’t leave out the SS, you can’t have N before E, and there must be the same number of Es and Ns. Except for the two special formats:

>>> print(format_grid(438710.908, 114792.248, form='TRAD'))
SU 387 147
>>> print(format_grid(438710.908, 114792.248, form='GPS'))
SU 38710 14792

The format can be given as upper case or lower case or a mixture.

>>> print(format_grid(438710.908, 114792.248, form='trad'))
SU 387 147

but in general the form argument must match "SS E* N*" (spaces optional)

>>> format_grid(432800, 250000, form='TT') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
FaultyFormError: This form argument was not matched --> form='TT'

Here are some more extreme examples:

>>> print(format_grid(314159, 271828, form='SS'))
SO
>>> print(format_grid(0, 0, form='SS'))
SV
>>> print(format_grid(432800, 1250000, form='SS'))
HP

The arguments can be negative…

>>> print(format_grid(-5, -5, form='SS'))
WE

…but must not be too far away from the grid:

>>> format_grid(-1e12, -5) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
FarFarAwayError: The spot with coordinates (-1e+12, -5) is too far from the OSGB grid
osgb.gridder.parse_grid(*grid_elements, **kwargs)

Parse a grid reference from a range of inputs.

The parse_grid routine extracts a (easting, northing) pair from a string, or a list of arguments, representing a grid reference. The pair returned are in units of metres from the false origin of the grid.

The arguments should be in one of the following three forms

  • A single string representing a grid reference

    >>> parse_grid("TA 123 678")
    (512300, 467800)
    >>> parse_grid("TA 12345 67890")
    (512345, 467890)
    

    The string can also refer to 100km, 10km, 1km, or even 10m squares:

    >>> parse_grid('TA')
    (500000, 400000)
    >>> parse_grid('TA15')
    (510000, 450000)
    >>> parse_grid('TA 12 56')
    (512000, 456000)
    >>> parse_grid('TA 1234 5678')
    (512340, 456780)
    

    The spaces are optional in all cases:

    >>> parse_grid(" TA 123 678 ")
    (512300, 467800)
    >>> parse_grid(" TA123 678 ")
    (512300, 467800)
    >>> parse_grid(" TA 123678")
    (512300, 467800)
    >>> parse_grid("TA123678")
    (512300, 467800)
    >>> parse_grid("TA1234567890")
    (512345, 467890)
    

    Here are some more extreme examples:

    >>> parse_grid('SV9055710820') # St Marys lifeboat station
    (90557, 10820)
    >>> parse_grid('HU4795841283') # Lerwick lifeboat station
    (447958, 1141283)
    >>> parse_grid('WE950950') # At sea, off the Scillies
    (-5000, -5000)
    

    Note in the last one that we are “off” the grid proper. This lets you work with “pseudo-grid-references” like these:

    >>> parse_grid('XD 61191 50692') # St Peter Port the Channel Islands
    (361191, -49308)
    >>> parse_grid('MC 03581 16564') # Rockall
    (-296419, 916564)
    
  • A two or three element list representing a grid reference

    >>> parse_grid('TA', 0, 0)
    (500000, 400000)
    >>> parse_grid('TA', 123, 678)
    (512300, 467800)
    >>> parse_grid('TA', 12345, 67890)
    (512345, 467890)
    >>> parse_grid('TA', '123 678')
    (512300, 467800)
    >>> parse_grid('TA', '12345 67890')
    (512345, 467890)
    >>> parse_grid('TA', '1234567890')
    (512345, 467890)
    

    Or even just two numbers (primarily included for testing purposes). Note that this allows floats, and that the results will come back as floats

    >>> parse_grid(314159, 271828)
    (314159.0, 271828.0)
    >>> parse_grid('314159 271828')
    (314159.0, 271828.0)
    >>> parse_grid(231413.123, 802143.456)
    (231413.123, 802143.456)
    

    If you are processing grid references from some external data source beware that if you use a list with bare numbers you may lose any leading zeros for grid references close to the SW corner of a grid square. This can lead to some ambiguity. Either make the numbers into strings to preserve the leading digits or supply a keyword argument figs to define how many figures are supposed to be in each easting and northing. Like this:

    >>> parse_grid('TA', 123, 8)
    (512300, 400800)
    >>> parse_grid('TA', 123, 81, figs=5)
    (500123, 400081)
    

    The default setting of figs is 3, which assumes you are using hectometres as in a traditional grid reference. The maximum is 5 and the minimum is the length of the longer of easting or northing.

  • A string or a list representing a map and a local grid reference, corresponding to the following examples:

    >>> parse_grid('176/224711') # Caesar's Camp
    (522400, 171100)
    >>> parse_grid(176, 224, 711)
    (522400, 171100)
    >>> parse_grid('A:164/352194') # Charlbury Station
    (435200, 219400)
    >>> parse_grid('B:OL43E/914701') # map Chesters Bridge
    (391400, 570100)
    >>> parse_grid('B:OL43E 914 701') # map Chesters Bridge
    (391400, 570100)
    >>> parse_grid('B:OL43E', '914701') # map 2-arg Chesters Bridge
    (391400, 570100)
    >>> parse_grid('B:OL43E', 914, 701) # map 3-arg Chesters Bridge
    (391400, 570100)
    >>> parse_grid(164, 513, 62) # Carfax
    (451300, 206200)
    >>> parse_grid('B:119/OL3/480103') # map with dual name
    (448000, 110300)
    >>> parse_grid('B:309S.a 26432 34013') # inset on B:309
    (226432, 534013)
    >>> parse_grid('B:368/OL47W', 723, 112) # 3-arg, dual name
    (272300, 711200)
    

    or finally just a sheet name; this will show the SW corner:

    >>> parse_grid('A:82')
    (195000, 530000)
    

    with the usual rule about assuming you meant Landrangers:

    >>> parse_grid(161)
    (309000, 205000)
    

    A map sheet with a grid ref that does not actually coincide will raise a SheetMismatchError error

    >>> parse_grid('176/924011') # doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    ...
    SheetMismatchError: Grid point (592400, 201100) is not on sheet A:176
    

    A map sheet that does not exist will raise an UndefinedSheetError error

    >>> parse_grid('B:999/924011') # doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    ...
    UndefinedSheetError: Sheet B:999 is not known here.
    

If there’s no matching input then a GarbageError error is raised.

>>> parse_grid('Somewhere in London') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
GarbageError: I can't read a grid reference from this -> Somewhere in London
osgb.gridder.sheet_list(easting, northing, series=u'ABCHJ')

Return a list of map sheets that show the (easting, northing) point given.

The optional argument “series” controls which maps are included in the list. The default is to include maps from all defined series.

>>> print(' '.join(sheet_list(438710.908, 114792.248, series='AB')))
A:196 B:OL22E

Currently the series included are:

A:
OS Landranger 1:50000 maps
B:
OS Explorer 1:25000 maps (some of these are designated as “Outdoor Leisure” maps)
C:
OS Seventh Series One-Inch 1:63360 maps
H:
Harvey British Mountain maps - mainly at 1:40000
J:
Harvey Super Walker maps - mainly at 1:25000

Note that the numbers returned for the Harvey maps have been invented for the purposes of this module. They do not appear on the maps themselves; instead the maps have titles. You can use the numbers returned as an index to the maps data to find the appropriate title.

>>> print(' '.join(sheet_list(314159, 271828)))
A:136 A:148 B:200E B:214E C:128

You can restrict the list to certain series. So if you only want Explorer maps use: series=’B’, and if you want only Explorers and Landrangers use: series=’AB’, and so on.

>>> print(''.join(sheet_list(651537, 313135, series='A')))
A:134

If the (easting, northing) pair is not covered by any map sheet you’ll get an empty list >>> sheet_list(0, 0) []

Legacy interface

osgb.legacy_interface.lonlat_to_osgb(lon, lat, digits=3, formatted=True, model='OSGB36')

Convert a longitude and latitude to Ordnance Survey grid reference.

Parameters:
lon

Longitude, presumed to be in OSG36 degrees (unless you set model=’WGS84’).

lat

Latitude, ditto.

digits

The number of digits to use for each direction in the final grid reference. 3 by default, grid references are up to 6.

formatted

Should the OSGB reference be nicely formatted (with whitespace)? By default true.

model

OSGB36 or WGS84, default OSGB36

Returns:

A string giving a formatted OSGB reference.

For example:

>>> print(lonlat_to_osgb (1.088978, 52.129892))
TM 114 525
>>> print(lonlat_to_osgb (1.088978, 52.129892, formatted=False))
TM114525
>>> print(lonlat_to_osgb (1.088978, 52.129892, 5))
TM 11400 52500

In the re-implemented version you can reverse arguments if you want to…

>>> print(lonlat_to_osgb(52.129892, 1.088978, 5))
TM 11400 52500
osgb.legacy_interface.osgb_to_lonlat(osgb_str, model='OSGB36')

Convert an Ordinance Survey reference to a longitude and latitude.

Parameters:
osgb_str

An Ordnance Survey grid reference in “letter-number” format. Case and spaces are cleaned up by this function, and resolution automatically detected, so that so that TM114 525, TM114525, and TM 11400 52500 are all recognised and identical.

model

OSGB36 or WGS84

Returns:

The longitude and latitude of the grid reference, according to the chosen model.

For example:

# just outside Ipswich, about 1.088975 52.129892
>>> lon1, lat1 = osgb_to_lonlat ('TM114 525')
>>> 1.0889 < lon1 < 1.0890
True
>>> 52.1298 < lat1 < 52.1299
True
>>> (round(lon1, 14), round(lat1, 14))
(1.08897495610794, 52.12989202825308)


# accepts poor formating
>>> lon2, lat2 = osgb_to_lonlat (' TM 114525 ')
>>> lon2 == lon1
True
>>> lat2 == lat1
True

# accepts higher resolution
>>> lon3, lat3 = osgb_to_lonlat ('TM1140052500')
>>> 1.0889 < lon3 < 1.0890
True
>>> 52.1298 < lat3 < 52.1299
True
>>> (round(lon3, 14), round(lat3, 14))
(1.08897495610794, 52.12989202825308)