smo_os_bng_grids 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/LICENSE +18 -0
- data/README.md +208 -0
- data/lib/smo_os_bng_grids/constants.rb +3 -0
- data/lib/smo_os_bng_grids/grid.rb +1 -1
- data/lib/smo_os_bng_grids/lister.rb +3 -3
- data/lib/smo_os_bng_grids/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a7b84aefde3700b30bac415cfc3063b022e1784142f9b7b500f9f3aea97c2e41
|
|
4
|
+
data.tar.gz: db5b6cf873e9d6f267997734544951fcc3f58db1533cba908aef4dbd4bd6a9b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 10aea651302aad06011352d8f0f250c5ceb86ce6a0abcbf9584e793c66045e3596560a85b9c637aa1bf67b2c81addf29ff2b4acb2beebd57cb0353c22b06d3df
|
|
7
|
+
data.tar.gz: 1ac8d3f6d4a0ea8f9ad9f7b991e3acc45c9b87a62ddff4a3805cdb1a77bb22a74aeb3676f0a066e14d159789486f6a5e07bd9d42c116e6ad44dcb9aff299ec0b
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-05-04
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `SmoOsBngGrids::Grid` for point-to-grid-ref lookup, bounds retrieval, and validation
|
|
14
|
+
- `SmoOsBngGrids::Lister` for listing, filtering, and spatial search of grid squares
|
|
15
|
+
- `SmoOsBngGrids::ShapefileWriter` for ESRI Shapefile export
|
|
16
|
+
- Support for 100km, 50km, 10km, 5km, and 1km resolutions
|
|
17
|
+
- Hardcoded geometry sourced from OS BNG Grids GeoPackage
|
|
18
|
+
- Circular radius and bounding box spatial search
|
|
19
|
+
- Test suite covering data loading, geometry, and bounds
|
data/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Open Government Licence v3.0
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sebastian Madrid Ontiveros
|
|
4
|
+
|
|
5
|
+
Contains OS data. Crown copyright and database right 2026.
|
|
6
|
+
|
|
7
|
+
Licensed under the Open Government Licence v3.0.
|
|
8
|
+
https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/
|
|
9
|
+
|
|
10
|
+
You are free to copy, publish, distribute, transmit, adapt and exploit the
|
|
11
|
+
information commercially or non-commercially, provided you acknowledge the
|
|
12
|
+
source by including the following attribution statement:
|
|
13
|
+
|
|
14
|
+
Contains OS data. Crown copyright and database right 2026.
|
|
15
|
+
|
|
16
|
+
The Ordnance Survey grid data included in this gem is sourced directly from
|
|
17
|
+
the OS BNG Grids GeoPackage published by Ordnance Survey under the Open
|
|
18
|
+
Government Licence v3.0.
|
data/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# smo_os_bng_grids
|
|
2
|
+
|
|
3
|
+
Pure Ruby gem for working with **Ordnance Survey British National Grid (BNG)** squares.
|
|
4
|
+
Point lookup, spatial search, bounds, corner coordinates, and Shapefile export — no external dependencies.
|
|
5
|
+
|
|
6
|
+
Developed by **Sebastian Madrid Ontiveros**.
|
|
7
|
+
Contains OS data. Crown copyright and database right 2025.
|
|
8
|
+
Licensed under the [Open Government Licence v3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).
|
|
9
|
+
|
|
10
|
+
[](https://badge.fury.io/rb/smo_os_bng_grids)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **Hardcoded geometry** sourced directly from the OS BNG Grids GeoPackage (EPSG:27700)
|
|
17
|
+
- Grid squares at **100km, 50km, 10km, 5km, and 1km** resolutions (910,091 squares total)
|
|
18
|
+
- **Point lookup** — find the grid ref containing any easting/northing at any resolution
|
|
19
|
+
- **Spatial search** — find all tiles intersecting a radius or bounding box around a point
|
|
20
|
+
- **Bounds** — retrieve min/max easting/northing for any grid ref
|
|
21
|
+
- **Corner points** — NW → NE → SE → SW → NW, ready for InfoWorks ICM `boundary_array`
|
|
22
|
+
- **Shapefile export** — pure Ruby SHP/SHX/DBF/PRJ writer, no GDAL required
|
|
23
|
+
- **Pure Ruby stdlib only** — works in InfoWorks ICM 2027 embedded Ruby
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
gem "smo_os_bng_grids"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or install directly:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
gem install smo_os_bng_grids
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
require "smo_os_bng_grids"
|
|
45
|
+
|
|
46
|
+
lister = SmoOsBngGrids::Lister.new
|
|
47
|
+
|
|
48
|
+
# Point lookup — what grid square is Edinburgh city centre in?
|
|
49
|
+
easting = 325000
|
|
50
|
+
northing = 673000
|
|
51
|
+
|
|
52
|
+
SmoOsBngGrids::Grid.ref_at(easting, northing, resolution: "10km") # => "NT27"
|
|
53
|
+
SmoOsBngGrids::Grid.ref_at(easting, northing, resolution: "1km") # => "NT2573"
|
|
54
|
+
|
|
55
|
+
# All resolutions at once
|
|
56
|
+
lister.find(easting, northing)
|
|
57
|
+
# => {"100km"=>"NT", "50km"=>"NTNW", "10km"=>"NT27", "5km"=>"NT27SE", "1km"=>"NT2573"}
|
|
58
|
+
|
|
59
|
+
# Bounds of a grid square
|
|
60
|
+
SmoOsBngGrids::Grid.bounds("NT27")
|
|
61
|
+
# => {min_e: 320000, min_n: 670000, max_e: 330000, max_n: 680000}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Grid resolutions
|
|
67
|
+
|
|
68
|
+
| Resolution | Count | Example ref | Square size |
|
|
69
|
+
|-----------|--------|-------------|-----------------|
|
|
70
|
+
| 100km | 91 | `NT` | 100km × 100km |
|
|
71
|
+
| 50km | 364 | `NTNW` | 50km × 50km |
|
|
72
|
+
| 10km | 9,100 | `NT27` | 10km × 10km |
|
|
73
|
+
| 5km | 36,400 | `NT27SE` | 5km × 5km |
|
|
74
|
+
| 1km | 910,000| `NT2573` | 1km × 1km |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Listing grid squares
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
lister = SmoOsBngGrids::Lister.new
|
|
82
|
+
|
|
83
|
+
# All 100km squares
|
|
84
|
+
lister.list("100km")
|
|
85
|
+
|
|
86
|
+
# All 10km squares within NT
|
|
87
|
+
lister.list("10km", within: "NT")
|
|
88
|
+
|
|
89
|
+
# All 1km squares within NT27
|
|
90
|
+
lister.list("1km", within: "NT27")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Each entry is a Hash:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
{
|
|
97
|
+
ref: "NT27",
|
|
98
|
+
min_e: 320000, min_n: 670000,
|
|
99
|
+
max_e: 330000, max_n: 680000,
|
|
100
|
+
points: [
|
|
101
|
+
[320000, 680000], # NW
|
|
102
|
+
[330000, 680000], # NE
|
|
103
|
+
[330000, 670000], # SE
|
|
104
|
+
[320000, 670000], # SW
|
|
105
|
+
[320000, 680000] # NW (closed)
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Spatial search
|
|
113
|
+
|
|
114
|
+
Find all tiles intersecting a radius or bounding box around a point:
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
lister = SmoOsBngGrids::Lister.new
|
|
118
|
+
|
|
119
|
+
# All 10km tiles within 12km of Edinburgh
|
|
120
|
+
tiles = lister.search(325000, 673000, resolution: "10km", radius: 12000)
|
|
121
|
+
tiles.each { |t| puts "#{t[:ref]} #{t[:distance_m]} m" }
|
|
122
|
+
|
|
123
|
+
# All 1km tiles within 1.5km of Edinburgh
|
|
124
|
+
lister.search(325000, 673000, resolution: "1km", radius: 1500)
|
|
125
|
+
|
|
126
|
+
# All 5km tiles within a 15km box around Edinburgh
|
|
127
|
+
lister.search(325000, 673000, resolution: "5km", box: 15000)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Radius search also returns `:distance_m` — `0.0` when the point is inside the tile.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Corner points — InfoWorks ICM
|
|
135
|
+
|
|
136
|
+
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:
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
lister = SmoOsBngGrids::Lister.new
|
|
140
|
+
|
|
141
|
+
# Single tile containing a point
|
|
142
|
+
tile = lister.search(325000, 673000, resolution: "10km", radius: 1).first
|
|
143
|
+
boundary_array = tile[:points]
|
|
144
|
+
# => [[320000, 680000], [330000, 680000], [330000, 670000], [320000, 670000], [320000, 680000]]
|
|
145
|
+
|
|
146
|
+
# Flat XY array if needed
|
|
147
|
+
flat_xy = tile[:points].flatten
|
|
148
|
+
# => [320000, 680000, 330000, 680000, 330000, 670000, 320000, 670000, 320000, 680000]
|
|
149
|
+
|
|
150
|
+
# Convert a ref string to a full entry
|
|
151
|
+
entry = lister.entry_for("NT27SE")
|
|
152
|
+
entry[:points] # => [[325000, 675000], [330000, 675000], [330000, 670000], [325000, 670000], [325000, 675000]]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Shapefile export
|
|
158
|
+
|
|
159
|
+
Export any set of entries to ESRI Shapefile format (OSGB36 / EPSG:27700).
|
|
160
|
+
Pure Ruby — no GDAL, no external gems, works in InfoWorks ICM 2027.
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
lister = SmoOsBngGrids::Lister.new
|
|
164
|
+
writer = SmoOsBngGrids::ShapefileWriter.new
|
|
165
|
+
|
|
166
|
+
# From list()
|
|
167
|
+
writer.write(lister.list("10km", within: "NT"), "/tmp/nt_10km")
|
|
168
|
+
|
|
169
|
+
# From search()
|
|
170
|
+
entries = lister.search(325000, 673000, resolution: "10km", radius: 20000)
|
|
171
|
+
writer.write(entries, "/tmp/edinburgh_10km_20km")
|
|
172
|
+
|
|
173
|
+
# From find() — one tile per resolution
|
|
174
|
+
found = lister.find(325000, 673000)
|
|
175
|
+
found.each do |res, ref|
|
|
176
|
+
writer.write([lister.entry_for(ref)], "/tmp/edinburgh_#{res}")
|
|
177
|
+
end
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Produces `.shp`, `.shx`, `.dbf`, `.prj` — open directly in QGIS or ArcGIS.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Grid reference validation
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
SmoOsBngGrids::Grid.valid?("NT") # => true
|
|
188
|
+
SmoOsBngGrids::Grid.valid?("NT27") # => true
|
|
189
|
+
SmoOsBngGrids::Grid.valid?("NT2573") # => true
|
|
190
|
+
SmoOsBngGrids::Grid.valid?("ZZ") # => false
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Data
|
|
196
|
+
|
|
197
|
+
All geometry is hardcoded from the **OS BNG Grids GeoPackage** published by Ordnance Survey.
|
|
198
|
+
Source: [github.com/OrdnanceSurvey/osbng-grids](https://github.com/OrdnanceSurvey/osbng-grids)
|
|
199
|
+
|
|
200
|
+
Contains OS data. Crown copyright and database right 2025.
|
|
201
|
+
Licensed under the Open Government Licence v3.0.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Attribution
|
|
206
|
+
|
|
207
|
+
Built by **Sebastian Madrid Ontiveros** to support hydraulic modelling and flood risk workflows in the UK.
|
|
208
|
+
If this gem saves you time, consider [buying Sebastian a coffee](https://buymeacoffee.com/smadrid).
|
|
@@ -32,6 +32,9 @@ module SmoOsBngGrids
|
|
|
32
32
|
|
|
33
33
|
QUADRANTS = %w[SW SE NW NE].freeze
|
|
34
34
|
|
|
35
|
+
MSG_INVALID_RESOLUTION = "Unknown resolution %s. Valid: #{VALID_RESOLUTIONS.join(', ')}"
|
|
36
|
+
MSG_PROVIDE_RADIUS_OR_BOX = "Provide radius: or box: (not both)"
|
|
37
|
+
|
|
35
38
|
# Quadrant offsets [easting_offset, northing_offset] from parent SW corner.
|
|
36
39
|
QUADRANT_OFFSETS = {
|
|
37
40
|
"SW" => [0, 0],
|
|
@@ -162,7 +162,7 @@ module SmoOsBngGrids
|
|
|
162
162
|
def self.validate_resolution!(res)
|
|
163
163
|
return if VALID_RESOLUTIONS.include?(res)
|
|
164
164
|
|
|
165
|
-
raise ArgumentError,
|
|
165
|
+
raise ArgumentError, format(MSG_INVALID_RESOLUTION, res.inspect)
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
def self.validate_coords!(easting, northing)
|
|
@@ -33,8 +33,8 @@ module SmoOsBngGrids
|
|
|
33
33
|
# @return [Array<Hash>] matching entries, each with :ref, :min_e, :min_n,
|
|
34
34
|
# :max_e, :max_n, :points, :distance_m (circle only)
|
|
35
35
|
def search(easting, northing, resolution: "10km", radius: nil, box: nil)
|
|
36
|
-
raise ArgumentError,
|
|
37
|
-
raise ArgumentError,
|
|
36
|
+
raise ArgumentError, MSG_PROVIDE_RADIUS_OR_BOX if radius.nil? && box.nil?
|
|
37
|
+
raise ArgumentError, MSG_PROVIDE_RADIUS_OR_BOX if radius && box
|
|
38
38
|
|
|
39
39
|
Grid.validate_coords!(easting, northing)
|
|
40
40
|
validate_resolution!(resolution)
|
|
@@ -153,7 +153,7 @@ module SmoOsBngGrids
|
|
|
153
153
|
def validate_resolution!(res)
|
|
154
154
|
return if VALID_RESOLUTIONS.include?(res)
|
|
155
155
|
|
|
156
|
-
raise ArgumentError,
|
|
156
|
+
raise ArgumentError, format(MSG_INVALID_RESOLUTION, res.inspect)
|
|
157
157
|
end
|
|
158
158
|
end
|
|
159
159
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smo_os_bng_grids
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sebastian Madrid Ontiveros
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-06-10 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: |
|
|
13
13
|
Developed by Sebastian Madrid Ontiveros. Pure Ruby gem providing all Ordnance Survey
|
|
@@ -20,11 +20,15 @@ description: |
|
|
|
20
20
|
Built to support hydraulic modelling and flood risk workflows in the UK.
|
|
21
21
|
If this gem saves you time, consider buying Sebastian a coffee at
|
|
22
22
|
https://buymeacoffee.com/smadrid
|
|
23
|
-
email:
|
|
23
|
+
email:
|
|
24
|
+
- sebasmadrid20@hotmail.com
|
|
24
25
|
executables: []
|
|
25
26
|
extensions: []
|
|
26
27
|
extra_rdoc_files: []
|
|
27
28
|
files:
|
|
29
|
+
- CHANGELOG.md
|
|
30
|
+
- LICENSE
|
|
31
|
+
- README.md
|
|
28
32
|
- lib/smo_os_bng_grids.rb
|
|
29
33
|
- lib/smo_os_bng_grids/constants.rb
|
|
30
34
|
- lib/smo_os_bng_grids/data/grid_100km.rb
|