The project is in a healthy, maintained state
Developed by Sebastian Madrid Ontiveros. Pure Ruby gem providing all Ordnance Survey British National Grid squares (100km, 50km, 10km, 5km, 1km) with hardcoded geometry sourced directly from the OS BNG Grids GeoPackage. Supports point-to-grid-ref lookup by easting/northing, bounds retrieval, grid square validation, listing with filters, and export to ESRI Shapefile format. No external dependencies. Uses only Ruby stdlib. Contains OS data. Crown copyright and database right 2025. Licensed under the Open Government Licence v3.0. Built to support hydraulic modelling and flood risk workflows in the UK. If this gem saves you time, consider buying Sebastian a coffee at https://buymeacoffee.com/smadrid
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

smo_os_bng_grids

Pure Ruby library for Ordnance Survey British National Grid (BNG) squares.

Point lookup · Spatial search · Bounds · Corner coordinates · Shapefile export

No external dependencies. Works in InfoWorks ICM 2027 embedded Ruby.


Gem Version License: OGL v3 Ruby ICM OS Data



Overview

smo_os_bng_grids provides hardcoded geometry sourced directly from the OS BNG Grids GeoPackage (EPSG:27700), covering all five standard resolutions from 100 km down to 1 km — 910,091 grid squares in total. The library uses pure Ruby standard library with no runtime downloads, making it fully compatible with InfoWorks ICM 2027's embedded Ruby environment.

Contains OS data. Crown copyright and database right 2025. Licensed under the Open Government Licence v3.0.


Features

Feature Description
Point lookup Find the grid reference containing any easting/northing at any resolution
Spatial search All tiles intersecting a radius or bounding box, with distance in metres
Bounds Min/max easting and northing for any grid reference string
Corner points NW → NE → SE → SW → NW, ready for ICM boundary_array
Shapefile export Pure Ruby SHP/SHX/DBF/PRJ writer. No GDAL required
Grid validation Check whether any grid reference string is valid
Pure stdlib No gems, no GDAL, no external dependencies

Grid resolutions

Resolution Count Example Square size
100 km 91 NT 100 km × 100 km
50 km 364 NTNW 50 km × 50 km
10 km 9,100 NT27 10 km × 10 km
5 km 36,400 NT27SE 5 km × 5 km
1 km 910,000 NT2573 1 km × 1 km

Installation

Add to your Gemfile:

gem "smo_os_bng_grids"

Or install directly:

gem install smo_os_bng_grids

Quick start

require "smo_os_bng_grids"

lister = SmoOsBngGrids::Lister.new

# Point lookup — what grid square is Edinburgh city centre in?
easting  = 325000
northing = 673000

SmoOsBngGrids::Grid.ref_at(easting, northing, resolution: "10km")  # => "NT27"
SmoOsBngGrids::Grid.ref_at(easting, northing, resolution: "1km")   # => "NT2573"

# All resolutions at once
lister.find(easting, northing)
# => {"100km"=>"NT", "50km"=>"NTNW", "10km"=>"NT27", "5km"=>"NT27SE", "1km"=>"NT2573"}

# Bounds of a grid square
SmoOsBngGrids::Grid.bounds("NT27")
# => {min_e: 320000, min_n: 670000, max_e: 330000, max_n: 680000}

Listing grid squares

lister = SmoOsBngGrids::Lister.new

# All 100 km squares
lister.list("100km")

# All 10 km squares within NT
lister.list("10km", within: "NT")

# All 1 km squares within NT27
lister.list("1km", within: "NT27")

Each entry is a Hash:

{
  ref:    "NT27",
  min_e:  320000, min_n: 670000,
  max_e:  330000, max_n: 680000,
  points: [
    [320000, 680000],  # NW
    [330000, 680000],  # NE
    [330000, 670000],  # SE
    [320000, 670000],  # SW
    [320000, 680000]   # NW (closed)
  ]
}

Spatial search

Find all tiles intersecting a radius or bounding box around a point:

lister = SmoOsBngGrids::Lister.new

# All 10 km tiles within 12 km of Edinburgh
tiles = lister.search(325000, 673000, resolution: "10km", radius: 12000)
tiles.each { |t| puts "#{t[:ref]}  #{t[:distance_m]} m" }

# All 1 km tiles within 1.5 km of Edinburgh
lister.search(325000, 673000, resolution: "1km", radius: 1500)

# All 5 km tiles within a 15 km box around Edinburgh
lister.search(325000, 673000, resolution: "5km", box: 15000)

Radius search returns :distance_m for each entry. Returns 0.0 when the point falls inside the tile.


Corner points — InfoWorks ICM

Every entry returned by list, search, and entry_for includes a :points array in NW → NE → SE → SW → NW order, matching the InfoWorks ICM boundary_array convention:

lister = SmoOsBngGrids::Lister.new

# Single tile containing a point
tile = lister.search(325000, 673000, resolution: "10km", radius: 1).first
boundary_array = tile[:points]
# => [[320000, 680000], [330000, 680000], [330000, 670000], [320000, 670000], [320000, 680000]]

# Flat XY array if needed
flat_xy = tile[:points].flatten
# => [320000, 680000, 330000, 680000, 330000, 670000, 320000, 670000, 320000, 680000]

# Convert a ref string to a full entry
entry = lister.entry_for("NT27SE")
entry[:points]
# => [[325000, 675000], [330000, 675000], [330000, 670000], [325000, 670000], [325000, 675000]]

Shapefile export

Export any set of entries to ESRI Shapefile format (OSGB36 / EPSG:27700). Pure Ruby — no GDAL, no external gems. Produces .shp, .shx, .dbf, and .prj files, ready to open directly in QGIS or ArcGIS.

lister = SmoOsBngGrids::Lister.new
writer = SmoOsBngGrids::ShapefileWriter.new

# From list()
writer.write(lister.list("10km", within: "NT"), "/tmp/nt_10km")

# From search()
entries = lister.search(325000, 673000, resolution: "10km", radius: 20000)
writer.write(entries, "/tmp/edinburgh_10km_20km")

# From find() — one tile per resolution
found = lister.find(325000, 673000)
found.each do |res, ref|
  writer.write([lister.entry_for(ref)], "/tmp/edinburgh_#{res}")
end

Grid reference validation

SmoOsBngGrids::Grid.valid?("NT")      # => true
SmoOsBngGrids::Grid.valid?("NT27")    # => true
SmoOsBngGrids::Grid.valid?("NT2573")  # => true
SmoOsBngGrids::Grid.valid?("ZZ")      # => false

Data

All geometry is hardcoded from the OS BNG Grids GeoPackage published by Ordnance Survey.

Source: github.com/OrdnanceSurvey/osbng-grids

Contains OS data. Crown copyright and database right 2025. Licensed under the Open Government Licence v3.0.


☕  Support this project

This gem is free and open source, built for the hydraulic modelling and flood risk community across the UK.
If it saves you time on a project, a coffee goes a long way.


Scan to buy Sebastian a coffee

buymeacoffee.com/smadrid


Built by Sebastian Madrid Ontiveros  ·  Edinburgh  ·  Hydraulic Modeller