This vignette covers the map projections available in geographiclib. Each projection has different properties making it suitable for different purposes.
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 divides the Earth into 60 zones, each 6 degrees wide. For polar regions (>84N or <80S), UPS (Universal Polar Stereographic) is used instead.
# 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
# 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:32701When 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
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-15For 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 147LCC is ideal for mid-latitude regions with greater east-west extent. It can use one standard parallel (tangent cone) or two (secant cone).
# 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.93Two 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
# 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.00Albers is an equal-area conic projection, ideal for thematic maps where accurate area representation is important.
# 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
# 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 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 0Albers 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.9972Conformal projection for polar regions. The default scale factor (k0 = 0.994) corresponds to UPS. Use k0 = 1.0 for true stereographic.
# 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 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.4The 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 TRUEThis projection preserves distances from the center point. Useful for showing distances from a specific location.
# 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
# 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 0A historical projection used for large-scale topographic mapping. It’s a transverse cylindrical projection that preserves scale along the central meridian.
# 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
# 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.18The gnomonic projection has a unique property: geodesics (great circles) appear as straight lines. This makes it invaluable for route planning.
# 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
# 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.91OSGB 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
# 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 3Different 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 |
vignette("geodesics") for distance and bearing
calculationsvignette("grid-reference-systems") for MGRS, Geohash,
etc.vignette("local-coordinates") for Local Cartesian (ENU)
and Geocentric