Skip to main content

Crate mgrs_convert

Crate mgrs_convert 

Source
Expand description

Convert WGS84 latitude-longitude to and from UTM, UPS, and MGRS.

The whole globe is covered: every valid latitude and longitude has a UTM (latitudes -80 to 84) or UPS (the polar caps) representation and an MGRS reference at every precision from 0 to 11 digit pairs, and every valid MGRS reference names a cell with a well defined center. Forward and reverse both ship. The projection core is the Krueger series to order n^6 from Karney, “Transverse Mercator with an accuracy of a few nanometers” (2011). The MGRS grid follows NGA TM 8358.1, checked cell by cell against GeographicLib’s GeoConvert.

This crate is std (the projection core needs the f64 math functions) and has zero runtime dependencies.

use mgrs_convert::{LatLon, Mgrs, Precision};

let position = LatLon::new(40.748333, -73.985278)?;
let cell = position.to_mgrs(Precision::ONE_METER);
assert_eq!(cell.to_string(), "18TWL8566411315");

let parsed: Mgrs = "18TWL8566411315".parse()?;
assert_eq!(parsed, cell);
let center = parsed.to_latlon();
assert!((center.lat() - 40.748333).abs() < 1e-5);
assert!((center.lon() + 73.985278).abs() < 1e-5);

§Types

Four types, one error enum, nothing else:

  • LatLon, a validated geodetic coordinate. Its LatLon::to_mgrs is infallible because every valid input has an MGRS reference.
  • UtmUps, a grid coordinate, where zone 0 is UPS. Its UtmUps::to_mgrs is fallible by design: the accepted transfer range extends 100 km past the MGRS grid on every side, and the ring in between returns Error::OutsideMgrsExtent instead of panicking.
  • Mgrs, a grid cell. Only constructible through validated paths, so its Display is total. Reverse conversion returns the cell center.
  • Precision, the validated digit-pair count, 0 through 11.

§Boundary behavior

  • Latitude 0 and -0.0 are both northern, band N, on every path.
  • The UTM latitude interval is [-80, 84), matching GeoConvert: -80 exactly is UTM band C, 84 exactly is UPS north, and the last UTM latitude is one ulp below 84 (band X).
  • A coordinate exactly on an excluded upper grid edge is nudged inside by 2^-28 m before truncation, so it formats as the last cell of its own square.
  • Grid-zone-only references such as "18T" are rejected with ParseErrorKind::GridZoneOnly, because a cell six degrees wide has no useful center.

§Migrating from geoconvert

geoconvert 1.0 callreplacement
LatLon::create(lat, lon)LatLon::new
latlon.to_mgrs(6)latlon.to_mgrs(Precision::new(6)?)
Mgrs::parse_str(s)s.parse::<Mgrs>()
mgrs.to_string()unchanged
mgrs.to_latlon()unchanged
UtmUps::create(z, north, e, n)UtmUps::utm or UtmUps::ups
utmups.to_mgrs(p)utmups.to_mgrs(Precision::new(p)?)?
Mgrs::create(...)removed, construct via to_mgrs or parse

The two new ?s are deliberate: precision validation and the MGRS-extent check return errors a caller can print instead of panicking.

use mgrs_convert::{Error, Precision, UtmUps};
// Precision is validated at the edge.
assert!(matches!(Precision::new(12), Err(Error::PrecisionOutOfRange { .. })));
// The transfer ring converts to lat/lon but has no MGRS reference.
let ring = UtmUps::utm(18, true, 50_000.0, 4_511_315.0)?;
assert!(matches!(ring.to_mgrs(Precision::ONE_METER), Err(Error::OutsideMgrsExtent { .. })));

§Performance

Linear per conversion, no allocation outside Display, no lookup table computed at runtime. The doctest below is the quadratic-regression tripwire: one hundred thousand round trips inside a generous fixed budget.

use mgrs_convert::{LatLon, Precision};
let start = std::time::Instant::now();
let mut checksum = 0.0_f64;
for i in 0..100_000_u32 {
    let lat = -80.0 + (f64::from(i) * 1.6377) % 160.0;
    let lon = -180.0 + (f64::from(i) * 3.9119) % 360.0;
    let p = LatLon::new(lat, lon)?;
    let back = p.to_utmups().to_latlon();
    checksum += back.lat();
}
assert!(checksum.is_finite());
assert!(start.elapsed().as_secs() < 60, "quadratic regression tripwire");

Structs§

LatLon
Geodetic coordinate on WGS84. Constructor-validated: latitude is a finite value in [-90, 90], longitude is finite and normalized to [-180, 180).
Mgrs
An MGRS cell, only constructible through the validated paths LatLon::to_mgrs, UtmUps::to_mgrs, and str::parse, so Display is total.
Precision
Digit pairs in an MGRS reference, 0 through 11. Cell size is 10^(5-p) metres: 100 km at 0, 1 m at 5, 1 mm at 8, 1 um at 11.
UtmUps
UTM or UPS coordinate. Zone 0 is UPS, matching GeographicLib’s convention.

Enums§

Error
The reasons a conversion or a parse can fail.
ParseErrorKind
The first parse rule an invalid MGRS string broke.