Skip to content

Commit

Permalink
Merge branch 'project'
Browse files Browse the repository at this point in the history
  • Loading branch information
edzer committed Dec 21, 2023
2 parents 83145d6 + 1cc62fb commit 54941dd
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 0 deletions.
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,9 @@ export(st_jitter)
export(st_join)
export(st_layers)
export(st_length)
export(st_line_interpolate)
export(st_line_merge)
export(st_line_project)
export(st_line_sample)
export(st_linestring)
export(st_m_range)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# version 1.0-16

* add `st_line_project()` to find how far a point is when projected on a line, and `st_line_interpolate()` to obtain a point at a certain distance along a line; #2291

# version 1.0-15

* add `st_perimeter()` to cover both geographic and projected coordinates; #268, #2279, by @JosiahParry
Expand Down
8 changes: 8 additions & 0 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,14 @@ CPL_nary_intersection <- function(sfc) {
.Call(`_sf_CPL_nary_intersection`, sfc)
}

CPL_line_project <- function(lines, points, normalized) {
.Call(`_sf_CPL_line_project`, lines, points, normalized)
}

CPL_line_interpolate <- function(lines, dists, normalized) {
.Call(`_sf_CPL_line_interpolate`, lines, dists, normalized)
}

CPL_hex_to_raw <- function(cx) {
.Call(`_sf_CPL_hex_to_raw`, cx)
}
Expand Down
49 changes: 49 additions & 0 deletions R/geom-measures.R
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,52 @@ st_distance = function(x, y, ..., dist_fun, by_element = FALSE,
d
}
}

check_lengths = function (dots) {
lengths <- vapply(dots, length, integer(1))
non_constant_lengths <- unique(lengths[lengths != 1])
if (length(non_constant_lengths) == 0) {
1
}
else if (length(non_constant_lengths) == 1) {
non_constant_lengths
}
else {
lengths_label <- paste0(non_constant_lengths, collapse = ", ")
stop(sprintf("Incompatible lengths: %s", lengths_label),
call. = FALSE)
}
}

recycle_common = function (dots) {
final_length <- check_lengths(dots)
lapply(dots, rep_len, final_length)
}


#' Project point on linestring, interpolate along a linestring
#'
#' Project point on linestring, interpolate along a linestring
#' @param line object of class `sfc` with `LINESTRING` geometry
#' @param point object of class `sfc` with `POINT` geometry
#' @param normalized logical; if `TRUE`, use or return distance normalised to 0-1
#' @name st_line_project_point
#' @returns `st_line_project` returns the distance(s) of point(s) along line(s), when projected on the line(s)
#' @export
#' @details
#' arguments `line`, `point` and `dist` are recycled to common length when needed
#' @examples
#' st_line_project(st_as_sfc("LINESTRING (0 0, 10 10)"), st_as_sfc(c("POINT (0 0)", "POINT (5 5)")))
#' st_line_project(st_as_sfc("LINESTRING (0 0, 10 10)"), st_as_sfc("POINT (5 5)"), TRUE)
st_line_project = function(line, point, normalized = FALSE) {
stopifnot(inherits(line, "sfc"), inherits(point, "sfc"),
all(st_dimension(line) == 1), all(st_dimension(point) == 0),
is.logical(normalized), length(normalized) == 1,
st_crs(line) == st_crs(point))
line = st_cast(line, "LINESTRING")
point = st_cast(point, "POINT")
if (isTRUE(st_is_longlat(line)))
message_longlat("st_project_point")
recycled = recycle_common(list(line, point))
CPL_line_project(recycled[[1]], recycled[[2]], normalized)
}
19 changes: 19 additions & 0 deletions R/geom-transformers.R
Original file line number Diff line number Diff line change
Expand Up @@ -1121,3 +1121,22 @@ st_line_sample = function(x, n, density, type = "regular", sample = NULL) {
if (length(pts) == 2 && is.numeric(pts))
assign(".geos_error", st_point(pts), envir=.sf_cache)
} #nocov end

#' @param dist numeric, vector with distance value(s)
#' @name st_line_project_point
#' @returns `st_line_interpolate` returns the point(s) at dist(s), when measured along (interpolated on) the line(s)
#' @export
#' @examples
#' st_line_interpolate(st_as_sfc("LINESTRING (0 0, 1 1)"), 1)
#' st_line_interpolate(st_as_sfc("LINESTRING (0 0, 1 1)"), 1, TRUE)
st_line_interpolate = function(line, dist, normalized = FALSE) {
stopifnot(inherits(line, "sfc"), all(st_dimension(line) == 1),
is.logical(normalized), length(normalized) == 1,
is.numeric(dist))
if (isTRUE(st_is_longlat(line)))
message_longlat("st_project_point")
line = st_cast(line, "LINESTRING")
recycled = recycle_common(list(line, dist))
st_sfc(CPL_line_interpolate(recycled[[1]], recycled[[2]], normalized),
crs = st_crs(line))
}
28 changes: 28 additions & 0 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,32 @@ BEGIN_RCPP
return rcpp_result_gen;
END_RCPP
}
// CPL_line_project
Rcpp::NumericVector CPL_line_project(Rcpp::List lines, Rcpp::List points, bool normalized);
RcppExport SEXP _sf_CPL_line_project(SEXP linesSEXP, SEXP pointsSEXP, SEXP normalizedSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::List >::type lines(linesSEXP);
Rcpp::traits::input_parameter< Rcpp::List >::type points(pointsSEXP);
Rcpp::traits::input_parameter< bool >::type normalized(normalizedSEXP);
rcpp_result_gen = Rcpp::wrap(CPL_line_project(lines, points, normalized));
return rcpp_result_gen;
END_RCPP
}
// CPL_line_interpolate
Rcpp::List CPL_line_interpolate(Rcpp::List lines, Rcpp::NumericVector dists, bool normalized);
RcppExport SEXP _sf_CPL_line_interpolate(SEXP linesSEXP, SEXP distsSEXP, SEXP normalizedSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::List >::type lines(linesSEXP);
Rcpp::traits::input_parameter< Rcpp::NumericVector >::type dists(distsSEXP);
Rcpp::traits::input_parameter< bool >::type normalized(normalizedSEXP);
rcpp_result_gen = Rcpp::wrap(CPL_line_interpolate(lines, dists, normalized));
return rcpp_result_gen;
END_RCPP
}
// CPL_hex_to_raw
Rcpp::List CPL_hex_to_raw(Rcpp::CharacterVector cx);
RcppExport SEXP _sf_CPL_hex_to_raw(SEXP cxSEXP) {
Expand Down Expand Up @@ -1476,6 +1502,8 @@ static const R_CallMethodDef CallEntries[] = {
{"_sf_CPL_transpose_sparse_incidence", (DL_FUNC) &_sf_CPL_transpose_sparse_incidence, 2},
{"_sf_CPL_nary_difference", (DL_FUNC) &_sf_CPL_nary_difference, 1},
{"_sf_CPL_nary_intersection", (DL_FUNC) &_sf_CPL_nary_intersection, 1},
{"_sf_CPL_line_project", (DL_FUNC) &_sf_CPL_line_project, 3},
{"_sf_CPL_line_interpolate", (DL_FUNC) &_sf_CPL_line_interpolate, 3},
{"_sf_CPL_hex_to_raw", (DL_FUNC) &_sf_CPL_hex_to_raw, 1},
{"_sf_CPL_raw_to_hex", (DL_FUNC) &_sf_CPL_raw_to_hex, 1},
{"_sf_CPL_read_mdim", (DL_FUNC) &_sf_CPL_read_mdim, 8},
Expand Down
38 changes: 38 additions & 0 deletions src/geos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1376,3 +1376,41 @@ Rcpp::List CPL_nary_intersection(Rcpp::List sfc) {
// return result
return ret;
}

// [[Rcpp::export]]
Rcpp::NumericVector CPL_line_project(Rcpp::List lines, Rcpp::List points, bool normalized) {
GEOSContextHandle_t hGEOSCtxt = CPL_geos_init();
int dim = 2;
std::vector<GeomPtr> l = geometries_from_sfc(hGEOSCtxt, lines, &dim);
std::vector<GeomPtr> p = geometries_from_sfc(hGEOSCtxt, points, &dim);
Rcpp::NumericVector ret(p.size());
if (normalized) {
for (size_t i = 0; i < l.size() && i < p.size(); i++)
ret[i] = GEOSProjectNormalized_r(hGEOSCtxt, l[i].get(), p[i].get());
} else {
for (size_t i = 0; i < l.size() && i < p.size(); i++)
ret[i] = GEOSProject_r(hGEOSCtxt, l[i].get(), p[i].get());
}
CPL_geos_finish(hGEOSCtxt);
// return result
return ret;
}

// [[Rcpp::export]]
Rcpp::List CPL_line_interpolate(Rcpp::List lines, Rcpp::NumericVector dists, bool normalized) {
GEOSContextHandle_t hGEOSCtxt = CPL_geos_init();
int dim = 2;
std::vector<GeomPtr> l = geometries_from_sfc(hGEOSCtxt, lines, &dim);
std::vector<GeomPtr> p(l.size());
if (normalized) {
for (int i = 0; i < (int) l.size() && i < dists.size(); i++)
p[i] = geos_ptr(GEOSInterpolateNormalized_r(hGEOSCtxt, l[i].get(), dists[i]), hGEOSCtxt);
} else {
for (int i = 0; i < (int) l.size() && i < dists.size(); i++)
p[i] = geos_ptr(GEOSInterpolate_r(hGEOSCtxt, l[i].get(), dists[i]), hGEOSCtxt);
}
Rcpp::List ret(sfc_from_geometry(hGEOSCtxt, p, dim));
CPL_geos_finish(hGEOSCtxt);
// return result
return ret;
}

0 comments on commit 54941dd

Please sign in to comment.