geodetic 0.1.0 → 0.3.0
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 +28 -4
- data/README.md +19 -5
- data/docs/coordinate-systems/bng.md +5 -5
- data/docs/coordinate-systems/ecef.md +23 -23
- data/docs/coordinate-systems/enu.md +3 -3
- data/docs/coordinate-systems/gars.md +246 -0
- data/docs/coordinate-systems/georef.md +221 -0
- data/docs/coordinate-systems/gh.md +7 -7
- data/docs/coordinate-systems/gh36.md +6 -6
- data/docs/coordinate-systems/h3.md +312 -0
- data/docs/coordinate-systems/ham.md +6 -6
- data/docs/coordinate-systems/index.md +40 -34
- data/docs/coordinate-systems/lla.md +26 -26
- data/docs/coordinate-systems/mgrs.md +3 -3
- data/docs/coordinate-systems/ned.md +3 -3
- data/docs/coordinate-systems/olc.md +6 -6
- data/docs/coordinate-systems/state-plane.md +2 -2
- data/docs/coordinate-systems/ups.md +4 -4
- data/docs/coordinate-systems/usng.md +2 -2
- data/docs/coordinate-systems/utm.md +23 -23
- data/docs/coordinate-systems/web-mercator.md +7 -7
- data/docs/getting-started/installation.md +17 -17
- data/docs/getting-started/quick-start.md +8 -8
- data/docs/index.md +22 -19
- data/docs/reference/areas.md +15 -15
- data/docs/reference/conversions.md +31 -31
- data/docs/reference/geoid-height.md +5 -5
- data/docs/reference/serialization.md +44 -44
- data/examples/01_basic_conversions.rb +10 -10
- data/examples/02_all_coordinate_systems.rb +24 -24
- data/lib/geodetic/areas/circle.rb +1 -1
- data/lib/geodetic/areas/polygon.rb +2 -2
- data/lib/geodetic/areas/rectangle.rb +6 -6
- data/lib/geodetic/{coordinates → coordinate}/bng.rb +3 -37
- data/lib/geodetic/{coordinates → coordinate}/ecef.rb +3 -33
- data/lib/geodetic/{coordinates → coordinate}/enu.rb +30 -1
- data/lib/geodetic/coordinate/gars.rb +233 -0
- data/lib/geodetic/coordinate/georef.rb +204 -0
- data/lib/geodetic/coordinate/gh.rb +161 -0
- data/lib/geodetic/{coordinates → coordinate}/gh36.rb +28 -187
- data/lib/geodetic/coordinate/h3.rb +413 -0
- data/lib/geodetic/coordinate/ham.rb +226 -0
- data/lib/geodetic/{coordinates → coordinate}/lla.rb +31 -1
- data/lib/geodetic/{coordinates → coordinate}/mgrs.rb +3 -33
- data/lib/geodetic/{coordinates → coordinate}/ned.rb +30 -1
- data/lib/geodetic/{coordinates → coordinate}/olc.rb +19 -225
- data/lib/geodetic/coordinate/spatial_hash.rb +342 -0
- data/lib/geodetic/{coordinates → coordinate}/state_plane.rb +30 -1
- data/lib/geodetic/{coordinates → coordinate}/ups.rb +3 -37
- data/lib/geodetic/{coordinates → coordinate}/usng.rb +3 -33
- data/lib/geodetic/{coordinates → coordinate}/utm.rb +3 -33
- data/lib/geodetic/{coordinates → coordinate}/web_mercator.rb +3 -33
- data/lib/geodetic/{coordinates.rb → coordinate.rb} +62 -45
- data/lib/geodetic/version.rb +1 -1
- data/lib/geodetic.rb +1 -1
- data/spatial_hash_idea.md +241 -0
- metadata +29 -20
- data/lib/geodetic/coordinates/gh.rb +0 -372
- data/lib/geodetic/coordinates/ham.rb +0 -435
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Geodetic::Coordinate::GEOREF
|
|
2
|
+
|
|
3
|
+
## World Geographic Reference System
|
|
4
|
+
|
|
5
|
+
GEOREF is a grid-based geocode developed by the US military and adopted by ICAO for air navigation and air defense reporting. It encodes latitude/longitude into a compact alphanumeric string using a false coordinate system that shifts longitude by +180 and latitude by +90 to make all values positive.
|
|
6
|
+
|
|
7
|
+
The encoding reads longitude first, then latitude at each level:
|
|
8
|
+
|
|
9
|
+
| Level | Characters | Description | Resolution |
|
|
10
|
+
|-------|-----------|-------------|------------|
|
|
11
|
+
| **Tile** | 1-2 | 15-degree grid (24 lng x 12 lat letters) | 15 degrees |
|
|
12
|
+
| **Degree** | 3-4 | 1-degree subdivision within tile (15 letters each) | 1 degree |
|
|
13
|
+
| **Minutes** | 5+ | Numeric digit pairs (lng digits, then lat digits) | Variable |
|
|
14
|
+
|
|
15
|
+
The character set uses 24 letters (A-Z, omitting I and O) for tiles, and 15 letters (A-Q, omitting I and O) for degree subdivisions. Minute pairs are decimal digits.
|
|
16
|
+
|
|
17
|
+
Valid code lengths: 2, 4, 8, 10, 12, 14 characters (not 6 -- minimum numeric portion is 2 digits per axis).
|
|
18
|
+
|
|
19
|
+
GEOREF is a **2D coordinate system** (no altitude). Conversions to/from other systems go through LLA as the intermediary. Each code represents a rectangular cell; the coordinate's point value is the cell's midpoint.
|
|
20
|
+
|
|
21
|
+
## Constructor
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# From a GEOREF string
|
|
25
|
+
coord = Geodetic::Coordinate::GEOREF.new("GJPJ3417")
|
|
26
|
+
|
|
27
|
+
# From any coordinate (converts via LLA)
|
|
28
|
+
coord = Geodetic::Coordinate::GEOREF.new(lla_coord)
|
|
29
|
+
coord = Geodetic::Coordinate::GEOREF.new(utm_coord, precision: 10)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Parameter | Type | Default | Description |
|
|
33
|
+
|-------------|-------------------|---------|------------------------------------------------|
|
|
34
|
+
| `source` | String or Coord | -- | A GEOREF string or any coordinate object |
|
|
35
|
+
| `precision` | Integer | 8 | Code length: 2, 4, 8, 10, 12, or 14 |
|
|
36
|
+
|
|
37
|
+
Raises `ArgumentError` if the source string is empty, has an invalid length (including 6), or contains invalid characters. String input is case-insensitive (normalized to uppercase).
|
|
38
|
+
|
|
39
|
+
## Attributes
|
|
40
|
+
|
|
41
|
+
| Attribute | Type | Access | Description |
|
|
42
|
+
|-----------|--------|-----------|--------------------------|
|
|
43
|
+
| `code` | String | read-only | The GEOREF code string |
|
|
44
|
+
|
|
45
|
+
GEOREF is **immutable** -- there are no setter methods.
|
|
46
|
+
|
|
47
|
+
## Precision
|
|
48
|
+
|
|
49
|
+
The precision (code length) determines the size of the encoded cell:
|
|
50
|
+
|
|
51
|
+
| Length | Resolution | Approximate Cell Size |
|
|
52
|
+
|--------|-----------|----------------------|
|
|
53
|
+
| 2 | 15 degrees | ~1,668 km x 1,668 km |
|
|
54
|
+
| 4 | 1 degree | ~111 km x 111 km |
|
|
55
|
+
| 8 | 1 minute | ~1.85 km x 1.85 km |
|
|
56
|
+
| 10 | 0.1 minute | ~185 m x 185 m |
|
|
57
|
+
| 12 | 0.01 minute | ~18.5 m x 18.5 m |
|
|
58
|
+
| 14 | 0.001 minute | ~1.85 m x 1.85 m |
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
coord.precision # => 8 (code length)
|
|
62
|
+
coord.precision_in_meters # => { lat: ~1850, lng: ~1850 }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Conversions
|
|
66
|
+
|
|
67
|
+
All conversions chain through LLA. The datum parameter defaults to `Geodetic::WGS84`.
|
|
68
|
+
|
|
69
|
+
### Instance Methods
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
coord.to_lla # => LLA (midpoint of the cell)
|
|
73
|
+
coord.to_ecef
|
|
74
|
+
coord.to_utm
|
|
75
|
+
coord.to_enu(reference_lla)
|
|
76
|
+
coord.to_ned(reference_lla)
|
|
77
|
+
coord.to_mgrs
|
|
78
|
+
coord.to_usng
|
|
79
|
+
coord.to_web_mercator
|
|
80
|
+
coord.to_ups
|
|
81
|
+
coord.to_state_plane(zone_code)
|
|
82
|
+
coord.to_bng
|
|
83
|
+
coord.to_gh36
|
|
84
|
+
coord.to_gh
|
|
85
|
+
coord.to_ham
|
|
86
|
+
coord.to_olc
|
|
87
|
+
coord.to_gars
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Class Methods
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
GEOREF.from_lla(lla_coord)
|
|
94
|
+
GEOREF.from_ecef(ecef_coord)
|
|
95
|
+
GEOREF.from_utm(utm_coord)
|
|
96
|
+
GEOREF.from_web_mercator(wm_coord)
|
|
97
|
+
GEOREF.from_gh(gh_coord)
|
|
98
|
+
GEOREF.from_gars(gars_coord)
|
|
99
|
+
# ... and all other coordinate systems
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### LLA Convenience Methods
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
lla = Geodetic::Coordinate::LLA.new(lat: 40.7128, lng: -74.0060)
|
|
106
|
+
georef = lla.to_georef # default precision 8
|
|
107
|
+
georef = lla.to_georef(precision: 10) # 0.1-minute precision
|
|
108
|
+
|
|
109
|
+
lla = Geodetic::Coordinate::LLA.from_georef(georef)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Serialization
|
|
113
|
+
|
|
114
|
+
### `to_s(truncate_to = nil)`
|
|
115
|
+
|
|
116
|
+
Returns the GEOREF string. An optional integer truncates to that precision (re-encodes from decoded coordinates).
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
coord = GEOREF.new("GJPJ3417")
|
|
120
|
+
coord.to_s # => "GJPJ3417"
|
|
121
|
+
coord.to_s(4) # => "GJPJ"
|
|
122
|
+
coord.to_s(2) # => "GJ"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `to_slug`
|
|
126
|
+
|
|
127
|
+
Alias for `to_s`. GEOREF codes are already URL-safe.
|
|
128
|
+
|
|
129
|
+
### `to_a`
|
|
130
|
+
|
|
131
|
+
Returns `[lat, lng]` of the cell midpoint.
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
coord.to_a # => [38.286..., -76.411...]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### `from_string` / `from_array`
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
GEOREF.from_string("GJPJ3417") # from GEOREF string
|
|
141
|
+
GEOREF.from_array([38.0, -76.0]) # from [lat, lng]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Neighbors
|
|
145
|
+
|
|
146
|
+
Returns all 8 adjacent cells as GEOREF instances.
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
coord = GEOREF.new("GJPJ3417")
|
|
150
|
+
neighbors = coord.neighbors
|
|
151
|
+
# => { N: GEOREF, S: GEOREF, E: GEOREF, W: GEOREF, NE: GEOREF, NW: GEOREF, SE: GEOREF, SW: GEOREF }
|
|
152
|
+
|
|
153
|
+
neighbors[:N].to_lla.lat > coord.to_lla.lat # => true
|
|
154
|
+
neighbors[:E].to_lla.lng > coord.to_lla.lng # => true
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Neighbors preserve the same precision as the original code. Latitude is clamped to valid range near the poles; longitude wraps at the antimeridian.
|
|
158
|
+
|
|
159
|
+
## Area
|
|
160
|
+
|
|
161
|
+
The `to_area` method returns the GEOREF cell as a `Geodetic::Areas::Rectangle`.
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
area = coord.to_area
|
|
165
|
+
# => Geodetic::Areas::Rectangle
|
|
166
|
+
|
|
167
|
+
area.includes?(coord.to_lla) # => true (midpoint is inside the cell)
|
|
168
|
+
area.nw # => LLA (northwest corner)
|
|
169
|
+
area.se # => LLA (southeast corner)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Equality
|
|
173
|
+
|
|
174
|
+
Two GEOREF instances are equal if their code strings match exactly.
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
GEOREF.new("GJPJ3417") == GEOREF.new("GJPJ3417") # => true
|
|
178
|
+
GEOREF.new("GJPJ3417") == GEOREF.new("GJPJ3418") # => false
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## `valid?`
|
|
182
|
+
|
|
183
|
+
Returns `true` if the code has a valid length (2, 4, 8, 10, 12, or 14), valid tile letters, valid degree letters, and properly formatted minute digits.
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
coord.valid? # => true
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Universal Distance and Bearing Methods
|
|
190
|
+
|
|
191
|
+
GEOREF supports all universal distance and bearing methods via the `DistanceMethods` and `BearingMethods` mixins:
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
a = GEOREF.new("GJPJ3417")
|
|
195
|
+
b = GEOREF.new("HJAL4243")
|
|
196
|
+
|
|
197
|
+
a.distance_to(b) # => Distance
|
|
198
|
+
a.straight_line_distance_to(b) # => Distance
|
|
199
|
+
a.bearing_to(b) # => Bearing
|
|
200
|
+
a.elevation_to(b) # => Float (degrees)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Character Sets
|
|
204
|
+
|
|
205
|
+
**Tile longitude** (24 letters): `A B C D E F G H J K L M N P Q R S T U V W X Y Z`
|
|
206
|
+
|
|
207
|
+
**Tile latitude** (12 letters): `A B C D E F G H J K L M`
|
|
208
|
+
|
|
209
|
+
**Degree subdivision** (15 letters): `A B C D E F G H J K L M N P Q`
|
|
210
|
+
|
|
211
|
+
All sets omit `I` and `O` to avoid confusion with digits `1` and `0`.
|
|
212
|
+
|
|
213
|
+
## Code Structure Examples
|
|
214
|
+
|
|
215
|
+
| Code | Meaning |
|
|
216
|
+
|------|---------|
|
|
217
|
+
| `GJ` | 15-degree tile (tile only) |
|
|
218
|
+
| `GJPJ` | 1-degree cell within tile |
|
|
219
|
+
| `GJPJ3417` | 1-minute cell (lng 34', lat 17') |
|
|
220
|
+
| `GJPJ342171` | 0.1-minute cell |
|
|
221
|
+
| `GJPJ34211712` | 0.01-minute cell |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Geodetic::
|
|
1
|
+
# Geodetic::Coordinate::GH
|
|
2
2
|
|
|
3
3
|
## Geohash (Base-32)
|
|
4
4
|
|
|
@@ -15,11 +15,11 @@ GH is a **2D coordinate system** (no altitude). Conversions to/from other system
|
|
|
15
15
|
|
|
16
16
|
```ruby
|
|
17
17
|
# From a geohash string
|
|
18
|
-
coord = Geodetic::
|
|
18
|
+
coord = Geodetic::Coordinate::GH.new("dr5ru7")
|
|
19
19
|
|
|
20
20
|
# From any coordinate (converts via LLA)
|
|
21
|
-
coord = Geodetic::
|
|
22
|
-
coord = Geodetic::
|
|
21
|
+
coord = Geodetic::Coordinate::GH.new(lla_coord)
|
|
22
|
+
coord = Geodetic::Coordinate::GH.new(utm_coord, precision: 8)
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
| Parameter | Type | Default | Description |
|
|
@@ -91,11 +91,11 @@ GH.from_gh36(gh36_coord)
|
|
|
91
91
|
### LLA Convenience Methods
|
|
92
92
|
|
|
93
93
|
```ruby
|
|
94
|
-
lla = Geodetic::
|
|
94
|
+
lla = Geodetic::Coordinate::LLA.new(lat: 40.689167, lng: -74.044444)
|
|
95
95
|
gh = lla.to_gh # default precision 12
|
|
96
96
|
gh = lla.to_gh(precision: 6) # custom precision
|
|
97
97
|
|
|
98
|
-
lla = Geodetic::
|
|
98
|
+
lla = Geodetic::Coordinate::LLA.from_gh(gh)
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
## Serialization
|
|
@@ -270,7 +270,7 @@ GH (base-32) is the de facto standard. Virtually all spatial databases and servi
|
|
|
270
270
|
### Converting Between GH and GH36
|
|
271
271
|
|
|
272
272
|
```ruby
|
|
273
|
-
gh = Geodetic::
|
|
273
|
+
gh = Geodetic::Coordinate::GH.new("dr5ru7")
|
|
274
274
|
gh36 = gh.to_gh36 # => GH36 instance
|
|
275
275
|
gh_back = gh36.to_gh # => GH instance
|
|
276
276
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Geodetic::
|
|
1
|
+
# Geodetic::Coordinate::GH36
|
|
2
2
|
|
|
3
3
|
## Geohash-36
|
|
4
4
|
|
|
@@ -13,11 +13,11 @@ GH36 is a **2D coordinate system** (no altitude). Conversions to/from other syst
|
|
|
13
13
|
|
|
14
14
|
```ruby
|
|
15
15
|
# From a geohash string
|
|
16
|
-
coord = Geodetic::
|
|
16
|
+
coord = Geodetic::Coordinate::GH36.new("bdrdC26BqH")
|
|
17
17
|
|
|
18
18
|
# From any coordinate (converts via LLA)
|
|
19
|
-
coord = Geodetic::
|
|
20
|
-
coord = Geodetic::
|
|
19
|
+
coord = Geodetic::Coordinate::GH36.new(lla_coord)
|
|
20
|
+
coord = Geodetic::Coordinate::GH36.new(utm_coord, precision: 8)
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
| Parameter | Type | Default | Description |
|
|
@@ -86,11 +86,11 @@ GH36.from_web_mercator(wm_coord)
|
|
|
86
86
|
### LLA Convenience Methods
|
|
87
87
|
|
|
88
88
|
```ruby
|
|
89
|
-
lla = Geodetic::
|
|
89
|
+
lla = Geodetic::Coordinate::LLA.new(lat: 40.689167, lng: -74.044444)
|
|
90
90
|
gh36 = lla.to_gh36 # default precision 10
|
|
91
91
|
gh36 = lla.to_gh36(precision: 5) # custom precision
|
|
92
92
|
|
|
93
|
-
lla = Geodetic::
|
|
93
|
+
lla = Geodetic::Coordinate::LLA.from_gh36(gh36)
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
## Serialization
|
|
@@ -0,0 +1,312 @@
|
|
|
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_slug`
|
|
178
|
+
|
|
179
|
+
Alias for `to_s`. H3 hex strings are already URL-safe.
|
|
180
|
+
|
|
181
|
+
### `to_a`
|
|
182
|
+
|
|
183
|
+
Returns `[lat, lng]` of the cell centroid.
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
coord.to_a # => [40.685..., -74.030...]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### `from_string` / `from_array`
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
H3.from_string("872a1072bffffff") # from hex string
|
|
193
|
+
H3.from_array([40.689167, -74.044444]) # from [lat, lng]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Neighbors
|
|
197
|
+
|
|
198
|
+
Returns all adjacent cells as an Array of H3 instances. Hexagons have 6 neighbors; pentagons have 5.
|
|
199
|
+
|
|
200
|
+
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.
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
coord = H3.new("872a1072bffffff")
|
|
204
|
+
neighbors = coord.neighbors
|
|
205
|
+
# => [H3, H3, H3, H3, H3, H3]
|
|
206
|
+
|
|
207
|
+
neighbors.length # => 6
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Grid Disk
|
|
211
|
+
|
|
212
|
+
The `grid_disk(k)` method returns all cells within `k` steps. This is a generalization of `neighbors` (which is `grid_disk(1)` minus self).
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
coord.grid_disk(0) # => [self] (1 cell)
|
|
216
|
+
coord.grid_disk(1) # => [self + 6 neighbors] (7 cells)
|
|
217
|
+
coord.grid_disk(2) # => 19 cells
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Parent and Children
|
|
221
|
+
|
|
222
|
+
Navigate the H3 hierarchy by moving to coarser or finer resolution levels.
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
coord = H3.new("872a1072bffffff") # resolution 7
|
|
226
|
+
parent = coord.parent(5) # => H3 at resolution 5
|
|
227
|
+
parent.resolution # => 5
|
|
228
|
+
|
|
229
|
+
children = coord.children(8) # => Array of 7 H3 cells at resolution 8
|
|
230
|
+
children.length # => 7
|
|
231
|
+
children.first.resolution # => 8
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`parent` raises `ArgumentError` if the target resolution is not coarser (lower number). `children` raises `ArgumentError` if the target resolution is not finer (higher number).
|
|
235
|
+
|
|
236
|
+
## Area
|
|
237
|
+
|
|
238
|
+
The `to_area` method returns the hexagonal cell boundary as an `Areas::Polygon` with 6 vertices (5 for pentagons).
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
area = coord.to_area
|
|
242
|
+
# => Geodetic::Areas::Polygon
|
|
243
|
+
|
|
244
|
+
area.includes?(coord.to_lla) # => true (centroid is inside the cell)
|
|
245
|
+
area.boundary.length # => 7 (6 vertices + closing point)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Pentagon Detection
|
|
249
|
+
|
|
250
|
+
12 cells at each resolution level are pentagons (artifacts of the icosahedral projection). These have 5 neighbors and 5 boundary vertices instead of 6.
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
coord.pentagon? # => false (most cells are hexagons)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Cell Area
|
|
257
|
+
|
|
258
|
+
```ruby
|
|
259
|
+
coord.cell_area # => 5182586.98 (square meters)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Equality
|
|
263
|
+
|
|
264
|
+
Two H3 instances are equal if their hex strings match exactly.
|
|
265
|
+
|
|
266
|
+
```ruby
|
|
267
|
+
H3.new("872a1072bffffff") == H3.new("872a1072bffffff") # => true
|
|
268
|
+
H3.new("872a1072bffffff") == H3.new(0x872a1072bffffff) # => true (integer)
|
|
269
|
+
H3.new("872a1072bffffff") == H3.new("87195da49ffffff") # => false
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## `valid?`
|
|
273
|
+
|
|
274
|
+
Returns `true` if the H3 cell index is valid according to the H3 library.
|
|
275
|
+
|
|
276
|
+
```ruby
|
|
277
|
+
coord.valid? # => true
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Universal Distance and Bearing Methods
|
|
281
|
+
|
|
282
|
+
H3 supports all universal distance and bearing methods via the `DistanceMethods` and `BearingMethods` mixins:
|
|
283
|
+
|
|
284
|
+
```ruby
|
|
285
|
+
a = H3.new("872a1072bffffff") # Statue of Liberty area
|
|
286
|
+
b = H3.new("87195da49ffffff") # London area
|
|
287
|
+
|
|
288
|
+
a.distance_to(b) # => Distance (~5,570 km)
|
|
289
|
+
a.straight_line_distance_to(b) # => Distance
|
|
290
|
+
a.bearing_to(b) # => Bearing
|
|
291
|
+
a.elevation_to(b) # => Float (degrees)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Well-Known H3 Cells
|
|
295
|
+
|
|
296
|
+
| Location | H3 Index (res 7) | Resolution |
|
|
297
|
+
|----------|------------------|------------|
|
|
298
|
+
| Statue of Liberty | `872a1072bffffff` | 7 |
|
|
299
|
+
| London | `87195da49ffffff` | 7 |
|
|
300
|
+
| Null Island (0, 0) | `87754e64dffffff` | 7 |
|
|
301
|
+
|
|
302
|
+
## Implementation Notes
|
|
303
|
+
|
|
304
|
+
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.
|
|
305
|
+
|
|
306
|
+
The library search order is:
|
|
307
|
+
1. `LIBH3_PATH` environment variable
|
|
308
|
+
2. `/opt/homebrew/lib/libh3.dylib` (macOS ARM)
|
|
309
|
+
3. `/usr/local/lib/libh3.dylib` (macOS Intel)
|
|
310
|
+
4. `/usr/lib/libh3.so` (Linux)
|
|
311
|
+
5. `/usr/local/lib/libh3.so` (Linux local)
|
|
312
|
+
6. Architecture-specific Linux paths
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Geodetic::
|
|
1
|
+
# Geodetic::Coordinate::HAM
|
|
2
2
|
|
|
3
3
|
## Maidenhead Locator System
|
|
4
4
|
|
|
@@ -21,11 +21,11 @@ HAM is a **2D coordinate system** (no altitude). Conversions to/from other syste
|
|
|
21
21
|
|
|
22
22
|
```ruby
|
|
23
23
|
# From a Maidenhead locator string
|
|
24
|
-
coord = Geodetic::
|
|
24
|
+
coord = Geodetic::Coordinate::HAM.new("FN31pr")
|
|
25
25
|
|
|
26
26
|
# From any coordinate (converts via LLA)
|
|
27
|
-
coord = Geodetic::
|
|
28
|
-
coord = Geodetic::
|
|
27
|
+
coord = Geodetic::Coordinate::HAM.new(lla_coord)
|
|
28
|
+
coord = Geodetic::Coordinate::HAM.new(utm_coord, precision: 8)
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
| Parameter | Type | Default | Description |
|
|
@@ -98,11 +98,11 @@ HAM.from_olc(olc_coord)
|
|
|
98
98
|
### LLA Convenience Methods
|
|
99
99
|
|
|
100
100
|
```ruby
|
|
101
|
-
lla = Geodetic::
|
|
101
|
+
lla = Geodetic::Coordinate::LLA.new(lat: 40.689167, lng: -74.044444)
|
|
102
102
|
ham = lla.to_ham # default precision 6
|
|
103
103
|
ham = lla.to_ham(precision: 8) # extended precision
|
|
104
104
|
|
|
105
|
-
lla = Geodetic::
|
|
105
|
+
lla = Geodetic::Coordinate::LLA.from_ham(ham)
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
## Serialization
|