geodetic 0.4.0 → 0.5.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 +82 -3
- data/README.md +162 -48
- data/docs/coordinate-systems/index.md +18 -18
- data/docs/index.md +49 -27
- data/docs/reference/arithmetic.md +368 -0
- data/docs/reference/conversions.md +3 -3
- data/docs/reference/feature.md +1 -1
- data/docs/reference/geojson.md +320 -0
- data/docs/reference/vector.md +256 -0
- data/examples/08_geodetic_arithmetic.rb +393 -0
- data/examples/09_geojson_export.rb +256 -0
- data/examples/README.md +30 -0
- data/lib/geodetic/areas/circle.rb +8 -0
- data/lib/geodetic/areas/polygon.rb +10 -0
- data/lib/geodetic/areas/regular_polygon.rb +1 -21
- data/lib/geodetic/coordinate/bng.rb +26 -26
- data/lib/geodetic/coordinate/ecef.rb +12 -12
- data/lib/geodetic/coordinate/enu.rb +26 -26
- data/lib/geodetic/coordinate/lla.rb +16 -16
- data/lib/geodetic/coordinate/mgrs.rb +26 -26
- data/lib/geodetic/coordinate/ned.rb +26 -26
- data/lib/geodetic/coordinate/spatial_hash.rb +22 -22
- data/lib/geodetic/coordinate/state_plane.rb +42 -42
- data/lib/geodetic/coordinate/ups.rb +21 -21
- data/lib/geodetic/coordinate/usng.rb +25 -25
- data/lib/geodetic/coordinate/utm.rb +12 -12
- data/lib/geodetic/coordinate/web_mercator.rb +21 -21
- data/lib/geodetic/coordinate.rb +29 -3
- data/lib/geodetic/distance.rb +5 -1
- data/lib/geodetic/geojson.rb +214 -0
- data/lib/geodetic/path.rb +71 -15
- data/lib/geodetic/segment.rb +23 -18
- data/lib/geodetic/vector.rb +242 -0
- data/lib/geodetic/version.rb +1 -1
- data/lib/geodetic.rb +6 -4
- metadata +8 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 82e74456b38ec9c888ee70e68de3e626f0333b9f791c8de7779f5aa544cbbbc7
|
|
4
|
+
data.tar.gz: a76e9a726d4a503f0e2331f321de25a3b7c155e8712a27a18c53ba8e5ab27953
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4bd3b6a3c7664f9f0b20c7bd5c9cee52d80528fe91f97d5a9d2a8a48c9f8314d34027f50ba3b091581650965721e8275b268564ac6fb203fdc896c1f7fa25fa4
|
|
7
|
+
data.tar.gz: f7d83378049a016a388e2f8b4130179e7166704e20b10525a6f5c5a88a12a8812b703fe484dbfd8c40d7af7d8e6ddda7a903052575ea52b48a169c1ebff3fd8d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
> [!CAUTION]
|
|
4
|
-
> This gem is under active development. APIs and features may change without notice.
|
|
5
|
-
|
|
6
3
|
All notable changes to this project will be documented in this file.
|
|
7
4
|
|
|
8
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
@@ -11,6 +8,88 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
8
|
## [Unreleased]
|
|
12
9
|
|
|
13
10
|
|
|
11
|
+
## [0.5.1] - 2026-03-10
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`Geodetic::GeoJSON` class** — build GeoJSON FeatureCollections from any mix of Geodetic objects
|
|
16
|
+
- **Constructor**: `GeoJSON.new`, `GeoJSON.new(obj, ...)`, `GeoJSON.new([array])`
|
|
17
|
+
- **Accumulate**: `<<` accepts single objects or arrays; returns `self` for chaining
|
|
18
|
+
- **Query**: `size`/`length`, `empty?`, `each`, and all `Enumerable` methods
|
|
19
|
+
- **Remove**: `delete(obj)`, `clear`
|
|
20
|
+
- **Export**: `to_h` (Ruby Hash), `to_json`/`to_json(pretty: true)` (JSON string), `save(path, pretty: false)` (file output)
|
|
21
|
+
- Non-Feature objects auto-wrapped as GeoJSON Features with empty properties
|
|
22
|
+
- Feature objects carry `label` → `"name"` and `metadata` → `properties`
|
|
23
|
+
- **`to_geojson` instance method** on all geometry types:
|
|
24
|
+
- All 18 coordinate classes → GeoJSON Point (via LLA; altitude included when non-zero)
|
|
25
|
+
- `Segment` → GeoJSON LineString (2 positions)
|
|
26
|
+
- `Path` → GeoJSON LineString (default) or Polygon (`as: :polygon`, auto-closes ring)
|
|
27
|
+
- `Areas::Polygon` and subclasses → GeoJSON Polygon
|
|
28
|
+
- `Areas::Circle` → GeoJSON Polygon (N-gon approximation, default 32 segments, configurable via `segments:`)
|
|
29
|
+
- `Areas::BoundingBox` → GeoJSON Polygon (4 corners, right-hand rule ring order)
|
|
30
|
+
- `Feature` → GeoJSON Feature with geometry and properties
|
|
31
|
+
- ENU/NED raise `ArgumentError` (relative systems require conversion first)
|
|
32
|
+
- GeoJSON export example (`examples/09_geojson_export.rb`) — 10-section demo covering `to_geojson` on all geometry types, FeatureCollection building, delete/clear, Enumerable, and file export
|
|
33
|
+
- Documentation: `docs/reference/geojson.md` (GeoJSON Export reference)
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Corrected stale numeric values in documentation:
|
|
38
|
+
- Seattle→Portland distance: 235393.17 → 235385.71 m (README, `docs/reference/conversions.md`)
|
|
39
|
+
- Seattle→Portland bearing: 188.2° → 186.25° (README, `docs/reference/conversions.md`, `docs/reference/arithmetic.md`)
|
|
40
|
+
- Seattle→Portland miles: 146.28 → 146.26 (README)
|
|
41
|
+
- Liberty→Empire bearing: 36.99° → 36.95° (README, `docs/reference/feature.md`)
|
|
42
|
+
- Fixed `docs/index.md` Key Features list to include all 18 coordinate systems (was missing GEOREF, GARS, H3)
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- Updated README with GeoJSON Export section, key features bullet, and example 09 in the examples table
|
|
47
|
+
- Updated `docs/index.md` with GeoJSON Export in key features and reference links
|
|
48
|
+
- Updated `examples/README.md` with example 09 description
|
|
49
|
+
|
|
50
|
+
## [0.5.0] - 2026-03-10
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **`Geodetic::Vector` class** — geodetic displacement pairing a Distance (magnitude) with a Bearing (direction)
|
|
55
|
+
- **Construction**: `Vector.new(distance:, bearing:)` with automatic coercion from numeric values
|
|
56
|
+
- **Components**: `north`, `east` — decomposed meters; `magnitude` — distance in meters
|
|
57
|
+
- **Factory methods**: `Vector.from_components(north:, east:)`, `Vector.from_segment(segment)`
|
|
58
|
+
- **Vincenty direct**: `destination_from(origin)` solves the direct geodetic problem on the WGS84 ellipsoid
|
|
59
|
+
- **Arithmetic**: `+`, `-` (component-wise), `*`, `/` (scalar), `-@` (unary minus); `Numeric * Vector` via coerce
|
|
60
|
+
- **Products**: `dot(other)`, `cross(other)`, `angle_between(other)`
|
|
61
|
+
- **Properties**: `zero?`, `normalize`, `reverse`/`inverse`
|
|
62
|
+
- **Comparable**: ordered by distance (magnitude)
|
|
63
|
+
- Near-zero results (< 1e-9 m) snap to clean zero vector
|
|
64
|
+
- **Geodetic arithmetic with `+` operator** — build geometry from coordinates, vectors, and distances:
|
|
65
|
+
- `Coordinate + Coordinate` → Segment
|
|
66
|
+
- `Coordinate + Coordinate + Coordinate` → Path (via Segment + Coordinate → Path)
|
|
67
|
+
- `Coordinate + Segment` → Path
|
|
68
|
+
- `Segment + Coordinate` → Path
|
|
69
|
+
- `Segment + Segment` → Path
|
|
70
|
+
- `Coordinate + Distance` → Circle
|
|
71
|
+
- `Distance + Coordinate` → Circle (commutative)
|
|
72
|
+
- `Coordinate + Vector` → Segment (Vincenty direct)
|
|
73
|
+
- `Vector + Coordinate` → Segment (reverse start to coordinate)
|
|
74
|
+
- `Segment + Vector` → Path (extend from endpoint)
|
|
75
|
+
- `Vector + Segment` → Path (prepend via reverse)
|
|
76
|
+
- `Path + Vector` → Path (extend from last point)
|
|
77
|
+
- **Translation with `*` operator and `translate` method** — uniform displacement across all geometric types:
|
|
78
|
+
- `Coordinate * Vector` → Coordinate (translated point)
|
|
79
|
+
- `Segment * Vector` → Segment (translated endpoints)
|
|
80
|
+
- `Path * Vector` → Path (translated waypoints)
|
|
81
|
+
- `Circle * Vector` → Circle (translated centroid, preserved radius)
|
|
82
|
+
- `Polygon * Vector` → Polygon (translated vertices)
|
|
83
|
+
- **`Segment#to_vector`** — extract a Vector from a Segment's length and bearing
|
|
84
|
+
- **`Path#to_corridor(width:)`** — convert a path into a Polygon corridor of a given width; uses mean bearing at interior waypoints to avoid self-intersection; accepts meters or a Distance object
|
|
85
|
+
- **Geodetic arithmetic example** (`examples/08_geodetic_arithmetic.rb`) — 11-section demo covering all arithmetic operators, Vector class, translation, corridors, and composed operations
|
|
86
|
+
- Documentation: `docs/reference/vector.md` (Vector reference), `docs/reference/arithmetic.md` (Geodetic Arithmetic reference)
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
|
|
90
|
+
- Updated README with Vector, Geodetic Arithmetic, and Corridors sections; added to key features list
|
|
91
|
+
- Updated `examples/README.md` with example 08 description
|
|
92
|
+
|
|
14
93
|
## [0.4.0] - 2026-03-10
|
|
15
94
|
|
|
16
95
|
### Added
|
data/README.md
CHANGED
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
- <strong>Segments</strong> - Directed two-point line segments with projection, intersection, and interpolation<br>
|
|
23
23
|
- <strong>Paths</strong> - Directed coordinate sequences with navigation, interpolation, closest approach, intersection, and area conversion<br>
|
|
24
24
|
- <strong>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
|
|
25
|
+
- <strong>Vectors</strong> - Geodetic displacement (distance + bearing) with full arithmetic and Vincenty direct<br>
|
|
26
|
+
- <strong>Geodetic Arithmetic</strong> - Compose geometry with operators: P1 + P2 → Segment, + P3 → Path, + Distance → Circle, * Vector → translate<br>
|
|
27
|
+
- <strong>GeoJSON Export</strong> - Build FeatureCollections from any mix of objects and save to file<br>
|
|
25
28
|
- <strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
|
|
26
29
|
- <strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
|
|
27
30
|
- <strong>Multiple Datums</strong> - WGS84, Clarke 1866, GRS 1980, Airy 1830, and more<br>
|
|
@@ -71,11 +74,15 @@ require "geodetic"
|
|
|
71
74
|
|
|
72
75
|
include Geodetic
|
|
73
76
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
# lng:, lon:, and long: are all accepted for longitude
|
|
78
|
+
lla = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
|
|
79
|
+
lla = Coordinate::LLA.new(lat: 47.6205, lon: -122.3493, alt: 184.0)
|
|
80
|
+
lla = Coordinate::LLA.new(lat: 47.6205, long: -122.3493, alt: 184.0)
|
|
81
|
+
# Readers: lla.lng, lla.lon, lla.long, lla.longitude all return the same value
|
|
82
|
+
ecef = Coordinate::ECEF.new(x: -2304643.57, y: -3638650.07, z: 4688674.43)
|
|
83
|
+
utm = Coordinate::UTM.new(easting: 548894.0, northing: 5272748.0, altitude: 184.0, zone: 10, hemisphere: "N")
|
|
84
|
+
enu = Coordinate::ENU.new(e: 100.0, n: 200.0, u: 50.0)
|
|
85
|
+
ned = Coordinate::NED.new(n: 200.0, e: 100.0, d: -50.0)
|
|
79
86
|
```
|
|
80
87
|
|
|
81
88
|
### GCS Shorthand
|
|
@@ -110,19 +117,19 @@ Geodetic::Coordinate.systems.map { |c| c.name.split('::').last }
|
|
|
110
117
|
Every coordinate system can convert to and from every other system:
|
|
111
118
|
|
|
112
119
|
```ruby
|
|
113
|
-
lla =
|
|
120
|
+
lla = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
|
|
114
121
|
|
|
115
122
|
# LLA to other systems
|
|
116
123
|
ecef = lla.to_ecef
|
|
117
124
|
utm = lla.to_utm
|
|
118
|
-
wm =
|
|
119
|
-
mgrs =
|
|
125
|
+
wm = Coordinate::WebMercator.from_lla(lla)
|
|
126
|
+
mgrs = Coordinate::MGRS.from_lla(lla)
|
|
120
127
|
|
|
121
128
|
# Convert back
|
|
122
129
|
lla_roundtrip = ecef.to_lla
|
|
123
130
|
|
|
124
131
|
# Local coordinate systems require a reference point
|
|
125
|
-
reference =
|
|
132
|
+
reference = Coordinate::LLA.new(lat: 47.62, lng: -122.35, alt: 0.0)
|
|
126
133
|
enu = lla.to_enu(reference)
|
|
127
134
|
ned = lla.to_ned(reference)
|
|
128
135
|
```
|
|
@@ -132,15 +139,15 @@ ned = lla.to_ned(reference)
|
|
|
132
139
|
All coordinate classes support `to_s`, `to_a`, `from_string`, and `from_array`. The `to_s` method accepts an optional precision parameter controlling the number of decimal places:
|
|
133
140
|
|
|
134
141
|
```ruby
|
|
135
|
-
lla =
|
|
142
|
+
lla = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
|
|
136
143
|
|
|
137
144
|
lla.to_s # => "47.620500, -122.349300, 184.00"
|
|
138
145
|
lla.to_s(3) # => "47.620, -122.349, 184.00"
|
|
139
146
|
lla.to_s(0) # => "48, -122, 184"
|
|
140
147
|
lla.to_a # => [47.6205, -122.3493, 184.0]
|
|
141
148
|
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
Coordinate::LLA.from_string("47.6205, -122.3493, 184.0")
|
|
150
|
+
Coordinate::LLA.from_array([47.6205, -122.3493, 184.0])
|
|
144
151
|
```
|
|
145
152
|
|
|
146
153
|
Default precisions by class: LLA=6, Bearing=4, all others=2. Passing `0` returns integers.
|
|
@@ -150,24 +157,24 @@ Default precisions by class: LLA=6, Bearing=4, all others=2. Passing `0` returns
|
|
|
150
157
|
All coordinate classes provide setter methods with type coercion and validation:
|
|
151
158
|
|
|
152
159
|
```ruby
|
|
153
|
-
lla =
|
|
160
|
+
lla = Coordinate::LLA.new(lat: 47.0, lng: -122.0, alt: 100.0)
|
|
154
161
|
lla.lat = 48.0 # validates -90..90
|
|
155
162
|
lla.lng = -121.0 # validates -180..180
|
|
156
163
|
lla.alt = 200.0 # no range constraint
|
|
157
164
|
lla.lat = 91.0 # => ArgumentError
|
|
158
165
|
|
|
159
|
-
utm =
|
|
166
|
+
utm = Coordinate::UTM.new(easting: 500000.0, northing: 5000000.0, zone: 10, hemisphere: 'N')
|
|
160
167
|
utm.zone = 15 # validates 1..60
|
|
161
168
|
utm.hemisphere = 'S' # validates 'N' or 'S'
|
|
162
169
|
utm.easting = -1.0 # => ArgumentError
|
|
163
170
|
|
|
164
171
|
# UPS cross-validates hemisphere/zone combinations
|
|
165
|
-
ups =
|
|
172
|
+
ups = Coordinate::UPS.new(hemisphere: 'N', zone: 'Y')
|
|
166
173
|
ups.zone = 'Z' # valid for hemisphere 'N'
|
|
167
174
|
ups.zone = 'A' # => ArgumentError (rolls back)
|
|
168
175
|
|
|
169
176
|
# BNG auto-updates grid_ref when easting/northing change
|
|
170
|
-
bng =
|
|
177
|
+
bng = Coordinate::BNG.new(easting: 530000, northing: 180000)
|
|
171
178
|
bng.easting = 430000 # grid_ref automatically recalculated
|
|
172
179
|
```
|
|
173
180
|
|
|
@@ -176,10 +183,10 @@ ECEF, ENU, NED, and WebMercator setters coerce to float with no range constraint
|
|
|
176
183
|
### DMS (Degrees, Minutes, Seconds)
|
|
177
184
|
|
|
178
185
|
```ruby
|
|
179
|
-
lla =
|
|
186
|
+
lla = Coordinate::LLA.new(lat: 37.7749, lng: -122.4192, alt: 15.0)
|
|
180
187
|
lla.to_dms # => "37° 46' 29.64\" N, 122° 25' 9.12\" W, 15.00 m"
|
|
181
188
|
|
|
182
|
-
|
|
189
|
+
Coordinate::LLA.from_dms("37° 46' 29.64\" N, 122° 25' 9.12\" W, 15.00 m")
|
|
183
190
|
```
|
|
184
191
|
|
|
185
192
|
### String-Based Coordinate Systems
|
|
@@ -187,12 +194,12 @@ Coordinates::LLA.from_dms("37° 46' 29.64\" N, 122° 25' 9.12\" W, 15.00 m")
|
|
|
187
194
|
MGRS and USNG use string representations:
|
|
188
195
|
|
|
189
196
|
```ruby
|
|
190
|
-
mgrs =
|
|
191
|
-
mgrs =
|
|
197
|
+
mgrs = Coordinate::MGRS.new(mgrs_string: "18SUJ2337006519")
|
|
198
|
+
mgrs = Coordinate::MGRS.from_string("18SUJ2337006519")
|
|
192
199
|
mgrs.to_s # => "18SUJ2337006519"
|
|
193
200
|
|
|
194
|
-
usng =
|
|
195
|
-
usng =
|
|
201
|
+
usng = Coordinate::USNG.new(usng_string: "18T WL 12345 67890")
|
|
202
|
+
usng = Coordinate::USNG.from_string("18T WL 12345 67890")
|
|
196
203
|
usng.to_s # => "18T WL 12345 67890"
|
|
197
204
|
```
|
|
198
205
|
|
|
@@ -208,9 +215,9 @@ portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
|
|
|
208
215
|
sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0.0)
|
|
209
216
|
|
|
210
217
|
d = seattle.distance_to(portland) # => Distance (meters)
|
|
211
|
-
d.meters # =>
|
|
218
|
+
d.meters # => 235385.71
|
|
212
219
|
d.to_km.to_f # => 235.39
|
|
213
|
-
d.to_mi.to_f # => 146.
|
|
220
|
+
d.to_mi.to_f # => 146.26
|
|
214
221
|
|
|
215
222
|
seattle.distance_to(portland, sf) # => [Distance, Distance] (radial)
|
|
216
223
|
seattle.distance_to([portland, sf]) # => [Distance, Distance] (radial)
|
|
@@ -252,12 +259,12 @@ seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
|
|
|
252
259
|
portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
|
|
253
260
|
|
|
254
261
|
b = seattle.bearing_to(portland) # => Bearing
|
|
255
|
-
b.degrees # =>
|
|
256
|
-
b.to_radians # => 3.
|
|
262
|
+
b.degrees # => 186.25
|
|
263
|
+
b.to_radians # => 3.25...
|
|
257
264
|
b.to_compass # => "S"
|
|
258
265
|
b.to_compass(points: 8) # => "S"
|
|
259
266
|
b.reverse # => Bearing (back azimuth)
|
|
260
|
-
b.to_s # => "
|
|
267
|
+
b.to_s # => "186.2539°"
|
|
261
268
|
```
|
|
262
269
|
|
|
263
270
|
**Instance method `elevation_to`** — vertical look angle:
|
|
@@ -403,7 +410,7 @@ geoid.convert_vertical_datum(47.6205, -122.3493, 184.0, "HAE", "NAVD88")
|
|
|
403
410
|
The `GeoidHeightSupport` module is mixed into LLA for convenience:
|
|
404
411
|
|
|
405
412
|
```ruby
|
|
406
|
-
lla =
|
|
413
|
+
lla = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
|
|
407
414
|
lla.geoid_height # => geoid undulation in meters
|
|
408
415
|
lla.orthometric_height # => height above mean sea level
|
|
409
416
|
```
|
|
@@ -414,10 +421,10 @@ A spatial hashing coordinate that encodes lat/lng into a compact, URL-friendly s
|
|
|
414
421
|
|
|
415
422
|
```ruby
|
|
416
423
|
# From a geohash string
|
|
417
|
-
gh36 =
|
|
424
|
+
gh36 = Coordinate::GH36.new("bdrdC26BqH")
|
|
418
425
|
|
|
419
426
|
# From any coordinate
|
|
420
|
-
gh36 =
|
|
427
|
+
gh36 = Coordinate::GH36.new(lla)
|
|
421
428
|
gh36 = lla.to_gh36(precision: 8)
|
|
422
429
|
|
|
423
430
|
# Decode back to LLA
|
|
@@ -441,10 +448,10 @@ The standard Geohash (base-32) algorithm by Gustavo Niemeyer, widely supported b
|
|
|
441
448
|
|
|
442
449
|
```ruby
|
|
443
450
|
# From a geohash string
|
|
444
|
-
gh =
|
|
451
|
+
gh = Coordinate::GH.new("dr5ru7")
|
|
445
452
|
|
|
446
453
|
# From any coordinate
|
|
447
|
-
gh =
|
|
454
|
+
gh = Coordinate::GH.new(lla)
|
|
448
455
|
gh = lla.to_gh(precision: 8)
|
|
449
456
|
|
|
450
457
|
# Decode back to LLA
|
|
@@ -468,10 +475,10 @@ The Maidenhead Locator System used worldwide in amateur radio for grid square id
|
|
|
468
475
|
|
|
469
476
|
```ruby
|
|
470
477
|
# From a Maidenhead locator string
|
|
471
|
-
ham =
|
|
478
|
+
ham = Coordinate::HAM.new("FN31pr")
|
|
472
479
|
|
|
473
480
|
# From any coordinate
|
|
474
|
-
ham =
|
|
481
|
+
ham = Coordinate::HAM.new(lla)
|
|
475
482
|
ham = lla.to_ham(precision: 8)
|
|
476
483
|
|
|
477
484
|
# Decode back to LLA
|
|
@@ -495,10 +502,10 @@ Google's open system for encoding locations into short, URL-friendly codes:
|
|
|
495
502
|
|
|
496
503
|
```ruby
|
|
497
504
|
# From a plus code string
|
|
498
|
-
olc =
|
|
505
|
+
olc = Coordinate::OLC.new("849VCWC8+R9")
|
|
499
506
|
|
|
500
507
|
# From any coordinate
|
|
501
|
-
olc =
|
|
508
|
+
olc = Coordinate::OLC.new(lla)
|
|
502
509
|
olc = lla.to_olc(precision: 11)
|
|
503
510
|
|
|
504
511
|
# Decode back to LLA
|
|
@@ -520,22 +527,22 @@ olc.precision_in_meters # => { lat: 13.9, lng: 13.9 }
|
|
|
520
527
|
|
|
521
528
|
```ruby
|
|
522
529
|
# Circle area
|
|
523
|
-
center =
|
|
530
|
+
center = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
|
|
524
531
|
circle = Areas::Circle.new(centroid: center, radius: 1000.0) # 1km radius
|
|
525
532
|
|
|
526
533
|
# Polygon area
|
|
527
534
|
points = [
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
535
|
+
Coordinate::LLA.new(lat: 47.60, lng: -122.35, alt: 0.0),
|
|
536
|
+
Coordinate::LLA.new(lat: 47.63, lng: -122.35, alt: 0.0),
|
|
537
|
+
Coordinate::LLA.new(lat: 47.63, lng: -122.33, alt: 0.0),
|
|
538
|
+
Coordinate::LLA.new(lat: 47.60, lng: -122.33, alt: 0.0),
|
|
532
539
|
]
|
|
533
540
|
polygon = Areas::Polygon.new(boundary: points)
|
|
534
541
|
polygon.centroid # => computed centroid as LLA
|
|
535
542
|
|
|
536
543
|
# BoundingBox area (accepts any coordinate type)
|
|
537
|
-
nw =
|
|
538
|
-
se =
|
|
544
|
+
nw = Coordinate::LLA.new(lat: 41.0, lng: -75.0)
|
|
545
|
+
se = Coordinate::LLA.new(lat: 40.0, lng: -74.0)
|
|
539
546
|
rect = Areas::BoundingBox.new(nw: nw, se: se)
|
|
540
547
|
rect.centroid # => LLA at center
|
|
541
548
|
rect.ne # => computed NE corner
|
|
@@ -626,18 +633,18 @@ route.select { |c| c.lat > 40.72 }
|
|
|
626
633
|
```ruby
|
|
627
634
|
liberty = Feature.new(
|
|
628
635
|
label: "Statue of Liberty",
|
|
629
|
-
geometry:
|
|
636
|
+
geometry: Coordinate::LLA.new(lat: 40.6892, lng: -74.0445, alt: 0),
|
|
630
637
|
metadata: { category: "monument", year: 1886 }
|
|
631
638
|
)
|
|
632
639
|
|
|
633
640
|
empire = Feature.new(
|
|
634
641
|
label: "Empire State Building",
|
|
635
|
-
geometry:
|
|
642
|
+
geometry: Coordinate::LLA.new(lat: 40.7484, lng: -73.9857, alt: 0),
|
|
636
643
|
metadata: { category: "building", floors: 102 }
|
|
637
644
|
)
|
|
638
645
|
|
|
639
646
|
liberty.distance_to(empire).to_km # => "8.24 km"
|
|
640
|
-
liberty.bearing_to(empire).degrees # => 36.
|
|
647
|
+
liberty.bearing_to(empire).degrees # => 36.95
|
|
641
648
|
|
|
642
649
|
# Area geometries use the centroid for distance/bearing
|
|
643
650
|
park = Feature.new(
|
|
@@ -649,14 +656,118 @@ park.distance_to(liberty).to_km # => "12.47 km"
|
|
|
649
656
|
|
|
650
657
|
All three attributes (`label`, `geometry`, `metadata`) are mutable.
|
|
651
658
|
|
|
659
|
+
### Vectors
|
|
660
|
+
|
|
661
|
+
`Vector` pairs a `Distance` (magnitude) with a `Bearing` (direction) to represent a geodetic displacement. It solves the Vincenty direct problem to compute destination points.
|
|
662
|
+
|
|
663
|
+
```ruby
|
|
664
|
+
v = Geodetic::Vector.new(distance: 10_000, bearing: 90.0)
|
|
665
|
+
v = Geodetic::Vector.new(distance: Distance.km(10), bearing: Bearing.new(90))
|
|
666
|
+
|
|
667
|
+
v.north # => north component in meters
|
|
668
|
+
v.east # => east component in meters
|
|
669
|
+
v.magnitude # => distance in meters
|
|
670
|
+
v.reverse # => same distance, opposite bearing
|
|
671
|
+
v.normalize # => unit vector (1 meter)
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
**Vector arithmetic:**
|
|
675
|
+
|
|
676
|
+
```ruby
|
|
677
|
+
v1 + v2 # => Vector (component-wise addition)
|
|
678
|
+
v1 - v2 # => Vector (component-wise subtraction)
|
|
679
|
+
v * 3 # => Vector (scale distance)
|
|
680
|
+
v / 2 # => Vector (scale distance)
|
|
681
|
+
-v # => Vector (reverse bearing)
|
|
682
|
+
v.dot(v2) # => Float (dot product)
|
|
683
|
+
v.cross(v2) # => Float (2D cross product)
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
**Factory methods:**
|
|
687
|
+
|
|
688
|
+
```ruby
|
|
689
|
+
Vector.from_components(north: 1000, east: 500)
|
|
690
|
+
Vector.from_segment(segment)
|
|
691
|
+
segment.to_vector
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
### Geodetic Arithmetic
|
|
695
|
+
|
|
696
|
+
Operators build geometry from coordinates, vectors, and distances:
|
|
697
|
+
|
|
698
|
+
```ruby
|
|
699
|
+
# Building geometry with +
|
|
700
|
+
p1 + p2 # => Segment
|
|
701
|
+
p1 + p2 + p3 # => Path
|
|
702
|
+
p1 + segment # => Path
|
|
703
|
+
segment + p3 # => Path
|
|
704
|
+
segment + segment # => Path
|
|
705
|
+
p1 + distance # => Circle
|
|
706
|
+
p1 + vector # => Segment (to destination)
|
|
707
|
+
segment + vector # => Path (extend from endpoint)
|
|
708
|
+
vector + segment # => Path (prepend via reverse)
|
|
709
|
+
path + vector # => Path (extend from last point)
|
|
710
|
+
vector + coordinate # => Segment
|
|
711
|
+
distance + coordinate # => Circle
|
|
712
|
+
|
|
713
|
+
# Translation with * or .translate
|
|
714
|
+
p1 * vector # => Coordinate (translated point)
|
|
715
|
+
segment * vector # => Segment (translated endpoints)
|
|
716
|
+
path * vector # => Path (translated waypoints)
|
|
717
|
+
circle * vector # => Circle (translated centroid)
|
|
718
|
+
polygon * vector # => Polygon (translated vertices)
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
### Corridors
|
|
722
|
+
|
|
723
|
+
Convert a path into a polygon corridor of a given width:
|
|
724
|
+
|
|
725
|
+
```ruby
|
|
726
|
+
route = seattle + portland + sf
|
|
727
|
+
corridor = route.to_corridor(width: 1000) # 1km wide polygon
|
|
728
|
+
corridor = route.to_corridor(width: Distance.km(1))
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
### GeoJSON Export
|
|
732
|
+
|
|
733
|
+
`GeoJSON` builds a GeoJSON FeatureCollection from any mix of Geodetic objects and writes it to a file.
|
|
734
|
+
|
|
735
|
+
```ruby
|
|
736
|
+
gj = Geodetic::GeoJSON.new
|
|
737
|
+
gj << seattle
|
|
738
|
+
gj << [portland, sf, la]
|
|
739
|
+
gj << Feature.new(label: "Route", geometry: route, metadata: { mode: "driving" })
|
|
740
|
+
gj << Areas::Circle.new(centroid: seattle, radius: 10_000)
|
|
741
|
+
|
|
742
|
+
gj.size # => 6
|
|
743
|
+
gj.to_h # => {"type" => "FeatureCollection", "features" => [...]}
|
|
744
|
+
gj.to_json # => compact JSON string
|
|
745
|
+
gj.save("map.geojson", pretty: true)
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
Every geometry type has a `to_geojson` method returning a GeoJSON-compatible Hash:
|
|
749
|
+
|
|
750
|
+
```ruby
|
|
751
|
+
seattle.to_geojson # => {"type" => "Point", ...}
|
|
752
|
+
Segment.new(seattle, portland).to_geojson # => {"type" => "LineString", ...}
|
|
753
|
+
route.to_geojson # => {"type" => "LineString", ...}
|
|
754
|
+
route.to_geojson(as: :polygon) # => {"type" => "Polygon", ...}
|
|
755
|
+
polygon.to_geojson # => {"type" => "Polygon", ...}
|
|
756
|
+
circle.to_geojson(segments: 64) # => {"type" => "Polygon", ...} (64-gon)
|
|
757
|
+
bbox.to_geojson # => {"type" => "Polygon", ...}
|
|
758
|
+
feature.to_geojson # => {"type" => "Feature", ...}
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
Features carry their `label` as `"name"` and `metadata` as `properties` in the GeoJSON output. Non-Feature objects added to the collection are auto-wrapped as Features with empty properties.
|
|
762
|
+
|
|
652
763
|
### Web Mercator Tile Coordinates
|
|
653
764
|
|
|
654
765
|
```ruby
|
|
655
|
-
wm =
|
|
766
|
+
wm = Coordinate::WebMercator.from_lla(lla)
|
|
656
767
|
wm.to_tile_coordinates(15) # => [x_tile, y_tile, zoom]
|
|
657
768
|
wm.to_pixel_coordinates(15) # => [x_pixel, y_pixel, zoom]
|
|
658
769
|
|
|
659
|
-
|
|
770
|
+
Coordinate::WebMercator.from_tile_coordinates(5241, 11438, 15)
|
|
660
771
|
```
|
|
661
772
|
|
|
662
773
|
## Available Datums
|
|
@@ -679,6 +790,9 @@ The [`examples/`](examples/) directory contains runnable demo scripts showing pr
|
|
|
679
790
|
| [`04_bearing_calculations.rb`](examples/04_bearing_calculations.rb) | Bearing class, compass directions, elevation angles, and chain bearings |
|
|
680
791
|
| [`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) |
|
|
681
792
|
| [`06_path_operations.rb`](examples/06_path_operations.rb) | Path class: construction, navigation, mutation, path arithmetic, closest approach, containment, Enumerable, equality, subpaths, split, interpolation, bounding boxes, polygon conversion, intersection, path-to-path/area closest points, and Feature integration |
|
|
793
|
+
| [`07_segments_and_shapes.rb`](examples/07_segments_and_shapes.rb) | Segment and polygon subclasses: Triangle, Rectangle, Pentagon, Hexagon, Octagon with containment, edges, and bounding boxes |
|
|
794
|
+
| [`08_geodetic_arithmetic.rb`](examples/08_geodetic_arithmetic.rb) | Geodetic arithmetic: building geometry with + (Segments, Paths, Circles), Vector class (Vincenty direct, components, arithmetic, dot/cross products), translation with * (Coordinates, Segments, Paths, Circles, Polygons), and corridors |
|
|
795
|
+
| [`09_geojson_export.rb`](examples/09_geojson_export.rb) | GeoJSON export: `to_geojson` on all geometry types, `GeoJSON` class for building FeatureCollections with `<<`, delete/clear, Enumerable, and `save` to file |
|
|
682
796
|
|
|
683
797
|
Run any example with:
|
|
684
798
|
|
|
@@ -6,54 +6,54 @@ The Geodetic gem supports 18 coordinate systems organized into six categories. A
|
|
|
6
6
|
|
|
7
7
|
| System | Class | Description |
|
|
8
8
|
|--------|-------|-------------|
|
|
9
|
-
| **LLA** | `Geodetic::Coordinate::LLA` | Latitude, Longitude, Altitude. The most common geographic coordinate system, expressing positions in decimal degrees with altitude in meters. Negative longitude is the Western hemisphere; negative latitude is the Southern hemisphere. |
|
|
10
|
-
| **ECEF** | `Geodetic::Coordinate::ECEF` | Earth-Centered, Earth-Fixed. A Cartesian coordinate system with the origin at the Earth's center of mass. Positions are expressed as X, Y, Z in meters. Commonly used in satellite navigation and aerospace applications. |
|
|
11
|
-
| **UTM** | `Geodetic::Coordinate::UTM` | Universal Transverse Mercator. Divides the Earth into 60 zones (each 6 degrees of longitude), projecting positions as easting/northing in meters within a zone and hemisphere. Covers latitudes 80S to 84N. |
|
|
9
|
+
| [**LLA**](lla.md) | `Geodetic::Coordinate::LLA` | Latitude, Longitude, Altitude. The most common geographic coordinate system, expressing positions in decimal degrees with altitude in meters. Negative longitude is the Western hemisphere; negative latitude is the Southern hemisphere. |
|
|
10
|
+
| [**ECEF**](ecef.md) | `Geodetic::Coordinate::ECEF` | Earth-Centered, Earth-Fixed. A Cartesian coordinate system with the origin at the Earth's center of mass. Positions are expressed as X, Y, Z in meters. Commonly used in satellite navigation and aerospace applications. |
|
|
11
|
+
| [**UTM**](utm.md) | `Geodetic::Coordinate::UTM` | Universal Transverse Mercator. Divides the Earth into 60 zones (each 6 degrees of longitude), projecting positions as easting/northing in meters within a zone and hemisphere. Covers latitudes 80S to 84N. |
|
|
12
12
|
|
|
13
13
|
## Local Tangent Plane Systems
|
|
14
14
|
|
|
15
15
|
| System | Class | Description |
|
|
16
16
|
|--------|-------|-------------|
|
|
17
|
-
| **ENU** | `Geodetic::Coordinate::ENU` | East, North, Up. A local tangent plane coordinate system centered on a reference point. Axes point East, North, and Up relative to the reference. Distances are in meters. Used in robotics, surveying, and local navigation. |
|
|
18
|
-
| **NED** | `Geodetic::Coordinate::NED` | North, East, Down. A local tangent plane coordinate system centered on a reference point. Axes point North, East, and Down. Used extensively in aerospace and aviation applications. Mathematically related to ENU by axis reordering and sign inversion. |
|
|
17
|
+
| [**ENU**](enu.md) | `Geodetic::Coordinate::ENU` | East, North, Up. A local tangent plane coordinate system centered on a reference point. Axes point East, North, and Up relative to the reference. Distances are in meters. Used in robotics, surveying, and local navigation. |
|
|
18
|
+
| [**NED**](ned.md) | `Geodetic::Coordinate::NED` | North, East, Down. A local tangent plane coordinate system centered on a reference point. Axes point North, East, and Down. Used extensively in aerospace and aviation applications. Mathematically related to ENU by axis reordering and sign inversion. |
|
|
19
19
|
|
|
20
20
|
## Military and Grid Systems
|
|
21
21
|
|
|
22
22
|
| System | Class | Description |
|
|
23
23
|
|--------|-------|-------------|
|
|
24
|
-
| **MGRS** | `Geodetic::Coordinate::MGRS` | Military Grid Reference System. An alphanumeric system based on UTM that identifies positions using grid zone designator, 100km square identifier, and numeric easting/northing. Variable precision from 10km down to 1m. |
|
|
25
|
-
| **USNG** | `Geodetic::Coordinate::USNG` | United States National Grid. Based on MGRS but formatted with spaces for readability. Used primarily within the United States for emergency services and land management. |
|
|
24
|
+
| [**MGRS**](mgrs.md) | `Geodetic::Coordinate::MGRS` | Military Grid Reference System. An alphanumeric system based on UTM that identifies positions using grid zone designator, 100km square identifier, and numeric easting/northing. Variable precision from 10km down to 1m. |
|
|
25
|
+
| [**USNG**](usng.md) | `Geodetic::Coordinate::USNG` | United States National Grid. Based on MGRS but formatted with spaces for readability. Used primarily within the United States for emergency services and land management. |
|
|
26
26
|
|
|
27
27
|
## Web Mapping
|
|
28
28
|
|
|
29
29
|
| System | Class | Description |
|
|
30
30
|
|--------|-------|-------------|
|
|
31
|
-
| **WebMercator** | `Geodetic::Coordinate::WebMercator` | Web Mercator (EPSG:3857). Also known as Pseudo-Mercator or Spherical Mercator. The projection used by Google Maps, OpenStreetMap, and Bing Maps. Positions are X/Y in meters. Latitude is clamped to approximately +/-85.05 degrees. Includes tile and pixel coordinate methods for web mapping applications. |
|
|
31
|
+
| [**WebMercator**](web-mercator.md) | `Geodetic::Coordinate::WebMercator` | Web Mercator (EPSG:3857). Also known as Pseudo-Mercator or Spherical Mercator. The projection used by Google Maps, OpenStreetMap, and Bing Maps. Positions are X/Y in meters. Latitude is clamped to approximately +/-85.05 degrees. Includes tile and pixel coordinate methods for web mapping applications. |
|
|
32
32
|
|
|
33
33
|
## Polar
|
|
34
34
|
|
|
35
35
|
| System | Class | Description |
|
|
36
36
|
|--------|-------|-------------|
|
|
37
|
-
| **UPS** | `Geodetic::Coordinate::UPS` | Universal Polar Stereographic. Covers the polar regions not handled by UTM (north of 84N and south of 80S). Uses a stereographic projection centered on each pole with zones Y/Z (north) and A/B (south). |
|
|
37
|
+
| [**UPS**](ups.md) | `Geodetic::Coordinate::UPS` | Universal Polar Stereographic. Covers the polar regions not handled by UTM (north of 84N and south of 80S). Uses a stereographic projection centered on each pole with zones Y/Z (north) and A/B (south). |
|
|
38
38
|
|
|
39
39
|
## Spatial Hashing
|
|
40
40
|
|
|
41
41
|
| System | Class | Description |
|
|
42
42
|
|--------|-------|-------------|
|
|
43
|
-
| **GH36** | `Geodetic::Coordinate::GH36` | Geohash-36. A hierarchical spatial hashing algorithm that encodes latitude/longitude into a compact, URL-friendly string using a case-sensitive 36-character alphabet (radix-36). Each hash represents a rectangular cell; the coordinate value is the cell midpoint. Supports neighbor lookup, area extraction via `to_area`, and configurable precision (default 10 characters for sub-meter resolution). |
|
|
44
|
-
| **GH** | `Geodetic::Coordinate::GH` | Geohash (base-32). The standard geohash algorithm by Gustavo Niemeyer using a 32-character alphabet (`0-9, b-z` excluding `a, i, l, o`). The de facto standard for spatial hashing, natively supported by Elasticsearch, Redis, PostGIS, and many geocoding services. Supports neighbor lookup, area extraction, and configurable precision (default 12 characters for sub-centimeter resolution). |
|
|
45
|
-
| **HAM** | `Geodetic::Coordinate::HAM` | Maidenhead Locator System. A hierarchical grid system used worldwide in amateur radio that encodes positions using alternating letter/digit pairs (e.g., `FN31pr`). Four levels of precision: Field (18x18), Square (10x10), Subsquare (24x24), Extended (10x10). Supports neighbor lookup, area extraction, and configurable precision (default 6 characters for ~5 km resolution). |
|
|
46
|
-
| **OLC** | `Geodetic::Coordinate::OLC` | Open Location Code (Plus Codes). Google's open system for encoding locations into short codes like `849VCWC8+R9`. Uses a 20-character alphabet with 5 paired levels of base-20 encoding plus optional grid refinement. Includes a `+` separator at position 8. Supports neighbor lookup, area extraction, and configurable precision (default 10 characters for ~14 m resolution). |
|
|
47
|
-
| **GEOREF** | `Geodetic::Coordinate::GEOREF` | World Geographic Reference System. A geocode system used in aviation and military applications that encodes positions using letter tiles (15° grid), letter degree subdivisions, and numeric minute pairs. Uses a 24-letter alphabet (A-Z excluding I and O). Supports variable precision from 15° tiles (2 chars) down to 0.01-minute resolution (12 chars). Default precision is 8 characters (1-minute resolution). |
|
|
48
|
-
| **GARS** | `Geodetic::Coordinate::GARS` | Global Area Reference System. An NGA standard that divides the world into 30-minute cells identified by a 3-digit longitude band (001-720) and 2-letter latitude band. Cells are subdivided into 15-minute quadrants (1-4) and 5-minute keypads (1-9, telephone layout). Variable precision: 5 chars (30'), 6 chars (15'), 7 chars (5'). Default precision is 7 characters. |
|
|
49
|
-
| **H3** | `Geodetic::Coordinate::H3` | H3 Hexagonal Hierarchical Index. Uber's spatial indexing system that divides the globe into hexagonal cells (and 12 pentagons) at 16 resolution levels (0-15). Each cell is a 64-bit integer displayed as a hex string. Unlike the rectangular spatial hashes, `to_area` returns an `Areas::Polygon` with 6 vertices (5 for pentagons) and `neighbors` returns an Array of 6 cells. **Requires `libh3` installed** (`brew install h3` on macOS). |
|
|
43
|
+
| [**GH36**](gh36.md) | `Geodetic::Coordinate::GH36` | Geohash-36. A hierarchical spatial hashing algorithm that encodes latitude/longitude into a compact, URL-friendly string using a case-sensitive 36-character alphabet (radix-36). Each hash represents a rectangular cell; the coordinate value is the cell midpoint. Supports neighbor lookup, area extraction via `to_area`, and configurable precision (default 10 characters for sub-meter resolution). |
|
|
44
|
+
| [**GH**](gh.md) | `Geodetic::Coordinate::GH` | Geohash (base-32). The standard geohash algorithm by Gustavo Niemeyer using a 32-character alphabet (`0-9, b-z` excluding `a, i, l, o`). The de facto standard for spatial hashing, natively supported by Elasticsearch, Redis, PostGIS, and many geocoding services. Supports neighbor lookup, area extraction, and configurable precision (default 12 characters for sub-centimeter resolution). |
|
|
45
|
+
| [**HAM**](ham.md) | `Geodetic::Coordinate::HAM` | Maidenhead Locator System. A hierarchical grid system used worldwide in amateur radio that encodes positions using alternating letter/digit pairs (e.g., `FN31pr`). Four levels of precision: Field (18x18), Square (10x10), Subsquare (24x24), Extended (10x10). Supports neighbor lookup, area extraction, and configurable precision (default 6 characters for ~5 km resolution). |
|
|
46
|
+
| [**OLC**](olc.md) | `Geodetic::Coordinate::OLC` | Open Location Code (Plus Codes). Google's open system for encoding locations into short codes like `849VCWC8+R9`. Uses a 20-character alphabet with 5 paired levels of base-20 encoding plus optional grid refinement. Includes a `+` separator at position 8. Supports neighbor lookup, area extraction, and configurable precision (default 10 characters for ~14 m resolution). |
|
|
47
|
+
| [**GEOREF**](georef.md) | `Geodetic::Coordinate::GEOREF` | World Geographic Reference System. A geocode system used in aviation and military applications that encodes positions using letter tiles (15° grid), letter degree subdivisions, and numeric minute pairs. Uses a 24-letter alphabet (A-Z excluding I and O). Supports variable precision from 15° tiles (2 chars) down to 0.01-minute resolution (12 chars). Default precision is 8 characters (1-minute resolution). |
|
|
48
|
+
| [**GARS**](gars.md) | `Geodetic::Coordinate::GARS` | Global Area Reference System. An NGA standard that divides the world into 30-minute cells identified by a 3-digit longitude band (001-720) and 2-letter latitude band. Cells are subdivided into 15-minute quadrants (1-4) and 5-minute keypads (1-9, telephone layout). Variable precision: 5 chars (30'), 6 chars (15'), 7 chars (5'). Default precision is 7 characters. |
|
|
49
|
+
| [**H3**](h3.md) | `Geodetic::Coordinate::H3` | H3 Hexagonal Hierarchical Index. Uber's spatial indexing system that divides the globe into hexagonal cells (and 12 pentagons) at 16 resolution levels (0-15). Each cell is a 64-bit integer displayed as a hex string. Unlike the rectangular spatial hashes, `to_area` returns an `Areas::Polygon` with 6 vertices (5 for pentagons) and `neighbors` returns an Array of 6 cells. **Requires `libh3` installed** (`brew install h3` on macOS). |
|
|
50
50
|
|
|
51
51
|
## Regional Systems
|
|
52
52
|
|
|
53
53
|
| System | Class | Description |
|
|
54
54
|
|--------|-------|-------------|
|
|
55
|
-
| **StatePlane** | `Geodetic::Coordinate::StatePlane` | State Plane Coordinate System. US state-based coordinate systems using Lambert Conformal Conic or Transverse Mercator projections. Each state has one or more zones with specific parameters. Coordinates are typically in US Survey Feet. |
|
|
56
|
-
| **BNG** | `Geodetic::Coordinate::BNG` | British National Grid. The official coordinate system for Great Britain, based on the OSGB36 datum with the Airy 1830 ellipsoid. Uses a Transverse Mercator projection and an alphanumeric grid reference system (e.g., "TQ 30 80"). |
|
|
55
|
+
| [**StatePlane**](state-plane.md) | `Geodetic::Coordinate::StatePlane` | State Plane Coordinate System. US state-based coordinate systems using Lambert Conformal Conic or Transverse Mercator projections. Each state has one or more zones with specific parameters. Coordinates are typically in US Survey Feet. |
|
|
56
|
+
| [**BNG**](bng.md) | `Geodetic::Coordinate::BNG` | British National Grid. The official coordinate system for Great Britain, based on the OSGB36 datum with the Airy 1830 ellipsoid. Uses a Transverse Mercator projection and an alphanumeric grid reference system (e.g., "TQ 30 80"). |
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|