Conversions Reference¶
Every coordinate class in the Geodetic gem can convert to every other coordinate class. Conversions are available as both instance methods (on the source object) and class methods (on the target class).
Conversion Method Patterns¶
Instance Methods (on the source)¶
Examples:
lla.to_ecef # LLA -> ECEF
lla.to_utm # LLA -> UTM
ecef.to_lla # ECEF -> LLA
utm.to_lla # UTM -> LLA
web_mercator.to_lla # WebMercator -> LLA
bng.to_utm # BNG -> UTM
ups.to_mgrs # UPS -> MGRS
state_plane.to_web_mercator # StatePlane -> WebMercator
Class Methods (on the target)¶
Examples:
Geodetic::Coordinate::ECEF.from_lla(lla)
Geodetic::Coordinate::LLA.from_ecef(ecef)
Geodetic::Coordinate::UTM.from_lla(lla)
Geodetic::Coordinate::LLA.from_utm(utm)
Geodetic::Coordinate::WebMercator.from_ecef(ecef)
Local Coordinate Systems (ENU, NED)¶
ENU and NED are local tangent plane systems that require a reference LLA point defining the origin of the local frame. All conversions to/from ENU and NED include a reference parameter.
Instance Methods¶
ref = Geodetic::Coordinate::LLA.new(lat: 38.8977, lng: -77.0365, alt: 0.0)
# LLA to local
lla.to_enu(ref)
lla.to_ned(ref)
# Local to LLA
enu.to_lla(ref)
ned.to_lla(ref)
# Local to UTM (requires reference)
enu.to_utm(ref)
ned.to_utm(ref)
# ENU <-> NED (direct, no reference needed)
enu.to_ned
ned.to_enu
# ECEF to local (reference_ecef required, reference_lla optional)
ecef.to_enu(reference_ecef, reference_lla)
ecef.to_ned(reference_ecef, reference_lla)
# Local to ECEF (reference_ecef required, reference_lla optional)
enu.to_ecef(reference_ecef, reference_lla)
ned.to_ecef(reference_ecef, reference_lla)
Class Methods¶
ref = Geodetic::Coordinate::LLA.new(lat: 38.8977, lng: -77.0365, alt: 0.0)
Geodetic::Coordinate::ENU.from_lla(lla, ref)
Geodetic::Coordinate::NED.from_lla(lla, ref)
Geodetic::Coordinate::LLA.from_enu(enu, ref)
Geodetic::Coordinate::LLA.from_ned(ned, ref)
# From other systems via reference
Geodetic::Coordinate::ENU.from_utm(utm, ref)
Geodetic::Coordinate::NED.from_utm(utm, ref)
Geodetic::Coordinate::UTM.from_enu(enu, ref)
Geodetic::Coordinate::UTM.from_ned(ned, ref)
# ECEF-based
Geodetic::Coordinate::ENU.from_ecef(ecef, ref_ecef, ref_lla)
Geodetic::Coordinate::NED.from_ecef(ecef, ref_ecef, ref_lla)
Geodetic::Coordinate::ECEF.from_enu(enu, ref_ecef, ref_lla)
Geodetic::Coordinate::ECEF.from_ned(ned, ref_ecef, ref_lla)
When reference_lla is omitted in ECEF-based conversions, it is computed automatically from reference_ecef via reference_ecef.to_lla.
MGRS and USNG Conversions¶
MGRS and USNG accept an optional precision parameter (1-5, default 5) controlling the coordinate resolution:
| Precision | Resolution |
|---|---|
| 1 | 10 km |
| 2 | 1 km |
| 3 | 100 m |
| 4 | 10 m |
| 5 | 1 m |
# LLA to MGRS with precision
Geodetic::Coordinate::MGRS.from_lla(lla, datum, precision)
lla_point.to_mgrs(datum, precision) # available on UPS, WebMercator, BNG, StatePlane
# LLA to USNG with precision
Geodetic::Coordinate::USNG.from_lla(lla, datum, precision)
# MGRS <-> USNG (direct conversion)
mgrs.to_usng # implicit via component transfer
Geodetic::Coordinate::USNG.from_mgrs(mgrs)
usng.to_mgrs
Geodetic::Coordinate::MGRS.from_usng(usng) # implicit via string
StatePlane Conversions¶
StatePlane requires a zone code when converting into the system:
# To StatePlane (zone_code required)
Geodetic::Coordinate::StatePlane.from_lla(lla, 'CA_I')
Geodetic::Coordinate::StatePlane.from_ecef(ecef, 'CA_I')
Geodetic::Coordinate::StatePlane.from_utm(utm, 'CA_I')
Geodetic::Coordinate::StatePlane.from_enu(enu, ref_lla, 'CA_I')
Geodetic::Coordinate::StatePlane.from_ned(ned, ref_lla, 'CA_I')
# From StatePlane (zone is stored in the object)
state_plane.to_lla
state_plane.to_ecef
state_plane.to_utm
state_plane.to_enu(ref_lla)
state_plane.to_ned(ref_lla)
BNG also provides conversion to StatePlane with a zone code:
Datum Parameter¶
Most conversion methods accept an optional datum parameter that defaults to Geodetic::WGS84:
# Using a different datum
clarke = Geodetic::Datum.new(name: 'CLARKE_1866')
lla.to_ecef(clarke)
ecef.to_lla(clarke)
lla.to_utm(clarke)
utm.to_lla(clarke)
StatePlane stores its datum internally and uses it when no datum argument is provided:
sp = Geodetic::Coordinate::StatePlane.new(
easting: 2000000.0, northing: 500000.0,
zone_code: 'CA_I', datum: clarke
)
sp.to_lla # uses the stored clarke datum
sp.to_lla(wgs84) # overrides with wgs84
Conversion Chains¶
Most conversions are not direct but route through intermediate systems. The gem handles this transparently. Here are the typical chains:
| Conversion | Chain |
|---|---|
| LLA <-> ECEF | Direct mathematical transformation |
| LLA <-> UTM | Direct mathematical transformation |
| LLA <-> ENU | LLA -> ECEF -> ENU (and reverse) |
| LLA <-> NED | LLA -> ECEF -> ENU -> NED (and reverse) |
| ENU <-> NED | Direct axis swap: NED(n, e, d) = ENU(e, n, -u) |
| UTM <-> ECEF | UTM -> LLA -> ECEF (and reverse) |
| UTM <-> ENU | UTM -> LLA -> ENU (and reverse) |
| MGRS <-> LLA | MGRS -> UTM -> LLA (and reverse) |
| USNG <-> LLA | USNG -> MGRS -> UTM -> LLA (and reverse) |
| WebMercator <-> LLA | Direct mathematical transformation |
| UPS <-> LLA | Direct mathematical transformation |
| BNG <-> LLA | BNG -> OSGB36 LLA -> WGS84 LLA (with datum shift) |
| StatePlane <-> LLA | Direct projection (Lambert or Transverse Mercator) |
| GH36 <-> LLA | Encode/decode via 6x6 matrix subdivision |
| GH <-> LLA | Encode/decode via bit-interleaved base-32 |
| HAM <-> LLA | Encode/decode via hierarchical letter/digit pairs |
| OLC <-> LLA | Encode/decode via base-20 pair encoding with grid refinement |
| Any <-> Any | Routes through LLA as the universal hub |
Conversion Accuracy Notes¶
- LLA <-> ECEF: Full precision. Iterative algorithm converges to sub-millimeter accuracy (tolerance: 1e-12 radians for latitude, 1e-12 meters for altitude, max 100 iterations).
- LLA <-> UTM: Simplified series expansion. Accurate for typical use but may diverge at extreme latitudes or far from the central meridian.
- ENU / NED: Full precision when going through ECEF. The rotation matrices are exact.
- MGRS / USNG: Precision depends on the grid precision level (1-5). A 5-digit precision gives 1-meter resolution.
- WebMercator: Latitude is clamped to ±85.0511 degrees. Altitude information is lost (always 0.0).
- UPS: Iterative refinement (5 iterations) for the inverse projection. Designed for polar regions.
- BNG: Uses a simplified datum transformation between OSGB36 and WGS84 (approximate offset). A full Helmert 7-parameter transformation would provide higher accuracy.
- StatePlane: Uses simplified projection formulas. Production applications may require the full NOAA/NGS projection equations for survey-grade accuracy.
- GH36: Precision depends on hash length. Default 10 characters gives sub-meter resolution. Altitude information is lost (always 0.0).
- GH: Precision depends on hash length. Default 12 characters gives sub-centimeter resolution. Altitude information is lost (always 0.0).
- HAM: Precision depends on locator length (2, 4, 6, or 8 characters). Default 6 characters gives ~5 km resolution. Altitude information is lost (always 0.0).
- OLC: Precision depends on code length. Default 10 characters gives ~14 m resolution. Codes longer than 10 use 5x4 grid refinement for sub-meter resolution. Altitude information is lost (always 0.0).
- Equality comparisons: All classes use tolerance-based equality. Coordinates (in meters) use 1e-6 m tolerance. LLA uses 1e-10 degrees for lat/lng and 1e-6 m for altitude. GH36, GH, HAM, and OLC use exact string comparison.
Distance Calculations¶
Universal distance methods are available on all coordinate types and work across different coordinate systems.
Great-Circle Distance (Vincenty)¶
distance_to(other, *others)— Instance method. Computes the Vincenty great-circle distance from the receiver to one or more target coordinates. Returns aDistancefor a single target, or an Array ofDistanceobjects for multiple targets (radial distances from the receiver).Geodetic::Coordinate.distance_between(*coords)— Class method onGeodetic::Coordinate. Computes consecutive chain distances between an ordered sequence of coordinates. Returns aDistancefor two coordinates, or an Array ofDistanceobjects for three or more.
Distanceobjects wrap a distance value and provide unit-aware access. Call.metersto get the raw Float value in meters, or.to_fto get the value in the current display unit.
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0.0)
# Radial distances from receiver
seattle.distance_to(portland) # => Distance (235385.71 m)
seattle.distance_to(portland, sf) # => [Distance, Distance] (Array)
# Consecutive chain distances
Geodetic::Coordinate.distance_between(seattle, portland, sf) # => [Distance, Distance] (Array)
Straight-Line Distance (ECEF Euclidean)¶
straight_line_distance_to(other, *others)— Instance method. Computes the Euclidean distance in ECEF (3D Cartesian) space. Returns aDistancefor a single target, or an Array ofDistanceobjects for multiple targets.Geodetic::Coordinate.straight_line_distance_between(*coords)— Class method. Computes consecutive chain Euclidean distances.
seattle.straight_line_distance_to(portland) # => Distance
Geodetic::Coordinate.straight_line_distance_between(seattle, portland) # => Distance
Cross-System Distances¶
Both distance_to and straight_line_distance_to accept any coordinate type. Coordinates are converted to LLA (for Vincenty) or ECEF (for Euclidean) internally:
utm = seattle.to_utm
mgrs = Geodetic::Coordinate::MGRS.from_lla(portland)
utm.distance_to(mgrs) # => Distance (235385.71 m)
ENU and NED (Relative Systems)¶
ENU and NED are relative coordinate systems and do not support distance_to or straight_line_distance_to directly. Convert to an absolute system first:
ref = Geodetic::Coordinate::LLA.new(lat: 47.62, lng: -122.35, alt: 0.0)
lla = enu.to_lla(ref)
lla.distance_to(other_lla)
ENU and NED retain horizontal_distance_to and local_bearing_to for local Euclidean operations within the tangent plane.
Bearing Calculations¶
Universal bearing methods are available on all coordinate types and work across different coordinate systems. All bearing methods return Bearing objects.
Great-Circle Bearing (Forward Azimuth)¶
bearing_to(other)— Instance method. Computes the great-circle forward azimuth from the receiver to the target coordinate. Returns aBearingobject.elevation_to(other)— Instance method. Computes the vertical look angle (elevation) from the receiver to the target. Returns a Float in degrees (-90 to +90).Geodetic::Coordinate.bearing_between(*coords)— Class method onGeodetic::Coordinate. Computes consecutive chain bearings between an ordered sequence of coordinates. Returns aBearingfor two coordinates, or an Array ofBearingobjects for three or more.
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0.0)
# Forward azimuth
b = seattle.bearing_to(portland) # => Bearing
b.degrees # => 186.25...
b.to_compass(points: 8) # => "S"
b.reverse # => Bearing (back azimuth)
# Elevation angle
seattle.elevation_to(portland) # => Float (degrees)
# Consecutive chain bearings
Geodetic::Coordinate.bearing_between(seattle, portland, sf) # => [Bearing, Bearing]
Cross-System Bearings¶
bearing_to and elevation_to accept any coordinate type. Coordinates are converted to LLA internally:
utm = seattle.to_utm
mgrs = Geodetic::Coordinate::MGRS.from_lla(portland)
utm.bearing_to(mgrs) # => Bearing
ENU and NED (Relative Systems)¶
ENU and NED are relative coordinate systems and do not support bearing_to or elevation_to directly (these raise ArgumentError). Convert to an absolute system first, or use the local methods:
local_bearing_to(other)— Local tangent plane bearing (degrees from north, 0-360)local_elevation_angle_to(other)— Local elevation angle (NED only, degrees)
Bearing Class¶
Bearing wraps an azimuth angle (0-360) with compass and radian conversions:
b = Geodetic::Bearing.new(225)
b.degrees # => 225.0
b.to_radians # => 3.926...
b.reverse # => Bearing (45)
b.to_compass(points: 4) # => "W"
b.to_compass(points: 8) # => "SW"
b.to_compass(points: 16) # => "SW"
b.to_s # => "225.0000°"
# Arithmetic
b + 10 # => Bearing (235°)
b - 10 # => Bearing (215°)
Bearing.new(90) - Bearing.new(45) # => 45.0 (Float, angular difference)