Grid Reference Systems: MGRS, Geohash, GARS, and Georef

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
library(geographiclib)

Contents

This vignette covers the grid reference systems available in geographiclib: MGRS, Geohash, GARS, and Georef. These systems encode geographic coordinates as alphanumeric strings, useful for human communication and data storage. It also covers GeoCoords for universal coordinate parsing and conversion, and DMS functions for degrees-minutes-seconds formatting.

Example Locations

We'll use locations from both hemispheres throughout this vignette:

locations <- data.frame(
  name = c("Sydney", "Hobart", "McMurdo Station", "South Pole",
           "London", "New York", "Tokyo", "Ushuaia"),
  lon = c(151.21, 147.32, 166.67, 0, -0.13, -74.01, 139.69, -68.30),
  lat = c(-33.87, -42.88, -77.85, -90, 51.51, 40.71, 35.69, -54.80)
)
locations

MGRS - Military Grid Reference System

MGRS is used by NATO militaries and provides unambiguous location references worldwide. It's based on UTM zones (or UPS for polar regions).

Basic Conversion

# Convert all locations to MGRS
pts <- cbind(locations$lon, locations$lat)
codes <- mgrs_fwd(pts)
data.frame(name = locations$name, mgrs = codes)

Understanding MGRS Codes

An MGRS code has several components:

# Sydney's MGRS code broken down
sydney_mgrs <- mgrs_fwd(c(151.21, -33.87))
sydney_mgrs

# Get full metadata from reverse conversion
mgrs_rev(sydney_mgrs)

Precision Levels

MGRS precision ranges from 100km (precision 0) to 1m (precision 5):

hobart <- c(147.32, -42.88)

precisions <- data.frame(
  precision = 0:5,
  resolution = c("100 km", "10 km", "1 km", "100 m", "10 m", "1 m"),
  code = sapply(0:5, function(p) mgrs_fwd(hobart, precision = p))
)
precisions

Polar Regions (UPS)

For polar regions (>84°N or <80°S), MGRS uses Universal Polar Stereographic:

# Antarctic locations
antarctic <- cbind(
  lon = c(166.67, 0, 77.85, -60),
  lat = c(-77.85, -90, -85, -82)
)

antarctic_mgrs <- mgrs_fwd(antarctic)
antarctic_mgrs

# Note zone = 0 indicates UPS
mgrs_rev(antarctic_mgrs)

Vectorized Operations

All MGRS functions are fully vectorized:

# Different precisions for different points
varied_precision <- mgrs_fwd(pts, precision = c(5, 4, 3, 2, 1, 0, 5, 4))
data.frame(name = locations$name, mgrs = varied_precision)

Geohash

Geohash encodes locations as base-32 strings with a useful property: truncating a geohash reduces precision but still contains the original point.

Basic Conversion

codes <- geohash_fwd(pts, len = 8)
data.frame(name = locations$name, geohash = codes)

The Truncation Property

This is Geohash's key feature - shorter codes are valid parent cells:

# Full precision for Sydney
sydney_gh <- geohash_fwd(c(151.21, -33.87), len = 12)
sydney_gh

# Truncate to see parent cells
data.frame(
  length = 12:4,
  geohash = substr(sydney_gh, 1, 12:4)
)

Resolution by Length

geohash_resolution(1:12)

Finding Required Length for Precision

# What length for ~1km precision?
geohash_length(resolution = 1/111)  # ~1 degree / 111 km

# What length for ~10m precision?
geohash_length(resolution = 10/111000)

Southern Hemisphere Examples

southern <- cbind(
  lon = c(151.21, 147.32, 166.67, -68.30, 77.85),
  lat = c(-33.87, -42.88, -77.85, -54.80, -85)
)
rownames(southern) <- c("Sydney", "Hobart", "McMurdo", "Ushuaia", "Amundsen-Scott area")

# Convert and reverse
gh_codes <- geohash_fwd(southern, len = 8)
gh_codes

geohash_rev(gh_codes)

GARS - Global Area Reference System

GARS is a military grid system with three precision levels: 30-minute, 15-minute, and 5-minute cells.

Basic Conversion

codes <- gars_fwd(pts, precision = 2)  # 5-minute precision
data.frame(name = locations$name, gars = codes)

Precision Levels

sydney <- c(151.21, -33.87)

gars_codes <- data.frame(
  precision = 0:2,
  resolution = c("30 minute", "15 minute", "5 minute"),
  code = sapply(0:2, function(p) gars_fwd(sydney, precision = p))
)
gars_codes

GARS for Antarctic Locations

antarctic_pts <- cbind(
  lon = c(166.67, 0, 77.85),
  lat = c(-77.85, -85, -82)
)

gars_fwd(antarctic_pts, precision = 2)

Georef - World Geographic Reference System

Georef is used primarily in aviation. It divides the world into 15° × 15° tiles then subdivides progressively.

Basic Conversion

codes <- georef_fwd(pts, precision = 2)
data.frame(name = locations$name, georef = codes)

Precision Levels

sydney <- c(151.21, -33.87)

georef_codes <- data.frame(
  precision = c(-1, 0, 2, 3),
  resolution = c("15 degree", "1 degree", "0.01 minute", "0.001 minute"),
  code = sapply(c(-1, 0, 2, 3), function(p) georef_fwd(sydney, precision = p))
)
georef_codes

Georef for Flight Planning

Georef is particularly useful for aviation across hemispheres:

# Flight path: Sydney to Santiago via Antarctica
flight_pts <- cbind(
  lon = c(151.21, 166.67, -70, -70.67),
  lat = c(-33.87, -77.85, -85, -33.45)
)
rownames(flight_pts) <- c("Sydney", "McMurdo", "Over Antarctica", "Santiago")

georef_fwd(flight_pts, precision = 2)

Comparison of Systems

Each system has different strengths:

| System | Best For | Precision Range | Key Feature | |--------|----------|-----------------|-------------| | MGRS | Military, hiking | 100km - 1m | Unambiguous worldwide | | Geohash | Databases, URLs | ~5000km - 1mm | Truncation preserves containment | | GARS | Military aviation | 30min - 5min | Simple, easy to read | | Georef | Aviation | 15° - 0.001min | Used in flight planning | | GeoCoords | Format conversion | N/A | Parses multiple input formats | | DMS | Human-readable | Variable | Degrees, minutes, seconds notation |

# Same location in all systems
pt <- c(147.32, -42.88)  # Hobart

data.frame(
  system = c("MGRS", "Geohash", "GARS", "Georef", "DMS"),
  code = c(
    mgrs_fwd(pt, precision = 3),
    geohash_fwd(pt, len = 8),
    gars_fwd(pt, precision = 2),
    georef_fwd(pt, precision = 2),
    paste(
      dms_encode(pt[2], prec = 4, indicator = "latitude"),
      dms_encode(pt[1], prec = 4, indicator = "longitude")
    )
  )
)

GeoCoords - Universal Coordinate Parsing

The geocoords_parse() function provides a flexible way to parse coordinate strings in multiple formats. This is particularly useful when working with data from different sources that may use different coordinate formats.

Parsing Various Formats

geocoords_parse() accepts coordinates in many formats:

# Parse MGRS codes
geocoords_parse("33TWN0500049000")

# Parse UTM strings
geocoords_parse("33N 505000 4900000")

# Parse DMS (degrees, minutes, seconds)
geocoords_parse("44d 0' 0\" N 33d 0' 0\" E")

# Parse decimal degrees (lat lon format)
geocoords_parse("44.0 33.0")

Parsing Multiple Coordinates

The function is vectorized for batch processing:

# Mixed format inputs
inputs <- c(
 "56HLU1060372300",           # MGRS (Sydney area)
 "55G 530000 5250000",        # UTM (Hobart area)
 "-33.87 151.21",             # Decimal degrees
 "51d 30' 0\" N 0d 7' 0\" W"  # DMS (London)
)

parsed <- geocoords_parse(inputs)
parsed[, c("lat", "lon", "zone", "northp")]

Integration with Other Functions

Once parsed, coordinates can be used with any grid reference or projection function:

# Parse any input format
input <- "33TWN0500049000"
coords <- geocoords_parse(input)

# Then use with any system
pt <- c(coords$lon, coords$lat)
data.frame(
 input = input,
 lat = coords$lat,
 lon = coords$lon,
 mgrs = mgrs_fwd(pt, precision = 5),
 geohash = geohash_fwd(pt, len = 8),
 gars = gars_fwd(pt, precision = 2),
 georef = georef_fwd(pt, precision = 2)
)

DMS - Degrees, Minutes, Seconds Conversion

DMS - Degrees, Minutes, Seconds Conversion

The DMS functions provide flexible parsing and formatting of angles in degrees-minutes-seconds notation. This is useful for working with coordinate data from various sources that use different formats.

Parsing DMS Strings

dms_decode() parses DMS strings and returns the angle in decimal degrees:

# Parse with hemisphere indicator
dms_decode("40d26'47\"N")

# Parse various formats
dms_decode(c(
  "40:26:47",           # Colon-separated
  "-74d0'21.5\"",       # Negative with d-'-" separators
  "51d30'N",            # Degrees and minutes only
  "40.446S"             # Decimal with hemisphere
))

The function returns both the angle and an indicator showing whether a hemisphere designator was present (0=none, 1=latitude N/S, 2=longitude E/W).

Parsing Coordinate Pairs

For latitude/longitude pairs, dms_decode_latlon() handles the hemisphere logic automatically:

# Parse a coordinate pair
dms_decode_latlon("40d26'47\"N", "74d0'21.5\"W")

# Vectorized for multiple coordinates
dms_decode_latlon(
  c("40d26'47\"N", "51d30'0\"N", "-33d52'10\""),
  c("74d0'21.5\"W", "0d7'0\"W", "151d12'30\"")
)

Parsing Angles and Azimuths

For angles without coordinates (like bearings or field-of-view):

# Parse angles (no hemisphere designators allowed)
dms_decode_angle(c("45:30:0", "123d45'6\"", "90.5"))

# Parse azimuths (E/W allowed, result in [-180, 180])
dms_decode_azimuth(c("45:30:0", "90W", "135E"))

Encoding to DMS Strings

Convert decimal degrees to formatted DMS strings:

# Basic encoding with automatic component selection
dms_encode(40.446195, prec = 5)
dms_encode(c(40.446, -74.006), prec = 3)

# With hemisphere indicators
dms_encode(40.446195, prec = 5, indicator = "latitude")
dms_encode(-74.006328, prec = 5, indicator = "longitude")

# Azimuth format (always positive, 0-360)
dms_encode(-45.5, indicator = "azimuth", prec = 4)

Output Formats

Control the output format with precision and separator options:

angle <- 40.446195

# Different precisions
data.frame(
  prec = 0:6,
  output = sapply(0:6, function(p) dms_encode(angle, prec = p))
)

# Colon separator (ISO 6709 style)
dms_encode(angle, prec = 5, sep = ":")

# Force specific trailing component
dms_encode(angle, component = "minute", prec = 4)
dms_encode(angle, component = "second", prec = 2)

Splitting and Combining Components

Work with individual degree, minute, second components:

# Split into degrees and minutes
dms_split(c(40.446, -74.256))

# Split into degrees, minutes, and seconds
dms_split(c(40.446195, -74.006328), seconds = TRUE)

# Combine components back to decimal
dms_combine(40, 26, 47)
dms_combine(
  d = c(40, -74, 51),
  m = c(26, 0, 30),
  s = c(47, 21.5, 0)
)

Round-Trip Conversion

DMS encoding and decoding are inverses (within precision limits):

# Original coordinates
original <- c(40.446195, -74.006328, 51.507351)

# Encode to DMS
encoded <- dms_encode(original, prec = 6, indicator = "latitude")
encoded

# Decode back
decoded <- dms_decode(encoded)
data.frame(
  original = original,
  encoded = encoded,
  decoded = decoded$angle,
  diff = abs(original - decoded$angle)
)

Integration with GeoCoords

DMS functions complement GeoCoords for complete coordinate handling:

# Parse mixed-format input with GeoCoords
input <- "40d26'47\"N 74d0'21.5\"W"
coords <- geocoords_parse(input)

# Format output in different styles
data.frame(
  format = c("decimal", "DMS", "DMS-colon", "MGRS"),
  value = c(
    sprintf("%.6f, %.6f", coords$lat, coords$lon),
    paste(
      dms_encode(coords$lat, prec = 5, indicator = "latitude"),
      dms_encode(coords$lon, prec = 5, indicator = "longitude")
    ),
    paste(
      dms_encode(coords$lat, prec = 5, sep = ":"),
      dms_encode(coords$lon, prec = 5, sep = ":")
    ),
    mgrs_fwd(c(coords$lon, coords$lat), precision = 4)
  )
)

See Also



Try the geographiclib package in your browser

Any scripts or data that you put into this service are public.

geographiclib documentation built on March 4, 2026, 9:07 a.m.