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,257 @@
|
|
|
1
|
+
# Geodetic::Coordinates::UTM
|
|
2
|
+
|
|
3
|
+
Universal Transverse Mercator -- a projected coordinate system that divides the Earth into 60 longitudinal zones (each 6 degrees wide) and two hemispheres (North and South). Positions within each zone are expressed as easting and northing distances in meters from the zone's origin. UTM is widely used in military, engineering, and surveying applications because it provides a flat, metric grid that minimizes distortion within each zone.
|
|
4
|
+
|
|
5
|
+
## Constructor
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
Geodetic::Coordinates::UTM.new(easting: 0.0, northing: 0.0, altitude: 0.0, zone: 1, hemisphere: 'N')
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| Parameter | Type | Default | Description |
|
|
12
|
+
|--------------|---------|---------|---------------------------------------------|
|
|
13
|
+
| `easting` | Float | `0.0` | Easting in meters (must be >= 0) |
|
|
14
|
+
| `northing` | Float | `0.0` | Northing in meters (must be >= 0) |
|
|
15
|
+
| `altitude` | Float | `0.0` | Altitude in meters above the ellipsoid |
|
|
16
|
+
| `zone` | Integer | `1` | UTM zone number (1..60) |
|
|
17
|
+
| `hemisphere` | String | `'N'` | Hemisphere: `'N'` (North) or `'S'` (South) |
|
|
18
|
+
|
|
19
|
+
Numeric values are coerced via `.to_f` / `.to_i`. The hemisphere string is uppercased automatically.
|
|
20
|
+
|
|
21
|
+
### Validation
|
|
22
|
+
|
|
23
|
+
The constructor raises `ArgumentError` if:
|
|
24
|
+
|
|
25
|
+
- `zone` is outside the range `1..60`
|
|
26
|
+
- `hemisphere` is not `'N'` or `'S'`
|
|
27
|
+
- `easting` is negative
|
|
28
|
+
- `northing` is negative
|
|
29
|
+
|
|
30
|
+
## Attributes
|
|
31
|
+
|
|
32
|
+
| Attribute | Alias | Access |
|
|
33
|
+
|--------------|-------|------------|
|
|
34
|
+
| `easting` | `x` | read/write |
|
|
35
|
+
| `northing` | `y` | read/write |
|
|
36
|
+
| `altitude` | `z` | read/write |
|
|
37
|
+
| `zone` | -- | read/write |
|
|
38
|
+
| `hemisphere` | -- | read/write |
|
|
39
|
+
|
|
40
|
+
Setters validate and coerce types. `easting` and `northing` must be non-negative (`ArgumentError`). `zone` must be `1..60`. `hemisphere` must be `'N'` or `'S'` (auto-upcased). `altitude` has no range constraint.
|
|
41
|
+
|
|
42
|
+
## Conversions
|
|
43
|
+
|
|
44
|
+
All conversion methods accept an optional `datum` parameter (defaults to `Geodetic::WGS84`).
|
|
45
|
+
|
|
46
|
+
### to_lla(datum = WGS84)
|
|
47
|
+
|
|
48
|
+
Converts to geodetic Latitude, Longitude, Altitude coordinates. Uses a series expansion for the inverse UTM projection.
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
utm = Geodetic::Coordinates::UTM.new(easting: 580000.0, northing: 4510000.0, zone: 18, hemisphere: 'N')
|
|
52
|
+
lla = utm.to_lla
|
|
53
|
+
# => Geodetic::Coordinates::LLA
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### UTM.from_lla(lla, datum = WGS84)
|
|
57
|
+
|
|
58
|
+
Creates a UTM from an LLA instance. The zone and hemisphere are computed automatically. Raises `ArgumentError` if the argument is not an `LLA`.
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
utm = Geodetic::Coordinates::UTM.from_lla(lla)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### to_ecef(datum = WGS84)
|
|
65
|
+
|
|
66
|
+
Converts to Earth-Centered, Earth-Fixed Cartesian coordinates. Internally converts to LLA first, then to ECEF.
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
ecef = utm.to_ecef
|
|
70
|
+
# => Geodetic::Coordinates::ECEF
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### UTM.from_ecef(ecef, datum = WGS84)
|
|
74
|
+
|
|
75
|
+
Creates a UTM from an ECEF instance. Raises `ArgumentError` if the argument is not an `ECEF`.
|
|
76
|
+
|
|
77
|
+
### to_enu(reference_lla, datum = WGS84)
|
|
78
|
+
|
|
79
|
+
Converts to East-North-Up local tangent plane coordinates relative to a reference LLA position.
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
origin = Geodetic::Coordinates::LLA.new(lat: 40.7128, lng: -74.0060, alt: 10.0)
|
|
83
|
+
enu = utm.to_enu(origin)
|
|
84
|
+
# => Geodetic::Coordinates::ENU
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Raises `ArgumentError` if `reference_lla` is not an `LLA`.
|
|
88
|
+
|
|
89
|
+
### UTM.from_enu(enu, reference_lla, datum = WGS84)
|
|
90
|
+
|
|
91
|
+
Creates a UTM from an ENU instance and a reference LLA origin. Raises `ArgumentError` if the arguments are not the expected types.
|
|
92
|
+
|
|
93
|
+
### to_ned(reference_lla, datum = WGS84)
|
|
94
|
+
|
|
95
|
+
Converts to North-East-Down local tangent plane coordinates relative to a reference LLA position.
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
ned = utm.to_ned(origin)
|
|
99
|
+
# => Geodetic::Coordinates::NED
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Raises `ArgumentError` if `reference_lla` is not an `LLA`.
|
|
103
|
+
|
|
104
|
+
### UTM.from_ned(ned, reference_lla, datum = WGS84)
|
|
105
|
+
|
|
106
|
+
Creates a UTM from a NED instance and a reference LLA origin. Raises `ArgumentError` if the arguments are not the expected types.
|
|
107
|
+
|
|
108
|
+
## Serialization
|
|
109
|
+
|
|
110
|
+
### to_s
|
|
111
|
+
|
|
112
|
+
Returns a comma-separated string of `easting, northing, altitude, zone, hemisphere`.
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
utm = Geodetic::Coordinates::UTM.new(easting: 580000.0, northing: 4510000.0, altitude: 10.0, zone: 18, hemisphere: 'N')
|
|
116
|
+
utm.to_s
|
|
117
|
+
# => "580000.0, 4510000.0, 10.0, 18, N"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### to_a
|
|
121
|
+
|
|
122
|
+
Returns a five-element array `[easting, northing, altitude, zone, hemisphere]`.
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
utm.to_a
|
|
126
|
+
# => [580000.0, 4510000.0, 10.0, 18, "N"]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### UTM.from_string(string)
|
|
130
|
+
|
|
131
|
+
Parses a comma-separated string into a UTM.
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
utm = Geodetic::Coordinates::UTM.from_string("580000.0, 4510000.0, 10.0, 18, N")
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### UTM.from_array(array)
|
|
138
|
+
|
|
139
|
+
Creates a UTM from a five-element array `[easting, northing, altitude, zone, hemisphere]`.
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
utm = Geodetic::Coordinates::UTM.from_array([580000.0, 4510000.0, 10.0, 18, "N"])
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Additional Methods
|
|
146
|
+
|
|
147
|
+
### ==(other)
|
|
148
|
+
|
|
149
|
+
Compares two UTM instances for approximate equality. Returns `true` if:
|
|
150
|
+
|
|
151
|
+
- `|easting difference| <= 1e-6`
|
|
152
|
+
- `|northing difference| <= 1e-6`
|
|
153
|
+
- `|altitude difference| <= 1e-6`
|
|
154
|
+
- `zone` values are equal
|
|
155
|
+
- `hemisphere` values are equal
|
|
156
|
+
|
|
157
|
+
Returns `false` if `other` is not a `UTM`.
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
a = Geodetic::Coordinates::UTM.new(easting: 580000.0, northing: 4510000.0, zone: 18, hemisphere: 'N')
|
|
161
|
+
b = Geodetic::Coordinates::UTM.new(easting: 580000.0, northing: 4510000.0, zone: 18, hemisphere: 'N')
|
|
162
|
+
a == b
|
|
163
|
+
# => true
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### distance_to(other, *others)
|
|
167
|
+
|
|
168
|
+
Computes the Vincenty great-circle distance to one or more other coordinates. Works across UTM zones and across different coordinate types (coordinates are converted to LLA internally). Returns a `Distance` for a single target or an Array of `Distance` objects for multiple targets (radial distances from the receiver).
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
a = Geodetic::Coordinates::UTM.new(easting: 580000.0, northing: 4510000.0, altitude: 10.0, zone: 18, hemisphere: 'N')
|
|
172
|
+
b = Geodetic::Coordinates::UTM.new(easting: 580100.0, northing: 4510200.0, altitude: 15.0, zone: 18, hemisphere: 'N')
|
|
173
|
+
a.distance_to(b)
|
|
174
|
+
# => Distance (meters, great-circle distance)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### straight_line_distance_to(other, *others)
|
|
178
|
+
|
|
179
|
+
Computes the Euclidean (straight-line) distance between two points in ECEF space. Accepts any coordinate type. Returns a `Distance` for a single target or an Array of `Distance` objects for multiple targets.
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
a.straight_line_distance_to(b)
|
|
183
|
+
# => Distance (meters)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### same_zone?(other)
|
|
187
|
+
|
|
188
|
+
Returns `true` if both UTM instances share the same zone number and hemisphere.
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
a = Geodetic::Coordinates::UTM.new(easting: 580000.0, northing: 4510000.0, zone: 18, hemisphere: 'N')
|
|
192
|
+
b = Geodetic::Coordinates::UTM.new(easting: 590000.0, northing: 4520000.0, zone: 18, hemisphere: 'N')
|
|
193
|
+
a.same_zone?(b)
|
|
194
|
+
# => true
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Raises `ArgumentError` if `other` is not a `UTM`.
|
|
198
|
+
|
|
199
|
+
### central_meridian
|
|
200
|
+
|
|
201
|
+
Returns the central meridian longitude (in degrees) for the UTM zone.
|
|
202
|
+
|
|
203
|
+
```ruby
|
|
204
|
+
utm = Geodetic::Coordinates::UTM.new(zone: 18, hemisphere: 'N')
|
|
205
|
+
utm.central_meridian
|
|
206
|
+
# => -75
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The formula is: `(zone - 1) * 6 - 180 + 3`.
|
|
210
|
+
|
|
211
|
+
## Code Examples
|
|
212
|
+
|
|
213
|
+
### Round-trip conversion
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
require 'geodetic'
|
|
217
|
+
|
|
218
|
+
# Start with an LLA and convert to UTM
|
|
219
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 40.7128, lng: -74.0060, alt: 10.0)
|
|
220
|
+
utm = lla.to_utm
|
|
221
|
+
puts utm.to_s
|
|
222
|
+
# zone and hemisphere are determined automatically
|
|
223
|
+
|
|
224
|
+
# Convert back to LLA
|
|
225
|
+
lla_roundtrip = utm.to_lla
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Distance calculations
|
|
229
|
+
|
|
230
|
+
```ruby
|
|
231
|
+
point_a = Geodetic::Coordinates::UTM.new(
|
|
232
|
+
easting: 583960.0, northing: 4507523.0,
|
|
233
|
+
altitude: 10.0, zone: 18, hemisphere: 'N'
|
|
234
|
+
)
|
|
235
|
+
point_b = Geodetic::Coordinates::UTM.new(
|
|
236
|
+
easting: 584060.0, northing: 4507623.0,
|
|
237
|
+
altitude: 15.0, zone: 18, hemisphere: 'N'
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Great-circle distance (works across zones and coordinate types)
|
|
241
|
+
puts "Distance: #{point_a.distance_to(point_b).meters} m"
|
|
242
|
+
|
|
243
|
+
# Straight-line (Euclidean) distance
|
|
244
|
+
puts "Straight: #{point_a.straight_line_distance_to(point_b).meters} m"
|
|
245
|
+
|
|
246
|
+
puts "Same zone? #{point_a.same_zone?(point_b)}"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Working with zones
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
# Check which zone a location falls in
|
|
253
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 48.8566, lng: 2.3522, alt: 35.0) # Paris
|
|
254
|
+
utm = lla.to_utm
|
|
255
|
+
puts "Zone: #{utm.zone}#{utm.hemisphere}" # => "31N"
|
|
256
|
+
puts "Central meridian: #{utm.central_meridian}" # => 3
|
|
257
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Geodetic::Coordinates::WebMercator
|
|
2
|
+
|
|
3
|
+
## Web Mercator (EPSG:3857)
|
|
4
|
+
|
|
5
|
+
Web Mercator is the de facto standard projection used by major web mapping platforms including Google Maps, OpenStreetMap, and Bing Maps. It projects the Earth onto a square grid using a spherical Mercator projection, making it well-suited for tiled map rendering.
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
point = Geodetic::Coordinates::WebMercator.new(x: 0.0, y: 0.0)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Parameters `x` and `y` are specified in **meters** from the projection origin (the intersection of the Equator and the Prime Meridian).
|
|
14
|
+
|
|
15
|
+
## Constants
|
|
16
|
+
|
|
17
|
+
| Constant | Description |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `EARTH_RADIUS` | Radius of the Earth used for projection calculations |
|
|
20
|
+
| `ORIGIN_SHIFT` | Half the circumference of the Earth at the Equator; defines the extent of the projected coordinate space |
|
|
21
|
+
| `MAX_LATITUDE` | Maximum representable latitude (~85.051°); the projection is undefined beyond this limit |
|
|
22
|
+
|
|
23
|
+
## Tile Coordinate Methods
|
|
24
|
+
|
|
25
|
+
Convert between Web Mercator coordinates and map tile indices at a given zoom level.
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
tile_x, tile_y = point.to_tile_coordinates(zoom)
|
|
29
|
+
|
|
30
|
+
point = Geodetic::Coordinates::WebMercator.from_tile_coordinates(x, y, zoom)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Pixel Coordinate Methods
|
|
34
|
+
|
|
35
|
+
Convert between Web Mercator coordinates and pixel positions at a given zoom level and tile size.
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
pixel_x, pixel_y = point.to_pixel_coordinates(zoom, tile_size)
|
|
39
|
+
|
|
40
|
+
point = Geodetic::Coordinates::WebMercator.from_pixel_coordinates(x, y, zoom, tile_size)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Tile Bounds
|
|
44
|
+
|
|
45
|
+
Retrieve the bounding box of a specific tile.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
bounds = Geodetic::Coordinates::WebMercator.tile_bounds(tile_x, tile_y, zoom)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Validation and Utility Methods
|
|
52
|
+
|
|
53
|
+
| Method | Description |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `valid?` | Returns `true` if the coordinates fall within the valid Web Mercator extent |
|
|
56
|
+
| `clamp!` | Clamps coordinates to the valid range, modifying the object in place |
|
|
57
|
+
|
|
58
|
+
### Universal Distance Methods
|
|
59
|
+
|
|
60
|
+
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.
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
wm_a = Geodetic::Coordinates::WebMercator.new(x: -13627665.0, y: 6044499.0)
|
|
64
|
+
wm_b = Geodetic::Coordinates::WebMercator.new(x: -13631157.0, y: 5694043.0)
|
|
65
|
+
wm_a.distance_to(wm_b) # => Distance (meters, great-circle)
|
|
66
|
+
wm_a.straight_line_distance_to(wm_b) # => Distance (meters, Euclidean)
|
|
67
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
Geodetic is tested with **Ruby 4.0.1**. It has no runtime dependencies beyond the Ruby standard library.
|
|
6
|
+
|
|
7
|
+
## Add to Your Gemfile
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem "geodetic"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then run:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Or Install Directly
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
gem install geodetic
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Require the Gem
|
|
26
|
+
|
|
27
|
+
The simplest way to load Geodetic is to require the top-level module:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
require "geodetic"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This loads the core module, the `Geodetic::Datum` class, and the `Geodetic::GeoidHeight` support. Coordinate types are loaded on demand when you first perform a conversion.
|
|
34
|
+
|
|
35
|
+
## Requiring Specific Coordinate Types
|
|
36
|
+
|
|
37
|
+
If you want to use a coordinate class directly without going through a conversion, require it explicitly:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
require "geodetic"
|
|
41
|
+
require "geodetic/coordinates/lla"
|
|
42
|
+
require "geodetic/coordinates/ecef"
|
|
43
|
+
require "geodetic/coordinates/utm"
|
|
44
|
+
require "geodetic/coordinates/enu"
|
|
45
|
+
require "geodetic/coordinates/ned"
|
|
46
|
+
require "geodetic/coordinates/mgrs"
|
|
47
|
+
require "geodetic/coordinates/usng"
|
|
48
|
+
require "geodetic/coordinates/web_mercator"
|
|
49
|
+
require "geodetic/coordinates/ups"
|
|
50
|
+
require "geodetic/coordinates/state_plane"
|
|
51
|
+
require "geodetic/coordinates/bng"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
You only need to require the types you plan to construct directly. Conversion methods handle their own requires internally.
|
|
55
|
+
|
|
56
|
+
## Verify the Installation
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
require "geodetic"
|
|
60
|
+
require "geodetic/coordinates/lla"
|
|
61
|
+
|
|
62
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
|
|
63
|
+
puts lla.to_s
|
|
64
|
+
#=> "47.6205, -122.3493, 184.0"
|
|
65
|
+
```
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Quick Start
|
|
2
|
+
|
|
3
|
+
This guide walks through the core features of Geodetic using the Seattle Space Needle as a reference point.
|
|
4
|
+
|
|
5
|
+
## 1. Create an LLA Coordinate
|
|
6
|
+
|
|
7
|
+
LLA (Latitude, Longitude, Altitude) is the most common starting point. All constructors use keyword arguments.
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
require "geodetic"
|
|
11
|
+
require "geodetic/coordinates/lla"
|
|
12
|
+
|
|
13
|
+
space_needle = Geodetic::Coordinates::LLA.new(
|
|
14
|
+
lat: 47.6205,
|
|
15
|
+
lng: -122.3493,
|
|
16
|
+
alt: 184.0
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
puts space_needle.lat #=> 47.6205
|
|
20
|
+
puts space_needle.longitude #=> -122.3493 (alias for lng)
|
|
21
|
+
puts space_needle.alt #=> 184.0
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 2. Convert to ECEF
|
|
25
|
+
|
|
26
|
+
ECEF (Earth-Centered, Earth-Fixed) represents a position as X, Y, Z coordinates in meters relative to the center of the Earth.
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
ecef = space_needle.to_ecef
|
|
30
|
+
|
|
31
|
+
puts ecef.x # X coordinate in meters
|
|
32
|
+
puts ecef.y # Y coordinate in meters
|
|
33
|
+
puts ecef.z # Z coordinate in meters
|
|
34
|
+
|
|
35
|
+
# Convert back to LLA
|
|
36
|
+
lla = ecef.to_lla
|
|
37
|
+
puts lla.to_s #=> "47.6205, -122.3493, 184.0"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 3. Convert to UTM
|
|
41
|
+
|
|
42
|
+
UTM (Universal Transverse Mercator) is widely used in mapping and surveying.
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
utm = space_needle.to_utm
|
|
46
|
+
|
|
47
|
+
puts utm.easting # easting in meters
|
|
48
|
+
puts utm.northing # northing in meters
|
|
49
|
+
puts utm.altitude # altitude in meters
|
|
50
|
+
puts utm.zone # UTM zone number (e.g., 10)
|
|
51
|
+
puts utm.hemisphere # "N" or "S"
|
|
52
|
+
|
|
53
|
+
# Convert back to LLA
|
|
54
|
+
lla = utm.to_lla
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 4. Convert to Local Coordinates (ENU and NED)
|
|
58
|
+
|
|
59
|
+
Local tangent plane coordinate systems require a reference point. ENU (East, North, Up) and NED (North, East, Down) are commonly used in navigation and aerospace.
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
# Define a reference point (e.g., a nearby base station)
|
|
63
|
+
reference = Geodetic::Coordinates::LLA.new(
|
|
64
|
+
lat: 47.6062,
|
|
65
|
+
lng: -122.3321,
|
|
66
|
+
alt: 0.0
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Convert to ENU relative to the reference
|
|
70
|
+
enu = space_needle.to_enu(reference)
|
|
71
|
+
|
|
72
|
+
puts enu.east # meters east of reference (alias: e)
|
|
73
|
+
puts enu.north # meters north of reference (alias: n)
|
|
74
|
+
puts enu.up # meters above reference (alias: u)
|
|
75
|
+
|
|
76
|
+
# Convert to NED relative to the reference
|
|
77
|
+
ned = space_needle.to_ned(reference)
|
|
78
|
+
|
|
79
|
+
puts ned.north # meters north of reference (alias: n)
|
|
80
|
+
puts ned.east # meters east of reference (alias: e)
|
|
81
|
+
puts ned.down # meters below reference (alias: d)
|
|
82
|
+
|
|
83
|
+
# ENU and NED convert directly to each other
|
|
84
|
+
ned_from_enu = enu.to_ned
|
|
85
|
+
enu_from_ned = ned.to_enu
|
|
86
|
+
|
|
87
|
+
# Convert back to LLA
|
|
88
|
+
lla = enu.to_lla(reference)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## 5. Use DMS Format
|
|
92
|
+
|
|
93
|
+
LLA coordinates can be displayed and parsed in Degrees, Minutes, Seconds format.
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
# Convert to DMS string
|
|
97
|
+
dms = space_needle.to_dms
|
|
98
|
+
puts dms #=> "47 37' 13.80\" N, 122 20' 57.48\" W, 184.00 m"
|
|
99
|
+
|
|
100
|
+
# Parse a DMS string back to LLA
|
|
101
|
+
lla = Geodetic::Coordinates::LLA.from_dms("47 37' 13.80\" N, 122 20' 57.48\" W, 184.00 m")
|
|
102
|
+
puts lla.lat #=> 47.6205
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## 6. Serialize and Deserialize
|
|
106
|
+
|
|
107
|
+
Every coordinate type supports `to_s`, `to_a`, `from_string`, and `from_array` for serialization.
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
# Serialize to string and array
|
|
111
|
+
str = space_needle.to_s #=> "47.6205, -122.3493, 184.0"
|
|
112
|
+
arr = space_needle.to_a #=> [47.6205, -122.3493, 184.0]
|
|
113
|
+
|
|
114
|
+
# Deserialize
|
|
115
|
+
lla_from_str = Geodetic::Coordinates::LLA.from_string(str)
|
|
116
|
+
lla_from_arr = Geodetic::Coordinates::LLA.from_array(arr)
|
|
117
|
+
|
|
118
|
+
# Works the same for all coordinate types
|
|
119
|
+
ecef = space_needle.to_ecef
|
|
120
|
+
ecef_str = ecef.to_s
|
|
121
|
+
ecef_arr = ecef.to_a
|
|
122
|
+
ecef_restored = Geodetic::Coordinates::ECEF.from_string(ecef_str)
|
|
123
|
+
ecef_restored = Geodetic::Coordinates::ECEF.from_array(ecef_arr)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 7. Work with Datums
|
|
127
|
+
|
|
128
|
+
Geodetic ships with 16 geodetic datums. All conversion methods default to WGS84 but accept an optional datum parameter.
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
# List available datums
|
|
132
|
+
Geodetic::Datum.list
|
|
133
|
+
|
|
134
|
+
# Use a specific datum
|
|
135
|
+
clarke = Geodetic::Datum.new(name: "CLARKE_1866")
|
|
136
|
+
|
|
137
|
+
ecef_clarke = space_needle.to_ecef(clarke)
|
|
138
|
+
utm_clarke = space_needle.to_utm(clarke)
|
|
139
|
+
|
|
140
|
+
# Inspect datum properties
|
|
141
|
+
puts clarke.name #=> "CLARKE_1866"
|
|
142
|
+
puts clarke.desc #=> "Clarke 1866"
|
|
143
|
+
puts clarke.a #=> 6378206.4 (semi-major axis in meters)
|
|
144
|
+
puts clarke.b #=> 6356583.7999989809 (semi-minor axis in meters)
|
|
145
|
+
puts clarke.f #=> flattening
|
|
146
|
+
puts clarke.e2 #=> eccentricity squared
|
|
147
|
+
|
|
148
|
+
# Look up datum info without creating an instance
|
|
149
|
+
info = Geodetic::Datum.get("WGS84")
|
|
150
|
+
puts info["desc"] #=> "World Geodetic System 1984"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Available datums include: WGS84, WGS72, GRS_1980, CLARKE_1866, CLARKE_1880, AIRY, MODIFIED_AIRY, AUSTRALIAN_NATIONAL, BESSEL_1841, EVEREST_INDIA_1830, EVEREST_BRUNEI_E_MALAYSIA, EVEREST_W_MALAYSIA_SINGAPORE, HELMERT_1906, HOUGH_1960, INTERNATIONAL_1924, and SOUTH_AMERICAN_1969.
|
|
154
|
+
|
|
155
|
+
## 8. Geoid Height
|
|
156
|
+
|
|
157
|
+
The `GeoidHeight` class converts between ellipsoidal heights (HAE) and orthometric heights (e.g., NAVD88, MSL). LLA coordinates include geoid height support directly.
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
# Get the geoid undulation at a location
|
|
161
|
+
undulation = space_needle.geoid_height
|
|
162
|
+
puts undulation # geoid height in meters (EGM2008 model)
|
|
163
|
+
|
|
164
|
+
# Get orthometric height (height above mean sea level)
|
|
165
|
+
ortho = space_needle.orthometric_height
|
|
166
|
+
puts ortho # orthometric height in meters
|
|
167
|
+
|
|
168
|
+
# Convert between vertical datums
|
|
169
|
+
lla_navd88 = space_needle.convert_height_datum("HAE", "NAVD88")
|
|
170
|
+
|
|
171
|
+
# Use a different geoid model
|
|
172
|
+
undulation_egm96 = space_needle.geoid_height("EGM96")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Available geoid models: EGM96, EGM2008, GEOID18, GEOID12B.
|
data/docs/index.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Geodetic
|
|
2
|
+
|
|
3
|
+
Geodetic is a Ruby gem for converting between geodetic coordinate systems. It provides a clean, consistent API for working with 12 coordinate systems, 16 geodetic datums, geoid height calculations, and geographic area computations.
|
|
4
|
+
|
|
5
|
+
## Coordinate Systems
|
|
6
|
+
|
|
7
|
+
Geodetic supports full bidirectional conversion between all 12 coordinate systems:
|
|
8
|
+
|
|
9
|
+
| System | Class | Description |
|
|
10
|
+
|--------|-------|-------------|
|
|
11
|
+
| **LLA** | `Geodetic::Coordinates::LLA` | Latitude, Longitude, Altitude |
|
|
12
|
+
| **ECEF** | `Geodetic::Coordinates::ECEF` | Earth-Centered, Earth-Fixed (X, Y, Z) |
|
|
13
|
+
| **UTM** | `Geodetic::Coordinates::UTM` | Universal Transverse Mercator |
|
|
14
|
+
| **ENU** | `Geodetic::Coordinates::ENU` | East, North, Up (local tangent plane) |
|
|
15
|
+
| **NED** | `Geodetic::Coordinates::NED` | North, East, Down (local tangent plane) |
|
|
16
|
+
| **MGRS** | `Geodetic::Coordinates::MGRS` | Military Grid Reference System |
|
|
17
|
+
| **USNG** | `Geodetic::Coordinates::USNG` | United States National Grid |
|
|
18
|
+
| **WebMercator** | `Geodetic::Coordinates::WebMercator` | Web Mercator projection (EPSG:3857) |
|
|
19
|
+
| **UPS** | `Geodetic::Coordinates::UPS` | Universal Polar Stereographic |
|
|
20
|
+
| **StatePlane** | `Geodetic::Coordinates::StatePlane` | State Plane Coordinate System |
|
|
21
|
+
| **BNG** | `Geodetic::Coordinates::BNG` | British National Grid |
|
|
22
|
+
| **GH36** | `Geodetic::Coordinates::GH36` | Geohash-36 (spatial hash, URL-friendly) |
|
|
23
|
+
|
|
24
|
+
## Additional Features
|
|
25
|
+
|
|
26
|
+
- **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.
|
|
27
|
+
- **Geoid height calculations** -- Convert between ellipsoidal and orthometric heights using models such as EGM96, EGM2008, GEOID18, and GEOID12B.
|
|
28
|
+
- **Geographic areas** -- `Geodetic::Areas::Circle`, `Geodetic::Areas::Polygon`, and `Geodetic::Areas::Rectangle` for point-in-area testing.
|
|
29
|
+
|
|
30
|
+
## Design Principles
|
|
31
|
+
|
|
32
|
+
- All constructors use **keyword arguments** for clarity.
|
|
33
|
+
- Every coordinate system supports **serialization** via `to_s` and `to_a`, and **deserialization** via `from_string` and `from_array`.
|
|
34
|
+
- Conversions are available as instance methods (`to_ecef`, `to_utm`, etc.) and class-level factory methods (`from_ecef`, `from_utm`, etc.).
|
|
35
|
+
|
|
36
|
+
## Quick Example
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
require "geodetic"
|
|
40
|
+
|
|
41
|
+
# Create an LLA coordinate (Seattle Space Needle)
|
|
42
|
+
lla = Geodetic::Coordinates::LLA.new(lat: 47.6205, lng: -122.3493, alt: 184.0)
|
|
43
|
+
|
|
44
|
+
# Convert to ECEF
|
|
45
|
+
ecef = lla.to_ecef
|
|
46
|
+
puts ecef.to_s
|
|
47
|
+
#=> "-2689653.22..., -4251180.82..., 4696587.81..."
|
|
48
|
+
|
|
49
|
+
# Convert back to LLA
|
|
50
|
+
lla_again = ecef.to_lla
|
|
51
|
+
puts lla_again.to_s
|
|
52
|
+
#=> "47.6205, -122.3493, 184.0"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Documentation
|
|
56
|
+
|
|
57
|
+
- [Getting Started: Installation](getting-started/installation.md)
|
|
58
|
+
- [Getting Started: Quick Start](getting-started/quick-start.md)
|