Nothing
# p4
# /
# p1 - p2 - p3
# \ | \
# p5 - p6
p1 <- sf::st_point(c(0, 0))
p2 <- sf::st_point(c(1, 0))
p3 <- sf::st_point(c(2, 0))
p4 <- sf::st_point(c(2, 1))
p5 <- sf::st_point(c(2, -1))
p6 <- sf::st_point(c(3, -1))
l1 <- sf::st_linestring(c(p1, p2))
l2 <- sf::st_linestring(c(p2, p3))
l3 <- sf::st_linestring(c(p2, p4))
l4 <- sf::st_linestring(c(p2, p5))
l5 <- sf::st_linestring(c(p3, p5))
l6 <- sf::st_linestring(c(p3, p6))
l7 <- sf::st_linestring(c(p5, p6))
test_that("a stroke is found even for a single segment", {
sfc <- sf::st_sfc(l1)
actual <- stroke(sfc)
expect_setequal(actual, sfc)
})
test_that("a stroke is found in a very simple network", {
sfc <- sf::st_sfc(l1, l2, l3)
# p4
# /
# p1 - p2 - p3
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3)), l3)
actual <- stroke(sfc)
expect_setequal(actual, expected)
})
test_that("a ring is recognized as a stroke", {
sfc <- sf::st_sfc(l2, l4, l6, l7)
expected <- sf::st_sfc(sf::st_linestring(c(p2, p3, p6, p5, p2)))
actual <- stroke(sfc)
expect_setequal(actual, expected)
})
test_that("a ring with a branch is recognized as one stroke", {
sfc <- sf::st_sfc(l1, l2, l4, l6, l7)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3, p6, p5, p2)))
actual <- stroke(sfc)
expect_setequal(actual, expected)
})
test_that("more strokes are recognized in a ring with multiple branches", {
sfc <- sf::st_sfc(l1, l2, l3, l4, l6, l7)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3, p6, p5, p2)), l3)
actual <- stroke(sfc)
expect_setequal(actual, expected)
})
test_that("sf objects can be used in input", {
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_setequal(actual, expected)
})
test_that("sfnetworks objects can be used in input", {
skip_if_not_installed("sfnetworks")
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_setequal(actual, expected)
})
test_that("multilinestrings are not supported", {
sfc <- sf::st_sfc(c(l1, l2), l3)
expect_error(stroke(sfc), "MULTILINESTRING")
})
test_that("proper attributes are returned for a very simple network", {
sfc <- sf::st_sfc(l1, l2, l3)
expected <- as.integer(c(1, 1, 2))
actual <- stroke(sfc, attributes = TRUE, flow_mode = TRUE)
expect_setequal(actual, expected)
})
test_that("proper attributes are returned for a more complex network", {
# p1 - p2 p3
# \ |
# p5 - p6
sfc <- sf::st_sfc(l1, l4, l5, l7)
expected <- c(1, 1, 2, 1)
actual <- stroke(
sfc, angle_threshold = 0, attributes = TRUE, flow_mode = TRUE
)
expect_setequal(actual, expected)
})
test_that("two linesegments are always merged if threshold is zero", {
sfc <- sf::st_sfc(l2, l4)
expected <- sf::st_sfc(sf::st_linestring(c(p5, p2, p3)))
actual <- stroke(sfc, angle_threshold = 0)
expect_setequal(actual, expected)
})
test_that("a more complex network with no threshold form a stroke", {
sfc <- sf::st_sfc(l1, l4, l5, l7)
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p5, p6)), l5)
actual <- stroke(sfc, angle_threshold = 0)
expect_setequal(actual, expected)
})
test_that("a more complex network with threshold does not form strokes", {
sfc <- sf::st_sfc(l1, l4, l5, l7)
expected <- sfc
actual <- stroke(sfc, angle_threshold = 150.)
expect_setequal(actual, expected)
})
test_that("attributes cannot be returned if not in flow mode", {
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", {
new_l1 <- sf::st_linestring(c(p1, p2, p5))
sfc <- sf::st_sfc(new_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_setequal(actual, expected)
})
test_that("edges are not split if flow_mode is true", {
new_l1 <- sf::st_linestring(c(p1, p2, p5))
sfc <- sf::st_sfc(new_l1, l2)
# p1 - p2 - p3
# \
# p5
expected <- sfc
actual <- stroke(sfc, flow_mode = TRUE)
expect_setequal(actual, expected)
})
test_that("a ring is recognized as a stroke also in flow_mode", {
sfc <- sf::st_sfc(l2, l4, l6, l7)
expected <- sf::st_sfc(sf::st_linestring(c(p2, p3, p6, p5, p2)))
actual <- stroke(sfc, flow_mode = TRUE)
expect_setequal(actual, expected)
})
test_that("strokes can be formed starting from a given edge", {
new_l1 <- sf::st_linestring(c(p1, p2, p3))
sfc <- sf::st_sfc(new_l1, l4, l7)
# p1 - p2 - p3
# \
# p5 - p6
expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p5, p6)))
actual <- stroke(sfc, flow_mode = FALSE, from_edge = list(3))
expect_setequal(actual, expected)
})
test_that("strokes can be formed starting from a given a list of edge ids", {
new_l1 <- sf::st_linestring(c(p1, p2, p3))
sfc <- sf::st_sfc(new_l1, l4, l7)
stroke_1 <- sf::st_linestring(c(p1, p2, p3))
stroke_2 <- sf::st_linestring(c(p1, p2, p5, p6))
expected <- sf::st_sfc(stroke_1, stroke_2)
actual <- stroke(sfc, flow_mode = FALSE, from_edge = list(1, 3))
expect_setequal(actual, expected)
})
test_that("same strokes can be formed when one of the edges is reversed", {
new_l1 <- sf::st_linestring(c(p1, p2, p3))
# reverse one of the edges
new_l4 <- sf::st_linestring(c(p5, p2))
sfc <- sf::st_sfc(new_l1, new_l4, l7)
stroke_1 <- sf::st_linestring(c(p1, p2, p3))
stroke_2 <- sf::st_linestring(c(p6, p5, p2, p1))
expected <- sf::st_sfc(stroke_1, stroke_2)
actual <- stroke(sfc, flow_mode = FALSE, from_edge = list(1, 2))
expect_setequal(actual, expected)
})
test_that("attributes can't be returned if edge is specified", {
sfc <- sf::st_sfc(l1, l2, l5, l7)
expect_error(stroke(sfc, attribute = TRUE, flow_mode = TRUE, from_edge = 3),
"from_edge is not compatible with attributes or flow_mode")
})
test_that("a ring is recognized when from_edge is specified", {
sfc <- sf::st_sfc(l2, l4, l6, l7)
expected <- sf::st_sfc(sf::st_linestring(c(p2, p3, p6, p5, p2)))
actual <- stroke(sfc, from_edge = 1)
expect_setequal(actual, expected)
})
test_that("flow mode does not break edges on a real dataset", {
skip_on_ci()
skip_on_cran()
bucharest <- get_example_data()
edges <- sf::st_geometry(bucharest$streets)
strokes <- rcoins::stroke(edges, flow_mode = TRUE)
# find out which of the initial edges are contained in each of the strokes
# NOTE: edges that form self-crossing strokes may be missed by the "contains"
# relationship. However, they are catched by the "overlaps" relationship,
# which is why we run both predicate and merge results
edges_in_stroke <- contains_or_overlaps(strokes, edges)
# merge the groups of edges in (multi)linestrings
merge_edges <- function(idx) {
union <- sf::st_union(edges[idx])
if (sf::st_geometry_type(union) == "LINESTRING") {
union
} else {
sf::st_line_merge(union)
}
}
edges_merged <- sf::st_sfc(sapply(edges_in_stroke, merge_edges),
crs = sf::st_crs(edges))
# compare the grouped edges to the strokes: if identical, this means that
# the strokes contain full edges, i.e. flow_mode is respected
# NOTE: element-wise comparison may fail because all "strokes" are
# linestrings, while "edges_merged" includes some multilinestrings in the case
# of self-intersections. We thus check for "topological" equality, by checking
# that for each pair of geometries A and B, A is in B and B is in A. For the
# reasons described above, we use "contains_or_overlaps" to this end.
edges_merged_in_strokes <- contains_or_overlaps(strokes, edges_merged)
strokes_in_edges_merged <- contains_or_overlaps(edges_merged, strokes)
expect_true(all(unlist(edges_merged_in_strokes) == seq_len(length(strokes))))
expect_true(all(unlist(strokes_in_edges_merged) == seq_len(length(strokes))))
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.