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.
# Locations at various latitudes
world_pts <- cbind(
lon = c(151.21, 0, -74.01, 0, 0),
lat = c(-33.87, 51.51, 40.71, 0, -90)
)
rownames(world_pts) <- c("Sydney", "London", "New York", "Equator/Prime", "South Pole")
# Antarctic stations
antarctic <- cbind(
lon = c(166.67, 77.85, -68.13, 39.58, 0),
lat = c(-77.85, -67.60, -67.57, -69.41, -90)
)
rownames(antarctic) <- c("McMurdo", "Davis", "Palmer", "Progress", "South Pole")Earth-Centered Earth-Fixed (ECEF) coordinates express positions as X, Y, Z relative to the Earth’s center:
# Points on the equator have Z ≈ 0
equator_pts <- cbind(
lon = c(0, 90, 180, -90),
lat = c(0, 0, 0, 0)
)
geocentric_fwd(equator_pts)
#> X Y Z lon lat h
#> 1 6378137 0 0 0 0 0
#> 2 0 6378137 0 90 0 0
#> 3 -6378137 0 0 180 0 0
#> 4 0 -6378137 0 -90 0 0
# Points at poles have X ≈ 0, Y ≈ 0
pole_pts <- cbind(lon = c(0, 0), lat = c(90, -90))
geocentric_fwd(pole_pts)
#> X Y Z lon lat h
#> 1 0 0 6356752 0 90 0
#> 2 0 0 -6356752 0 -90 0ECEF can include height above the WGS84 ellipsoid:
# Ground level vs airplane altitude (10km) vs satellite (400km)
pt <- c(151.21, -33.87)
data.frame(
location = c("Ground", "Aircraft (10km)", "ISS (~400km)", "GPS satellite (~20200km)"),
h = c(0, 10000, 400000, 20200000),
geocentric_fwd(cbind(rep(pt[1], 4), rep(pt[2], 4)),
h = c(0, 10000, 400000, 20200000))
)
#> location h X Y Z lon lat
#> 1 Ground 0 -4646017 2553114 -3534483 151.21 -33.87
#> 2 Aircraft (10km) 10000 -4653294 2557113 -3540056 151.21 -33.87
#> 3 ISS (~400km) 400000 -4937086 2713064 -3757407 151.21 -33.87
#> 4 GPS satellite (~20200km) 20200000 -19344970 10630591 -14792154 151.21 -33.87
#> h.1
#> 1 0
#> 2 10000
#> 3 400000
#> 4 20200000# Antarctic stations are all near the "bottom" of the coordinate system
geocentric_fwd(antarctic)
#> X Y Z lon lat h
#> 1 -1310449.4 310501.7 -6213433 166.67 -77.85 0
#> 2 513025.6 2382902.9 -5874236 77.85 -67.60 0
#> 3 909126.8 -2264950.0 -5872961 -68.13 -67.57 0
#> 4 1733893.8 1433382.5 -5948210 39.58 -69.41 0
#> 5 0.0 0.0 -6356752 0.00 -90.00 0
# Note the large negative Z values (Southern Hemisphere)
# and relatively small X, Y values (near the axis)ECEF is the native coordinate system for GPS satellites:
Local Cartesian, also called ENU (East-North-Up), creates a local coordinate system with:
This is ideal for local surveys, robotics, and navigation.
# Set up local system centered on Sydney
sydney <- c(151.21, -33.87)
nearby_pts <- cbind(
lon = c(151.21, 151.31, 151.11, 151.21, 151.21),
lat = c(-33.87, -33.87, -33.87, -33.77, -33.97)
)
rownames(nearby_pts) <- c("Origin", "East 10km", "West 10km", "North 10km", "South 10km")
localcartesian_fwd(nearby_pts, lon0 = sydney[1], lat0 = sydney[2])
#> x y z lon lat h
#> 1 0.000000e+00 0.000000 0.000000 151.21 -33.87 0
#> 2 9.252524e+03 -4.499921 -6.704168 151.31 -33.87 0
#> 3 -9.252524e+03 -4.499921 -6.704168 151.11 -33.87 0
#> 4 5.275069e-11 11091.908279 -9.679492 151.21 -33.77 0
#> 5 -2.150955e-10 -11092.088563 -9.679702 151.21 -33.97 0# Survey points around McMurdo Station
mcmurdo <- c(166.67, -77.85)
survey_pts <- cbind(
lon = c(166.67, 166.70, 166.64, 166.67, 166.73),
lat = c(-77.85, -77.85, -77.85, -77.84, -77.86)
)
rownames(survey_pts) <- c("Base", "Point A", "Point B", "Point C", "Point D")
result <- localcartesian_fwd(survey_pts, lon0 = mcmurdo[1], lat0 = mcmurdo[2])
result
#> x y z lon lat h
#> 1 0.000000e+00 0.000000 0.0000000 166.67 -77.85 0
#> 2 7.051476e+02 -0.180472 -0.0388546 166.70 -77.85 0
#> 3 -7.051476e+02 -0.180472 -0.0388546 166.64 -77.85 0
#> 4 -1.907097e-11 1116.439380 -0.0974277 166.67 -77.84 0
#> 5 1.409152e+03 -1117.161493 -0.2527202 166.73 -77.86 0
# Distances from base in meters
data.frame(
point = rownames(survey_pts),
east_m = round(result$x),
north_m = round(result$y),
distance_m = round(sqrt(result$x^2 + result$y^2))
)
#> point east_m north_m distance_m
#> 1 Base 0 0 0
#> 2 Point A 705 0 705
#> 3 Point B -705 0 705
#> 4 Point C 0 1116 1116
#> 5 Point D 1409 -1117 1798# Local system with height differences
# Simulating a hill near Sydney
pts_with_height <- cbind(
lon = c(151.21, 151.22, 151.20),
lat = c(-33.87, -33.87, -33.86)
)
heights <- c(0, 50, 100) # meters above ellipsoid
result <- localcartesian_fwd(pts_with_height,
lon0 = 151.21, lat0 = -33.87, h0 = 0,
h = heights)
result
#> x y z lon lat h
#> 1 0.0000 0.00000000 0.00000 151.21 -33.87 0
#> 2 925.2601 -0.04499957 49.93296 151.22 -33.87 50
#> 3 -925.3752 1109.17194197 99.83615 151.20 -33.86 100The WGS84 ellipsoid is the reference surface for GPS and most modern mapping.
ellipsoid_params()
#> $a
#> [1] 6378137
#>
#> $f
#> [1] 0.003352811
#>
#> $b
#> [1] 6356752
#>
#> $e2
#> [1] 0.00669438
#>
#> $ep2
#> [1] 0.006739497
#>
#> $n
#> [1] 0.00167922
#>
#> $area
#> [1] 5.100656e+14
#>
#> $volume
#> [1] 1.083207e+21Key parameters: - a: Semi-major axis (equatorial radius)
≈ 6,378,137 m - b: Semi-minor axis (polar radius) ≈
6,356,752 m - f: Flattening ≈ 1/298.257 - e2:
First eccentricity squared - area: Total surface area -
volume: Total volume
params <- ellipsoid_params()
# Equatorial vs polar radius difference
equatorial_radius <- params$a
polar_radius <- params$b
cat("Equatorial radius:", equatorial_radius, "m\n")
#> Equatorial radius: 6378137 m
cat("Polar radius:", polar_radius, "m\n")
#> Polar radius: 6356752 m
cat("Difference:", equatorial_radius - polar_radius, "m\n")
#> Difference: 21384.69 m
cat("Flattening:", 1/params$f, "(1/f)\n")
#> Flattening: 298.2572 (1/f)The Earth’s curvature varies with latitude:
# Curvature at various latitudes
lats <- c(0, -33.87, -42.88, -67.60, -77.85, -90)
names_lat <- c("Equator", "Sydney", "Hobart", "Davis", "McMurdo", "South Pole")
result <- ellipsoid_curvature(lats)
data.frame(
location = names_lat,
latitude = lats,
meridional_km = round(result$meridional / 1000, 2),
transverse_km = round(result$transverse / 1000, 2)
)
#> location latitude meridional_km transverse_km
#> 1 Equator 0.00 6335.44 6378.14
#> 2 Sydney -33.87 6355.25 6384.78
#> 3 Hobart -42.88 6365.01 6388.05
#> 4 Davis -67.60 6390.21 6396.46
#> 5 McMurdo -77.85 6396.73 6398.64
#> 6 South Pole -90.00 6399.59 6399.59# Properties of circles at different latitudes
lats <- c(0, -30, -45, -60, -75, -90)
result <- ellipsoid_circle(lats)
data.frame(
latitude = lats,
circle_radius_km = round(result$radius / 1000, 2),
meridian_dist_km = round(result$meridian_distance / 1000, 2)
)
#> latitude circle_radius_km meridian_dist_km
#> 1 0 6378.14 0.00
#> 2 -30 5528.26 -3320.11
#> 3 -45 4517.59 -4984.94
#> 4 -60 3197.10 -6654.07
#> 5 -75 1655.96 -8326.94
#> 6 -90 0.00 -10001.97For advanced geodetic calculations, various auxiliary latitudes are used:
# Compare different latitude types at 45°S
lats <- c(0, -30, -45, -60, -90)
result <- ellipsoid_latitudes(lats)
result
#> lat parametric geocentric rectifying authalic conformal isometric
#> 1 0 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000
#> 2 -30 -29.91675 -29.83364 -29.87515 -29.88900 -29.83368 -31.28104
#> 3 -45 -44.90379 -44.80758 -44.85568 -44.87170 -44.80768 -50.22747
#> 4 -60 -59.91661 -59.83308 -59.87489 -59.88879 -59.83322 -75.12340
#> 5 -90 -90.00000 -90.00000 -90.00000 -90.00000 -90.00000 -Inf
# The differences are small but matter for precise calculations# Typical GNSS workflow:
# 1. Receive ECEF coordinates from GPS
# 2. Convert to geodetic (lat/lon/height)
# 3. Convert to local for navigation
# Example GPS receiver data (ECEF, meters)
gps_ecef <- data.frame(
time = 1:5,
X = c(4648241, 4648242, 4648240, 4648243, 4648241),
Y = c(-2560342, -2560340, -2560343, -2560341, -2560342),
Z = c(-3526276, -3526275, -3526277, -3526274, -3526276)
)
# Convert to geodetic
geodetic <- geocentric_rev(gps_ecef$X, gps_ecef$Y, gps_ecef$Z)
# Convert to local (relative to first point)
local <- localcartesian_fwd(
cbind(geodetic$lon, geodetic$lat),
lon0 = geodetic$lon[1], lat0 = geodetic$lat[1], h0 = geodetic$h[1],
h = geodetic$h
)
data.frame(
time = gps_ecef$time,
east_m = round(local$x, 2),
north_m = round(local$y, 2),
up_m = round(local$z, 2)
)
#> time east_m north_m up_m
#> 1 1 0.00 0.00 0.00
#> 2 2 2.23 0.78 -0.63
#> 3 3 -1.36 -1.05 0.23
#> 4 4 1.84 2.37 -0.06
#> 5 5 0.00 0.00 0.00# Simulated survey data at McMurdo
mcmurdo <- c(166.67, -77.85, 10) # lon, lat, height
# Survey points in local coordinates
survey_local <- data.frame(
point = c("Control", "P1", "P2", "P3", "P4"),
east = c(0, 500, -300, 200, -100),
north = c(0, 200, 400, -150, -300),
up = c(0, 5, -2, 8, -5)
)
# Convert to geodetic for GPS upload
geodetic <- localcartesian_rev(
survey_local$east, survey_local$north, survey_local$up,
lon0 = mcmurdo[1], lat0 = mcmurdo[2], h0 = mcmurdo[3]
)
# Convert to ECEF for satellite positioning
ecef <- geocentric_fwd(
cbind(geodetic$lon, geodetic$lat),
h = geodetic$h
)
data.frame(
point = survey_local$point,
lon = round(geodetic$lon, 5),
lat = round(geodetic$lat, 5),
X = round(ecef$X),
Y = round(ecef$Y),
Z = round(ecef$Z)
)
#> point lon lat X Y Z
#> 1 Control 166.6700 -77.85000 -1310451 310502 -6213443
#> 2 P1 166.6913 -77.84821 -1310758 310061 -6213406
#> 3 P2 166.6572 -77.84642 -1310762 310884 -6213357
#> 4 P3 166.6785 -77.85134 -1310357 310274 -6213482
#> 5 P4 166.6657 -77.85269 -1310142 310532 -6213501| System | Description | Use Case |
|---|---|---|
| Geodetic (lon/lat/h) | Geographic coordinates | Maps, GIS, human communication |
| ECEF (X/Y/Z) | Earth-centered Cartesian | GPS satellites, orbit calculations |
| ENU (E/N/U) | Local tangent plane | Robotics, local surveys, navigation |
vignette("projections") for map projectionsvignette("geodesics") for distance and bearing
calculationsThese 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.