diff --git a/R/convenience_functions.R b/R/convenience_functions.R index 88b051a..5ed2413 100644 --- a/R/convenience_functions.R +++ b/R/convenience_functions.R @@ -83,6 +83,47 @@ euclideanDistance <- function (pointA, pointB) { sqrt(((pointA[, 1] - pointB[, 1]) ^ 2) + ((pointA[, 2] - pointB[, 2]) ^ 2)) } +#' Calculate the angle between a line and the horizontal given two +#' coordinate locations. +#' +#' @param x1 the x-coordinate of the first point. +#' @param y1 the y-coordinate of the first point. +#' @param x2 the x-coordinate of the second point. +#' @param y2 the y-coordinate of the second point. +#' +#' @return The angle between the line and the horizontal. +#' @keywords internal +#' @noRd +calc_angle <- function(x1, + y1, + x2, + y2) { + + # Get the height + + height <- abs(y2 - y1) + + # Get the base + + base <- abs(x2 - x1) + + # Get the angle + + angle <- round(rad_to_deg(atan(height/base)), + 3) +} + +#' Convert an angle in radians to degrees. +#' +#' @param radians the angle in radians. +#' +#' @return The angle in degrees. +#' @keywords internal +#' @noRd +rad_to_deg <- function(radians) { + return(radians * 180 / pi) +} + #' Add a unique identifier to each observation. #' #' @param x a `sf` object with geometries. diff --git a/R/st_nest_coordinates.R b/R/st_nest_coordinates.R new file mode 100644 index 0000000..ab6bbe8 --- /dev/null +++ b/R/st_nest_coordinates.R @@ -0,0 +1,114 @@ +#' Nest `POINT` geometries to `LINESTRING` +#' +#' @param x a `df` containing coordinates in columns named `x1`, `y1`, `x2`, +#' and `y2`. +#' +#' @return A `sf` object containing `LINESTRING` geometries **without** a CRS. +#' @export +#' +#' @examples +st_nest_coordinates <- function(x) { + + # Housekeeping + + x1 <- y1 <- x2 <- y2 <- NULL + + # If cols are present in the data run the following: + + if (any(c("x1", "y1", "x2", "y2") %in% colnames(x))) { + # Keep other columns + + x_other <- x |> + dplyr::select(-x1, + -y1, + -x2, + -y2) + + # Convert to an sf object + + x_nested <- sf::st_sf(data.frame(geometry = sf::st_as_sfc( + paste0("LINESTRING (", + x$x1, + " ", + x$y1, + ", ", + x$x2, + " ", + x$y2, + ")") + ))) + + # Add the other columns back in + + x_nested <- cbind(x_nested, + x_other) + + return(x_nested) + + } else { + stop("Coordinates must be provided in columns x1, y1, x2, and y2.") + } + +} + + +#' Unnest `POINT` geometries from `LINESTRINGS` +#' +#' @param x a `sf` object containing `LINESTRING` geometries. +#' +#' @return A `df` where each row contains the `x1`, `y1`, `x2`, and `y2` +#' coordinates for a line. +#' +#' @export +#' +#' @examples +st_unnest_coordinates <- function(x) { + + # Housekeeping + + id <- temp_id <- x1 <- y1 <- x2 <- y2 <- NULL + + # Check types and convert to linestrings. + + x_lines <- process_linestrings(x) + + # Keep other columns + + x_other <- x |> + add_unique_id() |> + sf::st_set_geometry(NULL) + + # Get coordinates + + x_coords <- as.data.frame(sf::st_coordinates(x_lines)) + + # Set colnames + + colnames(x_coords) <- c("x1", "y1", "id") + + # Group by id. Mutate x2 and y2 as lead values. + # This should result in a number of NAs. + # Drop them. + + # Add x2 and y2 + + x_coords <- x_coords |> + dplyr::group_by(id) |> + dplyr::mutate(x2 = dplyr::lead(x1, + n = 1, + order_by = id), + y2 = dplyr::lead(y1, + n = 1, + order_by = id)) |> + dplyr::ungroup() |> + stats::na.omit() + + # Add other columns + + x_coords <- dplyr::left_join(x_coords, + x_other, + by = c("id" = "temp_id")) |> + dplyr::select(-id) + + return(x_coords) +} diff --git a/R/st_orientation.R b/R/st_orientation.R new file mode 100644 index 0000000..5f5f787 --- /dev/null +++ b/R/st_orientation.R @@ -0,0 +1,56 @@ +#' Determine the interior angle between a `LINESTRING` and the horizontal. +#' +#' @param x a `sf` object containing `LINESTRING` geometries. +#' +#' @return An unnested `sf` object containing the orientation of each line +#' segment making up the original `LINESTRING` geometries. +#' @export +#' +#' @examples +st_orientation <- function(x) { + + # Housekeeping + + id <- x1 <- y1 <- x2 <- y2 <- NULL + + # Keep track of the crs + + x_crs <- sf::st_crs(x) + + # The extract coordinates function adds columns titled x1, y1, x2, and y2 + # It then computes the angles of each line string. + + x_orientation <- st_unnest_coordinates(x) |> + dplyr::mutate( + orientation = dplyr::case_when( + x2 > x1 & y2 < y1 ~ -calc_angle(x1, + y1, + x2, + y2), + x2 < x1 & + y2 > y1 ~ -calc_angle(x1, + y1, + x2, + y2), + y2 == y1 ~ 0, + x2 == x1 ~ 90, + TRUE ~ calc_angle(x1, + y1, + x2, + y2) + ) + ) + + # Nest the columns to form line strings + + x_orientation <- st_nest_coordinates(x_orientation) + + # Add the crs back in + + sf::st_crs(x_orientation) <- x_crs + + return(x_orientation) + +} + + diff --git a/_pkgdown.yml b/_pkgdown.yml index 38a7d46..168fabe 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -22,6 +22,7 @@ reference: contents: - add_unique_id - st_merge_spatialunits + - st_orientation - st_survey_spatialunits - subtitle: Datasets