geodetic 0.2.0 → 0.3.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 +30 -0
- data/README.md +83 -28
- data/docs/coordinate-systems/gars.md +0 -4
- data/docs/coordinate-systems/georef.md +0 -4
- data/docs/coordinate-systems/gh.md +0 -4
- data/docs/coordinate-systems/gh36.md +0 -4
- data/docs/coordinate-systems/h3.md +308 -0
- data/docs/coordinate-systems/ham.md +0 -4
- data/docs/coordinate-systems/index.md +25 -23
- data/docs/coordinate-systems/olc.md +0 -4
- data/docs/index.md +7 -3
- data/docs/reference/conversions.md +15 -15
- data/docs/reference/feature.md +116 -0
- data/docs/reference/map-rendering.md +32 -0
- data/docs/reference/serialization.md +4 -4
- data/examples/02_all_coordinate_systems.rb +0 -3
- data/examples/03_distance_calculations.rb +1 -0
- data/examples/04_bearing_calculations.rb +1 -0
- data/examples/05_map_rendering/.gitignore +2 -0
- data/examples/05_map_rendering/demo.rb +264 -0
- data/examples/05_map_rendering/icons/bridge.png +0 -0
- data/examples/05_map_rendering/icons/building.png +0 -0
- data/examples/05_map_rendering/icons/landmark.png +0 -0
- data/examples/05_map_rendering/icons/monument.png +0 -0
- data/examples/05_map_rendering/icons/park.png +0 -0
- data/examples/05_map_rendering/nyc_landmarks.png +0 -0
- data/examples/README.md +62 -0
- data/fiddle_pointer_buffer_pool.md +119 -0
- data/lib/geodetic/coordinate/bng.rb +14 -33
- data/lib/geodetic/coordinate/ecef.rb +5 -1
- data/lib/geodetic/coordinate/enu.rb +13 -0
- data/lib/geodetic/coordinate/gars.rb +2 -3
- data/lib/geodetic/coordinate/georef.rb +2 -3
- data/lib/geodetic/coordinate/gh.rb +2 -4
- data/lib/geodetic/coordinate/gh36.rb +4 -5
- data/lib/geodetic/coordinate/h3.rb +412 -0
- data/lib/geodetic/coordinate/ham.rb +2 -3
- data/lib/geodetic/coordinate/lla.rb +15 -1
- data/lib/geodetic/coordinate/mgrs.rb +1 -1
- data/lib/geodetic/coordinate/ned.rb +13 -0
- data/lib/geodetic/coordinate/olc.rb +0 -1
- data/lib/geodetic/coordinate/spatial_hash.rb +2 -2
- data/lib/geodetic/coordinate/state_plane.rb +9 -0
- data/lib/geodetic/coordinate/ups.rb +1 -1
- data/lib/geodetic/coordinate/usng.rb +1 -1
- data/lib/geodetic/coordinate/utm.rb +1 -1
- data/lib/geodetic/coordinate/web_mercator.rb +1 -1
- data/lib/geodetic/coordinate.rb +31 -26
- data/lib/geodetic/feature.rb +44 -0
- data/lib/geodetic/geoid_height.rb +11 -6
- data/lib/geodetic/version.rb +1 -1
- data/lib/geodetic.rb +1 -0
- data/mkdocs.yml +2 -0
- metadata +20 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 13b2b50f8ff03c2093abf1a2454f3db38942f9c32918eb23aab30b6c45a4146b
|
|
4
|
+
data.tar.gz: 2b3d5a7f325e2766196d2997e9c1781f0425abe016edeeb2fe4f155fc6a43f13
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d0378c9df0e04f9bb3de688303abae35857c4fbc594a26094a983417baa55004ef7f7f4cef7c8db7511b8fc769d0a368420e7601cb04e12eaf887b324e51e61
|
|
7
|
+
data.tar.gz: c20dd6fa952579d8d035e1afd146d7337caa087efd71df4db17dcc3918b8cb4c561237f1cb5820167d9f84a64209da48a1dd38d0b46a5bccf0e3ce6959aa06d3
|
data/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
## [0.3.1] - 2026-03-09
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`Geodetic::Feature` class** — wraps any coordinate or area geometry with a `label` and `metadata` hash; delegates `distance_to` and `bearing_to` to the underlying geometry, using the centroid for area geometries
|
|
19
|
+
- **Map rendering example** (`examples/05_map_rendering/`) — renders NYC landmarks on a raster map using [libgd-gis](https://rubygems.org/gems/libgd-gis), demonstrating Feature objects, polygon overlays, bearing arrows, icon compositing, and light/dark theme support
|
|
20
|
+
- `examples/README.md` describing all five example scripts
|
|
21
|
+
- Documentation: `docs/reference/feature.md` (Feature reference) and `docs/reference/map-rendering.md` (libgd-gis integration guide)
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Updated README, `docs/index.md`, and mkdocs nav to include Feature class and map rendering example
|
|
26
|
+
|
|
27
|
+
## [0.3.0] - 2026-03-08
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **H3 Hexagonal Hierarchical Index** (`Geodetic::Coordinate::H3`) — Uber's spatial indexing system, bringing total to 18 coordinate systems (324 conversion paths)
|
|
32
|
+
- H3 uses Ruby's `fiddle` to call the H3 v4 C API directly — no gem dependency beyond `fiddle`
|
|
33
|
+
- H3-specific features: `grid_disk(k)`, `parent(res)`, `children(res)`, `pentagon?`, `cell_area`, `h3_index`, `resolution` (0-15)
|
|
34
|
+
- H3 `to_area` returns `Areas::Polygon` (6 vertices for hexagons, 5 for pentagons) instead of `Areas::Rectangle`
|
|
35
|
+
- H3 `neighbors` returns Array of 6 cells instead of directional Hash with 8 cardinal keys
|
|
36
|
+
- Graceful degradation: H3 raises clear error with installation instructions if `libh3` is not found; all other coordinate systems work normally
|
|
37
|
+
- `H3.available?` class method to check for libh3 at runtime
|
|
38
|
+
- Documentation page: `docs/coordinate-systems/h3.md`
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- Updated all documentation to reflect 18 coordinate systems (README, docs, gemspec, CLAUDE.md)
|
|
43
|
+
|
|
14
44
|
## [0.2.0] - 2026-03-08
|
|
15
45
|
|
|
16
46
|
### Added
|
data/README.md
CHANGED
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
<td width="50%" valign="top">
|
|
14
14
|
<strong>Key Features</strong><br>
|
|
15
15
|
|
|
16
|
-
- <strong>
|
|
16
|
+
- <strong>18 Coordinate Systems</strong> - LLA, ECEF, UTM, ENU, NED, MGRS, USNG, Web Mercator, UPS, State Plane, BNG, GH36, GH, HAM, OLC, GEOREF, GARS, H3<br>
|
|
17
17
|
- <strong>Full Bidirectional Conversions</strong> - Every system converts to and from every other system<br>
|
|
18
18
|
- <strong>Distance Calculations</strong> - Vincenty great-circle and straight-line with unit tracking<br>
|
|
19
19
|
- <strong>Bearing Calculations</strong> - Forward azimuth, back azimuth, compass directions, elevation angles<br>
|
|
20
20
|
- <strong>Geoid Height Support</strong> - EGM96, EGM2008, GEOID18, GEOID12B models<br>
|
|
21
21
|
- <strong>Geographic Areas</strong> - Circle, Polygon, and Rectangle with point-in-area tests<br>
|
|
22
|
+
- <strong>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
|
|
22
23
|
- <strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
|
|
23
24
|
- <strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
|
|
24
25
|
- <strong>Multiple Datums</strong> - WGS84, Clarke 1866, GRS 1980, Airy 1830, and more<br>
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
</tr>
|
|
28
29
|
</table>
|
|
29
30
|
|
|
30
|
-
<p>Geodetic enables precise conversion between geodetic coordinate systems in Ruby. All
|
|
31
|
+
<p>Geodetic enables precise conversion between geodetic coordinate systems in Ruby. All 18 coordinate systems support complete bidirectional conversions with high precision. Review the <a href="https://madbomber.github.io/geodetic/">full documentation website</a> and explore the <a href="examples/">runnable examples</a>.</p>
|
|
31
32
|
|
|
32
33
|
## Installation
|
|
33
34
|
|
|
@@ -43,6 +44,20 @@ Or install directly:
|
|
|
43
44
|
gem install geodetic
|
|
44
45
|
```
|
|
45
46
|
|
|
47
|
+
### Optional: H3 Hexagonal Index
|
|
48
|
+
|
|
49
|
+
The H3 coordinate system requires Uber's [H3 C library](https://h3geo.org/) installed on your system. Without it, all other 17 coordinate systems work normally; H3 operations will raise a helpful error.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# macOS
|
|
53
|
+
brew install h3
|
|
54
|
+
|
|
55
|
+
# Linux (build from source)
|
|
56
|
+
# See https://h3geo.org/docs/installation
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
You can also set the `LIBH3_PATH` environment variable to point to a custom `libh3` location.
|
|
60
|
+
|
|
46
61
|
## Usage
|
|
47
62
|
|
|
48
63
|
### Basic Coordinate Creation
|
|
@@ -63,14 +78,29 @@ ned = Coordinates::NED.new(n: 200.0, e: 100.0, d: -50.0)
|
|
|
63
78
|
|
|
64
79
|
### GCS Shorthand
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
For convenience, you can define a short alias in your application:
|
|
67
82
|
|
|
68
83
|
```ruby
|
|
69
84
|
require "geodetic"
|
|
70
85
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
86
|
+
GCS = Geodetic::Coordinate
|
|
87
|
+
|
|
88
|
+
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
|
|
89
|
+
ecef = Geodetic::Coordinate::ECEF.new(x: -2304643.57, y: -3638650.07, z: 4688674.43)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Discovering Coordinate Systems
|
|
93
|
+
|
|
94
|
+
List all available coordinate systems at runtime:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
Geodetic::Coordinate.systems
|
|
98
|
+
# => [Geodetic::Coordinate::LLA, Geodetic::Coordinate::ECEF, Geodetic::Coordinate::UTM, ...]
|
|
99
|
+
|
|
100
|
+
# Get short names
|
|
101
|
+
Geodetic::Coordinate.systems.map { |c| c.name.split('::').last }
|
|
102
|
+
# => ["LLA", "ECEF", "UTM", "ENU", "NED", "MGRS", "USNG", "WebMercator",
|
|
103
|
+
# "UPS", "StatePlane", "BNG", "GH36", "GH", "HAM", "OLC", "GEOREF", "GARS", "H3"]
|
|
74
104
|
```
|
|
75
105
|
|
|
76
106
|
### Coordinate Conversions
|
|
@@ -171,9 +201,9 @@ Universal distance methods work across all coordinate types and return `Distance
|
|
|
171
201
|
**Instance method `distance_to`** — Vincenty great-circle distance:
|
|
172
202
|
|
|
173
203
|
```ruby
|
|
174
|
-
seattle =
|
|
175
|
-
portland =
|
|
176
|
-
sf =
|
|
204
|
+
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
|
|
205
|
+
portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
|
|
206
|
+
sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0.0)
|
|
177
207
|
|
|
178
208
|
d = seattle.distance_to(portland) # => Distance (meters)
|
|
179
209
|
d.meters # => 235393.17
|
|
@@ -187,23 +217,23 @@ seattle.distance_to([portland, sf]) # => [Distance, Distance] (radial)
|
|
|
187
217
|
**Class method `distance_between`** — consecutive chain distances:
|
|
188
218
|
|
|
189
219
|
```ruby
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
220
|
+
Geodetic::Coordinate.distance_between(seattle, portland) # => Distance
|
|
221
|
+
Geodetic::Coordinate.distance_between(seattle, portland, sf) # => [Distance, Distance] (chain)
|
|
222
|
+
Geodetic::Coordinate.distance_between([seattle, portland, sf]) # => [Distance, Distance] (chain)
|
|
193
223
|
```
|
|
194
224
|
|
|
195
225
|
**Straight-line (ECEF Euclidean) versions:**
|
|
196
226
|
|
|
197
227
|
```ruby
|
|
198
228
|
seattle.straight_line_distance_to(portland) # => Distance
|
|
199
|
-
|
|
229
|
+
Geodetic::Coordinate.straight_line_distance_between(seattle, portland) # => Distance
|
|
200
230
|
```
|
|
201
231
|
|
|
202
232
|
**Cross-system distances** — works between any coordinate types:
|
|
203
233
|
|
|
204
234
|
```ruby
|
|
205
235
|
utm = seattle.to_utm
|
|
206
|
-
mgrs =
|
|
236
|
+
mgrs = Geodetic::Coordinate::MGRS.from_lla(portland)
|
|
207
237
|
utm.distance_to(mgrs) # => Distance
|
|
208
238
|
```
|
|
209
239
|
|
|
@@ -216,8 +246,8 @@ Universal bearing methods work across all coordinate types and return `Bearing`
|
|
|
216
246
|
**Instance method `bearing_to`** — great-circle forward azimuth:
|
|
217
247
|
|
|
218
248
|
```ruby
|
|
219
|
-
seattle =
|
|
220
|
-
portland =
|
|
249
|
+
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
|
|
250
|
+
portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
|
|
221
251
|
|
|
222
252
|
b = seattle.bearing_to(portland) # => Bearing
|
|
223
253
|
b.degrees # => 188.2
|
|
@@ -231,8 +261,8 @@ b.to_s # => "188.2036°"
|
|
|
231
261
|
**Instance method `elevation_to`** — vertical look angle:
|
|
232
262
|
|
|
233
263
|
```ruby
|
|
234
|
-
a =
|
|
235
|
-
b =
|
|
264
|
+
a = Geodetic::Coordinate::LLA.new(lat: 47.62, lng: -122.35, alt: 0.0)
|
|
265
|
+
b = Geodetic::Coordinate::LLA.new(lat: 47.62, lng: -122.35, alt: 5000.0)
|
|
236
266
|
|
|
237
267
|
a.elevation_to(b) # => 89.9... (degrees, nearly straight up)
|
|
238
268
|
```
|
|
@@ -240,15 +270,15 @@ a.elevation_to(b) # => 89.9... (degrees, nearly straight up)
|
|
|
240
270
|
**Class method `bearing_between`** — consecutive chain bearings:
|
|
241
271
|
|
|
242
272
|
```ruby
|
|
243
|
-
|
|
244
|
-
|
|
273
|
+
Geodetic::Coordinate.bearing_between(seattle, portland) # => Bearing
|
|
274
|
+
Geodetic::Coordinate.bearing_between(seattle, portland, sf) # => [Bearing, Bearing] (chain)
|
|
245
275
|
```
|
|
246
276
|
|
|
247
277
|
**Cross-system bearings** — works between any coordinate types:
|
|
248
278
|
|
|
249
279
|
```ruby
|
|
250
280
|
utm = seattle.to_utm
|
|
251
|
-
mgrs =
|
|
281
|
+
mgrs = Geodetic::Coordinate::MGRS.from_lla(portland)
|
|
252
282
|
utm.bearing_to(mgrs) # => Bearing
|
|
253
283
|
```
|
|
254
284
|
|
|
@@ -391,9 +421,6 @@ gh36 = lla.to_gh36(precision: 8)
|
|
|
391
421
|
# Decode back to LLA
|
|
392
422
|
lla = gh36.to_lla
|
|
393
423
|
|
|
394
|
-
# URL slug (the hash itself is URL-safe)
|
|
395
|
-
gh36.to_slug # => "bdrdC26BqH"
|
|
396
|
-
|
|
397
424
|
# Neighbor cells
|
|
398
425
|
gh36.neighbors # => { N: GH36, S: GH36, E: GH36, W: GH36, NE: ..., NW: ..., SE: ..., SW: ... }
|
|
399
426
|
|
|
@@ -421,9 +448,6 @@ gh = lla.to_gh(precision: 8)
|
|
|
421
448
|
# Decode back to LLA
|
|
422
449
|
lla = gh.to_lla
|
|
423
450
|
|
|
424
|
-
# URL slug (the hash itself is URL-safe)
|
|
425
|
-
gh.to_slug # => "dr5ru7"
|
|
426
|
-
|
|
427
451
|
# Neighbor cells
|
|
428
452
|
gh.neighbors # => { N: GH, S: GH, E: GH, W: GH, NE: ..., NW: ..., SE: ..., SW: ... }
|
|
429
453
|
|
|
@@ -517,6 +541,36 @@ rect.sw # => computed SW corner
|
|
|
517
541
|
rect.includes?(point) # => true/false
|
|
518
542
|
```
|
|
519
543
|
|
|
544
|
+
### Features
|
|
545
|
+
|
|
546
|
+
`Feature` wraps a geometry (any coordinate or area) with a label and a metadata hash. It delegates `distance_to` and `bearing_to` to its geometry, using the centroid for area geometries.
|
|
547
|
+
|
|
548
|
+
```ruby
|
|
549
|
+
liberty = Feature.new(
|
|
550
|
+
label: "Statue of Liberty",
|
|
551
|
+
geometry: Coordinates::LLA.new(lat: 40.6892, lng: -74.0445, alt: 0),
|
|
552
|
+
metadata: { category: "monument", year: 1886 }
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
empire = Feature.new(
|
|
556
|
+
label: "Empire State Building",
|
|
557
|
+
geometry: Coordinates::LLA.new(lat: 40.7484, lng: -73.9857, alt: 0),
|
|
558
|
+
metadata: { category: "building", floors: 102 }
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
liberty.distance_to(empire).to_km # => "8.24 km"
|
|
562
|
+
liberty.bearing_to(empire).degrees # => 36.99
|
|
563
|
+
|
|
564
|
+
# Area geometries use the centroid for distance/bearing
|
|
565
|
+
park = Feature.new(
|
|
566
|
+
label: "Central Park",
|
|
567
|
+
geometry: Areas::Polygon.new(boundary: [...])
|
|
568
|
+
)
|
|
569
|
+
park.distance_to(liberty).to_km # => "12.47 km"
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
All three attributes (`label`, `geometry`, `metadata`) are mutable.
|
|
573
|
+
|
|
520
574
|
### Web Mercator Tile Coordinates
|
|
521
575
|
|
|
522
576
|
```ruby
|
|
@@ -542,9 +596,10 @@ The [`examples/`](examples/) directory contains runnable demo scripts showing pr
|
|
|
542
596
|
| Script | Description |
|
|
543
597
|
|--------|-------------|
|
|
544
598
|
| [`01_basic_conversions.rb`](examples/01_basic_conversions.rb) | LLA, ECEF, UTM, ENU, NED conversions and roundtrips |
|
|
545
|
-
| [`02_all_coordinate_systems.rb`](examples/02_all_coordinate_systems.rb) | All
|
|
599
|
+
| [`02_all_coordinate_systems.rb`](examples/02_all_coordinate_systems.rb) | All 18 coordinate systems, cross-system chains, and areas |
|
|
546
600
|
| [`03_distance_calculations.rb`](examples/03_distance_calculations.rb) | Distance class features, unit conversions, and arithmetic |
|
|
547
601
|
| [`04_bearing_calculations.rb`](examples/04_bearing_calculations.rb) | Bearing class, compass directions, elevation angles, and chain bearings |
|
|
602
|
+
| [`05_map_rendering/`](examples/05_map_rendering/) | Render landmarks on a raster map with Feature objects, polygon areas, bearing arrows, and icons using [libgd-gis](https://rubygems.org/gems/libgd-gis) |
|
|
548
603
|
|
|
549
604
|
Run any example with:
|
|
550
605
|
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# Geodetic::Coordinate::H3
|
|
2
|
+
|
|
3
|
+
## H3 Hexagonal Hierarchical Index
|
|
4
|
+
|
|
5
|
+
H3 is Uber's hierarchical geospatial indexing system that divides the globe into hexagonal cells (and 12 pentagons per resolution level) using an icosahedron projection. Each cell is identified by a 64-bit integer, typically displayed as a 15-character hex string like `872a1072bffffff`.
|
|
6
|
+
|
|
7
|
+
**H3 requires the `libh3` C library** installed on your system. Without it, all other coordinate systems work normally; H3 operations raise a clear error message with installation instructions.
|
|
8
|
+
|
|
9
|
+
### Prerequisites
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# macOS (Homebrew)
|
|
13
|
+
brew install h3
|
|
14
|
+
|
|
15
|
+
# Linux (build from source)
|
|
16
|
+
git clone https://github.com/uber/h3.git
|
|
17
|
+
cd h3
|
|
18
|
+
cmake -B build -DCMAKE_INSTALL_PREFIX=/usr/local
|
|
19
|
+
cmake --build build
|
|
20
|
+
sudo cmake --install build
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
You can also set the `LIBH3_PATH` environment variable to specify a custom library path:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
export LIBH3_PATH=/path/to/libh3.dylib
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Geodetic searches these paths automatically:
|
|
30
|
+
- `/opt/homebrew/lib/libh3.dylib` (macOS ARM Homebrew)
|
|
31
|
+
- `/usr/local/lib/libh3.dylib` (macOS Intel Homebrew)
|
|
32
|
+
- `/usr/lib/libh3.so` (Linux system)
|
|
33
|
+
- `/usr/local/lib/libh3.so` (Linux local install)
|
|
34
|
+
|
|
35
|
+
### Key Differences from Other Spatial Hashes
|
|
36
|
+
|
|
37
|
+
| Feature | GH/OLC/GARS/GEOREF/HAM | H3 |
|
|
38
|
+
|---------|------------------------|-----|
|
|
39
|
+
| Cell shape | Rectangle | Hexagon (6 vertices) |
|
|
40
|
+
| `to_area` returns | `Areas::Rectangle` | `Areas::Polygon` |
|
|
41
|
+
| `neighbors` returns | Hash with 8 cardinal keys | Array of 6 cells |
|
|
42
|
+
| Code format | String | 64-bit integer (hex string) |
|
|
43
|
+
| Dependency | None (pure Ruby) | `libh3` (C library via fiddle) |
|
|
44
|
+
|
|
45
|
+
H3 is a **2D coordinate system** (no altitude). Conversions to/from other systems go through LLA as the intermediary. Each hex string represents a hexagonal cell; the coordinate's point value is the cell's centroid.
|
|
46
|
+
|
|
47
|
+
## Constructor
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
# From a hex string
|
|
51
|
+
coord = Geodetic::Coordinate::H3.new("872a1072bffffff")
|
|
52
|
+
|
|
53
|
+
# From a hex string with 0x prefix
|
|
54
|
+
coord = Geodetic::Coordinate::H3.new("0x872a1072bffffff")
|
|
55
|
+
|
|
56
|
+
# From a 64-bit integer
|
|
57
|
+
coord = Geodetic::Coordinate::H3.new(0x872a1072bffffff)
|
|
58
|
+
|
|
59
|
+
# From any coordinate (converts via LLA)
|
|
60
|
+
coord = Geodetic::Coordinate::H3.new(lla_coord)
|
|
61
|
+
coord = Geodetic::Coordinate::H3.new(utm_coord, precision: 9)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
| Parameter | Type | Default | Description |
|
|
65
|
+
|-------------|-------------------------|---------|--------------------------------------------|
|
|
66
|
+
| `source` | String, Integer, or Coord | -- | An H3 hex string, integer, or coordinate |
|
|
67
|
+
| `precision` | Integer | 7 | H3 resolution level (0-15) |
|
|
68
|
+
|
|
69
|
+
Raises `ArgumentError` if the source string is empty, contains invalid hex characters, or does not represent a valid H3 cell index. String input is case-insensitive (normalized to lowercase). The `0x` prefix is stripped automatically.
|
|
70
|
+
|
|
71
|
+
## Attributes
|
|
72
|
+
|
|
73
|
+
| Attribute | Type | Access | Description |
|
|
74
|
+
|------------|---------|-----------|----------------------------------|
|
|
75
|
+
| `code` | String | read-only | The hex string representation |
|
|
76
|
+
| `h3_index` | Integer | read-only | The 64-bit H3 cell index |
|
|
77
|
+
|
|
78
|
+
H3 is **immutable** -- there are no setter methods.
|
|
79
|
+
|
|
80
|
+
## Resolution
|
|
81
|
+
|
|
82
|
+
H3 uses "resolution" (0-15) instead of string-length precision. Higher resolution means smaller cells.
|
|
83
|
+
|
|
84
|
+
| Resolution | Approximate Cell Area | Approximate Edge Length |
|
|
85
|
+
|------------|----------------------|------------------------|
|
|
86
|
+
| 0 | 4,357,449 km^2 | 1,108 km |
|
|
87
|
+
| 1 | 609,788 km^2 | 419 km |
|
|
88
|
+
| 2 | 86,801 km^2 | 158 km |
|
|
89
|
+
| 3 | 12,393 km^2 | 60 km |
|
|
90
|
+
| 4 | 1,770 km^2 | 23 km |
|
|
91
|
+
| 5 | 252 km^2 | 8.5 km |
|
|
92
|
+
| 6 | 36 km^2 | 3.2 km |
|
|
93
|
+
| 7 | 5.2 km^2 (default) | 1.2 km |
|
|
94
|
+
| 8 | 0.74 km^2 | 461 m |
|
|
95
|
+
| 9 | 0.105 km^2 | 174 m |
|
|
96
|
+
| 10 | 0.015 km^2 | 66 m |
|
|
97
|
+
| 11 | 0.002 km^2 | 25 m |
|
|
98
|
+
| 12 | 307 m^2 | 9.4 m |
|
|
99
|
+
| 13 | 43 m^2 | 3.6 m |
|
|
100
|
+
| 14 | 6.2 m^2 | 1.3 m |
|
|
101
|
+
| 15 | 0.9 m^2 | 0.5 m |
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
coord.resolution # => 7 (alias: coord.precision)
|
|
105
|
+
coord.cell_area # => 5182586.98 (square meters)
|
|
106
|
+
coord.precision_in_meters # => { lat: ~2276, lng: ~2276, area_m2: ~5182586 }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Checking Availability
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
Geodetic::Coordinate::H3.available? # => true if libh3 is found
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Conversions
|
|
116
|
+
|
|
117
|
+
All conversions chain through LLA. The datum parameter defaults to `Geodetic::WGS84`.
|
|
118
|
+
|
|
119
|
+
### Instance Methods
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
coord.to_lla # => LLA (centroid of the cell)
|
|
123
|
+
coord.to_ecef
|
|
124
|
+
coord.to_utm
|
|
125
|
+
coord.to_enu(reference_lla)
|
|
126
|
+
coord.to_ned(reference_lla)
|
|
127
|
+
coord.to_mgrs
|
|
128
|
+
coord.to_usng
|
|
129
|
+
coord.to_web_mercator
|
|
130
|
+
coord.to_ups
|
|
131
|
+
coord.to_state_plane(zone_code)
|
|
132
|
+
coord.to_bng
|
|
133
|
+
coord.to_gh36
|
|
134
|
+
coord.to_gh
|
|
135
|
+
coord.to_ham
|
|
136
|
+
coord.to_olc
|
|
137
|
+
coord.to_georef
|
|
138
|
+
coord.to_gars
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Class Methods
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
H3.from_lla(lla_coord)
|
|
145
|
+
H3.from_ecef(ecef_coord)
|
|
146
|
+
H3.from_utm(utm_coord)
|
|
147
|
+
H3.from_web_mercator(wm_coord)
|
|
148
|
+
H3.from_gh(gh_coord)
|
|
149
|
+
H3.from_georef(georef_coord)
|
|
150
|
+
H3.from_gars(gars_coord)
|
|
151
|
+
# ... and all other coordinate systems
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### LLA Convenience Methods
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
lla = Geodetic::Coordinate::LLA.new(lat: 40.689167, lng: -74.044444)
|
|
158
|
+
h3 = lla.to_h3 # default resolution 7
|
|
159
|
+
h3 = lla.to_h3(precision: 9) # resolution 9
|
|
160
|
+
|
|
161
|
+
lla = Geodetic::Coordinate::LLA.from_h3(h3)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Serialization
|
|
165
|
+
|
|
166
|
+
### `to_s(format = nil)`
|
|
167
|
+
|
|
168
|
+
Returns the hex string. Pass `:integer` to get the 64-bit integer value.
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
coord = H3.new("872a1072bffffff")
|
|
172
|
+
coord.to_s # => "872a1072bffffff"
|
|
173
|
+
coord.to_s(:integer) # => 608693941536498687
|
|
174
|
+
coord.h3_index # => 608693941536498687
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### `to_a`
|
|
178
|
+
|
|
179
|
+
Returns `[lat, lng]` of the cell centroid.
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
coord.to_a # => [40.685..., -74.030...]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### `from_string` / `from_array`
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
H3.from_string("872a1072bffffff") # from hex string
|
|
189
|
+
H3.from_array([40.689167, -74.044444]) # from [lat, lng]
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Neighbors
|
|
193
|
+
|
|
194
|
+
Returns all adjacent cells as an Array of H3 instances. Hexagons have 6 neighbors; pentagons have 5.
|
|
195
|
+
|
|
196
|
+
Note: unlike the rectangular spatial hashes which return a directional Hash (`:N`, `:S`, etc.), H3 returns a flat Array because hexagonal cells do not have cardinal directions.
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
coord = H3.new("872a1072bffffff")
|
|
200
|
+
neighbors = coord.neighbors
|
|
201
|
+
# => [H3, H3, H3, H3, H3, H3]
|
|
202
|
+
|
|
203
|
+
neighbors.length # => 6
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Grid Disk
|
|
207
|
+
|
|
208
|
+
The `grid_disk(k)` method returns all cells within `k` steps. This is a generalization of `neighbors` (which is `grid_disk(1)` minus self).
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
coord.grid_disk(0) # => [self] (1 cell)
|
|
212
|
+
coord.grid_disk(1) # => [self + 6 neighbors] (7 cells)
|
|
213
|
+
coord.grid_disk(2) # => 19 cells
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Parent and Children
|
|
217
|
+
|
|
218
|
+
Navigate the H3 hierarchy by moving to coarser or finer resolution levels.
|
|
219
|
+
|
|
220
|
+
```ruby
|
|
221
|
+
coord = H3.new("872a1072bffffff") # resolution 7
|
|
222
|
+
parent = coord.parent(5) # => H3 at resolution 5
|
|
223
|
+
parent.resolution # => 5
|
|
224
|
+
|
|
225
|
+
children = coord.children(8) # => Array of 7 H3 cells at resolution 8
|
|
226
|
+
children.length # => 7
|
|
227
|
+
children.first.resolution # => 8
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`parent` raises `ArgumentError` if the target resolution is not coarser (lower number). `children` raises `ArgumentError` if the target resolution is not finer (higher number).
|
|
231
|
+
|
|
232
|
+
## Area
|
|
233
|
+
|
|
234
|
+
The `to_area` method returns the hexagonal cell boundary as an `Areas::Polygon` with 6 vertices (5 for pentagons).
|
|
235
|
+
|
|
236
|
+
```ruby
|
|
237
|
+
area = coord.to_area
|
|
238
|
+
# => Geodetic::Areas::Polygon
|
|
239
|
+
|
|
240
|
+
area.includes?(coord.to_lla) # => true (centroid is inside the cell)
|
|
241
|
+
area.boundary.length # => 7 (6 vertices + closing point)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Pentagon Detection
|
|
245
|
+
|
|
246
|
+
12 cells at each resolution level are pentagons (artifacts of the icosahedral projection). These have 5 neighbors and 5 boundary vertices instead of 6.
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
coord.pentagon? # => false (most cells are hexagons)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Cell Area
|
|
253
|
+
|
|
254
|
+
```ruby
|
|
255
|
+
coord.cell_area # => 5182586.98 (square meters)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Equality
|
|
259
|
+
|
|
260
|
+
Two H3 instances are equal if their hex strings match exactly.
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
H3.new("872a1072bffffff") == H3.new("872a1072bffffff") # => true
|
|
264
|
+
H3.new("872a1072bffffff") == H3.new(0x872a1072bffffff) # => true (integer)
|
|
265
|
+
H3.new("872a1072bffffff") == H3.new("87195da49ffffff") # => false
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## `valid?`
|
|
269
|
+
|
|
270
|
+
Returns `true` if the H3 cell index is valid according to the H3 library.
|
|
271
|
+
|
|
272
|
+
```ruby
|
|
273
|
+
coord.valid? # => true
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Universal Distance and Bearing Methods
|
|
277
|
+
|
|
278
|
+
H3 supports all universal distance and bearing methods via the `DistanceMethods` and `BearingMethods` mixins:
|
|
279
|
+
|
|
280
|
+
```ruby
|
|
281
|
+
a = H3.new("872a1072bffffff") # Statue of Liberty area
|
|
282
|
+
b = H3.new("87195da49ffffff") # London area
|
|
283
|
+
|
|
284
|
+
a.distance_to(b) # => Distance (~5,570 km)
|
|
285
|
+
a.straight_line_distance_to(b) # => Distance
|
|
286
|
+
a.bearing_to(b) # => Bearing
|
|
287
|
+
a.elevation_to(b) # => Float (degrees)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Well-Known H3 Cells
|
|
291
|
+
|
|
292
|
+
| Location | H3 Index (res 7) | Resolution |
|
|
293
|
+
|----------|------------------|------------|
|
|
294
|
+
| Statue of Liberty | `872a1072bffffff` | 7 |
|
|
295
|
+
| London | `87195da49ffffff` | 7 |
|
|
296
|
+
| Null Island (0, 0) | `87754e64dffffff` | 7 |
|
|
297
|
+
|
|
298
|
+
## Implementation Notes
|
|
299
|
+
|
|
300
|
+
Geodetic uses Ruby's `fiddle` (part of the standard library) to call the H3 v4 C API directly. No gem dependency beyond `fiddle` is required. The H3 C library must be installed separately.
|
|
301
|
+
|
|
302
|
+
The library search order is:
|
|
303
|
+
1. `LIBH3_PATH` environment variable
|
|
304
|
+
2. `/opt/homebrew/lib/libh3.dylib` (macOS ARM)
|
|
305
|
+
3. `/usr/local/lib/libh3.dylib` (macOS Intel)
|
|
306
|
+
4. `/usr/lib/libh3.so` (Linux)
|
|
307
|
+
5. `/usr/local/lib/libh3.so` (Linux local)
|
|
308
|
+
6. Architecture-specific Linux paths
|