--- title: "Local Coordinates and the Ellipsoid" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Local Coordinates and the Ellipsoid} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(geographiclib) ``` ## Contents - [Example Locations](#example-locations) - [Geocentric (ECEF) Coordinates](#geocentric-ecef-coordinates) - [Local Cartesian (ENU) Coordinates](#local-cartesian-enu-coordinates) - [WGS84 Ellipsoid Properties](#wgs84-ellipsoid-properties) - [Combining Coordinate Systems](#combining-coordinate-systems) - [Coordinate System Summary](#coordinate-system-summary) - [See Also](#see-also) ## Example Locations ```{r locations} # 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") ``` ## Geocentric (ECEF) Coordinates Earth-Centered Earth-Fixed (ECEF) coordinates express positions as X, Y, Z relative to the Earth's center: - **X-axis**: Points toward 0°N, 0°E (intersection of equator and prime meridian) - **Y-axis**: Points toward 0°N, 90°E (intersection of equator and 90°E) - **Z-axis**: Points toward the North Pole ### Basic Conversion ```{r geocentric-basic} geocentric_fwd(world_pts) ``` ### Understanding ECEF ```{r geocentric-structure} # 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) # Points at poles have X ≈ 0, Y ≈ 0 pole_pts <- cbind(lon = c(0, 0), lat = c(90, -90)) geocentric_fwd(pole_pts) ``` ### Height Above Ellipsoid ECEF can include height above the WGS84 ellipsoid: ```{r geocentric-height} # 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)) ) ``` ### Antarctic Stations in ECEF ```{r geocentric-antarctic} # Antarctic stations are all near the "bottom" of the coordinate system geocentric_fwd(antarctic) # Note the large negative Z values (Southern Hemisphere) # and relatively small X, Y values (near the axis) ``` ### Round-trip Conversion ```{r geocentric-roundtrip} fwd <- geocentric_fwd(world_pts) rev <- geocentric_rev(fwd$X, fwd$Y, fwd$Z) # Verify accuracy max(abs(rev$lon - world_pts[,1])) max(abs(rev$lat - world_pts[,2])) max(abs(rev$h)) # Height should be ~0 ``` ### GPS Applications ECEF is the native coordinate system for GPS satellites: ```{r geocentric-gps} # Convert GPS receiver position to geodetic # (Example: receiver at Sydney at ~100m altitude) X <- 4648241 # meters Y <- -2560342 Z <- -3526276 geocentric_rev(X, Y, Z) ``` ## Local Cartesian (ENU) Coordinates Local Cartesian, also called ENU (East-North-Up), creates a local coordinate system with: - **East**: Positive X direction - **North**: Positive Y direction - **Up**: Positive Z direction This is ideal for local surveys, robotics, and navigation. ### Basic Conversion ```{r local-basic} # 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]) ``` ### Local Survey Application ```{r local-survey} # 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 # 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)) ) ``` ### Including Height ```{r local-height} # 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 ``` ### Round-trip Conversion ```{r local-roundtrip} pts <- cbind( lon = c(166.67, 166.70, 166.64), lat = c(-77.85, -77.84, -77.86) ) fwd <- localcartesian_fwd(pts, lon0 = 166.67, lat0 = -77.85) rev <- localcartesian_rev(fwd$x, fwd$y, fwd$z, lon0 = 166.67, lat0 = -77.85) max(abs(rev$lon - pts[,1])) max(abs(rev$lat - pts[,2])) ``` ### Robotics/Navigation Application ```{r local-robotics} # Robot path planning at Davis Station davis <- c(77.85, -67.60) # Waypoints for a robot traverse waypoints_enu <- data.frame( name = c("Start", "WP1", "WP2", "WP3", "End"), x = c(0, 100, 200, 250, 300), # East (meters) y = c(0, 50, 100, 100, 150), # North (meters) z = c(0, 0, 0, 0, 0) # Up (meters) ) # Convert to geographic coordinates for GPS navigation result <- localcartesian_rev( waypoints_enu$x, waypoints_enu$y, waypoints_enu$z, lon0 = davis[1], lat0 = davis[2] ) data.frame( waypoint = waypoints_enu$name, lon = round(result$lon, 6), lat = round(result$lat, 6) ) ``` ## WGS84 Ellipsoid Properties The WGS84 ellipsoid is the reference surface for GPS and most modern mapping. ### Basic Parameters ```{r ellipsoid-params} ellipsoid_params() ``` Key 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 ### Earth's Shape ```{r ellipsoid-shape} params <- ellipsoid_params() # Equatorial vs polar radius difference equatorial_radius <- params$a polar_radius <- params$b cat("Equatorial radius:", equatorial_radius, "m\n") cat("Polar radius:", polar_radius, "m\n") cat("Difference:", equatorial_radius - polar_radius, "m\n") cat("Flattening:", 1/params$f, "(1/f)\n") ``` ### Radii of Curvature The Earth's curvature varies with latitude: ```{r ellipsoid-curvature} # 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) ) ``` ### Circle of Latitude Properties ```{r ellipsoid-circle} # 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) ) ``` ### Auxiliary Latitudes For advanced geodetic calculations, various auxiliary latitudes are used: ```{r ellipsoid-auxiliary} # Compare different latitude types at 45°S lats <- c(0, -30, -45, -60, -90) result <- ellipsoid_latitudes(lats) result # The differences are small but matter for precise calculations ``` ## Combining Coordinate Systems ### GNSS Data Processing ```{r combined-gnss} # 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) ) ``` ### Antarctic Field Survey ```{r combined-antarctic} # 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) ) ``` ## Coordinate System Summary | 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 | ## See Also - `vignette("projections")` for map projections - `vignette("geodesics")` for distance and bearing calculations - [ECEF on Wikipedia](https://en.wikipedia.org/wiki/Earth-centered,_Earth-fixed_coordinate_system) - [Local tangent plane coordinates](https://en.wikipedia.org/wiki/Local_tangent_plane_coordinates)