---
title: "Grid Reference Systems: MGRS, Geohash, GARS, and Georef"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Grid Reference Systems: MGRS, Geohash, GARS, and Georef}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
library(geographiclib)
```

## Contents

- [Example Locations](#example-locations)
- [MGRS - Military Grid Reference System](#mgrs---military-grid-reference-system)
- [Geohash](#geohash)
- [GARS - Global Area Reference System](#gars---global-area-reference-system)
- [Georef - World Geographic Reference System](#georef---world-geographic-reference-system)
- [Comparison of Systems](#comparison-of-systems)
- [GeoCoords - Universal Coordinate Parsing](#geocoords---universal-coordinate-parsing)
- [DMS - Degrees, Minutes, Seconds Conversion](#dms---degrees-minutes-seconds-conversion)
- [See Also](#see-also)

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:

```{r locations}
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

```{r mgrs-basic}
# 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:

- **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

```{r mgrs-structure}
# 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):

```{r mgrs-precision}
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:

```{r mgrs-polar}
# 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:

```{r mgrs-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

```{r geohash-basic}
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:

```{r geohash-truncation}
# 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

```{r geohash-resolution}
geohash_resolution(1:12)
```

### Finding Required Length for Precision

```{r geohash-length}
# 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

```{r geohash-southern}
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

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

### Precision Levels

```{r gars-precision}
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

```{r gars-antarctic}
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

```{r georef-basic}
codes <- georef_fwd(pts, precision = 2)
data.frame(name = locations$name, georef = codes)
```

### Precision Levels

```{r georef-precision}
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:

```{r georef-flight}
# 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 |

```{r comparison}
# 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:

```{r geocoords-parse}
# 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:

```{r geocoords-batch}
# 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:

```{r geocoords-integration}
# 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:

```{r dms-decode}
# 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:
  
```{r dms-latlon}
# 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):

```{r dms-angle-azimuth}
# 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:

```{r dms-encode}
# 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:

```{r dms-format}
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:

```{r dms-split-combine}
# 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):

```{r dms-roundtrip}
# 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:

```{r dms-geocoords}
# 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

- `vignette("projections")` for map projections (UTM/UPS, LCC, etc.)
- `vignette("geodesics")` for distance and bearing calculations
- [MGRS on Wikipedia](https://en.wikipedia.org/wiki/Military_Grid_Reference_System)
- [Geohash on Wikipedia](https://en.wikipedia.org/wiki/Geohash)
