geodetic 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 495ba0272532d5591ecbb37869857675af00014d6863849c79c6fbef35e590c3
4
- data.tar.gz: dcb68a4385f7a888fbfb37b421d2f82c66aae077bda8557ce2a7b60c57e38b35
3
+ metadata.gz: 82e74456b38ec9c888ee70e68de3e626f0333b9f791c8de7779f5aa544cbbbc7
4
+ data.tar.gz: a76e9a726d4a503f0e2331f321de25a3b7c155e8712a27a18c53ba8e5ab27953
5
5
  SHA512:
6
- metadata.gz: 8f276d8924aad177efc94aee0037f1fbd4e1cb655668ed54ff5ba0e471c16dd91687966577505057e31a144ea545adf3d8843535ca0705fca69fc5e5bf9a3637
7
- data.tar.gz: f2d8102549b1502099d149453f5b18d8143b5799e3d3f5c2c4f947c86b7e2de1355e74916ae3b0d7376652fdb547ebd48488bff2eba18d60211da4ff0ff030a3
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,45 @@ 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
+
14
50
  ## [0.5.0] - 2026-03-10
15
51
 
16
52
  ### Added
data/README.md CHANGED
@@ -24,6 +24,7 @@
24
24
  - <strong>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
25
25
  - <strong>Vectors</strong> - Geodetic displacement (distance + bearing) with full arithmetic and Vincenty direct<br>
26
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>
27
28
  - <strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
28
29
  - <strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
29
30
  - <strong>Multiple Datums</strong> - WGS84, Clarke 1866, GRS 1980, Airy 1830, and more<br>
@@ -73,11 +74,15 @@ require "geodetic"
73
74
 
74
75
  include Geodetic
75
76
 
76
- lla = Coordinates::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
77
- ecef = Coordinates::ECEF.new(x: -2304643.57, y: -3638650.07, z: 4688674.43)
78
- utm = Coordinates::UTM.new(easting: 548894.0, northing: 5272748.0, altitude: 184.0, zone: 10, hemisphere: "N")
79
- enu = Coordinates::ENU.new(e: 100.0, n: 200.0, u: 50.0)
80
- ned = Coordinates::NED.new(n: 200.0, e: 100.0, d: -50.0)
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)
81
86
  ```
82
87
 
83
88
  ### GCS Shorthand
@@ -112,19 +117,19 @@ Geodetic::Coordinate.systems.map { |c| c.name.split('::').last }
112
117
  Every coordinate system can convert to and from every other system:
113
118
 
114
119
  ```ruby
115
- lla = Coordinates::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
120
+ lla = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
116
121
 
117
122
  # LLA to other systems
118
123
  ecef = lla.to_ecef
119
124
  utm = lla.to_utm
120
- wm = Coordinates::WebMercator.from_lla(lla)
121
- mgrs = Coordinates::MGRS.from_lla(lla)
125
+ wm = Coordinate::WebMercator.from_lla(lla)
126
+ mgrs = Coordinate::MGRS.from_lla(lla)
122
127
 
123
128
  # Convert back
124
129
  lla_roundtrip = ecef.to_lla
125
130
 
126
131
  # Local coordinate systems require a reference point
127
- reference = Coordinates::LLA.new(lat: 47.62, lng: -122.35, alt: 0.0)
132
+ reference = Coordinate::LLA.new(lat: 47.62, lng: -122.35, alt: 0.0)
128
133
  enu = lla.to_enu(reference)
129
134
  ned = lla.to_ned(reference)
130
135
  ```
@@ -134,15 +139,15 @@ ned = lla.to_ned(reference)
134
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:
135
140
 
136
141
  ```ruby
137
- lla = Coordinates::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
142
+ lla = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
138
143
 
139
144
  lla.to_s # => "47.620500, -122.349300, 184.00"
140
145
  lla.to_s(3) # => "47.620, -122.349, 184.00"
141
146
  lla.to_s(0) # => "48, -122, 184"
142
147
  lla.to_a # => [47.6205, -122.3493, 184.0]
143
148
 
144
- Coordinates::LLA.from_string("47.6205, -122.3493, 184.0")
145
- Coordinates::LLA.from_array([47.6205, -122.3493, 184.0])
149
+ Coordinate::LLA.from_string("47.6205, -122.3493, 184.0")
150
+ Coordinate::LLA.from_array([47.6205, -122.3493, 184.0])
146
151
  ```
147
152
 
148
153
  Default precisions by class: LLA=6, Bearing=4, all others=2. Passing `0` returns integers.
@@ -152,24 +157,24 @@ Default precisions by class: LLA=6, Bearing=4, all others=2. Passing `0` returns
152
157
  All coordinate classes provide setter methods with type coercion and validation:
153
158
 
154
159
  ```ruby
155
- lla = Coordinates::LLA.new(lat: 47.0, lng: -122.0, alt: 100.0)
160
+ lla = Coordinate::LLA.new(lat: 47.0, lng: -122.0, alt: 100.0)
156
161
  lla.lat = 48.0 # validates -90..90
157
162
  lla.lng = -121.0 # validates -180..180
158
163
  lla.alt = 200.0 # no range constraint
159
164
  lla.lat = 91.0 # => ArgumentError
160
165
 
161
- utm = Coordinates::UTM.new(easting: 500000.0, northing: 5000000.0, zone: 10, hemisphere: 'N')
166
+ utm = Coordinate::UTM.new(easting: 500000.0, northing: 5000000.0, zone: 10, hemisphere: 'N')
162
167
  utm.zone = 15 # validates 1..60
163
168
  utm.hemisphere = 'S' # validates 'N' or 'S'
164
169
  utm.easting = -1.0 # => ArgumentError
165
170
 
166
171
  # UPS cross-validates hemisphere/zone combinations
167
- ups = Coordinates::UPS.new(hemisphere: 'N', zone: 'Y')
172
+ ups = Coordinate::UPS.new(hemisphere: 'N', zone: 'Y')
168
173
  ups.zone = 'Z' # valid for hemisphere 'N'
169
174
  ups.zone = 'A' # => ArgumentError (rolls back)
170
175
 
171
176
  # BNG auto-updates grid_ref when easting/northing change
172
- bng = Coordinates::BNG.new(easting: 530000, northing: 180000)
177
+ bng = Coordinate::BNG.new(easting: 530000, northing: 180000)
173
178
  bng.easting = 430000 # grid_ref automatically recalculated
174
179
  ```
175
180
 
@@ -178,10 +183,10 @@ ECEF, ENU, NED, and WebMercator setters coerce to float with no range constraint
178
183
  ### DMS (Degrees, Minutes, Seconds)
179
184
 
180
185
  ```ruby
181
- lla = Coordinates::LLA.new(lat: 37.7749, lng: -122.4192, alt: 15.0)
186
+ lla = Coordinate::LLA.new(lat: 37.7749, lng: -122.4192, alt: 15.0)
182
187
  lla.to_dms # => "37° 46' 29.64\" N, 122° 25' 9.12\" W, 15.00 m"
183
188
 
184
- Coordinates::LLA.from_dms("37° 46' 29.64\" N, 122° 25' 9.12\" W, 15.00 m")
189
+ Coordinate::LLA.from_dms("37° 46' 29.64\" N, 122° 25' 9.12\" W, 15.00 m")
185
190
  ```
186
191
 
187
192
  ### String-Based Coordinate Systems
@@ -189,12 +194,12 @@ Coordinates::LLA.from_dms("37° 46' 29.64\" N, 122° 25' 9.12\" W, 15.00 m")
189
194
  MGRS and USNG use string representations:
190
195
 
191
196
  ```ruby
192
- mgrs = Coordinates::MGRS.new(mgrs_string: "18SUJ2337006519")
193
- mgrs = Coordinates::MGRS.from_string("18SUJ2337006519")
197
+ mgrs = Coordinate::MGRS.new(mgrs_string: "18SUJ2337006519")
198
+ mgrs = Coordinate::MGRS.from_string("18SUJ2337006519")
194
199
  mgrs.to_s # => "18SUJ2337006519"
195
200
 
196
- usng = Coordinates::USNG.new(usng_string: "18T WL 12345 67890")
197
- usng = Coordinates::USNG.from_string("18T WL 12345 67890")
201
+ usng = Coordinate::USNG.new(usng_string: "18T WL 12345 67890")
202
+ usng = Coordinate::USNG.from_string("18T WL 12345 67890")
198
203
  usng.to_s # => "18T WL 12345 67890"
199
204
  ```
200
205
 
@@ -210,9 +215,9 @@ portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
210
215
  sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0.0)
211
216
 
212
217
  d = seattle.distance_to(portland) # => Distance (meters)
213
- d.meters # => 235393.17
218
+ d.meters # => 235385.71
214
219
  d.to_km.to_f # => 235.39
215
- d.to_mi.to_f # => 146.28
220
+ d.to_mi.to_f # => 146.26
216
221
 
217
222
  seattle.distance_to(portland, sf) # => [Distance, Distance] (radial)
218
223
  seattle.distance_to([portland, sf]) # => [Distance, Distance] (radial)
@@ -254,12 +259,12 @@ seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
254
259
  portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
255
260
 
256
261
  b = seattle.bearing_to(portland) # => Bearing
257
- b.degrees # => 188.2
258
- b.to_radians # => 3.28...
262
+ b.degrees # => 186.25
263
+ b.to_radians # => 3.25...
259
264
  b.to_compass # => "S"
260
265
  b.to_compass(points: 8) # => "S"
261
266
  b.reverse # => Bearing (back azimuth)
262
- b.to_s # => "188.2036°"
267
+ b.to_s # => "186.2539°"
263
268
  ```
264
269
 
265
270
  **Instance method `elevation_to`** — vertical look angle:
@@ -405,7 +410,7 @@ geoid.convert_vertical_datum(47.6205, -122.3493, 184.0, "HAE", "NAVD88")
405
410
  The `GeoidHeightSupport` module is mixed into LLA for convenience:
406
411
 
407
412
  ```ruby
408
- lla = Coordinates::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
413
+ lla = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
409
414
  lla.geoid_height # => geoid undulation in meters
410
415
  lla.orthometric_height # => height above mean sea level
411
416
  ```
@@ -416,10 +421,10 @@ A spatial hashing coordinate that encodes lat/lng into a compact, URL-friendly s
416
421
 
417
422
  ```ruby
418
423
  # From a geohash string
419
- gh36 = Coordinates::GH36.new("bdrdC26BqH")
424
+ gh36 = Coordinate::GH36.new("bdrdC26BqH")
420
425
 
421
426
  # From any coordinate
422
- gh36 = Coordinates::GH36.new(lla)
427
+ gh36 = Coordinate::GH36.new(lla)
423
428
  gh36 = lla.to_gh36(precision: 8)
424
429
 
425
430
  # Decode back to LLA
@@ -443,10 +448,10 @@ The standard Geohash (base-32) algorithm by Gustavo Niemeyer, widely supported b
443
448
 
444
449
  ```ruby
445
450
  # From a geohash string
446
- gh = Coordinates::GH.new("dr5ru7")
451
+ gh = Coordinate::GH.new("dr5ru7")
447
452
 
448
453
  # From any coordinate
449
- gh = Coordinates::GH.new(lla)
454
+ gh = Coordinate::GH.new(lla)
450
455
  gh = lla.to_gh(precision: 8)
451
456
 
452
457
  # Decode back to LLA
@@ -470,10 +475,10 @@ The Maidenhead Locator System used worldwide in amateur radio for grid square id
470
475
 
471
476
  ```ruby
472
477
  # From a Maidenhead locator string
473
- ham = Coordinates::HAM.new("FN31pr")
478
+ ham = Coordinate::HAM.new("FN31pr")
474
479
 
475
480
  # From any coordinate
476
- ham = Coordinates::HAM.new(lla)
481
+ ham = Coordinate::HAM.new(lla)
477
482
  ham = lla.to_ham(precision: 8)
478
483
 
479
484
  # Decode back to LLA
@@ -497,10 +502,10 @@ Google's open system for encoding locations into short, URL-friendly codes:
497
502
 
498
503
  ```ruby
499
504
  # From a plus code string
500
- olc = Coordinates::OLC.new("849VCWC8+R9")
505
+ olc = Coordinate::OLC.new("849VCWC8+R9")
501
506
 
502
507
  # From any coordinate
503
- olc = Coordinates::OLC.new(lla)
508
+ olc = Coordinate::OLC.new(lla)
504
509
  olc = lla.to_olc(precision: 11)
505
510
 
506
511
  # Decode back to LLA
@@ -522,22 +527,22 @@ olc.precision_in_meters # => { lat: 13.9, lng: 13.9 }
522
527
 
523
528
  ```ruby
524
529
  # Circle area
525
- center = Coordinates::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
530
+ center = Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
526
531
  circle = Areas::Circle.new(centroid: center, radius: 1000.0) # 1km radius
527
532
 
528
533
  # Polygon area
529
534
  points = [
530
- Coordinates::LLA.new(lat: 47.60, lng: -122.35, alt: 0.0),
531
- Coordinates::LLA.new(lat: 47.63, lng: -122.35, alt: 0.0),
532
- Coordinates::LLA.new(lat: 47.63, lng: -122.33, alt: 0.0),
533
- Coordinates::LLA.new(lat: 47.60, lng: -122.33, alt: 0.0),
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),
534
539
  ]
535
540
  polygon = Areas::Polygon.new(boundary: points)
536
541
  polygon.centroid # => computed centroid as LLA
537
542
 
538
543
  # BoundingBox area (accepts any coordinate type)
539
- nw = Coordinates::LLA.new(lat: 41.0, lng: -75.0)
540
- se = Coordinates::LLA.new(lat: 40.0, lng: -74.0)
544
+ nw = Coordinate::LLA.new(lat: 41.0, lng: -75.0)
545
+ se = Coordinate::LLA.new(lat: 40.0, lng: -74.0)
541
546
  rect = Areas::BoundingBox.new(nw: nw, se: se)
542
547
  rect.centroid # => LLA at center
543
548
  rect.ne # => computed NE corner
@@ -628,18 +633,18 @@ route.select { |c| c.lat > 40.72 }
628
633
  ```ruby
629
634
  liberty = Feature.new(
630
635
  label: "Statue of Liberty",
631
- geometry: Coordinates::LLA.new(lat: 40.6892, lng: -74.0445, alt: 0),
636
+ geometry: Coordinate::LLA.new(lat: 40.6892, lng: -74.0445, alt: 0),
632
637
  metadata: { category: "monument", year: 1886 }
633
638
  )
634
639
 
635
640
  empire = Feature.new(
636
641
  label: "Empire State Building",
637
- geometry: Coordinates::LLA.new(lat: 40.7484, lng: -73.9857, alt: 0),
642
+ geometry: Coordinate::LLA.new(lat: 40.7484, lng: -73.9857, alt: 0),
638
643
  metadata: { category: "building", floors: 102 }
639
644
  )
640
645
 
641
646
  liberty.distance_to(empire).to_km # => "8.24 km"
642
- liberty.bearing_to(empire).degrees # => 36.99
647
+ liberty.bearing_to(empire).degrees # => 36.95
643
648
 
644
649
  # Area geometries use the centroid for distance/bearing
645
650
  park = Feature.new(
@@ -723,14 +728,46 @@ corridor = route.to_corridor(width: 1000) # 1km wide polygon
723
728
  corridor = route.to_corridor(width: Distance.km(1))
724
729
  ```
725
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
+
726
763
  ### Web Mercator Tile Coordinates
727
764
 
728
765
  ```ruby
729
- wm = Coordinates::WebMercator.from_lla(lla)
766
+ wm = Coordinate::WebMercator.from_lla(lla)
730
767
  wm.to_tile_coordinates(15) # => [x_tile, y_tile, zoom]
731
768
  wm.to_pixel_coordinates(15) # => [x_pixel, y_pixel, zoom]
732
769
 
733
- Coordinates::WebMercator.from_tile_coordinates(5241, 11438, 15)
770
+ Coordinate::WebMercator.from_tile_coordinates(5241, 11438, 15)
734
771
  ```
735
772
 
736
773
  ## Available Datums
@@ -755,6 +792,7 @@ The [`examples/`](examples/) directory contains runnable demo scripts showing pr
755
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 |
756
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 |
757
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 |
758
796
 
759
797
  Run any example with:
760
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
 
data/docs/index.md CHANGED
@@ -9,7 +9,7 @@
9
9
  <td width="50%" valign="top">
10
10
  <h2>Key Features</h2>
11
11
  <lu>
12
- <li><strong>18 Coordinate Systems</strong> - LLA, ECEF, UTM, ENU, NED, MGRS, USNG, Web Mercator, UPS, State Plane, BNG, GH36, GH, HAM, OLC<br>
12
+ <li><strong>18 Coordinate Systems</strong> - LLA, ECEF, UTM, ENU, NED, MGRS, USNG, Web Mercator, UPS, State Plane, BNG, GH36, GH, HAM, OLC, GEOREF, GARS, H3<br>
13
13
  <li><strong>Full Bidirectional Conversions</strong> - Every system converts to and from every other system<br>
14
14
  <li><strong>Distance Calculations</strong> - Vincenty great-circle and straight-line with unit tracking<br>
15
15
  <li><strong>Bearing Calculations</strong> - Forward azimuth, back azimuth, compass directions, elevation angles<br>
@@ -18,6 +18,7 @@
18
18
  <li><strong>Segments</strong> - Directed two-point line segments with projection, intersection, and interpolation<br>
19
19
  <li><strong>Paths</strong> - Directed coordinate sequences with navigation, interpolation, closest approach, intersection, and area conversion<br>
20
20
  <li><strong>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
21
+ <li><strong>GeoJSON Export</strong> - Build FeatureCollections from any mix of objects and save to file<br>
21
22
  <li><strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
22
23
  <li><strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
23
24
  <li><strong>Multiple Datums</strong> - WGS84, Clarke 1866, GRS 1980, Airy 1830, and more<br>
@@ -35,33 +36,33 @@ Geodetic supports full bidirectional conversion between all 18 coordinate system
35
36
 
36
37
  | System | Class | Description |
37
38
  |--------|-------|-------------|
38
- | **LLA** | `Geodetic::Coordinate::LLA` | Latitude, Longitude, Altitude |
39
- | **ECEF** | `Geodetic::Coordinate::ECEF` | Earth-Centered, Earth-Fixed (X, Y, Z) |
40
- | **UTM** | `Geodetic::Coordinate::UTM` | Universal Transverse Mercator |
41
- | **ENU** | `Geodetic::Coordinate::ENU` | East, North, Up (local tangent plane) |
42
- | **NED** | `Geodetic::Coordinate::NED` | North, East, Down (local tangent plane) |
43
- | **MGRS** | `Geodetic::Coordinate::MGRS` | Military Grid Reference System |
44
- | **USNG** | `Geodetic::Coordinate::USNG` | United States National Grid |
45
- | **WebMercator** | `Geodetic::Coordinate::WebMercator` | Web Mercator projection (EPSG:3857) |
46
- | **UPS** | `Geodetic::Coordinate::UPS` | Universal Polar Stereographic |
47
- | **StatePlane** | `Geodetic::Coordinate::StatePlane` | State Plane Coordinate System |
48
- | **BNG** | `Geodetic::Coordinate::BNG` | British National Grid |
49
- | **GH36** | `Geodetic::Coordinate::GH36` | Geohash-36 (spatial hash, URL-friendly) |
50
- | **GH** | `Geodetic::Coordinate::GH` | Geohash base-32 (standard geohash, widely supported) |
51
- | **HAM** | `Geodetic::Coordinate::HAM` | Maidenhead Locator System (amateur radio grid squares) |
52
- | **OLC** | `Geodetic::Coordinate::OLC` | Open Location Code / Plus Codes (Google's location encoding) |
53
- | **GEOREF** | `Geodetic::Coordinate::GEOREF` | World Geographic Reference System (aviation/military) |
54
- | **GARS** | `Geodetic::Coordinate::GARS` | Global Area Reference System (NGA standard) |
55
- | **H3** | `Geodetic::Coordinate::H3` | Uber's hexagonal hierarchical index (requires `libh3`) |
39
+ | [**LLA**](coordinate-systems/lla.md) | `Geodetic::Coordinate::LLA` | Latitude, Longitude, Altitude |
40
+ | [**ECEF**](coordinate-systems/ecef.md) | `Geodetic::Coordinate::ECEF` | Earth-Centered, Earth-Fixed (X, Y, Z) |
41
+ | [**UTM**](coordinate-systems/utm.md) | `Geodetic::Coordinate::UTM` | Universal Transverse Mercator |
42
+ | [**ENU**](coordinate-systems/enu.md) | `Geodetic::Coordinate::ENU` | East, North, Up (local tangent plane) |
43
+ | [**NED**](coordinate-systems/ned.md) | `Geodetic::Coordinate::NED` | North, East, Down (local tangent plane) |
44
+ | [**MGRS**](coordinate-systems/mgrs.md) | `Geodetic::Coordinate::MGRS` | Military Grid Reference System |
45
+ | [**USNG**](coordinate-systems/usng.md) | `Geodetic::Coordinate::USNG` | United States National Grid |
46
+ | [**WebMercator**](coordinate-systems/web-mercator.md) | `Geodetic::Coordinate::WebMercator` | Web Mercator projection (EPSG:3857) |
47
+ | [**UPS**](coordinate-systems/ups.md) | `Geodetic::Coordinate::UPS` | Universal Polar Stereographic |
48
+ | [**StatePlane**](coordinate-systems/state-plane.md) | `Geodetic::Coordinate::StatePlane` | State Plane Coordinate System |
49
+ | [**BNG**](coordinate-systems/bng.md) | `Geodetic::Coordinate::BNG` | British National Grid |
50
+ | [**GH36**](coordinate-systems/gh36.md) | `Geodetic::Coordinate::GH36` | Geohash-36 (spatial hash, URL-friendly) |
51
+ | [**GH**](coordinate-systems/gh.md) | `Geodetic::Coordinate::GH` | Geohash base-32 (standard geohash, widely supported) |
52
+ | [**HAM**](coordinate-systems/ham.md) | `Geodetic::Coordinate::HAM` | Maidenhead Locator System (amateur radio grid squares) |
53
+ | [**OLC**](coordinate-systems/olc.md) | `Geodetic::Coordinate::OLC` | Open Location Code / Plus Codes (Google's location encoding) |
54
+ | [**GEOREF**](coordinate-systems/georef.md) | `Geodetic::Coordinate::GEOREF` | World Geographic Reference System (aviation/military) |
55
+ | [**GARS**](coordinate-systems/gars.md) | `Geodetic::Coordinate::GARS` | Global Area Reference System (NGA standard) |
56
+ | [**H3**](coordinate-systems/h3.md) | `Geodetic::Coordinate::H3` | Uber's hexagonal hierarchical index (requires `libh3`) |
56
57
 
57
58
  ## Additional Features
58
59
 
59
- - **16 geodetic datums** -- WGS84, GRS 1980, Clarke 1866, Airy 1830, Bessel 1841, and more. All conversion methods accept an optional datum parameter, defaulting to WGS84.
60
- - **Geoid height calculations** -- Convert between ellipsoidal and orthometric heights using models such as EGM96, EGM2008, GEOID18, and GEOID12B.
61
- - **Geographic areas** -- `Geodetic::Areas::Circle`, `Geodetic::Areas::Polygon`, `Geodetic::Areas::BoundingBox`, plus polygon subclasses (`Triangle`, `Rectangle`, `Pentagon`, `Hexagon`, `Octagon`) for point-in-area testing.
62
- - **Segments** -- `Geodetic::Segment` is a directed two-point line segment with projection, intersection detection, interpolation, and membership testing. It is the geometric primitive underlying Path and Polygon operations.
63
- - **Paths** -- `Geodetic::Path` is a directed, ordered sequence of unique coordinates supporting navigation, segment analysis, interpolation, closest approach (geometric projection), containment testing, bounding boxes, polygon conversion, and path intersection detection.
64
- - **Features** -- `Geodetic::Feature` wraps any coordinate, area, or path with a label and metadata hash, delegating `distance_to` and `bearing_to` to the underlying geometry.
60
+ - **[16 geodetic datums](reference/datums.md)** -- WGS84, GRS 1980, Clarke 1866, Airy 1830, Bessel 1841, and more. All conversion methods accept an optional datum parameter, defaulting to WGS84.
61
+ - **[Geoid height calculations](reference/geoid-height.md)** -- Convert between ellipsoidal and orthometric heights using models such as EGM96, EGM2008, GEOID18, and GEOID12B.
62
+ - **[Geographic areas](reference/areas.md)** -- `Geodetic::Areas::Circle`, `Geodetic::Areas::Polygon`, `Geodetic::Areas::BoundingBox`, plus polygon subclasses (`Triangle`, `Rectangle`, `Pentagon`, `Hexagon`, `Octagon`) for point-in-area testing.
63
+ - **[Segments](reference/segment.md)** -- `Geodetic::Segment` is a directed two-point line segment with projection, intersection detection, interpolation, and membership testing. It is the geometric primitive underlying Path and Polygon operations.
64
+ - **[Paths](reference/path.md)** -- `Geodetic::Path` is a directed, ordered sequence of unique coordinates supporting navigation, segment analysis, interpolation, closest approach (geometric projection), containment testing, bounding boxes, polygon conversion, and path intersection detection.
65
+ - **[Features](reference/feature.md)** -- `Geodetic::Feature` wraps any coordinate, area, or path with a label and metadata hash, delegating `distance_to` and `bearing_to` to the underlying geometry.
65
66
 
66
67
  ## Design Principles
67
68
 
@@ -91,5 +92,26 @@ puts lla_again.to_s
91
92
 
92
93
  ## Documentation
93
94
 
94
- - [Getting Started: Installation](getting-started/installation.md)
95
- - [Getting Started: Quick Start](getting-started/quick-start.md)
95
+ ### Getting Started
96
+
97
+ - [Installation](getting-started/installation.md)
98
+ - [Quick Start](getting-started/quick-start.md)
99
+
100
+ ### Coordinate Systems
101
+
102
+ - [Coordinate Systems Overview](coordinate-systems/index.md)
103
+
104
+ ### Reference
105
+
106
+ - [Conversions](reference/conversions.md)
107
+ - [Serialization](reference/serialization.md)
108
+ - [Datums](reference/datums.md)
109
+ - [Geoid Height](reference/geoid-height.md)
110
+ - [Areas](reference/areas.md)
111
+ - [Path](reference/path.md)
112
+ - [Segment](reference/segment.md)
113
+ - [Feature](reference/feature.md)
114
+ - [Vector](reference/vector.md)
115
+ - [Arithmetic](reference/arithmetic.md)
116
+ - [GeoJSON Export](reference/geojson.md)
117
+ - [Map Rendering](reference/map-rendering.md)
@@ -30,7 +30,7 @@ seg = seattle + portland # => Geodetic::Segment
30
30
  seg.start_point # => seattle
31
31
  seg.end_point # => portland
32
32
  seg.length # => Distance (~235 km)
33
- seg.bearing # => Bearing (~188°)
33
+ seg.bearing # => Bearing (~186°)
34
34
  ```
35
35
 
36
36
  Works across any coordinate system — the Segment converts both points to LLA internally:
@@ -250,7 +250,7 @@ portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
250
250
  sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0.0)
251
251
 
252
252
  # Radial distances from receiver
253
- seattle.distance_to(portland) # => Distance (235393.17 m)
253
+ seattle.distance_to(portland) # => Distance (235385.71 m)
254
254
  seattle.distance_to(portland, sf) # => [Distance, Distance] (Array)
255
255
 
256
256
  # Consecutive chain distances
@@ -274,7 +274,7 @@ Both `distance_to` and `straight_line_distance_to` accept any coordinate type. C
274
274
  ```ruby
275
275
  utm = seattle.to_utm
276
276
  mgrs = Geodetic::Coordinate::MGRS.from_lla(portland)
277
- utm.distance_to(mgrs) # => Distance (235393.17 m)
277
+ utm.distance_to(mgrs) # => Distance (235385.71 m)
278
278
  ```
279
279
 
280
280
  ### ENU and NED (Relative Systems)
@@ -308,7 +308,7 @@ sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0.0)
308
308
 
309
309
  # Forward azimuth
310
310
  b = seattle.bearing_to(portland) # => Bearing
311
- b.degrees # => 188.2...
311
+ b.degrees # => 186.25...
312
312
  b.to_compass(points: 8) # => "S"
313
313
  b.reverse # => Bearing (back azimuth)
314
314
 
@@ -65,7 +65,7 @@ liberty.distance_to(LLA.new(lat: 40.7484, lng: -73.9857, alt: 0))
65
65
  Returns a `Geodetic::Bearing` from this feature to another feature, coordinate, or area. Uses the same centroid resolution as `distance_to`.
66
66
 
67
67
  ```ruby
68
- liberty.bearing_to(empire).degrees # => 36.99
68
+ liberty.bearing_to(empire).degrees # => 36.95
69
69
  liberty.bearing_to(empire).to_compass # => "NE"
70
70
  ```
71
71