Street Atlas (or Battleship) grids in QGIS 2.18

Another guest blog from Liam Mason, based on his lightning talk at the 8th Scottish QGIS user group. You can follow Liam on Twitter via @marinemaps

D5. Miss.

A2. Hit. You’ve sunk my battleship! 🙁

If you’ve used a street atlas or played the boardgame Battleship, you’ll be familar with grid systems using letters for the horizontal (x) coordinate and numbers for vertical (y) coordinate.

Whilst these grids don’t have the resolution of a coordinate system like latitude/longitude or eastings/northings, they allow readers to quickly identify where a street is located on an atlas, or a ship on a boardgame.

Battleship boardgame
Battleship is now geohipster, embracing the hexgrid

Creating these type of grids in QGIS takes advantage of the grid options in composer, the @gridnumber attribute, and a wee bit of maths.

Setting up the print composer

Create your map. In this example, I’ve used data from Natural Earth and projected on World Mercator / ESPG:3395 (because I want meridians and parallels of latitude & longitude to appear straight)

Example map in QGIS

Create a new print composer using Project > New Print Composer (or Ctrl & P)

QGIS project menu showing New Composer windo

Use the Add new map button in Composer to add the map content.

Add New Map button

(Leave space around the map for the graticules.)

Map on print composer showing white space

Adding a latitude / longitude graticule with custom format

Click on the map (boxes will appear in corners to show it’s selected). Click the Item Properties tab, scroll down to Grids, and expand using the wee triangle.

Add a grid using the + button. Double-click on the name (Grid 1) and rename to something like ‘Grid lines’

Item Properties showing Grid

I want my grid to use decimal degrees on WGS84 datum, so I select a CRS of EPSG:4326.

Using an X interval of 1 decimal degree and Y interval of 0.5 decimal degrees produces a grid that’s roughly rectangular.

Grid lines

Scroll down the item properties (past grid frame), and tick Draw coordinates. I only want the graticules down the right hand side and bottom, so I set Left and Top to Disabled.

Draw Coordinates

I want a more truncated latitude & longitude and use a custom format.

Set Format to Custom, use the expression button (to right of format) and expression based annotation .

expression annotation lat-lon

|| '°'
WHEN @grid_axis = 'x'
IF (@grid_number > 0, 'E' , 'W')
IF (@grid_number > 0, 'N' , 'S')

This expression returns an absolute value of @grid_number. Since the CRS is EPSG:4326, @grid_number is decimal degrees, and would normally return a negative value for longitudes west of 0.

The rest of the expression concatenates (||) the degree symbol, and a conditional statement.

When the grid axis is X, and value is greater than zero, append E for east, otherwise append W for west. If grid axis isn’t X (i.e Y) and and value is greater than zero, append N for north, otherwise append S for south.

Now I have latitude and longitude down right and bottom sides in the desired format.

Lat Lon Grid

Adding a numeric value

Scroll back up and add an other grid using the + button. Rename this one to something like ‘numeric’. This grid will be used for the numeric grid reference.

This time I only want annotations, so I change the Grid type to Frame and annotations only. The CRS again will be EPSG:4326 and the Y interval will be 0.5 but I want the numeric annotation to be in the middle of the grid lines, so add an Offset of 0.25 (half of interval).

numeric grid settings

Scroll down the item properties to Draw coordinates. I only want the numeric graticules down the left so I set Right, Bottom, and Top to Disabled.

numeric grid coordinates

Set Format to Custom, use the expression button and use expression based annotation . Note in the example above, 49°N is most southern latitude.

Expression annotation for numeric grid

((@grid_number-49)+0.25) / 0.5

The expression calls the grid number (decimal degrees including 0.25 offset), subtracts the lowest X value (49), adds the offset (0.25), and divides by the coordinate interval (0.5). (For values to start at zero, subtract the offset)

This creates a numeric grid which increases in a northerly direction. For a grid to increase in a southerly direction, use:


This similar to above, but substracts the largest X value and converts the number to an absolute value)

Adding a letter value

Scroll back up and add an other grid using the + button. Rename to something like ‘letters’.

Again I only want annotations, so similar to previous settings except the X interval will be 1 with an Offset of 0.5

Letter grid

Scroll down the item properties to Draw coordinates. I want the letters on the top so I set Left, Right, and Bottom to Disabled.

letter grid coordinate settings

Set Format to Custom and use the expression button to select expression based annotation . Note in this example, 13°W was the furthest west (-13)

expression for annotation letters

substr('ABCDEFGHIJKLMNOPQRST', ((@grid_number- -13) + 0.5) / 1 , 1)

The expression works like the numeric grid in that it calls the grid number (decimal degrees including offset), substracts the smallest Y number (-13), adds the offset (0.5), and divides by the coordinate interval (1).

This is nested within a substring query which calls a single character (defined by the length of 1) for the corresponding grid number . For example where the grid number is 3, the the third character, C, would be displayed.

And there you have it! A real world version of Battleship, (or a custom street atlas.)

Alphanumeric grid

The ICES statistical rectangle grid (a grid with more than one character)

My incentive for looking into these types of grids was to annotate fisheries maps with ICES statistical rectangles, which use alphanumeric codes to reference areas of the north east Atlantic.

I use the same X & Y internal and offset values as the examples above but different format expressions.

The numeric encoding is familiar enough. The differences from the examples above is that the smallest X is 36N and I’ve used left padding (lpad) to force leading zeroes to be displayed (eg 01, 02, 03…)

lpad(((@grid_number-36)+0.25) / 0.5, 2, 0)

The letter encoding is a wee bit different.

substr('A0A1A2A3B0B1B2B3B4B5B6B7B8B9C0C1C2C3C4C5C6C7C8C9D0D1D2D3D4D5D6D7D8D9E0E1E2E3E4E5E6E7E8E9F0F1F2F3F4F5F6F7F8F9G0G1G2G3G4G5G6G7G8G9H0H1H2H3H4H5H6H7H8H9J0J1J2J3J4J5J6J7J8J9K0K1K2K3K4K5K6K7K8K9L0L1L2L3L4L5L6L7L8L9M0M1M2M3M4M5M6M7M8', (@grid_number - -44) / 0.5 , 2)

The smallest Y is 44, but this time I need the grid value to increase by two at each step. I achieve this by dividing by 0.5 (half the interval of 1), I don’t correct for the offset (otherwise the first value would be 2 instead of 1), and the substring now has a length of 2.

ICES rectangle map

The finished article, ICES rectangle annotations which work anywhere in NE Atlantic.