Skip to content

Commit

Permalink
add function and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
fnattino committed Oct 15, 2024
1 parent d617c5d commit 68e6323
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 3 deletions.
32 changes: 32 additions & 0 deletions R/stroke.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#' Identify continuous lines in a network
#'
#' Apply the Continuity in Street Network (COINS) method to identify
#' sequences of edges that form naturally continuous strokes in a network.
#'
#' @param edges An object of class \code{\link[sf]{sf}} or
#' \code{\link[sfc]{sfc}}, including the edge geometries (should be of type
#' LineString or MultiLineString).
#'
#' @param angle_threshold Consecutive line segments can be considered part of
#' the same stroke if the internal angle they form is larger than
#' \code{angle_threshold} (in degrees). It should fall in the range
#' \code{0 \le angle_threshold \lt 180}.
#'
#' @param attributes If \code{TRUE}, return a label for each edge, representing
#' the groups each edge belongs to. Only possible for \code{flow_mode = TRUE}.
#'
#' @param flow_mode If \code{TRUE}, line segments that belong to the same edge
#' are not split across strokes (even if they form internal angles smaller than
#' \code{angle_threshold}).
#'
#' @param from_edge Only look for the continuous strokes that include the
#' provided edges or line segments.
#'
#' @return An object of class \code{\link[sf]{sf}} (if
#' \code{attributes = FALSE}), a vector with the same length as \code{edges}
#' otherwise.
#'
stroke <- function(edges, angle_threshold = 0., attributes = FALSE,
flow_mode = FALSE, from_edge = NULL) {
FALSE
}
43 changes: 43 additions & 0 deletions man/stroke.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions tests/testthat/test-my-test.R

This file was deleted.

158 changes: 158 additions & 0 deletions tests/testthat/test-stroke.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# p4
# /
# p1 - p2 - p3
# \ |
# p5 - p6
p1 <- sf::st_point(c(0,0))

Check warning on line 6 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=6,col=24,[commas_linter] Commas should always have a space after.
p2 <- sf::st_point(c(1,0))

Check warning on line 7 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=7,col=24,[commas_linter] Commas should always have a space after.
p3 <- sf::st_point(c(2,0))

Check warning on line 8 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=8,col=24,[commas_linter] Commas should always have a space after.
p4 <- sf::st_point(c(2,1))

Check warning on line 9 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=9,col=24,[commas_linter] Commas should always have a space after.
p5 <- sf::st_point(c(2,-1))

Check warning on line 10 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=10,col=24,[commas_linter] Commas should always have a space after.
p6 <- sf::st_point(c(3,-1))

Check warning on line 11 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=11,col=24,[commas_linter] Commas should always have a space after.

test_that("a stroke is found in a very simple network", {
l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p3))
l3 <- sf::st_linestring(c(p2, p4))
sfc <- sf::st_sfc(l1, l2, l3)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3)), l3)
actual <- stroke(sfc)
expect_equal(actual, expected)
})

test_that("sf objects can be used in input", {
l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p3))
l3 <- sf::st_linestring(c(p2, p4))
sfc <- sf::st_sfc(l1, l2, l3)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3)), l3)
actual <- sf::st_as_sf(sfc) |> stroke()
expect_equal(actual, expected)
})

test_that("sfnetworks objects can be used in input", {

skip_if_not_installed("sfnetworks")

l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p3))
l3 <- sf::st_linestring(c(p2, p4))
nodes <- sf::st_sfc(p1, p2, p3, p4)
edges <- sf::st_sf(from = c(1, 2, 2),
to = c(2, 3, 4),
geometry = sf::st_sfc(l1, l2, l3))
net <- sfnetworks::sfnetwork(nodes = nodes, edges = edges,
directed = FALSE, force = TRUE)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3)), l3)
actual <- stroke(net)
expect_equal(actual, expected)
})

test_that("multilinestrings are supported", {
l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p3))
l3 <- sf::st_linestring(c(p2, p4))
sfc <- sf::st_sfc(c(l1, l2), l3)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3)), l3)
actual <- stroke(sfc)
expect_equal(actual, expected)
})

test_that("proper attributes are returned for a very simple network", {
l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p3))
l3 <- sf::st_linestring(c(p2, p4))
sfc <- sf::st_sfc(l1, l2, l3)
expected <- as.integer(c(1, 1, 2))
actual <- stroke(sfc, attributes = TRUE, flow_mode = TRUE)
expect_equal(actual, expected)
})

test_that("two linesegments are always merged if threshold is zero", {
l1 <- sf::st_linestring(c(p3, p2))
l2 <- sf::st_linestring(c(p2, p5))
expected <- sf::st_sfc(sf::st_linestring(c(p3, p2, p5)))
actual <- stroke(sfc, angle_threshold = 0.)
expect_equal(actual, expected)
})

test_that("a more complex network with no threshold form a stroke", {
l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p5))
l3 <- sf::st_linestring(c(p5, p6))
l4 <- sf::st_linestring(c(p5, p3))
sfc <- sf::st_sfc(l1, l2, l3, l4)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3)), l4)
actual <- stroke(sfc, angle_threshold = 0.)
expect_equal(actual, expected)
})

test_that("a more complex network with threshold does not form strokes", {
l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p5))
l3 <- sf::st_linestring(c(p5, p6))
l4 <- sf::st_linestring(c(p5, p3))
sfc <- sf::st_sfc(l1, l2, l3, l4)
expected <- sfc
actual <- stroke(sfc, angle_threshold = 150.)
expect_equal(actual, expected)
})

test_that("attributes cannot be returned if not in flow mode", {
l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p3))
sfc <- sf::st_sfc(l1, l2)
expect_error(stroke(sfc, attributes = TRUE, flow_mode = FALSE),
"Stroke attributes can be returned only if `flow_mode = TRUE`)")
})

test_that("edges can be split if flow_mode is false", {
l1 <- sf::st_linestring(c(p1, p2, p5))
l2 <- sf::st_linestring(c(p2, p3))
sfc <- sf::st_sfc(l1, l2)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3)),
sf::st_linestring(c(p2, p5)))
actual <- stroke(sfc, flow_mode = FALSE)
expect_equal(actual, expected)
})

test_that("edges are not split if flow_mode is true", {
l1 <- sf::st_linestring(c(p1, p2, p5))
l2 <- sf::st_linestring(c(p2, p3))
sfc <- sf::st_sfc(l1, l2)
expected <- sfc
actual <- stroke(sfc, flow_mode = TRUE)
expect_equal(actual, expected)
})

test_that("strokes can be formed starting from a given edge", {

Check warning on line 128 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=128,col=11,[object_length_linter] Variable and function names should not be longer than 30 characters.

Check warning on line 128 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=128,col=11,[object_name_linter] Variable and function name style should match snake_case or symbols.

Check warning on line 128 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=128,col=63,[brace_linter] Opening curly braces should never go on their own line and should always be followed by a new line.
l1 <- sf::st_linestring(c(p1, p2, p3))

Check warning on line 129 in tests/testthat/test-stroke.R

View workflow job for this annotation

GitHub Actions / lint

file=tests/testthat/test-stroke.R,line=129,col=2,[indentation_linter] Indentation should be 0 spaces but is 2 spaces.
l2 <- sf::st_linestring(c(p2, p5))
l3 <- sf::st_linestring(c(p5, p6))
sfc <- sf::st_sfc(l1, l2, l3)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p5, p6))
actual <- stroke(sfc, flow_mode = FALSE, from_edge = 3)
expect_equal(actual, expected)
})

test_that("strokes can be formed starting from a given line segment", {
l1 <- sf::st_linestring(c(p1, p2, p3))
l2 <- sf::st_linestring(c(p2, p5, p6))
sfc <- sf::st_sfc(l1, l2)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p5, p6))
actual <- stroke(sfc, flow_mode = FALSE,
from_edge = sf::st_linestring(c(p5, p6)))
expect_equal(actual, expected)
})

test_that("attributes can be returned if edge is specified in flow mode", {
l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p3))
l3 <- sf::st_linestring(c(p3, p5))
l4 <- sf::st_linestring(c(p5, p6))
sfc <- sf::st_sfc(l1, l2, l3, l4)
expected <- as.integer(c(1, NA, 1, 1))
actual <- stroke(sfc, attribute = TRUE, flow_mode = TRUE, from_edge = 3)
expect_equal(actual, expected)
})

0 comments on commit 68e6323

Please sign in to comment.