This vignette covers the map projections available in geographiclib. Each projection has different properties making it suitable for different purposes.

Example Locations

We’ll use a mix of Northern and Southern Hemisphere locations:

# Australian cities
australia <- cbind(
  lon = c(151.21, 144.96, 153.02, 115.86, 138.60),
  lat = c(-33.87, -37.81, -27.47, -31.95, -34.93)
)
rownames(australia) <- c("Sydney", "Melbourne", "Brisbane", "Perth", "Adelaide")

# Antarctic stations
antarctic <- cbind(
  lon = c(166.67, 77.97, 39.58, -64.05, 0),
  lat = c(-77.85, -68.58, -67.60, -64.25, -90)
)
rownames(antarctic) <- c("McMurdo", "Davis", "Mawson", "Palmer", "South Pole")

# World cities
world_pts <- cbind(
  lon = c(-0.13, -74.01, 139.69, 151.21, -43.17),
  lat = c(51.51, 40.71, 35.69, -33.87, -22.91)
)
rownames(world_pts) <- c("London", "New York", "Tokyo", "Sydney", "Rio")

UTM/UPS - Universal Transverse Mercator

UTM divides the Earth into 60 zones, each 6 degrees wide. For polar regions (>84N or <80S), UPS (Universal Polar Stereographic) is used instead.

Basic Conversion

# Convert Australian cities
utmups_fwd(australia)
#>          x       y zone northp  convergence     scale    lon    lat        crs
#> 1 334435.7 6250816   56  FALSE  0.997812613 0.9999379 151.21 -33.87 EPSG:32756
#> 2 320422.8 5813305   55  FALSE  1.250945807 0.9999972 144.96 -37.81 EPSG:32755
#> 3 501976.0 6961506   56  FALSE -0.009225683 0.9996000 153.02 -27.47 EPSG:32756
#> 4 392259.4 6464539   50  FALSE  0.603322218 0.9997432 115.86 -31.95 EPSG:32750
#> 5 280787.4 6132090   54  FALSE  1.374728322 1.0001923 138.60 -34.93 EPSG:32754

Understanding UTM Zones

# Points at different longitudes show different zones
lon_transect <- cbind(
  lon = seq(-180, 180, by = 30),
  lat = -45
)

result <- utmups_fwd(lon_transect)
data.frame(
  lon = result$lon,
  zone = result$zone,
  northp = result$northp,
  crs = result$crs
)
#>     lon zone northp        crs
#> 1  -180    1  FALSE EPSG:32701
#> 2  -150    6  FALSE EPSG:32706
#> 3  -120   11  FALSE EPSG:32711
#> 4   -90   16  FALSE EPSG:32716
#> 5   -60   21  FALSE EPSG:32721
#> 6   -30   26  FALSE EPSG:32726
#> 7     0   31  FALSE EPSG:32731
#> 8    30   36  FALSE EPSG:32736
#> 9    60   41  FALSE EPSG:32741
#> 10   90   46  FALSE EPSG:32746
#> 11  120   51  FALSE EPSG:32751
#> 12  150   56  FALSE EPSG:32756
#> 13  180    1  FALSE EPSG:32701

Polar Regions (UPS)

When zone = 0, the projection is UPS rather than UTM:

# Antarctic stations
utmups_fwd(antarctic)
#>           x       y zone northp convergence     scale    lon    lat        crs
#> 1  539232.5 1357811   58  FALSE  -1.6326124 0.9996188 166.67 -77.85 EPSG:32758
#> 2  621006.2 2389548   43  FALSE  -2.7651884 0.9997792  77.97 -68.58 EPSG:32743
#> 3  524664.5 2501616   37  FALSE  -0.5362394 0.9996074  39.58 -67.60 EPSG:32737
#> 4  449103.3 2874707   20  FALSE   0.9457532 0.9996317 -64.05 -64.25 EPSG:32720
#> 5 2000000.0 2000000    0  FALSE   0.0000000 0.9940000   0.00 -90.00 EPSG:32761

Round-trip Conversion

fwd <- utmups_fwd(australia)
rev <- utmups_rev(fwd$x, fwd$y, fwd$zone, fwd$northp)

# Verify accuracy
max(abs(rev$lon - australia[,1]))
#> [1] 0
max(abs(rev$lat - australia[,2]))
#> [1] 7.105427e-15

Transverse Mercator (Custom)

For custom Transverse Mercator projections with user-defined central meridian and scale factor (unlike UTM which auto-selects zones):

# Custom TM centered on Tasmania
tm_fwd(australia, lon0 = 147, k0 = 1.0)
#>            x        y convergence     scale    lon    lat lon0
#> 1   389509.3 -3755726   -2.349232 1.0014708 151.21 -33.87  147
#> 2  -179577.2 -4186695    1.250946 0.9999972 144.96 -37.81  147
#> 3   595405.5 -3052957   -2.785128 1.0039790 153.02 -27.47  147
#> 4 -3004450.5 -3995140   17.749690 1.1129339 115.86 -31.95  147
#> 5  -768115.8 -3897657    4.833213 1.0068799 138.60 -34.93  147

# Compare series approximation vs exact
pts <- cbind(lon = c(147, 148, 149), lat = c(-42, -43, -44))
tm_fwd(pts, lon0 = 147)       # Fast (~5nm accuracy)
#>           x        y convergence     scale lon lat lon0
#> 1      0.00 -4647916   0.0000000 0.9992002 147 -42  147
#> 2  81476.04 -4759395  -0.6820358 0.9992819 148 -43  147
#> 3 160285.27 -4871868  -1.3896118 0.9995163 149 -44  147
tm_exact_fwd(pts, lon0 = 147) # Exact (slower)
#>           x        y convergence     scale lon lat lon0
#> 1      0.00 -4647916   0.0000000 0.9992002 147 -42  147
#> 2  81476.04 -4759395  -0.6820358 0.9992819 148 -43  147
#> 3 160285.27 -4871868  -1.3896118 0.9995163 149 -44  147

Lambert Conformal Conic (LCC)

LCC is ideal for mid-latitude regions with greater east-west extent. It can use one standard parallel (tangent cone) or two (secant cone).

Single Standard Parallel

# Project Australia using a single standard parallel at -35
lcc_fwd(australia, lon0 = 135, stdlat = -35)
#>            x           y convergence    scale    lon    lat
#> 1  1493548.7    3909.821   -9.297674 1.000193 151.21 -33.87
#> 2   876672.9 -355683.465   -5.712821 1.001213 144.96 -37.81
#> 3  1786319.5  675672.155  -10.335847 1.008404 153.02 -27.47
#> 4 -1801035.7  165367.391   10.978253 1.001395 115.86 -31.95
#> 5   328846.1    1839.535   -2.064875 1.000001 138.60 -34.93

Two Standard Parallels

Two standard parallels give better scale distribution across the region:

# Project Australia using two standard parallels
result <- lcc_fwd(australia, lon0 = 135, stdlat1 = -18, stdlat2 = -36)
result
#>            x          y convergence     scale    lon    lat
#> 1  1487815.5  -836523.8   -7.390430 0.9947423 151.21 -33.87
#> 2   881143.8 -1212440.8   -4.540943 1.0057047 144.96 -37.81
#> 3  1753258.1  -163784.3   -8.215642 0.9877621 153.02 -27.47
#> 4 -1786896.6  -665348.0    8.726270 0.9912930 115.86 -31.95
#> 5   327940.2  -862239.3   -1.641305 0.9971666 138.60 -34.93

LCC for Antarctica

# Antarctic projection centered on the pole
lcc_fwd(antarctic, lon0 = 0, stdlat1 = -71, stdlat2 = -89)
#>            x          y convergence     scale    lon    lat
#> 1   344966.9 -2165162.2  -165.11619 0.9879434 166.67 -77.85
#> 2  2314592.7  -343174.4   -77.24311 1.0064017  77.97 -68.58
#> 3  1569910.6  1056939.8   -39.21101 1.0093051  39.58 -67.60
#> 4 -2560607.7   412096.3    63.45288 1.0205891 -64.05 -64.25
#> 5        0.0  -867205.9     0.00000 1.8855739   0.00 -90.00

Albers Equal Area

Albers is an equal-area conic projection, ideal for thematic maps where accurate area representation is important.

Two Standard Parallels (Most Common)

# Albers Equal Area for Australia
albers_fwd(australia, lon0 = 132, stdlat1 = -18, stdlat2 = -36)
#>            x       y convergence    scale    lon    lat lon0
#> 1  2759463.2 5728938   -8.615282 1.558384 151.21 -33.87  132
#> 2  1893029.2 6112549   -5.812288 1.661597 144.96 -37.81  132
#> 3  2939004.6 5216954   -9.427029 1.421022 153.02 -27.47  132
#> 4 -2303568.9 5652426    7.238451 1.513623 115.86 -31.95  132
#> 5   955094.6 5986948   -2.959961 1.584571 138.60 -34.93  132

CONUS Albers (US Standard)

# Continental US configuration
conus <- cbind(
  lon = c(-122.42, -74.01, -87.63, -104.99, -118.24),
  lat = c(37.77, 40.71, 41.88, 39.74, 34.05)
)
rownames(conus) <- c("San Francisco", "New York", "Chicago", "Denver", "Los Angeles")

albers_fwd(conus, lon0 = -96, stdlat1 = 29.5, stdlat2 = 45.5)
#>            x         y convergence     scale     lon   lat lon0
#> 1 -2275633.0  299934.9  -15.928691 0.9903128 -122.42 37.77  -96
#> 2  1826049.7  523243.1   13.257832 0.9915130  -74.01 40.71  -96
#> 3   688859.4  472356.2    5.046296 0.9927869  -87.63 41.88  -96
#> 4  -762375.2  238443.6   -5.420096 0.9908110 -104.99 39.74  -96
#> 5 -2019412.3 -197542.9  -13.408558 0.9924666 -118.24 34.05  -96

Antarctic Albers

# Antarctic equal-area projection
albers_fwd(antarctic, lon0 = 0, stdlat1 = -72, stdlat2 = -60)
#>           x         y convergence        scale    lon    lat lon0
#> 1   6362785 -14451441  -151.42898 8.975620e+00 166.67 -77.85    0
#> 2  12417304   1547213   -70.84009 5.112554e+00  77.97 -68.58    0
#> 3   7706574   7855318   -35.96063 4.891702e+00  39.58 -67.60    0
#> 4 -11083150   4106510    58.19299 4.264540e+00 -64.05 -64.25    0
#> 5         0  10612815     0.00000 3.852793e+31   0.00 -90.00    0

Why Equal-Area Matters

Albers preserves area, making it suitable for: - Choropleth maps (population density, land use) - Statistical analysis where area matters - Environmental mapping

# Compare Albers (equal-area) vs LCC (conformal)
albers_result <- albers_fwd(australia, lon0 = 132, stdlat1 = -18, stdlat2 = -36)
lcc_result <- lcc_fwd(australia, lon0 = 132, stdlat1 = -18, stdlat2 = -36)

data.frame(
  city = rownames(australia),
  albers_scale = round(albers_result$scale, 4),
  lcc_scale = round(lcc_result$scale, 4)
)
#>        city albers_scale lcc_scale
#> 1    Sydney       1.5584    0.9947
#> 2 Melbourne       1.6616    1.0057
#> 3  Brisbane       1.4210    0.9878
#> 4     Perth       1.5136    0.9913
#> 5  Adelaide       1.5846    0.9972

Polar Stereographic

Conformal projection for polar regions. The default scale factor (k0 = 0.994) corresponds to UPS. Use k0 = 1.0 for true stereographic.

Antarctic Stations

# Antarctic stations with UPS-standard scale
polarstereo_fwd(antarctic, northp = FALSE, k0 = 0.994)
#>            x          y convergence    scale    lon    lat northp
#> 1   312134.1 -1317338.8     -166.67 1.005257 166.67 -77.85  FALSE
#> 2  2352282.0   501280.4      -77.97 1.029540  77.97 -68.58  FALSE
#> 3  1604242.5  1940574.9      -39.58 1.032951  39.58 -67.60  FALSE
#> 4 -2613033.8  1271640.2       64.05 1.045897 -64.05 -64.25  FALSE
#> 5        0.0        0.0        0.00 0.994000   0.00 -90.00  FALSE

Arctic Points

# Arctic circle of points
arctic <- cbind(lon = seq(0, 315, by = 45), lat = 85)
polarstereo_fwd(arctic, northp = TRUE)
#>           x         y convergence     scale lon lat northp
#> 1       0.0 -555457.4           0 0.9958948   0  85   TRUE
#> 2  392767.7 -392767.7          45 0.9958948  45  85   TRUE
#> 3  555457.4       0.0          90 0.9958948  90  85   TRUE
#> 4  392767.7  392767.7         135 0.9958948 135  85   TRUE
#> 5       0.0  555457.4         180 0.9958948 180  85   TRUE
#> 6 -392767.7  392767.7        -135 0.9958948 225  85   TRUE
#> 7 -555457.4       0.0         -90 0.9958948 270  85   TRUE
#> 8 -392767.7 -392767.7         -45 0.9958948 315  85   TRUE

# All points at same latitude have same distance from pole
result <- polarstereo_fwd(arctic, northp = TRUE)
sqrt(result$x^2 + result$y^2)  # All equal
#> [1] 555457.4 555457.4 555457.4 555457.4 555457.4 555457.4 555457.4 555457.4

Pole at Origin

The pole is always at the origin:

# South pole
polarstereo_fwd(c(0, -90), northp = FALSE)
#>   x y convergence scale lon lat northp
#> 1 0 0           0 0.994   0 -90  FALSE

# North pole
polarstereo_fwd(c(0, 90), northp = TRUE)
#>   x y convergence scale lon lat northp
#> 1 0 0           0 0.994   0  90   TRUE

Azimuthal Equidistant

This projection preserves distances from the center point. Useful for showing distances from a specific location.

Distances from Sydney

# Project world cities relative to Sydney
sydney <- c(151.21, -33.87)
result <- azeq_fwd(world_pts, lon0 = sydney[1], lat0 = sydney[2])
result
#>           x         y        azi     scale    lon    lat   lon0   lat0
#> 1 -11079224  12879887 -119.64500 0.1711505  -0.13  51.51 151.21 -33.87
#> 2  14570028   6581725   86.24134 0.2338675 -74.01  40.71 151.21 -33.87
#> 3  -1348791   7675580  -10.19020 0.7678612 139.69  35.69 151.21 -33.87
#> 4         0         0    0.00000 1.0000000 151.21 -33.87 151.21 -33.87
#> 5   3622420 -13046064   13.96290 0.4028404 -43.17 -22.91 151.21 -33.87

# Distance from Sydney (in km) = sqrt(x^2 + y^2) / 1000
distances <- sqrt(result$x^2 + result$y^2) / 1000
data.frame(
  city = rownames(world_pts),
  distance_km = round(distances)
)
#>       city distance_km
#> 1   London       16989
#> 2 New York       15988
#> 3    Tokyo        7793
#> 4   Sydney           0
#> 5      Rio       13540

Distances from South Pole

# Distance from South Pole to Antarctic stations
result <- azeq_fwd(antarctic, lon0 = 0, lat0 = -90)

distances <- sqrt(result$x^2 + result$y^2) / 1000
data.frame(
  station = rownames(antarctic),
  lat = antarctic[,2],
  distance_from_pole_km = round(distances)
)
#>               station    lat distance_from_pole_km
#> McMurdo       McMurdo -77.85                  1357
#> Davis           Davis -68.58                  2391
#> Mawson         Mawson -67.60                  2501
#> Palmer         Palmer -64.25                  2874
#> South Pole South Pole -90.00                     0

Cassini-Soldner Projection

A historical projection used for large-scale topographic mapping. It’s a transverse cylindrical projection that preserves scale along the central meridian.

Regional Mapping

# Tasmania centered on Hobart
tasmania <- cbind(
  lon = c(147.32, 145.49, 146.82, 148.29, 147.13),
  lat = c(-42.88, -40.83, -41.44, -42.15, -43.21)
)
rownames(tasmania) <- c("Hobart", "Launceston", "Devonport", "St Helens", "Dover")

cassini_fwd(tasmania, lon0 = 147, lat0 = -42)
#>            x          y      azi        rk    lon    lat
#> 1   26143.77  -97801.70 89.78225 0.9999916 147.32 -42.88
#> 2 -127363.69  128844.96 90.98739 0.9998004 145.49 -40.83
#> 3  -15043.16   62182.36 90.11913 0.9999972 146.82 -41.44
#> 4  106622.05  -17466.80 89.13423 0.9998602 148.29 -42.15
#> 5   10564.15 -134421.11 89.91099 0.9999986 147.13 -43.21

Cassini for Antarctic Survey

# McMurdo area survey
mcmurdo_area <- cbind(
  lon = c(166.67, 166.40, 167.00, 166.87, 168.40),
  lat = c(-77.85, -77.55, -78.15, -78.65, -77.18)
)
rownames(mcmurdo_area) <- c("McMurdo", "Marble Point", "Black Island",
                            "Minna Bluff", "Cape Adare")

cassini_fwd(mcmurdo_area, lon0 = 166.67, lat0 = -77.85)
#>           x             y      azi        rk    lon    lat
#> 1     0.000 -1.767322e-10 90.00000 1.0000000 166.67 -77.85
#> 2 -6500.514  3.347787e+04 90.26365 0.9999995 166.40 -77.55
#> 3  7567.890 -3.351489e+04 89.67703 0.9999993 167.00 -78.15
#> 4  4395.733 -8.932524e+04 89.80391 0.9999998 166.87 -78.65
#> 5 42862.413  7.416859e+04 88.31310 0.9999776 168.40 -77.18

Gnomonic Projection

The gnomonic projection has a unique property: geodesics (great circles) appear as straight lines. This makes it invaluable for route planning.

Great Circle Routes Appear Straight

# Project Sydney-London great circle path
sydney_london <- geodesic_path(c(151.21, -33.87), c(-0.13, 51.51), n = 10)

# Project onto gnomonic centered between them
gnomonic_fwd(cbind(sydney_london$lon, sydney_london$lat),
             lon0 = 75, lat0 = 10)
#>              x           y       azi         rk       lon        lat
#> 1   53750192.8 -38674589.3 105.87562 0.09561024 151.21000 -33.870000
#> 2   16125384.2  -7826853.0 109.10430 0.33428476 139.50728 -20.394877
#> 3    9508995.0  -2403418.9 106.14956 0.54390141 129.74369  -6.243880
#> 4    6375713.2    164588.2  96.06692 0.70600425 120.50187   8.085556
#> 5    4262116.9   1896931.5  76.23096 0.80631698 110.57824  22.181580
#> 6    2477411.8   3360033.4  45.83258 0.83606441  98.49021  35.519419
#> 7     662222.8   4848723.7  11.28405 0.79272387  81.94055  47.164224
#> 8   -1569003.9   6679635.7 -23.21534 0.68015913  57.97086  55.245877
#> 9   -5049826.5   9537836.5 -57.54997 0.50819491  27.52748  56.986363
#> 10 -13167755.5  16208232.0 -84.74250 0.29173829  -0.13000  51.510000

Route Planning

# Flights from Sydney - project candidate destinations
destinations <- cbind(
  lon = c(-0.13, -74.01, 139.69, 77.22, -43.17),
  lat = c(51.51, 40.71, 35.69, 28.61, -22.91)
)
rownames(destinations) <- c("London", "New York", "Tokyo", "Delhi", "Rio")

# Gnomonic from Sydney shows great circle routes as straight lines
gnomonic_fwd(destinations, lon0 = 151.21, lat0 = -33.87)
#>          x        y        azi          rk    lon    lat
#> 1      NaN      NaN -119.64500 -0.89276928  -0.13  51.51
#> 2      NaN      NaN   86.24134 -0.80937135 -74.01  40.71
#> 3 -3056293 17392486  -10.19020  0.33886934 139.69  35.69
#> 4      NaN      NaN  -53.28367 -0.06608416  77.22  28.61
#> 5      NaN      NaN   13.96290 -0.52164061 -43.17 -22.91

OSGB - Ordnance Survey National Grid

OSGB is specific to Great Britain. Note: It uses the OSGB36 datum, not WGS84.

# British locations (using approximate OSGB36 coordinates)
britain <- cbind(
  lon = c(-0.127, -3.188, -4.251, -1.890, -2.587),
  lat = c(51.507, 55.953, 55.864, 52.486, 51.454)
)
rownames(britain) <- c("London", "Edinburgh", "Glasgow", "Birmingham", "Cardiff")

# Convert to OSGB grid
osgb_fwd(britain)
#>    easting northing convergence     scale    lon    lat
#> 1 529972.5 180390.9  1.46617141 0.9998087 -0.127 51.507
#> 2 325826.3 673962.6 -0.98439586 0.9996688 -3.188 55.953
#> 3 259144.5 665711.6 -1.86347428 0.9998447 -4.251 55.864
#> 4 407468.8 287611.3  0.08725254 0.9996020 -1.890 52.486
#> 5 359217.8 172997.6 -0.45910375 0.9996217 -2.587 51.454

Grid References

# Get alphanumeric grid references
osgb_gridref(britain, precision = 3)  # 100m precision
#> [1] "TQ299803" "NT258739" "NS591657" "SP074876" "ST592729"

# Parse a grid reference
osgb_gridref_rev("TQ308080")
#>          lon      lat easting northing precision
#> 1 -0.1407134 50.85658  530850   108050         3

Projection Comparison

Different projections preserve different properties:

Projection Preserves Best For
UTM/UPS Shape (conformal) Global standard, topographic maps
Transverse Mercator Shape (conformal) Custom zone definitions
LCC Shape (conformal) Mid-latitude regional maps
Albers Equal Area Area Thematic/statistical maps
Polar Stereographic Shape (conformal) Polar regions
Azimuthal Equidistant Distance from center Showing distances from a point
Cassini-Soldner Scale on central meridian Large-scale surveys
Gnomonic Great circles as straight lines Route planning
OSGB Shape (conformal) British mapping

See Also