Skip to content

Commit 5501379

Browse files
committed
review area calculation functions
1 parent 89ae6c5 commit 5501379

File tree

7 files changed

+125
-70
lines changed

7 files changed

+125
-70
lines changed

NAMESPACE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ S3method(.check_samples,default)
2121
S3method(.check_samples,sits)
2222
S3method(.cube_adjust_crs,default)
2323
S3method(.cube_adjust_crs,grd_cube)
24+
S3method(.cube_area_freq,class_cube)
25+
S3method(.cube_area_freq,class_vector_cube)
26+
S3method(.cube_area_freq,default)
2427
S3method(.cube_as_sf,default)
2528
S3method(.cube_as_sf,raster_cube)
2629
S3method(.cube_bands,default)

R/api_cube.R

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -367,19 +367,13 @@ NULL
367367
#'
368368
#' @return A \code{vector} with the areas of the cube labels.
369369
.cube_class_areas <- function(cube) {
370-
# Get area for each class for each row of the cube
371-
freq_lst <- slider::slide(cube, function(tile) {
372-
# Get the frequency count and value for each labelled image
373-
.tile_area_freq(tile)
374-
})
375-
# Get a tibble by binding the row (duplicated labels with different counts)
376-
freq <- do.call(rbind, freq_lst)
370+
# Get cube area / pixel frequency
371+
freq <- .cube_area_freq(cube)
377372
# summarize the counts for each label
378373
freq <- freq |>
379374
dplyr::filter(!is.na(class)) |>
380375
dplyr::group_by(class) |>
381376
dplyr::summarise(area = sum(.data[["area"]]))
382-
383377
# Area is taken as the sum of pixels
384378
class_areas <- freq[["area"]]
385379
# Names of area are the classes
@@ -388,7 +382,40 @@ NULL
388382
class_areas[is.na(class_areas)] <- 0.0
389383
class_areas
390384
}
391-
385+
#' @title Return areas/frequency of classes of a class_cube
386+
#' @keywords internal
387+
#' @noRd
388+
#' @name .cube_area_freq
389+
#' @param cube class cube
390+
#'
391+
#' @return A \code{vector} with the area and pixel frequency of the cube labels.
392+
.cube_area_freq <- function(cube) {
393+
UseMethod(".cube_area_freq", cube)
394+
}
395+
#' @export
396+
.cube_area_freq.class_cube <- function(cube) {
397+
# Get area for each class for each row of the cube
398+
slider::slide_dfr(cube, function(tile) {
399+
# Get the frequency count and value for each labelled image
400+
.tile_area_freq(tile)
401+
}) |>
402+
dplyr::filter(!is.na(.data[["class"]]))
403+
}
404+
#' @export
405+
.cube_area_freq.class_vector_cube <- function(cube) {
406+
# Get area for each class for each row of the cube
407+
slider::slide_dfr(cube, function(tile) {
408+
# Get the frequency count and value for each labelled image
409+
.tile_area_freq(tile)
410+
}) |>
411+
dplyr::filter(!is.na(.data[["class"]]))
412+
}
413+
#' @export
414+
.cube_area_freq.default <- function(cube) {
415+
cube <- tibble::as_tibble(cube)
416+
cube <- .cube_find_class(cube)
417+
.cube_area_freq(cube)
418+
}
392419
#' @title Return bands of a data cube
393420
#' @keywords internal
394421
#' @noRd

R/api_message.R

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@
7676
)
7777
}
7878
}
79+
#' @title Warning when converting a slow area calculation will be performed
80+
#' @name .message_warnings_slow_area_calculation
81+
#' @noRd
82+
#' @returns Called for side effects
83+
.message_warnings_slow_area_calculation <- function() {
84+
if (.message_warnings()) {
85+
warning(.conf("messages", "summary_class_cube_slow_area"), call. = FALSE)
86+
}
87+
}
7988
#' @title Test if progress bar should be shown
8089
#' @name .message_progress
8190
#' @noRd

R/api_raster.R

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,20 @@
883883
terra::freq(x = rast, bylayer = TRUE)
884884
}
885885

886+
#' @title Raster package internal area value
887+
#' @name .raster_area
888+
#' @keywords internal
889+
#' @noRd
890+
#' @author Felipe Carlos, \email{efelipecarlos@@gmail.com}
891+
#'
892+
#' @param rast raster package object to count values
893+
#' @param ... additional parameters to be passed to raster package
894+
#'
895+
#' @return matrix with layer, value, and area columns
896+
.raster_area <- function(rast, ...) {
897+
terra::expanse(x = rast, ...)
898+
}
899+
886900
#' @title Raster package internal raster data type
887901
#' @name .raster_datatype
888902
#' @keywords internal

R/api_tile.R

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,24 +1393,53 @@ NULL
13931393
}
13941394
#' @export
13951395
.tile_area_freq.class_cube <- function(tile) {
1396-
# Open first raster
1397-
rast <- .raster_open_rast(.tile_path(tile))
1398-
# Retrieve the frequency
1399-
freq <- tibble::as_tibble(.raster_freq(rast))
1400-
# get labels
1401-
labels <- .tile_labels(tile)
1402-
# pixel area
1403-
# convert the area to hectares
1404-
# assumption: spatial resolution unit is meters
1405-
area <- freq[["count"]] * .tile_xres(tile) * .tile_yres(tile) / 10000.0
1406-
# Include class names
1407-
freq <- dplyr::mutate(
1408-
freq,
1409-
area = area,
1410-
class = labels[as.character(freq[["value"]])]
1396+
# get tile crs
1397+
tile_crs <- .tile_crs(tile)
1398+
# get tile crs unit (metre or degree)
1399+
tile_crs_unit <- sf::st_crs(tile_crs)$units_gdal
1400+
# extract the file path
1401+
tile_file <- .tile_paths(tile)
1402+
# read the files with terra
1403+
rast <- .raster_open_rast(tile_file)
1404+
# get area by pixels
1405+
if (tile_crs_unit == "metre") {
1406+
# get a frequency of values
1407+
class_areas <- .raster_freq(rast) |>
1408+
dplyr::select(-.data[["layer"]])
1409+
# transform to km^2
1410+
cell_size <- .tile_xres(tile) * .tile_yres(tile)
1411+
class_areas[["area"]] <- (class_areas[["count"]] * cell_size) / 1000000L
1412+
} else {
1413+
# get pixels by class
1414+
class_count <- .raster_freq(rast)
1415+
# get area by class in km^2
1416+
class_areas <- .raster_area(rast = rast, unit = "km", byValue = TRUE)
1417+
# Merge area and pixel count
1418+
class_areas <- dplyr::full_join(class_count, class_areas, by = "value") |>
1419+
dplyr::select(-.data[["layer.x"]], -.data[["layer.y"]])
1420+
}
1421+
# change value to character
1422+
class_areas <- dplyr::mutate(
1423+
class_areas,
1424+
value = as.character(.data[["value"]])
14111425
)
1412-
# Return frequencies
1413-
freq
1426+
# create a data.frame with the labels
1427+
tile_labels <- .tile_labels(tile)
1428+
df1 <- tibble::tibble(
1429+
value = names(tile_labels),
1430+
class = unname(tile_labels)
1431+
)
1432+
# join the labels with the areas
1433+
sum_areas <- dplyr::full_join(df1, class_areas, by = "value")
1434+
sum_areas <- dplyr::mutate(sum_areas,
1435+
area = signif(.data[["area"]], 2L),
1436+
.keep = "unused"
1437+
)
1438+
# replace na
1439+
sum_clean <- sum_areas |>
1440+
tidyr::replace_na(list(count = 0L, area = 0.0))
1441+
1442+
sum_clean
14141443
}
14151444
#' @export
14161445
.tile_area_freq.class_vector_cube <- function(tile) {

R/sits_summary.R

Lines changed: 16 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -447,49 +447,21 @@ summary.variance_cube <- function(object, ...,
447447
#' @export
448448
summary.class_cube <- function(object, ...) {
449449
.check_set_caller("summary_class_cube")
450-
# Extract classes values for each tiles using a sample size
451-
classes_areas <- slider::slide(object, function(tile) {
452-
# extract the file path
453-
tile_file <- .tile_paths(tile)
454-
# read the files with terra
455-
r <- .raster_open_rast(tile_file)
456-
# get a frequency of values
457-
class_areas <- .raster_freq(r)
458-
# transform to km^2
459-
cell_size <- .tile_xres(tile) * .tile_yres(tile)
460-
class_areas[["area"]] <- (class_areas[["count"]] * cell_size) / 1000000L
461-
# change value to character
462-
class_areas <- dplyr::mutate(
463-
class_areas,
464-
value = as.character(.data[["value"]])
465-
)
466-
# create a data.frame with the labels
467-
tile_labels <- .tile_labels(tile)
468-
df1 <- tibble::tibble(
469-
value = names(tile_labels),
470-
class = unname(tile_labels)
471-
)
472-
# join the labels with the areas
473-
sum_areas <- dplyr::full_join(df1, class_areas, by = "value")
474-
sum_areas <- dplyr::mutate(sum_areas,
475-
area_km2 = signif(.data[["area"]], 2L),
476-
.keep = "unused"
477-
)
478-
# remove layer information
479-
sum_clean <- sum_areas[, -3L] |>
480-
tidyr::replace_na(list(layer = 1L, count = 0L, area_km2 = 0.0))
481-
482-
sum_clean
450+
# check if cube has only metre crs
451+
cube_has_only_metre_crs <- slider::slide_lgl(object, function(tile) {
452+
# get tile crs
453+
tile_crs <- .tile_crs(tile)
454+
# check unit
455+
sf::st_crs(tile_crs)$units_gdal == "metre"
483456
})
484-
# Combine tiles areas
485-
classes_areas <- dplyr::bind_rows(classes_areas) |>
486-
dplyr::group_by(.data[["value"]], .data[["class"]]) |>
487-
dplyr::summarise(
488-
count = sum(.data[["count"]]),
489-
area_km2 = sum(.data[["area_km2"]]),
490-
.groups = "keep"
491-
) |>
492-
dplyr::ungroup()
493-
# Return classes areas
494-
classes_areas
457+
# all must be true
458+
cube_has_only_metre_crs <- all(cube_has_only_metre_crs)
459+
# if there is any non-metre crs, inform user about slow area calculation
460+
if (!cube_has_only_metre_crs) {
461+
# show warning message or slow area calculation
462+
.message_warnings_slow_area_calculation()
463+
}
464+
# Extract cube class areas
465+
.cube_area_freq(object) |>
466+
dplyr::rename(area_km2 = "area")
495467
}

inst/extdata/config_messages.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,5 +562,6 @@ sits_xgboost: "wrong input parameters - see example in documentation"
562562
summary_raster_cube: "check that input is regular data cube"
563563
summary_derived_cube: "check that input is probability data cube"
564564
summary_class_cube: "check that input is classified data cube"
565+
summary_class_cube_slow_area: "we detected that your cube uses a non-metre Coordinate Reference System (CRS) - this may use more processing time since additional unit transformations will be required."
565566
summary_class_cube_area: "some classes have no area: "
566567
expect_error: "called from expect_error"

0 commit comments

Comments
 (0)