tests/testthat/test-mgrs.R

test_that("mgrs_fwd works with single point", {
  code <- mgrs_fwd(cbind(147.325, -42.881))
  expect_type(code, "character")
  expect_length(code, 1)
  expect_match(code, "^[0-9]{2}[A-Z]{3}[0-9]+$")
})

test_that("mgrs_fwd works with multiple points", {
  x <- cbind(lon = c(147, 148, -100), lat = c(-42, -42, -42))
  codes <- mgrs_fwd(x)
  expect_type(codes, "character")
  expect_length(codes, 3)
  expect_true(all(grepl("^[0-9A-Z]+$", codes)))
})

test_that("mgrs_fwd handles different precision levels", {
  x <- cbind(147.325, -42.881)

  # Test each precision level
  for (prec in 0:5) {
    code <- mgrs_fwd(x, precision = prec)
    expect_type(code, "character")

    # MGRS string length varies by precision
    # Format: 2-digit zone + 3 letters + (2*precision) digits
    expected_length <- 5 + (2 * prec)
    expect_equal(nchar(code), expected_length)
  }
})

test_that("mgrs_fwd accepts vector of precisions", {
  x <- cbind(lon = c(147, 148, -100, 0, 10),
             lat = c(-42, -42, -42, 0, 10))
  codes <- mgrs_fwd(x, precision = c(0, 1, 2, 3, 5))

  expect_length(codes, 5)
  expect_equal(nchar(codes[1]), 5)   # precision 0
  expect_equal(nchar(codes[2]), 7)   # precision 1
  expect_equal(nchar(codes[3]), 9)   # precision 2
  expect_equal(nchar(codes[4]), 11)  # precision 3
  expect_equal(nchar(codes[5]), 15)  # precision 5
})

test_that("mgrs_fwd rejects invalid precision", {
  x <- cbind(147, -42)
  expect_error(mgrs_fwd(x, precision = 6), "precision must be between 0 and 5")
  expect_error(mgrs_fwd(x, precision = -1), "precision must be between 0 and 5")
})

test_that("mgrs_fwd accepts different input formats", {
  # Matrix
  code1 <- mgrs_fwd(cbind(147, -42))

  # Vector
  code2 <- mgrs_fwd(c(147, -42))

  # List
  code3 <- mgrs_fwd(list(lon = 147, lat = -42))

  expect_equal(code1, code2)
  expect_equal(code1, code3)
})

test_that("mgrs_rev returns data frame with correct structure", {
  code <- "55GEP0000050223"
  result <- mgrs_rev(code)

  expect_s3_class(result, "data.frame")
  expect_named(result, c('lon', 'lat', 'x', 'y', 'zone', 'northp', 'precision', 'convergence', 'scale', 'grid_zone', 'square_100km', 'crs'))
  expect_equal(nrow(result), 1)

  # Check column types
  expect_type(result$lon, "double")
  expect_type(result$lat, "double")
  expect_type(result$x, "double")
  expect_type(result$y, "double")
  expect_type(result$zone, "integer")
  expect_type(result$northp, "logical")
  expect_type(result$crs, "character")
})

test_that("mgrs_rev is vectorized", {
  codes <- c("55GEP0000050223", "55GEP8281849740", "14GMU1718149740")
  result <- mgrs_rev(codes)

  expect_equal(nrow(result), 3)
  expect_equal(result$zone, c(55, 55, 14))
  expect_equal(result$northp, c(FALSE, FALSE, FALSE))
})

test_that("mgrs_rev CRS codes are correct for standard UTM zones", {
  # Test northern and southern hemisphere
  codes <- c("55GEP0000050223", "33TWM0000050223")  # South and North examples
  result <- mgrs_rev(codes)

  # Southern hemisphere (zone 55)
  expect_match(result$crs[1], "^EPSG:327[0-9]{2}$")
  expect_equal(result$crs[1], "EPSG:32755")

  # Northern hemisphere (zone 33)
  expect_match(result$crs[2], "^EPSG:326[0-9]{2}$")
})

test_that("mgrs_rev handles polar regions correctly", {
  # North pole region
  north_codes <- mgrs_fwd(cbind(c(147, 148, -100), 88))
  result_north <- mgrs_rev(north_codes)

  expect_equal(result_north$zone, c(0, 0, 0))
  expect_equal(result_north$northp, c(TRUE, TRUE, TRUE))
  expect_equal(result_north$crs, c("EPSG:32661", "EPSG:32661", "EPSG:32661"))

  # South pole region
  south_codes <- mgrs_fwd(cbind(c(147, 148, -100), -88))
  result_south <- mgrs_rev(south_codes)

  expect_equal(result_south$zone, c(0, 0, 0))
  expect_equal(result_south$northp, c(FALSE, FALSE, FALSE))
  expect_equal(result_south$crs, c("EPSG:32761", "EPSG:32761", "EPSG:32761"))
})

test_that("mgrs round-trip conversion preserves location", {
  # Original coordinates
  x <- cbind(lon = c(147, 148, -100, 0, 45),
             lat = c(-42, -42, -42, 0, 60))

  # Forward then reverse
  codes <- mgrs_fwd(x, precision = 5)
  result <- mgrs_rev(codes)

  # Check coordinates match (within precision tolerance ~1m)
  expect_equal(result$lon, x[, 1], tolerance = 0.00001)
  expect_equal(result$lat, x[, 2], tolerance = 0.00001)
})

test_that("mgrs precision affects round-trip accuracy", {
  original <- cbind(147.325, -42.881)

  # Low precision should give less accurate results
  code_low <- mgrs_fwd(original, precision = 1)  # 10 km precision
  result_low <- mgrs_rev(code_low)

  # High precision should be more accurate
  code_high <- mgrs_fwd(original, precision = 5)  # 1 m precision
  result_high <- mgrs_rev(code_high)

  # Calculate differences
  diff_low <- sqrt((result_low$lon - original[1])^2 + (result_low$lat - original[2])^2)
  diff_high <- sqrt((result_high$lon - original[1])^2 + (result_high$lat - original[2])^2)

  expect_true(diff_high < diff_low)
  expect_true(diff_high < 0.0001)  # High precision very accurate
})

test_that("mgrs works across different UTM zones", {
  # Sample points from various zones
  x <- cbind(lon = c(-120, -60, 0, 60, 120, 180),
             lat = c(45, 45, 45, 45, 45, 45))

  codes <- mgrs_fwd(x)
  result <- mgrs_rev(codes)

  # Should have different zones
  expect_true(length(unique(result$zone)) > 1)

  # All should be northern hemisphere
  expect_true(all(result$northp))

  # All CRS codes should be EPSG:326XX
  expect_true(all(grepl("^EPSG:326[0-9]{2}$", result$crs)))
})

test_that("mgrs handles equator crossing", {
  # Points just north and south of equator
  x <- cbind(lon = c(30, 30), lat = c(1, -1))

  codes <- mgrs_fwd(x)
  result <- mgrs_rev(codes)

  # Should be same zone but different hemispheres
  expect_equal(result$zone[1], result$zone[2])
  expect_equal(result$northp, c(TRUE, FALSE))

  # Different CRS codes (326 vs 327)
  expect_match(result$crs[1], "^EPSG:326")
  expect_match(result$crs[2], "^EPSG:327")
})

test_that("mgrs_rev includes all expected fields", {
  code <- "55GEN2654152348"
  result <- mgrs_rev(code)

  expect_named(result, c("lon", "lat", "x", "y", "zone", "northp",
                         "precision", "convergence", "scale",
                         "grid_zone", "square_100km", "crs"))

  # Check new field types
  expect_type(result$convergence, "double")
  expect_type(result$scale, "double")
  expect_type(result$grid_zone, "character")
  expect_type(result$square_100km, "character")

  # Check values make sense
  expect_true(abs(result$convergence) < 180)  # Convergence in degrees
  expect_true(result$scale > 0.9 && result$scale < 1.1)  # Scale near 1.0
  expect_match(result$grid_zone, "^[0-9]{2}[A-Z]$")  # e.g., "55G"
  expect_match(result$square_100km, "^[A-Z]{2}$")  # e.g., "EN"
})

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.