geodetic 0.0.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 +7 -0
- data/.envrc +1 -0
- data/.github/workflows/deploy-github-pages.yml +52 -0
- data/CHANGELOG.md +15 -0
- data/COMMITS.md +196 -0
- data/LICENSE.txt +21 -0
- data/README.md +471 -0
- data/Rakefile +8 -0
- data/docs/coordinate-systems/bng.md +60 -0
- data/docs/coordinate-systems/ecef.md +215 -0
- data/docs/coordinate-systems/enu.md +77 -0
- data/docs/coordinate-systems/gh36.md +192 -0
- data/docs/coordinate-systems/index.md +93 -0
- data/docs/coordinate-systems/lla.md +304 -0
- data/docs/coordinate-systems/mgrs.md +81 -0
- data/docs/coordinate-systems/ned.md +83 -0
- data/docs/coordinate-systems/state-plane.md +60 -0
- data/docs/coordinate-systems/ups.md +53 -0
- data/docs/coordinate-systems/usng.md +74 -0
- data/docs/coordinate-systems/utm.md +257 -0
- data/docs/coordinate-systems/web-mercator.md +67 -0
- data/docs/getting-started/installation.md +65 -0
- data/docs/getting-started/quick-start.md +175 -0
- data/docs/index.md +58 -0
- data/docs/reference/areas.md +195 -0
- data/docs/reference/conversions.md +351 -0
- data/docs/reference/datums.md +134 -0
- data/docs/reference/geoid-height.md +182 -0
- data/docs/reference/serialization.md +252 -0
- data/examples/01_basic_conversions.rb +187 -0
- data/examples/02_all_coordinate_systems.rb +310 -0
- data/examples/03_distance_calculations.rb +224 -0
- data/examples/04_bearing_calculations.rb +236 -0
- data/lib/geodetic/areas/circle.rb +29 -0
- data/lib/geodetic/areas/polygon.rb +57 -0
- data/lib/geodetic/areas/rectangle.rb +55 -0
- data/lib/geodetic/areas.rb +5 -0
- data/lib/geodetic/bearing.rb +94 -0
- data/lib/geodetic/coordinates/bng.rb +366 -0
- data/lib/geodetic/coordinates/ecef.rb +229 -0
- data/lib/geodetic/coordinates/enu.rb +244 -0
- data/lib/geodetic/coordinates/gh36.rb +384 -0
- data/lib/geodetic/coordinates/lla.rb +268 -0
- data/lib/geodetic/coordinates/mgrs.rb +317 -0
- data/lib/geodetic/coordinates/ned.rb +246 -0
- data/lib/geodetic/coordinates/state_plane.rb +451 -0
- data/lib/geodetic/coordinates/ups.rb +325 -0
- data/lib/geodetic/coordinates/usng.rb +274 -0
- data/lib/geodetic/coordinates/utm.rb +261 -0
- data/lib/geodetic/coordinates/web_mercator.rb +242 -0
- data/lib/geodetic/coordinates.rb +260 -0
- data/lib/geodetic/datum.rb +62 -0
- data/lib/geodetic/distance.rb +146 -0
- data/lib/geodetic/geoid_height.rb +299 -0
- data/lib/geodetic/version.rb +5 -0
- data/lib/geodetic.rb +13 -0
- data/mkdocs.yml +140 -0
- data/sig/geodetic.rbs +4 -0
- metadata +104 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Geodetic::Coordinates::ECEF
|
|
2
|
+
|
|
3
|
+
Earth-Centered, Earth-Fixed -- a Cartesian coordinate system with its origin at the center of mass of the Earth. The X axis points toward the intersection of the Prime Meridian and the Equator, the Y axis points toward 90 degrees East longitude on the Equator, and the Z axis points toward the North Pole. All values are in meters.
|
|
4
|
+
|
|
5
|
+
ECEF is useful for satellite positioning, radar tracking, and any application requiring a simple Cartesian frame tied to the rotating Earth.
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
Geodetic::Coordinates::ECEF.new(x: 0.0, y: 0.0, z: 0.0)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Default | Description |
|
|
14
|
+
|-----------|-------|---------|---------------------------------|
|
|
15
|
+
| `x` | Float | `0.0` | X coordinate in meters |
|
|
16
|
+
| `y` | Float | `0.0` | Y coordinate in meters |
|
|
17
|
+
| `z` | Float | `0.0` | Z coordinate in meters |
|
|
18
|
+
|
|
19
|
+
All values are coerced to `Float` via `.to_f`. There is no range validation -- any real-valued triple is accepted.
|
|
20
|
+
|
|
21
|
+
## Attributes
|
|
22
|
+
|
|
23
|
+
| Attribute | Access |
|
|
24
|
+
|-----------|------------|
|
|
25
|
+
| `x` | read/write |
|
|
26
|
+
| `y` | read/write |
|
|
27
|
+
| `z` | read/write |
|
|
28
|
+
|
|
29
|
+
## Conversions
|
|
30
|
+
|
|
31
|
+
All conversion methods accept an optional `datum` parameter (defaults to `Geodetic::WGS84`).
|
|
32
|
+
|
|
33
|
+
### to_lla(datum = WGS84)
|
|
34
|
+
|
|
35
|
+
Converts to geodetic Latitude, Longitude, Altitude coordinates using an iterative algorithm. The iteration converges when both latitude and altitude changes are below `1e-12`, with a maximum of 100 iterations.
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
ecef = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
39
|
+
lla = ecef.to_lla
|
|
40
|
+
# => Geodetic::Coordinates::LLA
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### ECEF.from_lla(lla, datum = WGS84)
|
|
44
|
+
|
|
45
|
+
Creates an ECEF from an LLA instance. Raises `ArgumentError` if the argument is not an `LLA`.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
ecef = Geodetic::Coordinates::ECEF.from_lla(lla)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### to_utm(datum = WGS84)
|
|
52
|
+
|
|
53
|
+
Converts to Universal Transverse Mercator coordinates. Internally converts to LLA first, then to UTM.
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
utm = ecef.to_utm
|
|
57
|
+
# => Geodetic::Coordinates::UTM
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### ECEF.from_utm(utm, datum = WGS84)
|
|
61
|
+
|
|
62
|
+
Creates an ECEF from a UTM instance. Raises `ArgumentError` if the argument is not a `UTM`.
|
|
63
|
+
|
|
64
|
+
### to_enu(reference_ecef, reference_lla = nil)
|
|
65
|
+
|
|
66
|
+
Converts to East-North-Up local tangent plane coordinates relative to a reference ECEF position. If `reference_lla` is not provided, it is computed from `reference_ecef` via `to_lla`.
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
ref_ecef = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
70
|
+
point_ecef = Geodetic::Coordinates::ECEF.new(x: 1130740.0, y: -4828573.0, z: 3991580.0)
|
|
71
|
+
enu = point_ecef.to_enu(ref_ecef)
|
|
72
|
+
# => Geodetic::Coordinates::ENU
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Raises `ArgumentError` if `reference_ecef` is not an `ECEF`.
|
|
76
|
+
|
|
77
|
+
### ECEF.from_enu(enu, reference_ecef, reference_lla = nil)
|
|
78
|
+
|
|
79
|
+
Creates an ECEF from an ENU instance and a reference ECEF origin. Raises `ArgumentError` if the arguments are not the expected types.
|
|
80
|
+
|
|
81
|
+
### to_ned(reference_ecef, reference_lla = nil)
|
|
82
|
+
|
|
83
|
+
Converts to North-East-Down local tangent plane coordinates. Internally converts to ENU first, then swaps axes to NED.
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
ned = point_ecef.to_ned(ref_ecef)
|
|
87
|
+
# => Geodetic::Coordinates::NED
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### ECEF.from_ned(ned, reference_ecef, reference_lla = nil)
|
|
91
|
+
|
|
92
|
+
Creates an ECEF from a NED instance and a reference ECEF origin. Raises `ArgumentError` if the arguments are not the expected types.
|
|
93
|
+
|
|
94
|
+
## Serialization
|
|
95
|
+
|
|
96
|
+
### to_s
|
|
97
|
+
|
|
98
|
+
Returns a comma-separated string of `x, y, z`.
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
ecef = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
102
|
+
ecef.to_s
|
|
103
|
+
# => "1130730.0, -4828583.0, 3991570.0"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### to_a
|
|
107
|
+
|
|
108
|
+
Returns a three-element array `[x, y, z]`.
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
ecef.to_a
|
|
112
|
+
# => [1130730.0, -4828583.0, 3991570.0]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### ECEF.from_string(string)
|
|
116
|
+
|
|
117
|
+
Parses a comma-separated string into an ECEF.
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
ecef = Geodetic::Coordinates::ECEF.from_string("1130730.0, -4828583.0, 3991570.0")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### ECEF.from_array(array)
|
|
124
|
+
|
|
125
|
+
Creates an ECEF from a three-element array `[x, y, z]`.
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
ecef = Geodetic::Coordinates::ECEF.from_array([1130730.0, -4828583.0, 3991570.0])
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Additional Methods
|
|
132
|
+
|
|
133
|
+
### ==(other)
|
|
134
|
+
|
|
135
|
+
Compares two ECEF instances for approximate equality. Returns `true` if the absolute difference for each of `x`, `y`, and `z` is `<= 1e-6` meters. Returns `false` if `other` is not an `ECEF`.
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
a = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
139
|
+
b = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
140
|
+
a == b
|
|
141
|
+
# => true
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### distance_to(other, *others)
|
|
145
|
+
|
|
146
|
+
Computes the Vincenty great-circle distance to one or more other coordinates. Accepts any coordinate type (coordinates are converted to LLA internally). Returns a `Distance` for a single target or an Array of `Distance` objects for multiple targets (radial distances from the receiver).
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
a = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
150
|
+
b = Geodetic::Coordinates::ECEF.new(x: 1130740.0, y: -4828573.0, z: 3991580.0)
|
|
151
|
+
a.distance_to(b)
|
|
152
|
+
# => Distance (meters, great-circle distance)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### straight_line_distance_to(other, *others)
|
|
156
|
+
|
|
157
|
+
Computes the Euclidean (straight-line) distance between two points in ECEF space. Accepts any coordinate type (coordinates are converted to ECEF internally). Returns a `Distance` for a single target or an Array of `Distance` objects for multiple targets.
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
a.straight_line_distance_to(b)
|
|
161
|
+
# => Distance (17.320508075688775 m)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Code Examples
|
|
165
|
+
|
|
166
|
+
### Round-trip conversion
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
require 'geodetic'
|
|
170
|
+
|
|
171
|
+
# Create an ECEF coordinate
|
|
172
|
+
ecef = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
173
|
+
|
|
174
|
+
# Convert to LLA and back
|
|
175
|
+
lla = ecef.to_lla
|
|
176
|
+
ecef_roundtrip = lla.to_ecef
|
|
177
|
+
ecef == ecef_roundtrip
|
|
178
|
+
# => true
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Distance between two points
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
station_a = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
185
|
+
station_b = Geodetic::Coordinates::ECEF.new(x: 1131000.0, y: -4828300.0, z: 3991800.0)
|
|
186
|
+
|
|
187
|
+
# Great-circle distance (Vincenty)
|
|
188
|
+
distance = station_a.distance_to(station_b)
|
|
189
|
+
puts "Great-circle distance: #{distance.meters} meters"
|
|
190
|
+
|
|
191
|
+
# Straight-line (Euclidean) distance
|
|
192
|
+
straight = station_a.straight_line_distance_to(station_b)
|
|
193
|
+
puts "Straight-line distance: #{straight.meters} meters"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Local tangent plane from ECEF
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
origin = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
200
|
+
target = Geodetic::Coordinates::ECEF.new(x: 1130740.0, y: -4828573.0, z: 3991580.0)
|
|
201
|
+
|
|
202
|
+
# Provide reference LLA to avoid recomputing it
|
|
203
|
+
ref_lla = origin.to_lla
|
|
204
|
+
|
|
205
|
+
enu = target.to_enu(origin, ref_lla)
|
|
206
|
+
ned = target.to_ned(origin, ref_lla)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Using a non-default datum
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
clarke66 = Geodetic::Datum.new(name: 'CLARKE_1866')
|
|
213
|
+
ecef = Geodetic::Coordinates::ECEF.new(x: 1130730.0, y: -4828583.0, z: 3991570.0)
|
|
214
|
+
lla = ecef.to_lla(clarke66)
|
|
215
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Geodetic::Coordinates::ENU - East, North, Up
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
ENU (East, North, Up) is a local tangent plane coordinate system. It defines positions relative to a local reference point on the Earth's surface, with axes pointing East, North, and Up.
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
ENU.new(e: 0.0, n: 0.0, u: 0.0)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
All parameters are in **meters**.
|
|
14
|
+
|
|
15
|
+
## Attribute Aliases
|
|
16
|
+
|
|
17
|
+
| Primary | Alias |
|
|
18
|
+
|---------|---------|
|
|
19
|
+
| `e` | `east` |
|
|
20
|
+
| `n` | `north` |
|
|
21
|
+
| `u` | `up` |
|
|
22
|
+
|
|
23
|
+
## Reference Point
|
|
24
|
+
|
|
25
|
+
ENU is a local coordinate system. Conversions to and from global coordinate systems (such as LLA or ECEF) require a **reference point** specified as an LLA coordinate. This reference point defines the origin of the local tangent plane.
|
|
26
|
+
|
|
27
|
+
The one exception is the conversion between ENU and NED, which does not require a reference point.
|
|
28
|
+
|
|
29
|
+
## Conversions
|
|
30
|
+
|
|
31
|
+
### Direct (no reference point needed)
|
|
32
|
+
|
|
33
|
+
- **ENU <-> NED** — A simple axis remap: `ENU(e, n, u)` corresponds to `NED(n, e, -u)`.
|
|
34
|
+
|
|
35
|
+
### Via reference point
|
|
36
|
+
|
|
37
|
+
- **ENU -> LLA** — Requires a reference LLA.
|
|
38
|
+
- **ENU -> ECEF** — Requires a reference LLA.
|
|
39
|
+
|
|
40
|
+
## Methods
|
|
41
|
+
|
|
42
|
+
| Method | Description |
|
|
43
|
+
|---------------------------------|--------------------------------------------------------------------|
|
|
44
|
+
| `horizontal_distance_to(other)` | Horizontal (E-N plane) distance to another ENU point (meters) |
|
|
45
|
+
| `local_bearing_to(other)` | Bearing from this point to another ENU point (degrees from north, 0-360) |
|
|
46
|
+
| `distance_to_origin` | Euclidean distance from this point to the origin (meters) |
|
|
47
|
+
| `bearing_from_origin` | Bearing from the origin to this point (degrees from north, 0-360) |
|
|
48
|
+
| `horizontal_distance_to_origin` | Horizontal distance from this point to the origin (meters) |
|
|
49
|
+
|
|
50
|
+
### Universal Distance and Bearing Methods
|
|
51
|
+
|
|
52
|
+
ENU is a relative coordinate system. The universal `distance_to`, `straight_line_distance_to`, `bearing_to`, and `elevation_to` methods raise `ArgumentError` because ENU cannot be converted to an absolute system without a reference point. Convert to an absolute system first:
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
ref = Geodetic::Coordinates::LLA.new(lat: 47.62, lng: -122.35, alt: 0.0)
|
|
56
|
+
lla = enu.to_lla(ref)
|
|
57
|
+
lla.distance_to(other_lla) # Vincenty great-circle distance
|
|
58
|
+
lla.bearing_to(other_lla) # Great-circle forward azimuth (Bearing object)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Bearing Convention
|
|
62
|
+
|
|
63
|
+
Bearing is measured in **degrees from north**, clockwise, in the range **0-360**.
|
|
64
|
+
|
|
65
|
+
## Example
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
point = Geodetic::Coordinates::ENU.new(e: 100.0, n: 200.0, u: 50.0)
|
|
69
|
+
|
|
70
|
+
point.east # => 100.0
|
|
71
|
+
point.north # => 200.0
|
|
72
|
+
point.up # => 50.0
|
|
73
|
+
|
|
74
|
+
point.distance_to_origin # => Euclidean distance in meters
|
|
75
|
+
point.bearing_from_origin # => Bearing in degrees from north
|
|
76
|
+
point.horizontal_distance_to_origin # => Horizontal distance in meters
|
|
77
|
+
```
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Geodetic::Coordinates::GH36
|
|
2
|
+
|
|
3
|
+
## Geohash-36
|
|
4
|
+
|
|
5
|
+
Geohash-36 is a hierarchical spatial hashing algorithm that encodes latitude/longitude into a compact, URL-friendly string using a case-sensitive 36-character alphabet. It uses a 6x6 grid subdivision (radix-36) providing higher precision per character than standard Geohash (radix-32).
|
|
6
|
+
|
|
7
|
+
Character set: `23456789bBCdDFgGhHjJKlLMnNPqQrRtTVWX`
|
|
8
|
+
(avoids vowels, vowel-like numbers, and ambiguous characters like 0/O, 1/I/l)
|
|
9
|
+
|
|
10
|
+
GH36 is a **2D coordinate system** (no altitude). Conversions to/from other systems go through LLA as the intermediary. Each geohash string represents a rectangular cell; the coordinate's point value is the cell's midpoint.
|
|
11
|
+
|
|
12
|
+
## Constructor
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# From a geohash string
|
|
16
|
+
coord = Geodetic::Coordinates::GH36.new("bdrdC26BqH")
|
|
17
|
+
|
|
18
|
+
# From any coordinate (converts via LLA)
|
|
19
|
+
coord = Geodetic::Coordinates::GH36.new(lla_coord)
|
|
20
|
+
coord = Geodetic::Coordinates::GH36.new(utm_coord, precision: 8)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
| Parameter | Type | Default | Description |
|
|
24
|
+
|-------------|-------------------|---------|----------------------------------------------|
|
|
25
|
+
| `source` | String or Coord | — | A geohash string or any coordinate object |
|
|
26
|
+
| `precision` | Integer | 10 | Hash length (ignored when source is a String) |
|
|
27
|
+
|
|
28
|
+
Raises `ArgumentError` if the source is an empty string, contains invalid characters, or is not a recognized coordinate type.
|
|
29
|
+
|
|
30
|
+
## Attributes
|
|
31
|
+
|
|
32
|
+
| Attribute | Type | Access | Description |
|
|
33
|
+
|-----------|--------|-----------|---------------------------------|
|
|
34
|
+
| `geohash` | String | read-only | The geohash-36 encoded string |
|
|
35
|
+
|
|
36
|
+
GH36 is **immutable** — there are no setter methods.
|
|
37
|
+
|
|
38
|
+
## Precision
|
|
39
|
+
|
|
40
|
+
The precision (hash length) determines the size of the cell:
|
|
41
|
+
|
|
42
|
+
| Length | Approximate Resolution |
|
|
43
|
+
|--------|----------------------|
|
|
44
|
+
| 5 | ~1.4 km |
|
|
45
|
+
| 8 | ~6.5 m |
|
|
46
|
+
| 10 | ~0.3 m (default) |
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
coord.precision # => 10 (hash length)
|
|
50
|
+
coord.precision_in_meters # => { lat: 0.31, lng: 0.62 }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Longer hashes yield finer precision. Longitude precision is always coarser than latitude (cells are wider than tall).
|
|
54
|
+
|
|
55
|
+
## Conversions
|
|
56
|
+
|
|
57
|
+
All conversions chain through LLA. The datum parameter defaults to `Geodetic::WGS84`.
|
|
58
|
+
|
|
59
|
+
### Instance Methods
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
coord.to_lla # => LLA (midpoint of the cell)
|
|
63
|
+
coord.to_ecef
|
|
64
|
+
coord.to_utm
|
|
65
|
+
coord.to_enu(reference_lla)
|
|
66
|
+
coord.to_ned(reference_lla)
|
|
67
|
+
coord.to_mgrs
|
|
68
|
+
coord.to_usng
|
|
69
|
+
coord.to_web_mercator
|
|
70
|
+
coord.to_ups
|
|
71
|
+
coord.to_state_plane(zone_code)
|
|
72
|
+
coord.to_bng
|
|
73
|
+
coord.to_gh36 # identity (via mixin)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Class Methods
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
GH36.from_lla(lla_coord)
|
|
80
|
+
GH36.from_ecef(ecef_coord)
|
|
81
|
+
GH36.from_utm(utm_coord)
|
|
82
|
+
GH36.from_web_mercator(wm_coord)
|
|
83
|
+
# ... and all other coordinate systems
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### LLA Convenience Methods
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 40.689167, lng: -74.044444)
|
|
90
|
+
gh36 = lla.to_gh36 # default precision 10
|
|
91
|
+
gh36 = lla.to_gh36(precision: 5) # custom precision
|
|
92
|
+
|
|
93
|
+
lla = Geodetic::Coordinates::LLA.from_gh36(gh36)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Serialization
|
|
97
|
+
|
|
98
|
+
### `to_s(truncate_to = nil)`
|
|
99
|
+
|
|
100
|
+
Returns the geohash string. An optional integer truncates to that length.
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
coord = GH36.new("bdrdC26BqH")
|
|
104
|
+
coord.to_s # => "bdrdC26BqH"
|
|
105
|
+
coord.to_s(5) # => "bdrdC"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### `to_slug`
|
|
109
|
+
|
|
110
|
+
Alias for `to_s`. The geohash is already URL-safe.
|
|
111
|
+
|
|
112
|
+
### `to_a`
|
|
113
|
+
|
|
114
|
+
Returns `[lat, lng]` of the cell midpoint.
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
coord.to_a # => [40.689, -74.044]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `from_string` / `from_array`
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
GH36.from_string("bdrdC26BqH") # from geohash string
|
|
124
|
+
GH36.from_array([40.689, -74.044]) # from [lat, lng]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Neighbors
|
|
128
|
+
|
|
129
|
+
Returns all 8 adjacent geohash cells as GH36 instances.
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
coord = GH36.new(LLA.new(lat: 40.0, lng: -74.0))
|
|
133
|
+
neighbors = coord.neighbors
|
|
134
|
+
# => { N: GH36, S: GH36, E: GH36, W: GH36, NE: GH36, NW: GH36, SE: GH36, SW: GH36 }
|
|
135
|
+
|
|
136
|
+
neighbors[:N].to_lla.lat > coord.to_lla.lat # => true
|
|
137
|
+
neighbors[:E].to_lla.lng > coord.to_lla.lng # => true
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Neighbor computation propagates carries when the adjustment wraps beyond the matrix edge, recursing on the parent prefix.
|
|
141
|
+
|
|
142
|
+
## Area
|
|
143
|
+
|
|
144
|
+
The `to_area` method returns the geohash cell as a `Geodetic::Areas::Rectangle`.
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
area = coord.to_area
|
|
148
|
+
# => Geodetic::Areas::Rectangle
|
|
149
|
+
|
|
150
|
+
area.includes?(coord.to_lla) # => true (midpoint is inside the cell)
|
|
151
|
+
area.nw # => LLA (northwest corner)
|
|
152
|
+
area.se # => LLA (southeast corner)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Equality
|
|
156
|
+
|
|
157
|
+
Two GH36 instances are equal if their geohash strings match exactly.
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
GH36.new("bdrdC26BqH") == GH36.new("bdrdC26BqH") # => true
|
|
161
|
+
GH36.new("bdrdC26BqH") == GH36.new("bdrdC26Bq2") # => false
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## `valid?`
|
|
165
|
+
|
|
166
|
+
Returns `true` if all characters are in the valid Geohash-36 alphabet.
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
coord.valid? # => true
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Universal Distance and Bearing Methods
|
|
173
|
+
|
|
174
|
+
GH36 supports all universal distance and bearing methods via the `DistanceMethods` and `BearingMethods` mixins:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
a = GH36.new(LLA.new(lat: 40.689167, lng: -74.044444))
|
|
178
|
+
b = GH36.new(LLA.new(lat: 51.504444, lng: -0.086666))
|
|
179
|
+
|
|
180
|
+
a.distance_to(b) # => Distance (~5,570 km)
|
|
181
|
+
a.straight_line_distance_to(b) # => Distance
|
|
182
|
+
a.bearing_to(b) # => Bearing (~51°)
|
|
183
|
+
a.elevation_to(b) # => Float (degrees)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Cross-system distances work too:
|
|
187
|
+
|
|
188
|
+
```ruby
|
|
189
|
+
utm = seattle_lla.to_utm
|
|
190
|
+
gh36 = GH36.new(portland_lla)
|
|
191
|
+
utm.distance_to(gh36) # => Distance
|
|
192
|
+
```
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Coordinate Systems Overview
|
|
2
|
+
|
|
3
|
+
The Geodetic gem supports 12 coordinate systems organized into six categories. All coordinate classes live under `Geodetic::Coordinates`.
|
|
4
|
+
|
|
5
|
+
## Global Systems
|
|
6
|
+
|
|
7
|
+
| System | Class | Description |
|
|
8
|
+
|--------|-------|-------------|
|
|
9
|
+
| **LLA** | `Geodetic::Coordinates::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::Coordinates::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::Coordinates::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
|
+
|
|
13
|
+
## Local Tangent Plane Systems
|
|
14
|
+
|
|
15
|
+
| System | Class | Description |
|
|
16
|
+
|--------|-------|-------------|
|
|
17
|
+
| **ENU** | `Geodetic::Coordinates::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::Coordinates::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
|
+
|
|
20
|
+
## Military and Grid Systems
|
|
21
|
+
|
|
22
|
+
| System | Class | Description |
|
|
23
|
+
|--------|-------|-------------|
|
|
24
|
+
| **MGRS** | `Geodetic::Coordinates::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::Coordinates::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
|
+
|
|
27
|
+
## Web Mapping
|
|
28
|
+
|
|
29
|
+
| System | Class | Description |
|
|
30
|
+
|--------|-------|-------------|
|
|
31
|
+
| **WebMercator** | `Geodetic::Coordinates::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
|
+
|
|
33
|
+
## Polar
|
|
34
|
+
|
|
35
|
+
| System | Class | Description |
|
|
36
|
+
|--------|-------|-------------|
|
|
37
|
+
| **UPS** | `Geodetic::Coordinates::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
|
+
|
|
39
|
+
## Spatial Hashing
|
|
40
|
+
|
|
41
|
+
| System | Class | Description |
|
|
42
|
+
|--------|-------|-------------|
|
|
43
|
+
| **GH36** | `Geodetic::Coordinates::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
|
+
|
|
45
|
+
## Regional Systems
|
|
46
|
+
|
|
47
|
+
| System | Class | Description |
|
|
48
|
+
|--------|-------|-------------|
|
|
49
|
+
| **StatePlane** | `Geodetic::Coordinates::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. |
|
|
50
|
+
| **BNG** | `Geodetic::Coordinates::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"). |
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Conversion Matrix
|
|
55
|
+
|
|
56
|
+
Every coordinate system can convert to every other coordinate system. The table below confirms full interoperability:
|
|
57
|
+
|
|
58
|
+
| From \ To | LLA | ECEF | UTM | ENU | NED | MGRS | USNG | WebMercator | UPS | StatePlane | BNG | GH36 |
|
|
59
|
+
|-----------|-----|------|-----|-----|-----|------|------|-------------|-----|------------|-----|------|
|
|
60
|
+
| **LLA** | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
61
|
+
| **ECEF** | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
62
|
+
| **UTM** | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
63
|
+
| **ENU** | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
64
|
+
| **NED** | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y |
|
|
65
|
+
| **MGRS** | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y |
|
|
66
|
+
| **USNG** | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y |
|
|
67
|
+
| **WebMercator**| Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y |
|
|
68
|
+
| **UPS** | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y |
|
|
69
|
+
| **StatePlane** | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y |
|
|
70
|
+
| **BNG** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y |
|
|
71
|
+
| **GH36** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- |
|
|
72
|
+
|
|
73
|
+
## Universal Distance and Bearing Calculations
|
|
74
|
+
|
|
75
|
+
All coordinate systems support universal distance calculations via `distance_to` (Vincenty great-circle) and `straight_line_distance_to` (ECEF Euclidean). These methods work across different coordinate types -- for example, computing the distance from a UTM coordinate to an MGRS coordinate. Class-level methods `GCS.distance_between` and `GCS.straight_line_distance_between` compute consecutive chain distances across a sequence of coordinates.
|
|
76
|
+
|
|
77
|
+
All coordinate systems also support universal bearing calculations via `bearing_to` (great-circle forward azimuth) and `elevation_to` (vertical look angle). These return `Bearing` and `Float` objects respectively. The class-level method `GCS.bearing_between` computes consecutive chain bearings.
|
|
78
|
+
|
|
79
|
+
See the [Conversions Reference](../reference/conversions.md#distance-calculations) for details on distances and [Bearing Calculations](../reference/conversions.md#bearing-calculations) for bearings.
|
|
80
|
+
|
|
81
|
+
> **Note:** ENU and NED are relative systems and must be converted to an absolute system (e.g., LLA) before using universal distance and bearing methods. They retain `local_bearing_to` and `local_elevation_angle_to` for tangent-plane operations.
|
|
82
|
+
|
|
83
|
+
## Conversion Paths
|
|
84
|
+
|
|
85
|
+
Conversions typically route through **LLA** or **ECEF** as intermediate steps:
|
|
86
|
+
|
|
87
|
+
- **LLA** serves as the universal hub. Most systems convert to LLA first, then from LLA to the target system.
|
|
88
|
+
- **ECEF** is the intermediate for local tangent plane systems (ENU, NED), since the rotation from global Cartesian to local frames is straightforward in ECEF.
|
|
89
|
+
- **ENU and NED** convert between each other directly by reordering axes and inverting the vertical component.
|
|
90
|
+
- **MGRS and USNG** route through UTM, which in turn routes through LLA.
|
|
91
|
+
- **WebMercator, UPS, BNG, StatePlane, and GH36** all convert through LLA.
|
|
92
|
+
|
|
93
|
+
For example, converting from BNG to NED follows the chain: `BNG -> LLA -> ECEF -> ENU -> NED`. The gem handles this automatically when you call a conversion method.
|