From ee6ecb4ba8db4026512db703e0c585ec5ed2d95e Mon Sep 17 00:00:00 2001 From: Felipe Carlos Date: Sat, 18 May 2024 11:49:12 -0300 Subject: [PATCH] include raster support in the detections api --- DESCRIPTION | 1 + NAMESPACE | 1 + R/api_detect_changes.R | 199 +++++++++++++++++++++++++++++- R/api_dtw.R | 142 +++++++++++++++------ R/api_patterns.R | 20 +++ R/api_predictors.R | 28 +++++ R/sits_detect_change.R | 148 ++++++++++++++++++++-- R/sits_dtw.R | 79 +++--------- inst/extdata/config_internals.yml | 11 ++ inst/extdata/config_messages.yml | 1 + man/sits_detect_change.Rd | 59 ++++++++- man/sits_dtw.Rd | 8 +- 12 files changed, 575 insertions(+), 122 deletions(-) create mode 100644 R/api_patterns.R diff --git a/DESCRIPTION b/DESCRIPTION index b1d7616c0..78c7bad04 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -148,6 +148,7 @@ Collate: 'api_mosaic.R' 'api_opensearch.R' 'api_parallel.R' + 'api_patterns.R' 'api_period.R' 'api_plot_time_series.R' 'api_plot_raster.R' diff --git a/NAMESPACE b/NAMESPACE index 551bffa80..00b17c51d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -339,6 +339,7 @@ S3method(sits_cube,local_cube) S3method(sits_cube,sar_cube) S3method(sits_cube,stac_cube) S3method(sits_detect_change,default) +S3method(sits_detect_change,raster_cube) S3method(sits_detect_change,sits) S3method(sits_get_data,csv) S3method(sits_get_data,data.frame) diff --git a/R/api_detect_changes.R b/R/api_detect_changes.R index 1a07970b1..7602d9b49 100644 --- a/R/api_detect_changes.R +++ b/R/api_detect_changes.R @@ -25,20 +25,211 @@ # Divide samples in chunks to parallel processing parts <- .pred_create_partition(pred = samples, partitions = multicores) # Detect changes! - detections <- .jobs_map_parallel_dfr(parts, function(part) { + .jobs_map_parallel_dfr(parts, function(part) { # Get samples values <- .pred_part(part) # Detect changes! For detection, models can be time-aware. So, the # complete data, including dates, must be passed as argument. - detections <- cd_method(values[["time_series"]]) + detections <- cd_method(values[["time_series"]], "ts") detections <- tibble::tibble(detections) # Prepare result result <- tibble::tibble(data.frame(values, detections = detections)) class(result) <- class(values) # return result - }, progress = progress) +} - return(detections) +#' @title Detect changes from a chunk of raster data using multicores +#' @name .detect_change_tile +#' @keywords internal +#' @noRd +#' @param tile Single tile of a data cube. +#' @param band Band to be produced. +#' @param cd_method Change Detection Model. +#' @param block Optimized block to be read into memory. +#' @param roi Region of interest. +#' @param filter_fn Smoothing filter function to be applied to the data. +#' @param impute_fn Imputation function. +#' @param output_dir Output directory. +#' @param version Version of result. +#' @param verbose Print processing information? +#' @param progress Show progress bar? +#' @return List of the classified raster layers. +.detect_change_tile <- function(tile, + band, + cd_method, + block, + roi, + filter_fn, + impute_fn, + output_dir, + version, + verbose, + progress) { + # Output file + out_file <- .file_derived_name( + tile = tile, + band = band, + version = version, + output_dir = output_dir + ) + # Resume feature + if (file.exists(out_file)) { + if (.check_messages()) { + .check_recovery(out_file) + } + detected_changes_tile <- .tile_derived_from_file( + file = out_file, + band = band, + base_tile = tile, + labels = .ml_labels_code(cd_method), + derived_class = "detections_cube", + update_bbox = TRUE + ) + return(detected_changes_tile) + } + # Show initial time for tile classification + tile_start_time <- .tile_classif_start( + tile = tile, + verbose = verbose + ) + # Tile timeline + tile_timeline <- .tile_timeline(tile) + # Create chunks as jobs + chunks <- .tile_chunks_create( + tile = tile, + overlap = 0, + block = block + ) + # By default, update_bbox is FALSE + update_bbox <- FALSE + if (.has(roi)) { + # How many chunks there are in tile? + nchunks <- nrow(chunks) + # Intersecting chunks with ROI + chunks <- .chunks_filter_spatial( + chunks = chunks, + roi = roi + ) + # Should bbox of resulting tile be updated? + update_bbox <- nrow(chunks) != nchunks + } + # Process jobs in parallel + block_files <- .jobs_map_parallel_chr(chunks, function(chunk) { + # Job block + block <- .block(chunk) + # Block file name + block_file <- .file_block_name( + pattern = .file_pattern(out_file), + block = block, + output_dir = output_dir + ) + # Resume processing in case of failure + if (.raster_is_valid(block_file)) { + return(block_file) + } + # Read and preprocess values + values <- .classify_data_read( + tile = tile, + block = block, + bands = .ml_bands(cd_method), + ml_model = cd_method, + impute_fn = impute_fn, + filter_fn = filter_fn + ) + # Get mask of NA pixels + na_mask <- C_mask_na(values) + # Fill with zeros remaining NA pixels + values <- C_fill_na(values, 0) + # Used to check values (below) + n_input_pixels <- nrow(values) + # Include names in cube predictors + colnames(values) <- .pred_features_name( + .ml_bands(cd_method), tile_timeline + ) + # Prepare values + values <- .pred_as_ts(values, .ml_bands(cd_method), tile_timeline) |> + tidyr::nest(.by = "sample_id", .key = "time_series") + # Log here + .debug_log( + event = "start_block_data_detection", + key = "model", + value = .ml_class(cd_method) + ) + # Detect changes! + values <- cd_method(values[["time_series"]], "cube") |> + dplyr::as_tibble() + # Are the results consistent with the data input? + .check_processed_values( + values = values, + n_input_pixels = n_input_pixels + ) + # Log here + .debug_log( + event = "end_block_data_detection", + key = "model", + value = .ml_class(cd_method) + ) + # Prepare probability to be saved + band_conf <- .conf_derived_band( + derived_class = "detections_cube", + band = band + ) + offset <- .offset(band_conf) + if (.has(offset) && offset != 0) { + values <- values - offset + } + scale <- .scale(band_conf) + if (.has(scale) && scale != 1) { + values <- values / scale + } + # Mask NA pixels with same probabilities for all classes + values[na_mask, ] <- 0 # event detection = 1, no event = 0 + # Log + .debug_log( + event = "start_block_data_save", + key = "file", + value = block_file + ) + # Prepare and save results as raster + .raster_write_block( + files = block_file, + block = block, + bbox = .bbox(chunk), + values = values, + data_type = .data_type(band_conf), + missing_value = .miss_value(band_conf), + crop_block = NULL + ) + # Log + .debug_log( + event = "end_block_data_save", + key = "file", + value = block_file + ) + # Free memory + gc() + # Returned block file + block_file + }, progress = progress) + # Merge blocks into a new detections_cube tile + detections_tile <- .tile_derived_merge_blocks( + file = out_file, + band = band, + labels = .ml_labels_code(cd_method), + base_tile = tile, + block_files = block_files, + derived_class = "detections_cube", + multicores = .jobs_multicores(), + update_bbox = update_bbox + ) + # show final time for detection + .tile_classif_end( + tile = tile, + start_time = tile_start_time, + verbose = verbose + ) + # Return detections tile + detections_tile } diff --git a/R/api_dtw.R b/R/api_dtw.R index 4a0d553c6..b94b243f3 100644 --- a/R/api_dtw.R +++ b/R/api_dtw.R @@ -1,38 +1,37 @@ - -#' @title Extract temporal pattern of samples using temporal median. -#' @name .pattern_temporal_median +# ---- Distances ---- +#' @title Calculate the DTW distance between temporal patterns and time-series. +#' @name .dtw_distance +#' @description This function calculates the DTW distance between label patterns +#' and real data (e.g., sample data, data cube data). The distance is calculated +#' without a window. It's use is recommended for big datasets. #' @keywords internal #' @noRd -.pattern_temporal_median <- function(samples) { - samples |> - dplyr::group_by(.data[["label"]]) |> - dplyr::group_map(function(data, name) { - ts_median <- dplyr::bind_rows(data[["time_series"]]) |> - dplyr::group_by(.data[["Index"]]) |> - dplyr::summarize(dplyr::across(dplyr::everything(), - stats::median, na.rm = TRUE)) |> - dplyr::select(-.data[["Index"]]) - - ts_median["label"] <- name - ts_median - }) +.dtw_distance <- function(data, patterns) { + # Prepare input data + data <- as.matrix(.ts_values(data)) + # Calculate the DTW distance between `data` and `patterns` + purrr::map_dfc(patterns, function(pattern) { + # Prepare pattern data + pattern_ts <- as.matrix(.ts_values(pattern)) + # Calculate distance + stats::setNames( + data.frame(distance = dtw_distance(data, pattern_ts)), + pattern[["label"]][[1]] + ) + }) } - -#' @title Calculate the DTW distance between label patterns and sample data. -#' @name .pattern_distance_dtw +#' @title Calculate the DTW distance between temporal patterns and time-series. +#' @name .dtw_distance_windowed #' @description This function calculates the DTW distance between label patterns -#' and sample data in a given temporal window. +#' and real data (e.g., sample data, data cube data). The distance is calculated +#' using windows. #' @keywords internal #' @noRd -.pattern_distance_dtw <- function(data, patterns, windows) { +.dtw_distance_windowed <- function(data, patterns, windows) { # Calculate the DTW distance between `data` and `patterns` - purrr::map_dfc(1:length(patterns), function(pattern_index) { - # Get pattern metadata - pattern <- patterns[pattern_index][[1]] - pattern_label <- unique(pattern[["label"]]) + purrr::map_dfc(patterns, function(pattern) { # Get pattern data - pattern_ts <- dplyr::select(pattern, -.data[["label"]]) - pattern_ts <- as.matrix(pattern_ts) + pattern_ts <- as.matrix(.ts_values(pattern)) # Windowed search distances <- purrr::map_df(windows, function(window) { # Get time-series in the window @@ -41,16 +40,89 @@ .data[["Index"]] >= window[["start"]], .data[["Index"]] <= window[["end"]]) # Remove the time reference column - data_in_window <- dplyr::select(data_in_window, -.data[["Index"]]) - # Transform values in matrix (as expected in the cpp code) - data_in_window <- as.matrix(data_in_window) - data_in_window <- data_in_window + data_in_window <- as.matrix(.ts_values(data_in_window)) # Calculate distance - distance_from_pattern <- dtw_distance(data_in_window, pattern_ts) - # Prepare result and return it - data.frame(distance = distance_from_pattern) + data.frame(distance = dtw_distance(data_in_window, pattern_ts)) }) # Associate the pattern name with the distances - stats::setNames(distances, pattern_label) + stats::setNames(distances, pattern[["label"]][[1]]) + }) +} +# ---- Operation mode ---- +#' @title Search for events in time series using complete data (no windowing). +#' @name .dtw_complete_ts +#' @description This function searches for events in time series without +#' windowing. +#' @keywords internal +#' @noRd +.dtw_complete_ts <- function(values, patterns, threshold, ...) { + # Do the change detection for each time-series + purrr::map_vec(values, function(value_row) { + # Search for the patterns + patterns_distances <- .dtw_distance( + data = value_row, + patterns = patterns + ) + # Remove distances out the user-defined threshold + as.numeric(any(patterns_distances <= threshold)) + }) +} +#' @title Search for events in time series using windowing. +#' @name .dtw_windowed_ts +#' @description This function searches for events in time series with windowing. +#' @keywords internal +#' @noRd +.dtw_windowed_ts <- function(values, patterns, window, threshold) { + # Extract dates + dates_min <- .ts_min_date(values[[1]]) + dates_max <- .ts_max_date(values[[1]]) + # Assume time-series are regularized, then use the period + # as the step of the moving window. As a result, we have + # one step per iteration. + dates_step <- lubridate::as.period( + lubridate::int_diff(.ts_index(values[[1]])) + ) + dates_step <- dates_step[[1]] + # Create comparison windows + comparison_windows <- .period_windows( + period = window, + step = dates_step, + start_date = dates_min, + end_date = dates_max + ) + # Do the change detection for each time-series + purrr::map(values, function(value_row) { + # Search for the patterns + patterns_distances <- .dtw_distance_windowed( + data = value_row, + patterns = patterns, + windows = comparison_windows + ) + # Remove distances out the user-defined threshold + patterns_distances[patterns_distances > threshold] <- NA + # Define where each label was detected. For this, first + # get from each label the minimal distance + detections_idx <- apply(patterns_distances, 2, which.min) + detections_name <- names(detections_idx) + # For each label, extract the metadata where they had + # minimal distance + purrr::map_df(seq_len(length(detections_idx)), function(idx) { + # Extract detection name and inced + detection_name <- detections_name[idx] + detection_idx <- detections_idx[idx] + # Extract detection distance (min one defined above) + detection_distance <- patterns_distances[detection_idx,] + detection_distance <- detection_distance[detection_name] + detection_distance <- as.numeric(detection_distance) + # Extract detection dates + detection_dates <- comparison_windows[[detection_idx]] + # Prepare result and return it! + tibble::tibble( + change = detection_name, + distance = detection_distance, + from = detection_dates[["start"]], + to = detection_dates[["end"]] + ) + }) }) } diff --git a/R/api_patterns.R b/R/api_patterns.R new file mode 100644 index 000000000..721eb393a --- /dev/null +++ b/R/api_patterns.R @@ -0,0 +1,20 @@ + +#' @title Extract temporal pattern from samples data. +#' @name .pattern_temporal_median +#' @keywords internal +#' @noRd +#' @param samples Samples data. +.pattern_temporal_median <- function(samples) { + samples |> + dplyr::group_by(.data[["label"]]) |> + dplyr::group_map(function(data, name) { + ts_median <- dplyr::bind_rows(data[["time_series"]]) |> + dplyr::group_by(.data[["Index"]]) |> + dplyr::summarize(dplyr::across(dplyr::everything(), + stats::median, na.rm = TRUE)) |> + dplyr::select(-.data[["Index"]]) + + ts_median["label"] <- name + ts_median + }) +} diff --git a/R/api_predictors.R b/R/api_predictors.R index d0912cbdb..31d945476 100644 --- a/R/api_predictors.R +++ b/R/api_predictors.R @@ -149,6 +149,31 @@ dplyr::ungroup() return(frac) } +#' @title Convert predictors to ts +#' @keywords internal +#' @noRd +#' @param data Predictor data to be converted. +#' @param bands Name of the bands available in the given predictor data. +#' @param timeline Timeline of the predictor data. +#' @return Predictor data as ts. +.pred_as_ts <- function(data, bands, timeline) { + data |> + dplyr::as_tibble() |> + tidyr::pivot_longer( + cols = dplyr::everything(), + cols_vary = "fastest", + names_to = ".value", + names_pattern = paste0( + "^(", paste(bands, collapse = "|"), ")" + ) + ) |> + dplyr::mutate( + sample_id = rep( seq_len(nrow(data)), each = dplyr::n() / nrow(data) ), + label = "NoClass", + Index = rep(timeline, nrow(data)), + .before = 1 + ) +} # ---- Partitions ---- #' @title Get predictors of a given partition #' @keywords internal @@ -157,3 +182,6 @@ .pred_part <- function(part) { .default(part[["predictors"]][[1]]) } + + + diff --git a/R/sits_detect_change.R b/R/sits_detect_change.R index 9c3ebfe5c..6cf1a8b3e 100644 --- a/R/sits_detect_change.R +++ b/R/sits_detect_change.R @@ -9,14 +9,35 @@ #' each time series with a set of change/no-change patterns, and indicates #' places and dates where change has been detected. #' -#' @param data Set of time series. -#' @param cd_method Change detection method (with parameters). -#' @param ... Other relevant parameters. -#' @param filter_fn Smoothing filter function to be applied to the data. -#' @param multicores Number of threads to process the time series. -#' @param progress Show progress bar? -#' @return Set of time series where significant change has been -#' detected. +#' @param data Set of time series. +#' @param cd_method Change detection method (with parameters). +#' @param ... Other parameters for specific functions. +#' @param roi Region of interest (either an sf object, shapefile, +#' or a numeric vector with named XY values +#' ("xmin", "xmax", "ymin", "ymax") or +#' named lat/long values +#' ("lon_min", "lat_min", "lon_max", "lat_max"). +#' @param filter_fn Smoothing filter to be applied - optional +#' (clousure containing object of class "function"). +#' @param impute_fn Imputation function to remove NA. +#' @param start_date Start date for the classification +#' (Date in YYYY-MM-DD format). +#' @param end_date End date for the classification +#' (Date im YYYY-MM-DD format). +#' @param memsize Memory available for classification in GB +#' (integer, min = 1, max = 16384). +#' @param multicores Number of cores to be used for classification +#' (integer, min = 1, max = 2048). +#' @param output_dir Valid directory for output file. +#' (character vector of length 1). +#' @param version Version of the output +#' (character vector of length 1). +#' @param verbose Logical: print information about processing time? +#' @param progress Logical: Show progress bar? +#' @return Time series with detection labels for +#' each point (tibble of class "sits") +#' or a data cube indicating detections in each pixel +#' (tibble of class "detections_cube"). #' @export sits_detect_change <- function(data, cd_method, @@ -42,15 +63,120 @@ sits_detect_change.sits <- function(data, .check_is_sits_model(cd_method) .check_int_parameter(multicores, min = 1, max = 2048) .check_progress(progress) - # Do detection - detections <- .detect_change_ts( + # Detect changes + .detect_change_ts( samples = data, cd_method = cd_method, filter_fn = filter_fn, multicores = multicores, progress = progress ) - return(detections) +} + +#' @rdname sits_detect_change +#' @export +sits_detect_change.raster_cube <- function(data, + cd_method, ..., + roi = NULL, + filter_fn = NULL, + impute_fn = impute_linear(), + start_date = NULL, + end_date = NULL, + memsize = 8L, + multicores = 2L, + output_dir, + version = "v1", + verbose = FALSE, + progress = TRUE) { + # set caller for error messages + .check_set_caller("sits_detect_change_raster") + # preconditions + .check_is_raster_cube(data) + .check_that(.cube_is_regular(data)) + .check_is_sits_model(cd_method) + .check_int_parameter(memsize, min = 1, max = 16384) + .check_int_parameter(multicores, min = 1, max = 2048) + .check_output_dir(output_dir) + # version is case-insensitive in sits + version <- .check_version(version) + .check_progress(progress) + # Get default proc bloat + proc_bloat <- .conf("processing_bloat_cpu") + # Spatial filter + if (.has(roi)) { + roi <- .roi_as_sf(roi) + data <- .cube_filter_spatial(cube = data, roi = roi) + } + # Temporal filter + if (.has(start_date) || .has(end_date)) { + data <- .cube_filter_interval( + cube = data, start_date = start_date, end_date = end_date + ) + } + if (.has(filter_fn)) + .check_filter_fn(filter_fn) + # Retrieve the samples from the model + samples <- .ml_samples(cd_method) + # Do the samples and tile match their timeline length? + .check_samples_tile_match_timeline(samples = samples, tile = data) + # Do the samples and tile match their bands? + .check_samples_tile_match_bands(samples = samples, tile = data) + # Check memory and multicores + # Get block size + block <- .raster_file_blocksize(.raster_open_rast(.tile_path(data))) + # Check minimum memory needed to process one block + job_memsize <- .jobs_memsize( + job_size = .block_size(block = block, overlap = 0), + npaths = length(.tile_paths(data)) + length(.ml_labels(cd_method)), + nbytes = 8, + proc_bloat = proc_bloat + ) + # Update multicores parameter + multicores <- .jobs_max_multicores( + job_memsize = job_memsize, + memsize = memsize, + multicores = multicores + ) + # Update block parameter + block <- .jobs_optimal_block( + job_memsize = job_memsize, + block = block, + image_size = .tile_size(.tile(data)), + memsize = memsize, + multicores = multicores + ) + # Terra requires at least two pixels to recognize an extent as valid + # polygon and not a line or point + block <- .block_regulate_size(block) + # Prepare parallel processing + .parallel_start( + workers = multicores, log = verbose, + output_dir = output_dir + ) + on.exit(.parallel_stop(), add = TRUE) + # Show block information + start_time <- .classify_verbose_start(verbose, block) + # Process each tile sequentially + detections_cube <- .cube_foreach_tile(data, function(tile) { + # Detect changes + detections_tile <- .detect_change_tile( + tile = tile, + band = "detection", + cd_method = cd_method, + block = block, + roi = roi, + filter_fn = filter_fn, + impute_fn = impute_fn, + output_dir = output_dir, + version = version, + verbose = verbose, + progress = progress + ) + return(detections_tile) + }) + # Show block information + .classify_verbose_end(verbose, start_time) + return(detections_cube) } #' @rdname sits_detect_change diff --git a/R/sits_dtw.R b/R/sits_dtw.R index 2476d6e38..34d0480c8 100644 --- a/R/sits_dtw.R +++ b/R/sits_dtw.R @@ -12,11 +12,11 @@ #' @param samples Time series with the training samples. #' @param ... Other relevant parameters. #' @param threshold Threshold used to define if an event was detected. -#' Default is `Inf`. #' @param window ISO8601-compliant time period used to define the #' DTW moving window, with number and unit, #' where "D", "M" and "Y" stands for days, month and -#' year; e.g., "P16D" for 16 days. +#' year; e.g., "P16D" for 16 days. This parameter is not +#' used in operations with data cubes. #' @return Change detection method prepared to be passed to #' \code{\link[sits]{sits_detect_change_method}} #' @export @@ -24,7 +24,7 @@ sits_dtw <- function(samples = NULL, ..., - threshold = Inf, + threshold = NULL, window = NULL) { .check_set_caller("sits_dtw") train_fun <- @@ -35,67 +35,22 @@ sits_dtw <- # Sample labels labels <- .samples_labels(samples) # Get samples patterns (temporal median) - train_samples_patterns <- .pattern_temporal_median(samples) + train_samples <- .predictors(samples) + patterns <- .pattern_temporal_median(samples) # Define detection function - detect_change_fun <- function(values) { - # Extract dates - dates <- values[[1]][["Index"]] - dates_min <- min(dates) - dates_max <- max(dates) - # Assume time-series are regularized, then use the period - # as the step of the moving window. As a result, we have - # one step per iteration. - dates_step <- lubridate::as.period( - lubridate::int_diff(dates) + detect_change_fun <- function(values, type) { + # Define the type of the operation + dtw_fun <- .dtw_windowed_ts + if (type == "cube") { + dtw_fun <- .dtw_complete_ts + } + # Detect changes + dtw_fun( + values = values, + patterns = patterns, + window = window, + threshold = threshold ) - dates_step <- dates_step[[1]] - # Create comparison windows - comparison_windows <- .period_windows( - period = window, - step = dates_step, - start_date = dates_min, - end_date = dates_max - ) - # Do the change detection for each time-series - purrr::map(values, function(value_row) { - # Search for the patterns - patterns_distances <- .pattern_distance_dtw( - data = value_row, - patterns = train_samples_patterns, - windows = comparison_windows - ) - # Remove distances out the user-defined threshold - patterns_distances[patterns_distances > threshold] <- NA - # Define where each label was detected. For this, first - # get from each label the minimal distance - detections_idx <- - apply(patterns_distances, 2, which.min) - detections_name <- names(detections_idx) - # For each label, extract the metadata where they had - # minimal distance - purrr::map_df(1:length(detections_idx), function(idx) { - # Extract detection name and inced - detection_name <- detections_name[idx] - detection_idx <- detections_idx[idx] - # Extract detection distance (min one defined above) - detection_distance <- - patterns_distances[detection_idx,] - detection_distance <- - detection_distance[detection_name] - detection_distance <- - as.numeric(detection_distance) - # Extract detection dates - detection_dates <- - comparison_windows[[detection_idx]] - # Prepare result and return it! - data.frame( - change = detection_name, - distance = detection_distance, - from = detection_dates[["start"]], - to = detection_dates[["end"]] - ) - }) - }) } # Set model class detect_change_fun <- .set_class(detect_change_fun, diff --git a/inst/extdata/config_internals.yml b/inst/extdata/config_internals.yml index 2ce5e84ec..fbb263315 100644 --- a/inst/extdata/config_internals.yml +++ b/inst/extdata/config_internals.yml @@ -171,6 +171,17 @@ derived_cube : offset_value : 0 scale_factor : 1 + detections_cube : + s3_class : [ "detections_cube", "derived_cube", "raster_cube" ] + bands : + detection : + data_type : "INT1U" + missing_value: 255 + minimum_value: 0 + maximum_value: 254 + offset_value : 0 + scale_factor : 1 + # Vector cube definitions vector_cube : segs_cube : diff --git a/inst/extdata/config_messages.yml b/inst/extdata/config_messages.yml index b2784898d..2fb27077d 100644 --- a/inst/extdata/config_messages.yml +++ b/inst/extdata/config_messages.yml @@ -352,6 +352,7 @@ sits_cube_default: "requested source has not been registered in sits\n - if poss sits_cube_copy: "wrong input parameters - see example in documentation" sits_cube_local_cube: "wrong input parameters - see example in documentation" sits_cube_local_cube_vector_band: "one vector_band must be provided (either segments, class, or probs)" +sits_detect_change_method: "wrong input parameters - see example in documentation" sits_detect_change_method_model: "cd_method is not a valid function" sits_detect_change_method_timeline: "samples have different timeline lengths" sits_detect_change_sits: "wrong input parameters - see example in documentation" diff --git a/man/sits_detect_change.Rd b/man/sits_detect_change.Rd index f7fd752e6..27ea46345 100644 --- a/man/sits_detect_change.Rd +++ b/man/sits_detect_change.Rd @@ -3,6 +3,7 @@ \name{sits_detect_change} \alias{sits_detect_change} \alias{sits_detect_change.sits} +\alias{sits_detect_change.raster_cube} \alias{sits_detect_change.default} \title{Detect changes in time series} \usage{ @@ -24,6 +25,23 @@ sits_detect_change( progress = TRUE ) +\method{sits_detect_change}{raster_cube}( + data, + cd_method, + ..., + roi = NULL, + filter_fn = NULL, + impute_fn = impute_linear(), + start_date = NULL, + end_date = NULL, + memsize = 8L, + multicores = 2L, + output_dir, + version = "v1", + verbose = FALSE, + progress = TRUE +) + \method{sits_detect_change}{default}(data, cd_method, ...) } \arguments{ @@ -31,17 +49,46 @@ sits_detect_change( \item{cd_method}{Change detection method (with parameters).} -\item{...}{Other relevant parameters.} +\item{...}{Other parameters for specific functions.} + +\item{filter_fn}{Smoothing filter to be applied - optional +(clousure containing object of class "function").} + +\item{multicores}{Number of cores to be used for classification +(integer, min = 1, max = 2048).} + +\item{progress}{Logical: Show progress bar?} + +\item{roi}{Region of interest (either an sf object, shapefile, +or a numeric vector with named XY values +("xmin", "xmax", "ymin", "ymax") or +named lat/long values +("lon_min", "lat_min", "lon_max", "lat_max").} + +\item{impute_fn}{Imputation function to remove NA.} + +\item{start_date}{Start date for the classification +(Date in YYYY-MM-DD format).} + +\item{end_date}{End date for the classification +(Date im YYYY-MM-DD format).} + +\item{memsize}{Memory available for classification in GB +(integer, min = 1, max = 16384).} -\item{filter_fn}{Smoothing filter function to be applied to the data.} +\item{output_dir}{Valid directory for output file. +(character vector of length 1).} -\item{multicores}{Number of threads to process the time series.} +\item{version}{Version of the output +(character vector of length 1).} -\item{progress}{Show progress bar?} +\item{verbose}{Logical: print information about processing time?} } \value{ -Set of time series where significant change has been - detected. +Time series with detection labels for + each point (tibble of class "sits") + or a data cube indicating detections in each pixel + (tibble of class "detections_cube"). } \description{ Given a set of time series or an image, this function compares diff --git a/man/sits_dtw.Rd b/man/sits_dtw.Rd index 6df87adc4..390040c22 100644 --- a/man/sits_dtw.Rd +++ b/man/sits_dtw.Rd @@ -4,20 +4,20 @@ \alias{sits_dtw} \title{Dynamic Time Warping for Detect changes.} \usage{ -sits_dtw(samples = NULL, ..., threshold = Inf, window = NULL) +sits_dtw(samples = NULL, ..., threshold = NULL, window = NULL) } \arguments{ \item{samples}{Time series with the training samples.} \item{...}{Other relevant parameters.} -\item{threshold}{Threshold used to define if an event was detected. -Default is `Inf`.} +\item{threshold}{Threshold used to define if an event was detected.} \item{window}{ISO8601-compliant time period used to define the DTW moving window, with number and unit, where "D", "M" and "Y" stands for days, month and -year; e.g., "P16D" for 16 days.} +year; e.g., "P16D" for 16 days. This parameter is not +used in operations with data cubes.} } \value{ Change detection method prepared to be passed to