@@ -140,6 +140,9 @@ angle_diff <- function(l, angle, bidirectional = FALSE, absolute = TRUE) {
140140# ' plot(l$geometry, col = 2:5)
141141# ' midpoints <- line_midpoint(l)
142142# ' plot(midpoints, add = TRUE)
143+ # ' # compare with sf::st_point_on_surface:
144+ # ' midpoints2 <- sf::st_point_on_surface(l)
145+ # ' plot(midpoints2, add = TRUE, col = "red")
143146line_midpoint <- function (l , tolerance = NULL ) {
144147 if (is.null(tolerance )) {
145148 sub <- lwgeom :: st_linesubstring(x = l , from = 0 , to = 0.5 )
@@ -158,7 +161,9 @@ line_midpoint <- function(l, tolerance = NULL) {
158161# ' but does not always return the number of segments requested.
159162# '
160163# ' @inheritParams line2df
161- # ' @param segment_length The approximate length of segments in the output (overides n_segments if set)
164+ # ' @param segment_length The approximate length of segments in the output (overrides n_segments if set)
165+ # ' @param n_segments The number of segments to divide the line into.
166+ # ' If there are multiple lines, this should be a vector of the same length.
162167# ' @param use_rsgeo Should the `rsgeo` package be used?
163168# ' If `rsgeo` is available, this faster implementation is used by default.
164169# ' If `rsgeo` is not available, the `lwgeom` package is used.
@@ -174,49 +179,72 @@ line_midpoint <- function(l, tolerance = NULL) {
174179# ' plot(l_seg_multi["ID"])
175180# ' plot(l_seg_multi$geometry, col = seq_along(l_seg_multi), lwd = 5)
176181# ' round(st_length(l_seg_multi))
177- # ' # rsgeo implementation:
178- # ' rsmulti = line_segment(l, segment_length = 1000, use_rsgeo = TRUE)
179- # ' plot(rsmulti["ID"])
180- # ' plot(rsmulti$geometry, col = seq_along(l_seg_multi), lwd = 5)
181- # ' # round(st_length(rsmulti))
182- # ' # waldo::compare(l_seg_multi, rsmulti)
182+ # ' # rsgeo implementation (default if available):
183+ # ' if (rlang::is_installed("rsgeo")) {
184+ # ' rsmulti = line_segment(l, segment_length = 1000, use_rsgeo = TRUE)
185+ # ' plot(rsmulti["ID"])
186+ # ' }
187+ # ' # Check they have the same total length, to nearest mm:
188+ # ' # round(sum(st_length(l_seg_multi)), 3) == round(sum(st_length(rsmulti)), 3)
189+ # ' # With n_segments for 1 line:
190+ # ' l_seg_multi_n <- line_segment(l[1, ], n_segments = 3, use_rsgeo = FALSE)
191+ # ' l_seg_multi_n <- line_segment(l$geometry[1], n_segments = 3, use_rsgeo = FALSE)
192+ # ' l_seg_multi_n <- line_segment(l$geometry[1], n_segments = 3, use_rsgeo = TRUE)
193+ # ' # With n_segments for all 3 lines:
194+ # ' l_seg_multi_n <- line_segment(l, n_segments = 2)
195+ # ' nrow(l_seg_multi_n) == nrow(l) * 2
183196line_segment <- function (
184197 l ,
185198 segment_length = NA ,
199+ n_segments = NA ,
186200 use_rsgeo = NULL ,
187201 debug_mode = FALSE ) {
202+ # Defensive programming:
203+ if (is.na(segment_length ) && is.na(n_segments )) {
204+ rlang :: abort(
205+ " segment_length or n_segments must be set." ,
206+ call = rlang :: caller_env()
207+ )
208+ }
188209 UseMethod(" line_segment" )
189210}
190211# ' @export
191212line_segment.sf <- function (
192213 l ,
193214 segment_length = NA ,
215+ n_segments = NA ,
194216 use_rsgeo = NULL ,
195- debug_mode = FALSE ) {
196- if (is.na(segment_length )) {
197- rlang :: abort(
198- " `segment_length` must be set." ,
199- call = rlang :: caller_env()
200- )
217+ debug_mode = FALSE
218+ ) {
219+ # Get n_segments if not provided:
220+ if (is.na(n_segments )) {
221+ segment_lengths <- as.numeric(sf :: st_length(l ))
222+ n_segments <- n_segments(segment_lengths , segment_length )
223+ } else {
224+ if (length(n_segments ) != nrow(l )) {
225+ if (length(n_segments ) == 1 ) {
226+ message(" Setting n_segments to " , n_segments , " for all lines" )
227+ n_segments <- rep.int(n_segments , nrow(l ))
228+ }
229+ }
201230 }
202231 # Decide whether to use rsgeo or lwgeom, if not set:
203232 if (is.null(use_rsgeo )) {
204233 use_rsgeo <- use_rsgeo(l )
205234 }
206235 if (use_rsgeo ) {
207236 # If using rsgeo, we can do the whole thing in one go:
208- segment_lengths <- as.numeric(sf :: st_length(l ))
209- n_segments <- n_segments(segment_lengths , segment_length )
210237 res <- line_segment_rsgeo(l , n_segments = n_segments )
211238 return (res )
212239 }
240+ # lwgeom implementation:
213241 n_row_l <- nrow(l )
214242 if (n_row_l > 1 ) {
215243 res_list <- pbapply :: pblapply(seq(n_row_l ), function (i ) {
216244 if (debug_mode ) {
217245 message(paste0(" Processing row " , i , " of " , n_row_l ))
218246 }
219- l_segmented <- line_segment1(l [i , ], n_segments = NA , segment_length = segment_length )
247+ l_segmented <- line_segment1(l [i , ], n_segments = n_segments [ i ] , segment_length = NA )
220248 res_names <- names(sf :: st_drop_geometry(l_segmented ))
221249 # Work-around for https://github.com/ropensci/stplanr/issues/531
222250 if (i == 1 ) {
@@ -228,20 +256,20 @@ line_segment.sf <- function(
228256 res <- bind_sf(res_list )
229257 } else {
230258 # If there's only one row:
231- res <- line_segment1(l , n_segments = NA , segment_length = segment_length )
259+ res <- line_segment1(l , n_segments = n_segments )
232260 }
233261 res
234262}
235263
236-
237264# ' @export
238265line_segment.sfc_LINESTRING <- function (
239266 l ,
240267 segment_length = NA ,
268+ n_segments = NA ,
241269 use_rsgeo = NULL ,
242270 debug_mode = FALSE ) {
243271 l <- sf :: st_as_sf(l )
244- res <- line_segment(l , segment_length = segment_length , use_rsgeo , debug_mode )
272+ res <- line_segment(l , segment_length = segment_length , n_segments = n_segments , use_rsgeo , debug_mode )
245273 sf :: st_geometry(res )
246274}
247275
@@ -267,7 +295,8 @@ line_segment.sfc_LINESTRING <- function(
267295line_segment1 <- function (
268296 l ,
269297 n_segments = NA ,
270- segment_length = NA ) {
298+ segment_length = NA
299+ ) {
271300 UseMethod(" line_segment1" )
272301}
273302# ' @export
@@ -383,7 +412,7 @@ line_segment_rsgeo <- function(l, n_segments) {
383412 res_sf <- sf :: st_as_sf(
384413 res_tbl ,
385414 geometry = res ,
386- crs = crs
415+ crs = crs
387416 )
388417 res_sf
389418}
0 commit comments