Ravn::Geo::

MGRS class

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

Constants

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

Attributes

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)

Public Class Methods

from_utm( utm, lat=nil )

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.

# File lib/ravn/geo/mgrs.rb, line 51
def self::from_utm( utm, lat=nil )
        lat ||= utm.to_position.lat
        band = MGRS_LAT_BANDS[ ( lat.to_f / 8 + 10 ).floor ]

        # columns in zone 1 are A-H, zone 2 J-R, zone 3 S-Z, then repeating every 3rd zone
        col = ( utm.easting.to_f / 100e3 ).floor
        # note: -1 because eastings start at 166e3 due to 500km false origin
        e100k = E100K_LETTERS[ (utm.zone - 1) % 3 ][ col - 1 ]

        # rows in even zones are A-V, in odd zones are F-E
        row = ( utm.northing.to_f / 100e3 ).floor % 20
        n100k = N100K_LETTERS[ (utm.zone - 1 ) % 2 ][ row ]

        # Truncate easting/northing to within 100km grid square & round to 1-metre precision
        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.

# File lib/ravn/geo/mgrs.rb, line 104
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( mgrs )

Parse a string to a UTM object.

# File lib/ravn/geo/mgrs.rb, line 81
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

Public Instance Methods

==( other )

Basic equality check.

# File lib/ravn/geo/mgrs.rb, line 157
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
position()
Alias for: to_position
to_position()

Convenience method: convert to a latitude/longitude position object, by way of UTM.

# File lib/ravn/geo/mgrs.rb, line 212
def to_position
        return self.utm.position
end
Also aliased as: position
to_s()

Return the standard MGRS representation.

# File lib/ravn/geo/mgrs.rb, line 143
def to_s
        return "%02d%s %s%s %s %s" % [
                self.zone,
                self.band,
                self.e100k,
                self.n100k,
                self.easting,
                self.northing
        ]
end
to_utm()

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.

# File lib/ravn/geo/mgrs.rb, line 173
def to_utm
        band_idx = MGRS_LAT_BANDS.index( self.band )
        hemisphere = band_idx >= MGRS_LAT_BANDS.index('N') ? 'N' : 'S'

        # Get easting specified by e100k (note +1 because eastings start at 166e3 due to 500km false origin)
        col = E100K_LETTERS[(self.zone - 1) % 3].index( self.e100k ) + 1
        e100kNum = col * 100e3 # e100k in metres

        # Get northing specified by n100k
        row = N100K_LETTERS[(self.zone-1) % 2].index( self.n100k );
        n100kNum = row * 100e3 # n100k in metres

        # Get latitude of (bottom of) band (10 bands above the equator, 8°latitude each)
        latBand = ( band_idx - 10 ) * 8

        # Get southern-most northing of bottom of band, using floor() to extend to include entirety
        # of bottom-most 100km square - note in northern hemisphere, centre of zone will be furthest
        # south; in southern hemisphere extremity of zone will be furthest south, so use 3°E / 0°E
        lon = band_idx >= MGRS_LAT_BANDS.index('N') ? 3 : 0
        nBand = ( Ravn::Geo::Position.new( latBand, lon ).utm.northing / 100e3 ).floor * 100e3

        # 100km grid square row letters repeat every 2,000km north; add enough
        # 2,000km blocks to get into required band
        n2M = 0 # northing of 2,000km block
        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
Also aliased as: utm
utm()
Alias for: to_utm