Converted from MIT licensed: github.com/chrisveness/geodesy/blob/master/mgrs.js Original author: Chris Veness chrisv@movable-type.co.uk
Military Grid Reference System (MGRS/NATO) grid references provides geocoordinate references covering the entire globe, based on UTM projections.
MGRS
references comprise a grid zone designator, a 100km square identification, and an easting and northing (in metres); e.g. ‘31U DQ 48251 11932’.
Ref: www.fgdc.gov/standards/projects/FGDC-standards-projects/usng/fgdc_std_011_2001_usng.pdf
- E100K_LETTERS
100km grid square column (‘e’) letters repeat every third zone
- MATCHER
A valid MGRS
string matcher.
This supports separation by spaces, or a concatenated string with 1m precision.
- N100K_LETTERS
100km grid square row (‘n’) letters repeat every other zone
- band R
8° latitudinal band (C..X covering 80°S..84°N).
- e100k R
First letter (E) of 100km grid square.
- easting R
Easting in metres within 100km grid square.
- n100k R
Second letter (N) of 100km grid square.
- northing R
Northing in metres within 100km grid square.
- zone R
6° longitudinal zone (1..60 covering 180°W..180°E)
Convert UTM to a Military Grid Reference System (MGRS/NATO) object.
Note: A known latitude is required to calculate the band. Sending it if already known will save a good deal of unnecessary calculations.
def self::from_utm( utm, lat=nil )
lat ||= utm.to_position.lat
band = MGRS_LAT_BANDS[ ( lat.to_f / 8 + 10 ).floor ]
col = ( utm.easting.to_f / 100e3 ).floor
e100k = E100K_LETTERS[ (utm.zone - 1) % 3 ][ col - 1 ]
row = ( utm.northing.to_f / 100e3 ).floor % 20
n100k = N100K_LETTERS[ (utm.zone - 1 ) % 2 ][ row ]
easting = ( utm.easting % 100e3 ).floor
northing = ( utm.northing % 100e3 ).floor
return new(
zone: utm.zone,
band: band,
e100k: e100k,
n100k: n100k,
easting: easting,
northing: northing
)
end
new( zone:, band:, e100k:, n100k:, easting:, northing: )
Instance a new MGRS
object under the WGS84 datum.
zone: 6° longitudinal zone (1..60 covering 180°W..180°E) band: 8° latitudinal band (C..X covering 80°S..84°N). e100k: First letter (E) of 100km grid square. n100k: Second letter (N) of 100km grid square. easting: Easting in metres within 100km grid square. northing: Northing in metres within 100km grid square.
def initialize( zone:, band:, e100k:, n100k:, easting:, northing: )
@zone = zone
@band = band
@e100k = e100k
@n100k = n100k
@easting = easting
@northing = northing
raise RangeError, "Invalid zone %p" % [ zone ] if zone <= 1 || zone >= 60
raise RangeError, "Invalid band %p" % [ band ] unless MGRS_LAT_BANDS.index( band )
unless E100K_LETTERS[ ( zone - 1 ) % 3 ].index( e100k )
raise RangeError, "Invalid e100k column %p for zone %p" % [ e100k, zone ]
end
unless N100K_LETTERS.first.index( n100k )
raise RangeError, "Invalid n100k row %p for zone %p" % [ n100k, zone ]
end
end
Parse a string to a UTM object.
def self::parse( mgrs )
match = mgrs.match( MATCHER ) or raise ArgumentError, "Invalid MGRS: %p" % [ mgrs ]
return new(
zone: match[ :zone ].to_i,
band: match[ :band ],
e100k: match[ :e100k ],
n100k: match[ :n100k ],
easting: match[ :easting ].to_i,
northing: match[ :northing ].to_i
)
end
Basic equality check.
def ==( other )
return self.zone == other.zone &&
self.band == other.band &&
self.e100k == other.e100k &&
self.n100k == other.n100k &&
self.easting == other.easting &&
self.northing == other.northing
end
Convenience method: convert to a latitude/longitude position object, by way of UTM.
def to_position
return self.utm.position
end
Return the standard MGRS
representation.
def to_s
return "%02d%s %s%s %s %s" % [
self.zone,
self.band,
self.e100k,
self.n100k,
self.easting,
self.northing
]
end
Converts MGRS
grid reference to UTM coordinates.
Grid references refer to squares rather than points (with the size of the square indicated by the precision of the reference); this conversion will return the UTM coordinate of the SW corner of the grid reference square.
def to_utm
band_idx = MGRS_LAT_BANDS.index( self.band )
hemisphere = band_idx >= MGRS_LAT_BANDS.index('N') ? 'N' : 'S'
col = E100K_LETTERS[(self.zone - 1) % 3].index( self.e100k ) + 1
e100kNum = col * 100e3
row = N100K_LETTERS[(self.zone-1) % 2].index( self.n100k );
n100kNum = row * 100e3
latBand = ( band_idx - 10 ) * 8
lon = band_idx >= MGRS_LAT_BANDS.index('N') ? 3 : 0
nBand = ( Ravn::Geo::Position.new( latBand, lon ).utm.northing / 100e3 ).floor * 100e3
n2M = 0
n2M += 2000e3 while n2M + n100kNum + self.northing < nBand
return Ravn::Geo::UTM.new(
zone: self.zone,
hemisphere: hemisphere,
easting: e100kNum + self.easting,
northing: n2M + n100kNum + self.northing
)
end