Nothing
# p4
# |
# p1 - p2 ---- p3
# |
# p5
p1 <- sf::st_point(c(0, 0))
p2 <- sf::st_point(c(1, 0))
p3 <- sf::st_point(c(3, 0))
p4 <- sf::st_point(c(2, 1))
p5 <- sf::st_point(c(2, -1))
e1 <- sf::st_linestring(c(p1, p2))
e2 <- sf::st_linestring(c(p2, p3))
e3 <- sf::st_linestring(c(p4, p5))
nodes <- sf::st_sfc(p1, p2, p3, p4, p5)
edges <- sf::st_as_sf(sf::st_sfc(e1, e2, e3))
edges$from <- c(1, 2, 4)
edges$to <- c(2, 3, 5)
network <- sfnetworks::sfnetwork(nodes = nodes, edges = edges,
directed = FALSE, force = TRUE,
node_key = "x")
# p9 ----- p8
# | / |
# | p10 |
# | / |
# p6 ----- p7
p6 <- sf::st_point(c(-1, -1))
p7 <- sf::st_point(c(1, -1))
p8 <- sf::st_point(c(1, 1))
p9 <- sf::st_point(c(-1, 1))
p10 <- sf::st_point(c(0, 0))
e4 <- sf::st_linestring(c(p6, p7))
e5 <- sf::st_linestring(c(p7, p8))
e6 <- sf::st_linestring(c(p8, p9))
e7 <- sf::st_linestring(c(p9, p6))
e8 <- sf::st_linestring(c(p6, p10))
e9 <- sf::st_linestring(c(p8, p10))
nodes_shortpath <- sf::st_sfc(p6, p7, p8, p9, p10)
edges_shortpath <- sf::st_as_sf(sf::st_sfc(e4, e5, e6, e7, e8, e9))
edges_shortpath$from <- c(1, 2, 3, 4, 1, 3)
edges_shortpath$to <- c(2, 3, 4, 1, 5, 5)
edges_shortpath$length <- c(2, 2, 2, 2, sqrt(2) / 2, sqrt(2) / 2)
network_shortpath <- sfnetworks::sfnetwork(nodes = nodes_shortpath,
edges = edges_shortpath,
directed = FALSE, force = TRUE,
node_key = "x")
# p4
# |
# p1 - p2 |
# |
# p5
nodes_no_crossings <- sf::st_sfc(p1, p2, p4, p5)
edges_no_crossings <- sf::st_as_sf(sf::st_sfc(e1, e3))
edges_no_crossings$from <- c(1, 3)
edges_no_crossings$to <- c(2, 4)
network_no_crossings <- sfnetworks::sfnetwork(nodes = nodes_no_crossings,
edges = edges_no_crossings,
directed = FALSE, force = TRUE,
node_key = "x")
test_that("Network objects can be set up with no modifications", {
edges <- sf::st_sfc(e1, e2, e3)
network <- as_network(edges, flatten = FALSE, clean = FALSE)
expect_true(inherits(network, "sfnetwork"))
nodes_actual <- sf::st_geometry(sf::st_as_sf(network, "nodes"))
edges_actual <- sf::st_as_sf(network, "edges")
nodes_expected <- sf::st_sfc(p1, p2, p3, p4, p5)
from_expected <- c(1, 2, 4)
to_expected <- c(2, 3, 5)
expect_setequal(sf::st_geometry(edges_actual), edges)
expect_setequal(edges_actual$from, from_expected)
expect_setequal(edges_actual$to, to_expected)
expect_setequal(nodes_actual, nodes_expected)
})
test_that("Network flattening inject intersection within edges", {
nodes <- sf::st_sfc(p2, p3, p4, p5)
edges <- sf::st_as_sf(sf::st_sfc(e2, e3))
edges$from <- c(1, 3)
edges$to <- c(2, 4)
network <- sfnetworks::sfnetwork(nodes = nodes, edges = edges,
directed = FALSE, force = TRUE,
node_key = "x")
network_flat <- flatten_network(network)
nodes_actual <- sf::st_geometry(sf::st_as_sf(network_flat, "nodes"))
edges_actual <- sf::st_geometry(sf::st_as_sf(network_flat, "edges"))
intersection <- sf::st_intersection(e2, e3)
edges_expected <- sf::st_sfc(sf::st_linestring(c(p2, intersection, p3)),
sf::st_linestring(c(p4, intersection, p5)))
expect_setequal(nodes_actual, nodes)
expect_setequal(edges_actual, edges_expected)
})
test_that("Network cleaning transforms shared internal points to nodes", {
nodes <- sf::st_sfc(p2, p3, p4, p5)
intersection <- sf::st_intersection(e2, e3)
edges <- sf::st_as_sf(sf::st_sfc(sf::st_linestring(c(p2, intersection, p3)),
sf::st_linestring(c(p4, intersection, p5))))
edges$from <- c(1, 3)
edges$to <- c(2, 4)
network <- sfnetworks::sfnetwork(nodes = nodes, edges = edges,
directed = FALSE, force = TRUE,
node_key = "x")
network_clean <- clean_network(network)
nodes_actual <- sf::st_geometry(sf::st_as_sf(network_clean, "nodes"))
edges_actual <- sf::st_geometry(sf::st_as_sf(network_clean, "edges"))
nodes_expected <- sf::st_sfc(p2, p3, p4, p5, intersection)
edges_expected <- sf::st_sfc(sf::st_linestring(c(p2, intersection)),
sf::st_linestring(c(intersection, p3)),
sf::st_linestring(c(p4, intersection)),
sf::st_linestring(c(intersection, p5)))
expect_setequal(nodes_actual, nodes_expected)
expect_setequal(edges_actual, edges_expected)
})
test_that("Network cleaning drops pseudo nodes", {
nodes <- sf::st_sfc(p1, p2, p3)
edges <- sf::st_as_sf(sf::st_sfc(e1, e2))
edges$from <- c(1, 2)
edges$to <- c(2, 3)
network <- sfnetworks::sfnetwork(nodes = nodes, edges = edges,
directed = FALSE, force = TRUE,
node_key = "x")
network_clean <- clean_network(network)
nodes_actual <- sf::st_geometry(sf::st_as_sf(network_clean, "nodes"))
edges_actual <- sf::st_geometry(sf::st_as_sf(network_clean, "edges"))
nodes_expected <- sf::st_sfc(p1, p3)
edges_expected <- sf::st_sfc(sf::st_linestring(c(p1, p2, p3)))
expect_setequal(nodes_actual, nodes_expected)
expect_setequal(edges_actual, edges_expected)
})
test_that("Network cleaning drops disconnected components", {
nodes <- sf::st_sfc(p2, p3, p4, p5)
edges <- sf::st_as_sf(sf::st_sfc(e2, e3))
edges$from <- c(1, 3)
edges$to <- c(2, 4)
network <- sfnetworks::sfnetwork(nodes = nodes, edges = edges,
directed = FALSE, force = TRUE,
node_key = "x")
network_clean <- clean_network(network)
nodes_actual <- sf::st_geometry(sf::st_as_sf(network_clean, "nodes"))
edges_actual <- sf::st_geometry(sf::st_as_sf(network_clean, "edges"))
nodes_expected <- sf::st_sfc(p2, p3)
edges_expected <- sf::st_sfc(e2)
expect_setequal(nodes_actual, nodes_expected)
expect_setequal(edges_actual, edges_expected)
})
test_that("Network simplification drops loops and multiple edges", {
nodes <- sf::st_sfc(p2, p3)
p6 <- sf::st_point(c(3, -1))
edges <- sf::st_as_sf(sf::st_sfc(e2,
sf::st_linestring(c(p2, p4, p3)),
sf::st_linestring(c(p3, p5, p6, p3))))
edges$from <- c(1, 1, 2)
edges$to <- c(2, 2, 2)
network <- sfnetworks::sfnetwork(nodes = nodes, edges = edges,
directed = FALSE, force = TRUE,
node_key = "x")
network_simplified <- simplify_network(network)
nodes_simplified <- sf::st_geometry(sf::st_as_sf(network_simplified, "nodes"))
edges_simplified <- sf::st_geometry(sf::st_as_sf(network_simplified, "edges"))
edges_expected <- sf::st_sfc(e2)
expect_setequal(nodes_simplified, nodes)
expect_setequal(edges_simplified, edges_expected)
# Also check that the simplification is run as part of the network cleaning
network_clean <- clean_network(network)
nodes_clean <- sf::st_geometry(sf::st_as_sf(network_clean, "nodes"))
edges_clean <- sf::st_geometry(sf::st_as_sf(network_clean, "edges"))
expect_setequal(nodes_clean, nodes_simplified)
expect_setequal(edges_clean, edges_simplified)
})
test_that("Weights only include edge lengths if no opt args are given", {
network_weights <- add_weights(network)
edges <- sf::st_as_sf(network_weights, "edges")
expect_true("weight" %in% colnames(edges))
weight_actual <- edges[["weight"]]
weight_expected <- sf::st_length(edges)
expect_equal(weight_actual, weight_expected)
})
test_that("Weights can also include distance from target", {
target <- sf::st_point(c(0, 0))
network_weights <- add_weights(network, target = target)
edges <- sf::st_as_sf(network_weights, "edges")
expect_true("weight" %in% colnames(edges))
weight_actual <- edges[["weight"]]
weight_expected <- sf::st_length(edges) + drop(sf::st_distance(edges, target))
expect_equal(weight_actual, weight_expected)
})
test_that("Weights can also include penalty in excluded area", {
# Exclusion area is a circle around midpoint of e1, penalty should be added
# only to e1
center <- (p1 + p2) / 2
area <- sf::st_buffer(center, 0.1)
penalty <- 123
network_weights <- add_weights(network, exclude_area = area,
penalty = penalty)
edges <- sf::st_as_sf(network_weights, "edges")
expect_true("weight" %in% colnames(edges))
weight_actual <- edges[["weight"]]
weight_expected <- sf::st_length(edges)
weight_expected[1] <- weight_expected[1] + penalty
expect_equal(weight_actual, weight_expected)
})
test_that("Weights can account for both distance from target and excl. area", {
target <- sf::st_point(c(0, 0))
# Exclusion area is a circle around midpoint of e1, penalty should be added
# only to e1
center <- (p1 + p2) / 2
area <- sf::st_buffer(center, 0.1)
penalty <- 123
network_weights <- add_weights(network, target = target,
exclude_area = area, penalty = penalty)
edges <- sf::st_as_sf(network_weights, "edges")
expect_true("weight" %in% colnames(edges))
weight_actual <- edges[["weight"]]
weight_expected <- sf::st_length(edges) + drop(sf::st_distance(edges, target))
weight_expected[1] <- weight_expected[1] + penalty
expect_equal(weight_actual, weight_expected)
})
test_that("Weight name can be changed", {
network_weights <- add_weights(network, weight_name = "length")
edges <- sf::st_as_sf(network_weights, "edges")
colnames_expected <- c("from", "to", "x", "length")
expect_equal(colnames(edges), colnames_expected)
})
test_that("Shortest path works for single-edge path", {
endpoints <- sf::st_sfc(p6, p7)
path <- shortest_path(network_shortpath, from = endpoints[1],
to = endpoints[2], weights = "length")
path_expected <- sf::st_sfc(sf::st_linestring(c(p6, p7)))
expect_equal(path, path_expected)
})
test_that("Shortest path can reorient edges to return a LINESTRING", {
# The expected path should merge edges "e8" and "e9", which have opposite
# directions. The result should always be a linestring (not a
# multi-linestring)
endpoints <- sf::st_sfc(p6, p8)
path <- shortest_path(network_shortpath, from = endpoints[1],
to = endpoints[2], weights = "length")
path_expected <- sf::st_sfc(sf::st_linestring(c(p6, p10, p8)))
expect_equal(path, path_expected)
})
test_that("Nearest node always return one point", {
# Even if the feature is equidistant from two nodes
target <- (p1 + p2) / 2
nearest <- nearest_node(network, target)
expect_length(nearest, 1)
expected <- sf::st_sfc(p1)
expect_equal(nearest, expected)
})
test_that("Nearest node also works with a linestring as target", {
# Even if the feature is equidistant from two nodes
target <- sf::st_linestring(c(sf::st_point(c(4, 0)),
sf::st_point(c(5, 0))))
nearest <- nearest_node(network, target)
expect_length(nearest, 1)
expected <- sf::st_sfc(p3)
expect_equal(nearest, expected)
})
test_that("Filter network properly splits network across adjacent regions", {
area_1 <- sf::st_as_sfc(sf::st_bbox(c(xmin = -1, xmax = 0.5,
ymin = -1, ymax = 1)))
area_2 <- sf::st_as_sfc(sf::st_bbox(c(xmin = 0.5, xmax = 1,
ymin = -1, ymax = 1)))
network_area_1 <- filter_network(network_shortpath, area_1)
network_area_2 <- filter_network(network_shortpath, area_2)
edges_area_1 <- sf::st_geometry(sf::st_as_sf(network_area_1, "edges"))
nodes_area_1 <- sf::st_geometry(sf::st_as_sf(network_area_1, "nodes"))
edges_area_2 <- sf::st_geometry(sf::st_as_sf(network_area_2, "edges"))
nodes_area_2 <- sf::st_geometry(sf::st_as_sf(network_area_2, "nodes"))
expect_length(edges_area_1, 2)
expect_length(nodes_area_1, 3)
expect_length(edges_area_2, 1)
expect_length(nodes_area_2, 2)
})
test_that("Filter network drops smallest disconnected components", {
# p4 is within the area, but it is left out since it remains disconnected
# from the main network component
area <- sf::st_as_sfc(sf::st_bbox(c(xmin = -1, xmax = 4,
ymin = 0, ymax = 2)))
network_filtered <- filter_network(network, area)
edges_area <- sf::st_geometry(sf::st_as_sf(network_filtered, "edges"))
nodes_area <- sf::st_geometry(sf::st_as_sf(network_filtered, "nodes"))
expect_length(edges_area, 2)
expect_length(nodes_area, 3)
})
test_that("Network setup with real data", {
edges <- bucharest_osm$streets
network <- as_network(edges, clean = FALSE, flatten = FALSE)
edges_actual <- sf::st_geometry(sf::st_as_sf(network, "edges"))
edges_expected <- sf::st_geometry(edges)
expect_setequal(edges_actual, edges_expected)
})
test_that("Flattening network with no crossings does not fail", {
network_no_crossings_flat <- flatten_network(network_no_crossings)
expect_true(inherits(network_no_crossings_flat, "sfnetwork"))
})
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.