The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Map Projections

library(geographiclib)

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

Contents

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

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.