Here is some code to explore the variety available in calculating area across various packages.
x <- rnaturalearth::ne_countries(country = c("Greenland", "Spain"), returnclass = "sf")
as.numeric(sf::st_area(x))/1e6
## Greenland 2.166e6 km² -41, 72
## Spain 505990 km² -2, 40
## the numbers are different in different projections
as.numeric(sf::st_area(sf::st_transform(x, "+proj=laea")))/1e6
=## especially different in this one
as.numeric(sf::st_area(sf::st_transform(x, "EPSG:3857")))/1e6
## what did sp do?
spx <- sf::as_Spatial(x)
## area in native units (not useful, unless you want the metrics that you see when you plot)
rgeos::gArea(spx, byid = T)
## this is the same as sf now, so sf pivots on longlat vs non-longlat
rgeos::gArea(sf::as_Spatial(sf::st_transform(x, "EPSG:3857")), byid = TRUE)
## raster was the same, corrected for longlat
raster::area(spx)
## just gives native for projected
raster::area(sf::as_Spatial(sf::st_transform(x, "EPSG:3857")))
## but assumes it is longlat if it looks like longlat
spx@proj4string@projargs <- NA_character_
raster::area(spx)
## what does terra do
terra::expanse(terra::vect(x))
## gives the correct answer no matter what
terra::expanse(terra::project(terra::vect(x), "EPSG:3857"))
nocrs <- terra::project(terra::vect(x), "EPSG:3857")
terra::set.crs(nocrs, NA)
## this is the actual native area again (because terra ignores geography because it doesn't have a crs)
terra::expanse(nocrs)
nocrs_longlat <- terra::vect(x)
terra::set.crs(nocrs_longlat, NA)
terra::expanse(nocrs_longlat) ## different to raster which assumed longlat when it looked like it
## slightly different for the longlat case, because this is ellipsoidal (same as terra)
sf::sf_use_s2(FALSE)
as.numeric(sf::st_area(x))/1e6
as.numeric(sf::st_area(sf::st_transform(x, "+proj=laea")))/1e6
as.numeric(sf::st_area(sf::st_transform(x, "EPSG:3857")))/1e6
## ok but maybe *this* laea is not suitable, they stay the same because this projection is _equal_area_
as.numeric(sf::st_area(sf::st_transform(x, "+proj=laea +lon_0=-41 +lat_0=72")))/1e6
as.numeric(sf::st_area(sf::st_transform(x, "+proj=laea +lon_0=-2 +lat_0=40")))/1e6
## if we use an equidistant projection our numbers for area are better when we're centred on the object
as.numeric(sf::st_area(sf::st_transform(x, "+proj=aeqd +lon_0=-41 +lat_0=72")))/1e6 ## good for Greenland
as.numeric(sf::st_area(sf::st_transform(x, "+proj=aeqd +lon_0=-2 +lat_0=40")))/1e6 ## good for Spain
suppressPackageStartupMessages({ #' @title get administritive outlines for a country #' @description using the geoBoundaires API, #' download the geojson outline of a country #' @param country character vector of country names #' @param admin_level character vector of admin levels to download #' @return sf object of the outlines #' @details check out the documentation for the geoboundaries API at: #' geoBoundaries.org #' geo_bounds <- function(country, admin_level = c("ADM0", "ADM1", "ADM2")) { country <- countrycode::countrycode(country, origin = "country.name", destination = "iso3c" ) url <- paste( "https://www.geoboundaries.org/api/current/gbOpen/", country, admin_level[1], sep = "/" ) get <- httr::GET(url) cont <- httr::content(get, as = "parsed") area <- sf::read_sf(cont$gjDownloadURL) return(area) } #' @title get the area of a polygon in multiple projections and from packages #' #' @param v sf object #' @param add_crs character or numeric vector of crs to add to the sf object test_proj_area <- function(v, add_crs = c(4326, 3857)) { crs_opts <- suppressMessages(crsuggest::suggest_crs(v)) auto_epsg <- as.numeric(crs_opts$crs_code) area_tab <- function(x) { if (!suppressWarnings(is.na((as.numeric(x))))) { c_crs <- paste0("EPSG:", x) } else { c_crs <- x } vp <- sf::st_transform(v, c_crs) sf_a <- function(.s2, .crs) { suppressMessages(sf::sf_use_s2(.s2)) vp |> sf::st_transform(.crs) |> sf::st_area() |> units::set_units("km^2") } terra_a <- function(.transform, .crs) { tv <- terra::vect(vp) # tv <- terra::project(tv, .crs) terra::expanse(tv, transform = .transform) |> units::as_units("m^2") |> units::set_units("km^2") } tibble::tibble( "crs" = c_crs, "lon-lat" = sf::st_is_longlat(vp), "sf s2" = sf_a(TRUE, c_crs), "sf no s2" = sf_a(FALSE, c_crs), "terra trans" = terra_a(TRUE, c_crs), "terra no trans" = terra_a(FALSE, c_crs), ) } purrr::map(c(add_crs, auto_epsg), area_tab) |> dplyr::bind_rows() } bolivia <- geo_bounds("Bolivia") plot(sf::st_geometry(bolivia), axes = TRUE) tab <- test_proj_area(bolivia, add_crs = c( 4326, 4674, 9005, 3857, "+proj=laea +lon_0=-41 +lat_0=72", # inappropriate laea for bolivia "+proj=laea +lon_0=-64 +lat_0=-16", # appropriate laea for bolivia "+proj=aeqd +lon_0=-2 +lat_0=40", # inappropriate aeqd for bolivia "+proj=aeqd +lon_0=-64 +lat_0=-16" # appropriate aeqd for bolivia ) ) options(width = 100) print(dplyr::mutate( tab, across(where(is.numeric), ~ tibble::num(., digits = 3)) )) })Created on 2023-07-06 with reprex v2.0.2