I am using QGIS v3.12 and projection EPSG:27700 - British National Grid.

I am trying to add a label to my map layout which will give the grid reference of the centre of the map as a UK 8 digit grid reference ( e.g. "AB123456" ).

So far I have managed to get the x and y coordinates using the expression:

Grid reference at map centre: [%round(x (map_get (item_variables('Map1'), 'map_extent_center')),0)%] , [%round(y (map_get (item_variables('Map1'), 'map_extent_center')),0)%]

Is there any function available in QGIS that can translate these coordinates into the required format?

Sorry my coding skills are not that great!


1 Answer 1


You need a custom function which I have translated from my Java translation of the OS Visual Basic to python. Insert the following into a new Function in the Expression Function editor (see this class for more details of how to do this).

from qgis.core import *
from qgis.gui import *

gridLetters = [["SV", "SQ", "SL", "SF", "SA", "NV", "NQ", "NL", "NF", "NA", "HV", "HQ", "HL"],                          
               ["SW", "SR", "SM", "SG", "SB", "NW", "NR", "NM", "NG", "NB", "HW", "HR", "HM"],                          
               ["SX", "SS", "SN", "SH", "SC", "NX", "NS", "NN", "NH", "NC", "HX", "HS", "HN"],                          
               ["SY", "ST", "SO", "SJ", "SD", "NY", "NT", "NO", "NJ", "ND", "HY", "HT", "HO"],                          
               ["SZ", "SU", "SP", "SK", "SE", "NZ", "NU", "NP", "NK", "NE", "HZ", "HU", "HP"],                          
               ["TV", "TQ", "TL", "TF", "TA", "OV", "OQ", "OL", "OF", "OA", "JV", "JQ", "JL"],                          
               ["TW", "TR", "TM", "TG", "TB", "OW", "OR", "OM", "OG", "OB", "JW", "JR", "JM"]]                          

@qgsfunction(args='auto' , group='Custom', usesgeometry=True)                                                                                                                        
def gridSquare(size, geometry, feature, parent): 
    centroid = geometry.centroid() 
    easting = centroid.asPoint().x()
    northing = centroid.asPoint().y()    
    xText = "%06d" % easting                                                                                            
    yText = "%07d" % northing                                                                                           
    x = int(xText[:1])                                                                                                  
    y = int(yText[:2])                                                                                                  

    gl = gridLetters[x][y]                                                                                              

    result = gl                                                                                                         

    if size == 100:                                                                                                     
        return result                                                                                                   
    elif size == 10:                                                                                                    
        return gl + xText[1:2] + yText[2:3]                                                                             
    elif size == 1:                                                                                                     
        return gl + xText[1:3] + yText[2:4]                                                                             
    elif size == .1:                                                                                                    
        return gl + xText[1:4] + yText[2:5]                                                                             
    elif size == .01:                                                                                                   
        return gl + xText[1:5] + yText[2:6]                                                                             
    elif size == .001:                                                                                                     
        return gl + xText[1:6] + yText[2:7]

You can then use expressions like:

 gridSquare(.1, $geometry) 

to get output like TF392033 or gridSquare(1, $geometry) to get TF3903. In a layout you should be able to do something like:

[%gridSquare(.1, map_get(item_variables('Map 1'), 'map_extent_center'))%]

but I can only get NULL as a response to any of the item_variables function calls - may be my map isn't called Map 1?

Update - if I rename my map to map then this works:

Map Centre is at [%gridSquare(.1, map_get(item_variables('map'), 'map_extent_center'))%]

enter image description here


I've packaged this into a QGIS plugin (experimental) which should show up in the repository soonish. In the meantime you can get it from https://github.com/ianturton/osgridref

  • Ian, thabnk you very much for taking the time to help me with this. I have added the custom function as suggested and it works splendidly.
    – Jools
    Commented Apr 20, 2020 at 19:54
  • please accept the answer if it solves your problem
    – Ian Turton
    Commented Apr 21, 2020 at 7:49
  • This solution is brilliant. I'm also trying to use it to label a grid in a composer with OS grid squares around the edge, does anyone know what function I'd use to create that?
    – geobar
    Commented Sep 8, 2022 at 11:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.