I am looking to recreate the full Ordnance Survey National Grid (as shown here https://upload.wikimedia.org/wikipedia/commons/f/f5/Ordnance_Survey_National_Grid.svg) in R.
I can create the grid for the 4 levels without any issues (the 1km version takes about 20 minutes for me with 36GB of RAM):
library(sf)
OS_National_Grid_BBox <- st_bbox(c(xmin = -1000000, xmax = 1500000, ymax = 2000000, ymin = -500000), crs = st_crs(27700))
OS_National_Grid_500km <- st_make_grid(st_as_sfc(OS_National_Grid_BBox), square = T, cellsize = c(500000, 500000)) %>% st_sf()
OS_National_Grid_100km <- st_make_grid(st_as_sfc(OS_National_Grid_BBox), square = T, cellsize = c(100000, 100000)) %>% st_sf()
OS_National_Grid_10km <- st_make_grid(st_as_sfc(OS_National_Grid_BBox), square = T, cellsize = c(10000, 10000)) %>% st_sf()
OS_National_Grid_1km <- st_make_grid(st_as_sfc(OS_National_Grid_BBox), square = T, cellsize = c(1000, 1000)) %>% st_sf()
However, how can I name all the 500km, 100km, 10km and 1km cells to reflect the naming/coding convention shown in the image?
I guess not too many folks want to tie up their computer for 20 minutes to generate the 6,125,000 polygons en route to testing their answer. As it happens, I had to upgrade my Windows-based cloud server to 32GB just to create the 1km squares...
Unfortunately when you create your grid squares, they are ordered bottom-to-top, left-to-right, whereas the labels are ordered top-to-bottom, left-to-right. This makes the labeling a little trickier. For the 500km boxes, we would expect to be able to label them with LETTERS[-9]
, but because of the ordering we need to label them with LETTERS[-9][rep(4:0 * 5, each = 5) + 1:5]
.
We can create our named grids by binding together a data frame with the grid names to your grid object, like this:
gridref500 <- LETTERS[-9][rep(4:0 * 5, each = 5) + 1:5]
OS_National_Grid_500km <- st_as_sfc(OS_National_Grid_BBox) %>%
st_make_grid(square = TRUE, cellsize = c(5e5, 5e5)) %>%
cbind(data.frame(Grid_Ref = gridref500)) %>%
st_sf()
Now we can plot to ensure we have the correct labels:
library(ggplot2)
ggplot(OS_National_Grid_500km) +
geom_sf(fill = "white") +
geom_sf_text(aes(label = Grid_Ref), size = 5)
The second level is tougher, since we need to repeat the rows and columns within each of the squares. This requires a bit of modular maths to get the indexing right:
gridref100 <- rep(gridref500, each = 5) %>%
split(0:124 %/% 25) %>%
lapply(rep, 5) %>%
do.call(c, .) %>%
paste0(split(gridref500, 0:24 %/% 5) %>%
lapply(rep, 5) %>%
do.call(c, .) %>%
rep(5))
OS_National_Grid_100km <- st_as_sfc(OS_National_Grid_BBox) %>%
st_make_grid(square = TRUE, cellsize = c(1e5, 1e5)) %>%
cbind(data.frame(Grid_Ref = gridref100)) %>%
st_sf()
But we can see this is also effective:
ggplot(OS_National_Grid_100km) +
geom_sf(fill = "white") +
geom_sf_text(aes(label = Grid_Ref), size = 3)
Again, the next layer becomes more complex because of the repetitions, modular maths and subsetting, but can be achieved like this:
gridref10 <- rep(gridref100, each = 10) %>%
split(0:6249 %/% 250) %>%
lapply(rep, 10) %>%
do.call(c, .) %>%
paste0(as.character(rep(0:9, 6250)) %>%
paste0(rep(rep(0:9, each = 250), 25)))
OS_National_Grid_10km <- st_as_sfc(OS_National_Grid_BBox) %>%
st_make_grid(square = TRUE, cellsize = c(1e4, 1e4)) %>%
cbind(data.frame(Grid_Ref = gridref10)) %>%
st_sf()
Obviously, I can't plot the entire grid now because it would be far too small to see individual squares (let alone their labels), so I will just pull out TQ
to ensure the numbering is correct:
TQ <- OS_National_Grid_10km[substr(OS_National_Grid_10km$Grid_Ref, 1, 2) == "TQ",]
ggplot(TQ) +
geom_sf(fill = "white") +
geom_sf_text(aes(label = Grid_Ref))
The finest squares would be possible to label too in a similar way to the 10km boxes, with the added complication that after labeling you need to swap the fourth and fifth digits:
gridref1 <- rep(gridref10, each = 10) %>%
split(0:624999 %/% 2500) %>%
lapply(rep, 10) %>%
do.call(c, .) %>%
paste0(as.character(rep(0:9, 625000)) %>%
paste0(rep(rep(0:9, each = 2500), 250)))
swapchar <- substr(gridref1, 4, 4)
substr(gridref1, 4, 4) <- substr(gridref1, 5, 5)
substr(gridref1, 5, 5) <- swapchar
OS_National_Grid_1km <- st_as_sfc(OS_National_Grid_BBox) %>%
st_make_grid(square = TRUE, cellsize = c(1e3, 1e3)) %>%
cbind(data.frame(Grid_Ref = gridref1)) %>%
st_sf()
Again, we need to pick out a small subset to show this works:
ss <- with(OS_National_Grid_1km,
which(paste0(substr(Grid_Ref, 1, 3), substr(Grid_Ref, 5, 5)) == "TQ28"))
TQ28 <- OS_National_Grid_1km[ss,]
ggplot(TQ28) +
geom_sf(fill = "white") +
geom_sf_text(aes(label = Grid_Ref))