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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +1 -0
  3. data/.github/workflows/deploy-github-pages.yml +52 -0
  4. data/CHANGELOG.md +15 -0
  5. data/COMMITS.md +196 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +471 -0
  8. data/Rakefile +8 -0
  9. data/docs/coordinate-systems/bng.md +60 -0
  10. data/docs/coordinate-systems/ecef.md +215 -0
  11. data/docs/coordinate-systems/enu.md +77 -0
  12. data/docs/coordinate-systems/gh36.md +192 -0
  13. data/docs/coordinate-systems/index.md +93 -0
  14. data/docs/coordinate-systems/lla.md +304 -0
  15. data/docs/coordinate-systems/mgrs.md +81 -0
  16. data/docs/coordinate-systems/ned.md +83 -0
  17. data/docs/coordinate-systems/state-plane.md +60 -0
  18. data/docs/coordinate-systems/ups.md +53 -0
  19. data/docs/coordinate-systems/usng.md +74 -0
  20. data/docs/coordinate-systems/utm.md +257 -0
  21. data/docs/coordinate-systems/web-mercator.md +67 -0
  22. data/docs/getting-started/installation.md +65 -0
  23. data/docs/getting-started/quick-start.md +175 -0
  24. data/docs/index.md +58 -0
  25. data/docs/reference/areas.md +195 -0
  26. data/docs/reference/conversions.md +351 -0
  27. data/docs/reference/datums.md +134 -0
  28. data/docs/reference/geoid-height.md +182 -0
  29. data/docs/reference/serialization.md +252 -0
  30. data/examples/01_basic_conversions.rb +187 -0
  31. data/examples/02_all_coordinate_systems.rb +310 -0
  32. data/examples/03_distance_calculations.rb +224 -0
  33. data/examples/04_bearing_calculations.rb +236 -0
  34. data/lib/geodetic/areas/circle.rb +29 -0
  35. data/lib/geodetic/areas/polygon.rb +57 -0
  36. data/lib/geodetic/areas/rectangle.rb +55 -0
  37. data/lib/geodetic/areas.rb +5 -0
  38. data/lib/geodetic/bearing.rb +94 -0
  39. data/lib/geodetic/coordinates/bng.rb +366 -0
  40. data/lib/geodetic/coordinates/ecef.rb +229 -0
  41. data/lib/geodetic/coordinates/enu.rb +244 -0
  42. data/lib/geodetic/coordinates/gh36.rb +384 -0
  43. data/lib/geodetic/coordinates/lla.rb +268 -0
  44. data/lib/geodetic/coordinates/mgrs.rb +317 -0
  45. data/lib/geodetic/coordinates/ned.rb +246 -0
  46. data/lib/geodetic/coordinates/state_plane.rb +451 -0
  47. data/lib/geodetic/coordinates/ups.rb +325 -0
  48. data/lib/geodetic/coordinates/usng.rb +274 -0
  49. data/lib/geodetic/coordinates/utm.rb +261 -0
  50. data/lib/geodetic/coordinates/web_mercator.rb +242 -0
  51. data/lib/geodetic/coordinates.rb +260 -0
  52. data/lib/geodetic/datum.rb +62 -0
  53. data/lib/geodetic/distance.rb +146 -0
  54. data/lib/geodetic/geoid_height.rb +299 -0
  55. data/lib/geodetic/version.rb +5 -0
  56. data/lib/geodetic.rb +13 -0
  57. data/mkdocs.yml +140 -0
  58. data/sig/geodetic.rbs +4 -0
  59. 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
+ ```