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
#>              name    lon    lat
#> 1          Sydney 151.21 -33.87
#> 2          Hobart 147.32 -42.88
#> 3 McMurdo Station 166.67 -77.85
#> 4      South Pole   0.00 -90.00
#> 5          London  -0.13  51.51
#> 6        New York -74.01  40.71
#> 7           Tokyo 139.69  35.69
#> 8         Ushuaia -68.30 -54.80

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)
#>              name            mgrs
#> 1          Sydney 56HLH3443550816
#> 2          Hobart 55GEN2613352461
#> 3 McMurdo Station 58CEU3923257811
#> 4      South Pole   BAN0000000000
#> 5          London 30UXC9915210446
#> 6        New York 18TWL8362507036
#> 7           Tokyo 54SUE8146950356
#> 8         Ushuaia 19FEV4500027239

Understanding MGRS Codes

An MGRS code has several components:

  • Grid Zone Designator (e.g., “56H”): UTM zone number + latitude band letter
  • 100km Square ID (e.g., “LU”): Two letters identifying the 100km square
  • Numerical Location: Easting and northing within the square
# Sydney's MGRS code broken down
sydney_mgrs <- mgrs_fwd(c(151.21, -33.87))
sydney_mgrs
#> [1] "56HLH3443550816"

# Get full metadata from reverse conversion
mgrs_rev(sydney_mgrs)
#>      lon    lat        x       y zone northp precision convergence     scale
#> 1 151.21 -33.87 334435.5 6250816   56  FALSE         5   0.9978138 0.9999379
#>   grid_zone square_100km        crs
#> 1       56H           LH EPSG:32756

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
#>   precision resolution            code
#> 1         0     100 km           55GEN
#> 2         1      10 km         55GEN25
#> 3         2       1 km       55GEN2652
#> 4         3      100 m     55GEN261524
#> 5         4       10 m   55GEN26135246
#> 6         5        1 m 55GEN2613352461

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
#> [1] "58CEU3923257811" "BAN0000000000"   "BHP4301516908"   "AQS2960244788"

# Note zone = 0 indicates UPS
mgrs_rev(antarctic_mgrs)
#>         lon       lat         x       y zone northp precision convergence
#> 1 166.67000 -77.85000  539232.5 1357812   58  FALSE         5   -1.632614
#> 2  45.00000 -89.99999 2000000.5 2000000    0  FALSE         5  -45.000000
#> 3  77.84997 -85.00000 2543015.5 2116908    0  FALSE         5  -77.849968
#> 4 -60.00004 -82.00000 1229602.5 2444788    0  FALSE         5   60.000039
#>       scale grid_zone square_100km        crs
#> 1 0.9996188       58C           EU EPSG:32758
#> 2 0.9940000         B           AN EPSG:32761
#> 3 0.9958948         B           HP EPSG:32761
#> 4 0.9988601         A           QS EPSG:32761

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)
#>              name            mgrs
#> 1          Sydney 56HLH3443550816
#> 2          Hobart   55GEN26135246
#> 3 McMurdo Station     58CEU392578
#> 4      South Pole         BAN0000
#> 5          London         30UXC91
#> 6        New York           18TWL
#> 7           Tokyo 54SUE8146950356
#> 8         Ushuaia   19FEV45002723

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)
#>              name  geohash
#> 1          Sydney r3gx2f5u
#> 2          Hobart r22u03gj
#> 3 McMurdo Station pdnt0ev6
#> 4      South Pole h0000000
#> 5          London gcpvj117
#> 6        New York dr5reg58
#> 7           Tokyo xn7749pj
#> 8         Ushuaia 4qr2jzcq

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
#> [1] "r3gx2f5ubqkh"

# Truncate to see parent cells
data.frame(
  length = 12:4,
  geohash = substr(sydney_gh, 1, 12:4)
)
#>   length      geohash
#> 1     12 r3gx2f5ubqkh
#> 2     11 r3gx2f5ubqkh
#> 3     10 r3gx2f5ubqkh
#> 4      9 r3gx2f5ubqkh
#> 5      8 r3gx2f5ubqkh
#> 6      7 r3gx2f5ubqkh
#> 7      6 r3gx2f5ubqkh
#> 8      5 r3gx2f5ubqkh
#> 9      4 r3gx2f5ubqkh

Resolution by Length

geohash_resolution(1:12)
#>    len lat_resolution lon_resolution
#> 1    1   4.500000e+01   4.500000e+01
#> 2    2   5.625000e+00   1.125000e+01
#> 3    3   1.406250e+00   1.406250e+00
#> 4    4   1.757812e-01   3.515625e-01
#> 5    5   4.394531e-02   4.394531e-02
#> 6    6   5.493164e-03   1.098633e-02
#> 7    7   1.373291e-03   1.373291e-03
#> 8    8   1.716614e-04   3.433228e-04
#> 9    9   4.291534e-05   4.291534e-05
#> 10  10   5.364418e-06   1.072884e-05
#> 11  11   1.341105e-06   1.341105e-06
#> 12  12   1.676381e-07   3.352761e-07

Finding Required Length for Precision

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

# What length for ~10m precision?
geohash_length(resolution = 10/111000)
#> [1] 9

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
#> [1] "r3gx2f5u" "r22u03gj" "pdnt0ev6" "4qr2jzcq" "j8zk7w0x"

geohash_rev(gh_codes)
#>         lon       lat len lat_resolution lon_resolution
#> 1 151.21016 -33.87008   8   0.0001716614   0.0003433228
#> 2 147.31997 -42.88007   8   0.0001716614   0.0003433228
#> 3 166.66998 -77.85007   8   0.0001716614   0.0003433228
#> 4 -68.30011 -54.80006   8   0.0001716614   0.0003433228
#> 5  77.84998 -84.99993   8   0.0001716614   0.0003433228

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)
#>              name    gars
#> 1          Sydney 663ES36
#> 2          Hobart 655DY44
#> 3 McMurdo Station 694BA36
#> 4      South Pole 361AA37
#> 5          London 360MV48
#> 6        New York 212LX43
#> 7           Tokyo 640LM33
#> 8         Ushuaia 224CY33

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
#>   precision resolution    code
#> 1         0  30 minute   663ES
#> 2         1  15 minute  663ES3
#> 3         2   5 minute 663ES36

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)
#> [1] "694BA36" "361AL37" "516AS48"

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)
#>              name   georef
#> 1          Sydney YDBM1207
#> 2          Hobart XDNC1907
#> 3 McMurdo Station ZABN4009
#> 4      South Pole NAAA0000
#> 5          London MKQG5230
#> 6        New York HJAL5942
#> 7           Tokyo XJEF4141
#> 8         Ushuaia HCGF4212

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
#>   precision   resolution       code
#> 1        -1    15 degree         YD
#> 2         0     1 degree       YDBM
#> 3         2  0.01 minute   YDBM1207
#> 4         3 0.001 minute YDBM126078

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)
#> [1] "YDBM1207" "ZABN4009" "HAFF0000" "HDEM1932"

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")
    )
  )
)
#>    system                   code
#> 1    MGRS            55GEN261524
#> 2 Geohash               r22u03gj
#> 3    GARS                655DY44
#> 4  Georef               XDNC1907
#> 5     DMS 42d52'48"S 147d19'12"E

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")
#>        lat      lon zone northp  easting northing
#> 1 47.39444 15.06626   33   TRUE 505000.5  5249000

# Parse UTM strings
geocoords_parse("33N 505000 4900000")
#>        lat      lon zone northp easting northing
#> 1 44.25322 15.06263   33   TRUE  505000  4900000

# Parse DMS (degrees, minutes, seconds)
geocoords_parse("44d 0' 0\" N 33d 0' 0\" E")
#>   lat lon zone northp easting northing
#> 1  NA  NA   NA     NA      NA       NA

# Parse decimal degrees (lat lon format)
geocoords_parse("44.0 33.0")
#>   lat lon zone northp easting northing
#> 1  44  33   36   TRUE   5e+05  4871873

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")]
#>      lat    lon zone northp
#> 1     NA     NA   NA     NA
#> 2     NA     NA   NA     NA
#> 3 -33.87 151.21   56  FALSE
#> 4     NA     NA   NA     NA

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)
)
#>             input      lat      lon            mgrs  geohash    gars   georef
#> 1 33TWN0500049000 47.39444 15.06626 33TWN0500049000 u26twgfu 391ML14 PKAC0323

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")
#>      angle indicator
#> 1 40.44639         1

# 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
))
#>       angle indicator
#> 1  40.44639         0
#> 2 -74.00597         0
#> 3  51.50000         1
#> 4 -40.44600         1

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")
#>        lat       lon
#> 1 40.44639 -74.00597

# 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\"")
)
#>         lat         lon
#> 1  40.44639 -74.0059722
#> 2  51.50000  -0.1166667
#> 3 -33.86944 151.2083333

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"))
#> [1]  45.5000 123.7517  90.5000

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

Encoding to DMS Strings

Convert decimal degrees to formatted DMS strings:

# Basic encoding with automatic component selection
dms_encode(40.446195, prec = 5)
#> [1] "40d26'46.3\""
dms_encode(c(40.446, -74.006), prec = 3)
#> [1] "40d26.8'"  "-74d00.4'"

# With hemisphere indicators
dms_encode(40.446195, prec = 5, indicator = "latitude")
#> [1] "40d26'46.3\"N"
dms_encode(-74.006328, prec = 5, indicator = "longitude")
#> [1] "074d00'22.8\"W"

# Azimuth format (always positive, 0-360)
dms_encode(-45.5, indicator = "azimuth", prec = 4)
#> [1] "314d30'00\""

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))
)
#>   prec       output
#> 1    0           40
#> 2    1         40.4
#> 3    2       40d27'
#> 4    3     40d26.8'
#> 5    4    40d26'46"
#> 6    5  40d26'46.3"
#> 7    6 40d26'46.30"

# Colon separator (ISO 6709 style)
dms_encode(angle, prec = 5, sep = ":")
#> [1] "40:26:46.3"

# Force specific trailing component
dms_encode(angle, component = "minute", prec = 4)
#> [1] "40d26.7717'"
dms_encode(angle, component = "second", prec = 2)
#> [1] "40d26'46.30\""

Splitting and Combining Components

Work with individual degree, minute, second components:

# Split into degrees and minutes
dms_split(c(40.446, -74.256))
#>     d      m
#> 1  40  26.76
#> 2 -74 -15.36

# Split into degrees, minutes, and seconds
dms_split(c(40.446195, -74.006328), seconds = TRUE)
#>     d  m        s
#> 1  40 26  46.3020
#> 2 -74  0 -22.7808

# Combine components back to decimal
dms_combine(40, 26, 47)
#> [1] 40.44639
dms_combine(
  d = c(40, -74, 51),
  m = c(26, 0, 30),
  s = c(47, 21.5, 0)
)
#> [1]  40.44639 -73.99403  51.50000

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
#> [1] "40d26'46.30\"N" "74d00'22.78\"S" "51d30'26.46\"N"

# Decode back
decoded <- dms_decode(encoded)
data.frame(
  original = original,
  encoded = encoded,
  decoded = decoded$angle,
  diff = abs(original - decoded$angle)
)
#>    original       encoded   decoded         diff
#> 1  40.44620 40d26'46.30"N  40.44619 5.555556e-07
#> 2 -74.00633 74d00'22.78"S -74.00633 2.222222e-07
#> 3  51.50735 51d30'26.46"N  51.50735 1.000000e-06

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)
  )
)
#>      format                      value
#> 1   decimal      40.446389, -74.005972
#> 2       DMS 40d26'47.0"N 074d00'21.5"W
#> 3 DMS-colon     40:26:47.0 -74:00:21.5
#> 4      MGRS              18TWK84297777

See Also