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,304 @@
|
|
|
1
|
+
# Geodetic::Coordinates::LLA
|
|
2
|
+
|
|
3
|
+
Latitude, Longitude, Altitude -- the most common geodetic coordinate system. Represents a position on (or above/below) the Earth's surface using angular degrees and a height in meters above the WGS84 reference ellipsoid.
|
|
4
|
+
|
|
5
|
+
- A negative latitude is in the Southern hemisphere.
|
|
6
|
+
- A negative longitude is in the Western hemisphere.
|
|
7
|
+
- Altitude is in decimal meters above the ellipsoid.
|
|
8
|
+
|
|
9
|
+
LLA is the **hub class** in the Geodetic library. It can convert directly to all other coordinate systems (ECEF, UTM, ENU, NED), making it the central interchange format.
|
|
10
|
+
|
|
11
|
+
## Constructor
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
Geodetic::Coordinates::LLA.new(lat: 0.0, lng: 0.0, alt: 0.0)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
| Parameter | Type | Default | Description |
|
|
18
|
+
|-----------|-------|---------|--------------------------------------------|
|
|
19
|
+
| `lat` | Float | `0.0` | Latitude in decimal degrees (-90..90) |
|
|
20
|
+
| `lng` | Float | `0.0` | Longitude in decimal degrees (-180..180) |
|
|
21
|
+
| `alt` | Float | `0.0` | Altitude in meters above the WGS84 ellipsoid |
|
|
22
|
+
|
|
23
|
+
All values are coerced to `Float` via `.to_f`.
|
|
24
|
+
|
|
25
|
+
### Validation
|
|
26
|
+
|
|
27
|
+
The constructor raises `ArgumentError` if:
|
|
28
|
+
|
|
29
|
+
- `lat` is outside the range `-90..90`
|
|
30
|
+
- `lng` is outside the range `-180..180`
|
|
31
|
+
|
|
32
|
+
## Attributes
|
|
33
|
+
|
|
34
|
+
| Attribute | Alias | Access |
|
|
35
|
+
|-------------|-------------|-----------------|
|
|
36
|
+
| `lat` | `latitude` | read/write |
|
|
37
|
+
| `lng` | `longitude` | read/write |
|
|
38
|
+
| `alt` | `altitude` | read/write |
|
|
39
|
+
|
|
40
|
+
Setters validate ranges and coerce to `Float`. Setting `lat` outside `-90..90` or `lng` outside `-180..180` raises `ArgumentError`. The `alt` setter has no range constraint.
|
|
41
|
+
|
|
42
|
+
## Conversions
|
|
43
|
+
|
|
44
|
+
All conversion methods accept an optional `datum` parameter (defaults to `Geodetic::WGS84`).
|
|
45
|
+
|
|
46
|
+
### to_ecef(datum = WGS84)
|
|
47
|
+
|
|
48
|
+
Converts to Earth-Centered, Earth-Fixed Cartesian coordinates.
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 38.8977, lng: -77.0365, alt: 17.0)
|
|
52
|
+
ecef = lla.to_ecef
|
|
53
|
+
# => Geodetic::Coordinates::ECEF
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### LLA.from_ecef(ecef, datum = WGS84)
|
|
57
|
+
|
|
58
|
+
Creates an LLA from an ECEF instance. Raises `ArgumentError` if the argument is not an `ECEF`.
|
|
59
|
+
|
|
60
|
+
### to_utm(datum = WGS84)
|
|
61
|
+
|
|
62
|
+
Converts to Universal Transverse Mercator coordinates. The UTM zone and hemisphere are computed automatically from the longitude and latitude.
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
utm = lla.to_utm
|
|
66
|
+
# => Geodetic::Coordinates::UTM
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### LLA.from_utm(utm, datum = WGS84)
|
|
70
|
+
|
|
71
|
+
Creates an LLA from a UTM instance. Raises `ArgumentError` if the argument is not a `UTM`.
|
|
72
|
+
|
|
73
|
+
### to_enu(reference_lla)
|
|
74
|
+
|
|
75
|
+
Converts to East-North-Up local tangent plane coordinates relative to a reference LLA position.
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
origin = Geodetic::Coordinates::LLA.new(lat: 38.8977, lng: -77.0365, alt: 17.0)
|
|
79
|
+
point = Geodetic::Coordinates::LLA.new(lat: 38.8987, lng: -77.0355, alt: 20.0)
|
|
80
|
+
enu = point.to_enu(origin)
|
|
81
|
+
# => Geodetic::Coordinates::ENU
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Raises `ArgumentError` if `reference_lla` is not an `LLA`.
|
|
85
|
+
|
|
86
|
+
### LLA.from_enu(enu, reference_lla)
|
|
87
|
+
|
|
88
|
+
Creates an LLA from an ENU instance and a reference LLA origin. Raises `ArgumentError` if the arguments are not the expected types.
|
|
89
|
+
|
|
90
|
+
### to_ned(reference_lla)
|
|
91
|
+
|
|
92
|
+
Converts to North-East-Down local tangent plane coordinates relative to a reference LLA position.
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
ned = point.to_ned(origin)
|
|
96
|
+
# => Geodetic::Coordinates::NED
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Raises `ArgumentError` if `reference_lla` is not an `LLA`.
|
|
100
|
+
|
|
101
|
+
### LLA.from_ned(ned, reference_lla)
|
|
102
|
+
|
|
103
|
+
Creates an LLA from a NED instance and a reference LLA origin. Raises `ArgumentError` if the arguments are not the expected types.
|
|
104
|
+
|
|
105
|
+
## Serialization
|
|
106
|
+
|
|
107
|
+
### to_s
|
|
108
|
+
|
|
109
|
+
Returns a comma-separated string of `lat, lng, alt`.
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 38.8977, lng: -77.0365, alt: 17.0)
|
|
113
|
+
lla.to_s
|
|
114
|
+
# => "38.8977, -77.0365, 17.0"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### to_a
|
|
118
|
+
|
|
119
|
+
Returns a three-element array `[lat, lng, alt]`.
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
lla.to_a
|
|
123
|
+
# => [38.8977, -77.0365, 17.0]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### LLA.from_string(string)
|
|
127
|
+
|
|
128
|
+
Parses a comma-separated string into an LLA.
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
lla = Geodetic::Coordinates::LLA.from_string("38.8977, -77.0365, 17.0")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### LLA.from_array(array)
|
|
135
|
+
|
|
136
|
+
Creates an LLA from a three-element array `[lat, lng, alt]`.
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
lla = Geodetic::Coordinates::LLA.from_array([38.8977, -77.0365, 17.0])
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### to_dms
|
|
143
|
+
|
|
144
|
+
Converts to a Degrees-Minutes-Seconds string with hemisphere indicators and altitude.
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 38.8977, lng: -77.0365, alt: 17.0)
|
|
148
|
+
lla.to_dms
|
|
149
|
+
# => "38° 53' 51.72\" N, 77° 2' 11.40\" W, 17.00 m"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### LLA.from_dms(dms_str)
|
|
153
|
+
|
|
154
|
+
Parses a DMS-formatted string back into an LLA. The expected format is:
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
DD° MM' SS.ss" N/S, DDD° MM' SS.ss" E/W[, altitude m]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The altitude portion is optional and defaults to `0.0`.
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
lla = Geodetic::Coordinates::LLA.from_dms("38° 53' 51.72\" N, 77° 2' 11.40\" W, 17.0 m")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Raises `ArgumentError` if the string does not match the expected format.
|
|
167
|
+
|
|
168
|
+
## Additional Methods
|
|
169
|
+
|
|
170
|
+
### ==(other)
|
|
171
|
+
|
|
172
|
+
Compares two LLA instances for approximate equality. Returns `true` if:
|
|
173
|
+
|
|
174
|
+
- `|lat difference| <= 1e-10`
|
|
175
|
+
- `|lng difference| <= 1e-10`
|
|
176
|
+
- `|alt difference| <= 1e-6`
|
|
177
|
+
|
|
178
|
+
Returns `false` if `other` is not an `LLA`.
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
a = Geodetic::Coordinates::LLA.new(lat: 38.8977, lng: -77.0365, alt: 17.0)
|
|
182
|
+
b = Geodetic::Coordinates::LLA.new(lat: 38.8977, lng: -77.0365, alt: 17.0)
|
|
183
|
+
a == b
|
|
184
|
+
# => true
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### to_gh36(precision: 10)
|
|
188
|
+
|
|
189
|
+
Converts to Geohash-36 coordinates with configurable precision.
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
gh36 = lla.to_gh36
|
|
193
|
+
gh36 = lla.to_gh36(precision: 5) # coarser precision
|
|
194
|
+
# => Geodetic::Coordinates::GH36
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### LLA.from_gh36(gh36_coord, datum = WGS84)
|
|
198
|
+
|
|
199
|
+
Creates an LLA from a GH36 instance. Returns the midpoint of the geohash cell.
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
lla = Geodetic::Coordinates::LLA.from_gh36(gh36)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## GeoidHeightSupport Mixin
|
|
206
|
+
|
|
207
|
+
LLA includes the `Geodetic::GeoidHeightSupport` module, which provides methods for working with geoid heights and vertical datum conversions.
|
|
208
|
+
|
|
209
|
+
### geoid_height(geoid_model = 'EGM2008')
|
|
210
|
+
|
|
211
|
+
Returns the geoid undulation (in meters) at the coordinate's lat/lng for the specified geoid model.
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 38.8977, lng: -77.0365, alt: 17.0)
|
|
215
|
+
lla.geoid_height
|
|
216
|
+
# => Float (geoid undulation in meters)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Supported models: `'EGM96'`, `'EGM2008'`, `'GEOID18'`, `'GEOID12B'`.
|
|
220
|
+
|
|
221
|
+
### orthometric_height(geoid_model = 'EGM2008')
|
|
222
|
+
|
|
223
|
+
Returns the orthometric height (height above the geoid / mean sea level) by subtracting the geoid undulation from the ellipsoidal altitude.
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
lla.orthometric_height
|
|
227
|
+
# => Float (meters above geoid)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### convert_height_datum(from_datum, to_datum, geoid_model = 'EGM2008')
|
|
231
|
+
|
|
232
|
+
Converts the altitude between vertical datums and returns a new LLA with the adjusted height. The original instance is not modified.
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
lla_navd88 = lla.convert_height_datum('HAE', 'NAVD88')
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Supported vertical datums: `'NAVD88'`, `'NGVD29'`, `'MSL'`, `'HAE'`.
|
|
239
|
+
|
|
240
|
+
### Class Method: LLA.with_geoid_height(geoid_model = 'EGM2008')
|
|
241
|
+
|
|
242
|
+
Sets the default geoid model for the class. Returns the class for chaining.
|
|
243
|
+
|
|
244
|
+
```ruby
|
|
245
|
+
Geodetic::Coordinates::LLA.with_geoid_height('EGM96')
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Code Examples
|
|
249
|
+
|
|
250
|
+
### Round-trip conversion
|
|
251
|
+
|
|
252
|
+
```ruby
|
|
253
|
+
require 'geodetic'
|
|
254
|
+
|
|
255
|
+
# Create an LLA coordinate
|
|
256
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 40.7128, lng: -74.0060, alt: 10.0)
|
|
257
|
+
|
|
258
|
+
# Convert to ECEF and back
|
|
259
|
+
ecef = lla.to_ecef
|
|
260
|
+
lla_roundtrip = ecef.to_lla
|
|
261
|
+
lla == lla_roundtrip
|
|
262
|
+
# => true
|
|
263
|
+
|
|
264
|
+
# Convert to UTM and back
|
|
265
|
+
utm = lla.to_utm
|
|
266
|
+
lla_roundtrip = utm.to_lla
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Local tangent plane
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
origin = Geodetic::Coordinates::LLA.new(lat: 40.7128, lng: -74.0060, alt: 10.0)
|
|
273
|
+
target = Geodetic::Coordinates::LLA.new(lat: 40.7138, lng: -74.0050, alt: 15.0)
|
|
274
|
+
|
|
275
|
+
enu = target.to_enu(origin)
|
|
276
|
+
ned = target.to_ned(origin)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### DMS formatting
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
lla = Geodetic::Coordinates::LLA.new(lat: -33.8688, lng: 151.2093, alt: 58.0)
|
|
283
|
+
puts lla.to_dms
|
|
284
|
+
# => "33° 52' 7.68" S, 151° 12' 33.48" E, 58.00 m"
|
|
285
|
+
|
|
286
|
+
restored = Geodetic::Coordinates::LLA.from_dms(lla.to_dms)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Geoid height operations
|
|
290
|
+
|
|
291
|
+
```ruby
|
|
292
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 38.8977, lng: -77.0365, alt: 50.0)
|
|
293
|
+
|
|
294
|
+
# Get geoid undulation at this location
|
|
295
|
+
puts lla.geoid_height # EGM2008 (default)
|
|
296
|
+
puts lla.geoid_height('EGM96') # EGM96
|
|
297
|
+
|
|
298
|
+
# Get height above mean sea level
|
|
299
|
+
puts lla.orthometric_height
|
|
300
|
+
|
|
301
|
+
# Convert from ellipsoidal height to NAVD88
|
|
302
|
+
lla_navd88 = lla.convert_height_datum('HAE', 'NAVD88')
|
|
303
|
+
puts lla_navd88.alt
|
|
304
|
+
```
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Geodetic::Coordinates::MGRS - Military Grid Reference System
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
MGRS (Military Grid Reference System) is a geocoordinate standard used by NATO militaries for locating points on Earth. It is based on the UTM coordinate system, augmented with a lettering scheme for grid zone designators and 100km square identifiers.
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
### From a complete MGRS string
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
MGRS.new(mgrs_string: "18SUJ2337006519")
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### From individual components
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
MGRS.new(
|
|
19
|
+
grid_zone: "18S",
|
|
20
|
+
square_id: "UJ",
|
|
21
|
+
easting: 23370,
|
|
22
|
+
northing: 06519,
|
|
23
|
+
precision: 5
|
|
24
|
+
)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## String Representation
|
|
28
|
+
|
|
29
|
+
| Method | Description |
|
|
30
|
+
|---------------|-----------------------------------------------|
|
|
31
|
+
| `from_string` | Parses an MGRS string into its components |
|
|
32
|
+
| `to_s` | Returns the compact MGRS string representation |
|
|
33
|
+
|
|
34
|
+
### Example
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
mgrs = Geodetic::Coordinates::MGRS.new(mgrs_string: "18SUJ2337006519")
|
|
38
|
+
mgrs.to_s # => "18SUJ2337006519"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Precision Levels
|
|
42
|
+
|
|
43
|
+
MGRS supports five levels of precision, controlling the number of digits in the easting and northing components:
|
|
44
|
+
|
|
45
|
+
| Precision | Digits (per axis) | Resolution |
|
|
46
|
+
|-----------|--------------------|------------|
|
|
47
|
+
| 1 | 1 | 10 km |
|
|
48
|
+
| 2 | 2 | 1 km |
|
|
49
|
+
| 3 | 3 | 100 m |
|
|
50
|
+
| 4 | 4 | 10 m |
|
|
51
|
+
| 5 | 5 | 1 m |
|
|
52
|
+
|
|
53
|
+
## Components
|
|
54
|
+
|
|
55
|
+
| Component | Description | Example |
|
|
56
|
+
|--------------|----------------------------------------------------|---------|
|
|
57
|
+
| `grid_zone` | UTM zone number + latitude band letter | `18S` |
|
|
58
|
+
| `square_id` | 100km square identification (two-letter code) | `UJ` |
|
|
59
|
+
| `easting` | Easting within the 100km square | `23370` |
|
|
60
|
+
| `northing` | Northing within the 100km square | `06519` |
|
|
61
|
+
| `precision` | Number of digits per coordinate (1-5) | `5` |
|
|
62
|
+
|
|
63
|
+
## Conversions
|
|
64
|
+
|
|
65
|
+
MGRS converts to other coordinate systems via **UTM** and **LLA**:
|
|
66
|
+
|
|
67
|
+
- **MGRS -> UTM** — Decomposes the grid zone and square ID back to UTM easting/northing.
|
|
68
|
+
- **MGRS -> LLA** — Converts through UTM to latitude/longitude.
|
|
69
|
+
|
|
70
|
+
## Example
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
mgrs = Geodetic::Coordinates::MGRS.new(mgrs_string: "18SUJ2337006519")
|
|
74
|
+
|
|
75
|
+
mgrs.grid_zone # => "18S"
|
|
76
|
+
mgrs.square_id # => "UJ"
|
|
77
|
+
mgrs.easting # => 23370
|
|
78
|
+
mgrs.northing # => 06519
|
|
79
|
+
mgrs.precision # => 5
|
|
80
|
+
mgrs.to_s # => "18SUJ2337006519"
|
|
81
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Geodetic::Coordinates::NED - North, East, Down
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
NED (North, East, Down) is a local tangent plane coordinate system commonly used in aviation and navigation. It defines positions relative to a local reference point on the Earth's surface, with axes pointing North, East, and Down.
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
NED.new(n: 0.0, e: 0.0, d: 0.0)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
All parameters are in **meters**.
|
|
14
|
+
|
|
15
|
+
## Attribute Aliases
|
|
16
|
+
|
|
17
|
+
| Primary | Alias |
|
|
18
|
+
|---------|---------|
|
|
19
|
+
| `n` | `north` |
|
|
20
|
+
| `e` | `east` |
|
|
21
|
+
| `d` | `down` |
|
|
22
|
+
|
|
23
|
+
## Reference Point
|
|
24
|
+
|
|
25
|
+
NED 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 NED and ENU, which does not require a reference point.
|
|
28
|
+
|
|
29
|
+
## Conversions
|
|
30
|
+
|
|
31
|
+
### Direct (no reference point needed)
|
|
32
|
+
|
|
33
|
+
- **NED <-> ENU** — A simple axis remap:
|
|
34
|
+
```
|
|
35
|
+
NED(n, e, d) <-> ENU(e, n, -d)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Via reference point
|
|
39
|
+
|
|
40
|
+
- **NED -> LLA** — Requires a reference LLA.
|
|
41
|
+
- **NED -> ECEF** — Requires a reference LLA.
|
|
42
|
+
|
|
43
|
+
## Methods
|
|
44
|
+
|
|
45
|
+
| Method | Description |
|
|
46
|
+
|---------------------------------|--------------------------------------------------------------------------|
|
|
47
|
+
| `horizontal_distance_to(other)` | Horizontal (N-E plane) distance to another NED point (meters) |
|
|
48
|
+
| `local_bearing_to(other)` | Bearing from this point to another NED point (degrees from north, 0-360) |
|
|
49
|
+
| `local_elevation_angle_to(other)` | Elevation angle from this point to another NED point (degrees) |
|
|
50
|
+
| `distance_to_origin` | Euclidean distance from this point to the origin (meters) |
|
|
51
|
+
| `elevation_angle` | Elevation angle from the origin to this point (degrees) |
|
|
52
|
+
| `bearing_from_origin` | Bearing from the origin to this point (degrees from north, 0-360) |
|
|
53
|
+
| `horizontal_distance_to_origin` | Horizontal distance from this point to the origin (meters) |
|
|
54
|
+
|
|
55
|
+
### Universal Distance and Bearing Methods
|
|
56
|
+
|
|
57
|
+
NED is a relative coordinate system. The universal `distance_to`, `straight_line_distance_to`, `bearing_to`, and `elevation_to` methods raise `ArgumentError` because NED cannot be converted to an absolute system without a reference point. Convert to an absolute system first:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
ref = Geodetic::Coordinates::LLA.new(lat: 47.62, lng: -122.35, alt: 0.0)
|
|
61
|
+
lla = ned.to_lla(ref)
|
|
62
|
+
lla.distance_to(other_lla) # Vincenty great-circle distance
|
|
63
|
+
lla.bearing_to(other_lla) # Great-circle forward azimuth (Bearing object)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Bearing Convention
|
|
67
|
+
|
|
68
|
+
Bearing is measured in **degrees from north**, clockwise, in the range **0-360**.
|
|
69
|
+
|
|
70
|
+
## Example
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
point = Geodetic::Coordinates::NED.new(n: 200.0, e: 100.0, d: -50.0)
|
|
74
|
+
|
|
75
|
+
point.north # => 200.0
|
|
76
|
+
point.east # => 100.0
|
|
77
|
+
point.down # => -50.0
|
|
78
|
+
|
|
79
|
+
point.distance_to_origin # => Euclidean distance in meters
|
|
80
|
+
point.bearing_from_origin # => Bearing in degrees from north
|
|
81
|
+
point.elevation_angle # => Elevation angle in degrees
|
|
82
|
+
point.horizontal_distance_to_origin # => Horizontal distance in meters
|
|
83
|
+
```
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Geodetic::Coordinates::StatePlane
|
|
2
|
+
|
|
3
|
+
## US State Plane Coordinate System
|
|
4
|
+
|
|
5
|
+
The State Plane Coordinate System (SPCS) is a set of 124 geographic zones used across the United States. Each zone uses either a Lambert Conformal Conic or Transverse Mercator projection, chosen to minimize distortion within that zone. Units are typically expressed in **US Survey Feet**.
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
point = Geodetic::Coordinates::StatePlane.new(
|
|
11
|
+
easting: 0.0,
|
|
12
|
+
northing: 0.0,
|
|
13
|
+
zone_code: 'CA_I',
|
|
14
|
+
datum: Geodetic::Datum::WGS84
|
|
15
|
+
)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- `easting` — Easting coordinate (typically in US Survey Feet)
|
|
19
|
+
- `northing` — Northing coordinate (typically in US Survey Feet)
|
|
20
|
+
- `zone_code` — Identifier for the State Plane zone
|
|
21
|
+
- `datum` — The geodetic datum; stored internally on the coordinate object
|
|
22
|
+
|
|
23
|
+
## Available Zones
|
|
24
|
+
|
|
25
|
+
The following zone codes are currently supported:
|
|
26
|
+
|
|
27
|
+
| Zone Code | State / Region |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `CA_I` | California, Zone I |
|
|
30
|
+
| `CA_II` | California, Zone II |
|
|
31
|
+
| `TX_NORTH` | Texas, North |
|
|
32
|
+
| `FL_EAST` | Florida, East |
|
|
33
|
+
| `NY_LONG_ISLAND` | New York, Long Island |
|
|
34
|
+
|
|
35
|
+
## Projections
|
|
36
|
+
|
|
37
|
+
Each zone uses one of two map projections depending on its geographic shape:
|
|
38
|
+
|
|
39
|
+
- **Lambert Conformal Conic** — Used for zones that are wider east-to-west
|
|
40
|
+
- **Transverse Mercator** — Used for zones that are longer north-to-south
|
|
41
|
+
|
|
42
|
+
## Unit Conversion
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
meters_point = point.to_meters
|
|
46
|
+
feet_point = point.to_us_survey_feet
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Methods
|
|
50
|
+
|
|
51
|
+
| Method | Description |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `zone_info` | Returns metadata about the current zone (projection type, parameters, bounds) |
|
|
54
|
+
| `valid?` | Returns `true` if the coordinates fall within the defined zone bounds |
|
|
55
|
+
| `zones_for_state(state)` | Class method. Returns available zone codes for a given US state |
|
|
56
|
+
| `find_zone_for_lla(lla)` | Class method. Determines the appropriate State Plane zone for a given LLA coordinate |
|
|
57
|
+
|
|
58
|
+
## Datum
|
|
59
|
+
|
|
60
|
+
The `StatePlane` coordinate object stores its associated datum internally, accessible for reference during conversions and transformations.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Geodetic::Coordinates::UPS
|
|
2
|
+
|
|
3
|
+
## Universal Polar Stereographic
|
|
4
|
+
|
|
5
|
+
The Universal Polar Stereographic (UPS) coordinate system covers the polar regions of the Earth that fall outside the UTM grid: latitudes north of 84°N and south of 80°S. It uses a stereographic projection centered on each pole.
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
point = Geodetic::Coordinates::UPS.new(
|
|
11
|
+
easting: 0.0,
|
|
12
|
+
northing: 0.0,
|
|
13
|
+
hemisphere: 'N',
|
|
14
|
+
zone: 'Y'
|
|
15
|
+
)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- `easting` — Easting coordinate in meters
|
|
19
|
+
- `northing` — Northing coordinate in meters
|
|
20
|
+
- `hemisphere` — `'N'` for the North Pole region, `'S'` for the South Pole region
|
|
21
|
+
- `zone` — UPS zone letter
|
|
22
|
+
|
|
23
|
+
## Zones
|
|
24
|
+
|
|
25
|
+
UPS divides each polar region into two zones based on longitude:
|
|
26
|
+
|
|
27
|
+
| Hemisphere | Zones | Longitude Range |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| North | Y, Z | Y: 180°W to 0°; Z: 0° to 180°E |
|
|
30
|
+
| South | A, B | A: 180°W to 0°; B: 0° to 180°E |
|
|
31
|
+
|
|
32
|
+
## False Origin
|
|
33
|
+
|
|
34
|
+
Both easting and northing use a **false origin of 2,000,000 meters** to ensure all coordinate values remain positive within the projection.
|
|
35
|
+
|
|
36
|
+
## Methods
|
|
37
|
+
|
|
38
|
+
| Method | Description |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `grid_convergence` | Returns the angular difference between grid north and true north at the point |
|
|
41
|
+
| `point_scale_factor` | Returns the scale distortion factor at the point's location |
|
|
42
|
+
| `valid?` | Returns `true` if the coordinates represent a valid UPS position |
|
|
43
|
+
|
|
44
|
+
### Universal Distance Methods
|
|
45
|
+
|
|
46
|
+
The universal `distance_to` method computes the Vincenty great-circle distance (in meters) to any other coordinate type. The `straight_line_distance_to` method computes the Euclidean distance in ECEF space. Both accept single or multiple targets.
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
ups_a = Geodetic::Coordinates::UPS.new(easting: 2000000.0, northing: 2000000.0, hemisphere: 'N', zone: 'Z')
|
|
50
|
+
ups_b = Geodetic::Coordinates::UPS.new(easting: 2100000.0, northing: 2100000.0, hemisphere: 'N', zone: 'Z')
|
|
51
|
+
ups_a.distance_to(ups_b) # => Distance (meters, great-circle)
|
|
52
|
+
ups_a.straight_line_distance_to(ups_b) # => Distance (meters, Euclidean)
|
|
53
|
+
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Geodetic::Coordinates::USNG - US National Grid
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
USNG (US National Grid) is a coordinate system based on MGRS, adopted for use by US emergency services and civilian agencies. It provides a consistent, interoperable grid reference system across the United States.
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
### From a complete USNG string
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
USNG.new(usng_string: "18T WL 12345 67890")
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### From individual components
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
USNG.new(
|
|
19
|
+
grid_zone: "18T",
|
|
20
|
+
square_id: "WL",
|
|
21
|
+
easting: 12345,
|
|
22
|
+
northing: 67890,
|
|
23
|
+
precision: 5
|
|
24
|
+
)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Format Differences from MGRS
|
|
28
|
+
|
|
29
|
+
USNG uses a **spaced format** for readability, whereas MGRS uses a compact (unspaced) format:
|
|
30
|
+
|
|
31
|
+
| System | Format |
|
|
32
|
+
|--------|---------------------------|
|
|
33
|
+
| USNG | `18T WL 12345 67890` |
|
|
34
|
+
| MGRS | `18TWL1234567890` |
|
|
35
|
+
|
|
36
|
+
## String Representation
|
|
37
|
+
|
|
38
|
+
| Method | Description |
|
|
39
|
+
|--------------------------|-----------------------------------------------------|
|
|
40
|
+
| `from_string` | Parses a USNG string into its components |
|
|
41
|
+
| `to_s` | Returns the USNG string representation |
|
|
42
|
+
| `to_full_format` | Returns the full spaced USNG format |
|
|
43
|
+
| `to_abbreviated_format` | Returns an abbreviated representation |
|
|
44
|
+
|
|
45
|
+
## Validation
|
|
46
|
+
|
|
47
|
+
| Method | Description |
|
|
48
|
+
|----------|-------------------------------------------------------------------|
|
|
49
|
+
| `valid?` | Checks that the zone designator falls within valid US zones |
|
|
50
|
+
|
|
51
|
+
## Conversions
|
|
52
|
+
|
|
53
|
+
USNG converts through **MGRS** internally. All coordinate conversions follow the chain:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
USNG <-> MGRS <-> UTM <-> LLA
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Example
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
usng = Geodetic::Coordinates::USNG.new(usng_string: "18T WL 12345 67890")
|
|
63
|
+
|
|
64
|
+
usng.grid_zone # => "18T"
|
|
65
|
+
usng.square_id # => "WL"
|
|
66
|
+
usng.easting # => 12345
|
|
67
|
+
usng.northing # => 67890
|
|
68
|
+
usng.precision # => 5
|
|
69
|
+
|
|
70
|
+
usng.to_s # => "18T WL 12345 67890"
|
|
71
|
+
usng.to_full_format # => Full spaced format
|
|
72
|
+
usng.to_abbreviated_format # => Abbreviated format
|
|
73
|
+
usng.valid? # => true (if within valid US zones)
|
|
74
|
+
```
|