Skip to content

Commit

Permalink
Merge pull request #10 from anirudhgovind/dev
Browse files Browse the repository at this point in the history
add: proximity bands
  • Loading branch information
anirudhgovind authored Mar 12, 2024
2 parents fddb4ee + 05bad13 commit f0079f5
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 49 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by roxygen2: do not edit by hand

export(add_unique_id)
export(st_create_proximitybands)
export(st_create_streetblocks)
export(st_create_tessellations)
export(st_explode)
Expand Down
115 changes: 115 additions & 0 deletions R/st_create_proximitybands.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#' Create proximity band spatial units.
#'
#' @param x an `sf` object with `LINESTRING` geometries representing a street
#' network.
#' @param band_width numeric; width of the band as measured from the centerline
#' of the `LINESTRING` representing the street.
#' @param segment_length numeric; Length of segments street linestrings are
#' subdivided into. Default = 2.
#' @param merge_threshold numeric; value represents the smallest acceptable
#' size for a spatial unit. Contiguous units will be iteratively merged until
#' this value is reached. To skip this process, set `merge_threshold = NULL`.
#' See st_merge_spatialunits() for more details.
#' @param merge_type string; Passed on to st_merge_spatialunits().
#' Criteria with which polygons are merged. Must be one of
#' "min_centroid_distance", "min_shared_boundary", or "max_shared_boundary".
#' Default = "min_centroid_distance".
#' See st_merge_spatialunits() for more details.
#' @param contiguity string; one of "queen" or "rook". Default = "rook".
#' @param verbose logical; if `FALSE` no status messages will be output.
#'
#' @return An `sf` object with `POLYGON` geometries representing tessellated
#' spatial units.
#' @seealso st_merge_spatialunits, [moter::motess()]
#' @references Araldi, A., & Fusco, G. (2019). From the street to the
#' metropolitan region: Pedestrian perspective in urban fabric analysis.
#' Environment and Planning B: Urban Analytics and City Science, 46(7),
#' 1243–1263. https://doi.org/10.1177/2399808319832612
#' @export
#'
#' @examples
#' proximity_bands <- st_create_proximitybands(x = bangalore_highways,
#' verbose = FALSE, merge_threshold = NULL)
#' plot(proximity_bands)
st_create_proximitybands <- function(x,
band_width = 10,
segment_length = 2,
merge_threshold = 1015,
merge_type = "min_centroid_distance",
contiguity = "rook",
verbose = T) {

# Turn off s2 geometry

sf::sf_use_s2(FALSE)

# Check polygons (if present) and linestrings data

if (sf::st_is_longlat(x) == TRUE) {
# Keep track of original CRS

x_crs <- sf::st_crs(x)

# Reproject to 3857

x <- sf::st_transform(x,
3857)

}
# Process line strings

x <- process_linestrings(x)

x <- sf::st_sf(x)

# Add IDs to the linestrings

x <- add_unique_id(x)

# Draw buffers around the street network

x_buffer <- sf::st_buffer(x,
band_width)

x_buffer <- sf::st_union(x_buffer)

voronoi_polygons <- segment_and_tessellate(x = x,
segment_length = segment_length)

# Clip vor polygons to the drawn buffer

suppressMessages(suppressWarnings(
x_prox_band <- sf::st_intersection(voronoi_polygons,
x_buffer)
))

if (verbose == T) {
message(paste0(nrow(x_prox_band)," proximity bands created."))
}

if (is.null(merge_threshold)) {
warning(
"Small geometries (< 1 m^2) may be present. Use st_merge_spatialunits() to aggregate them."
)
}

if (!is.null(merge_threshold)) {
x_prox_band <- st_merge_spatialunits(
x = x_prox_band,
merge_threshold = merge_threshold,
merge_type = merge_type,
contiguity = contiguity,
verbose = verbose
)
}

# If geometry is long lat, convert CRS back to the original.

if (sf::st_is_longlat(x) == TRUE) {
x_prox_band <- sf::st_transform(x_prox_band,
x_crs)
}

return(x_prox_band)

}
118 changes: 73 additions & 45 deletions R/st_create_tessellations.R
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ st_create_tessellations <-
contiguity = "rook",
verbose = T) {


# Init vars used further down

temp_id <- geometry <- NULL

# Turn off s2 geometry

sf::sf_use_s2(FALSE)

if (type == "morphological") {
# Init vars used further down

temp_id <- geometry <- NULL

# Check data types.

Expand Down Expand Up @@ -87,49 +89,10 @@ st_create_tessellations <-
x <- sf::st_buffer(x,
dist = (-1 * shrink_extent))

# Slice into segments

segments <- sf::st_segmentize(x, segment_length)

# Extract points from the segments

suppressMessages(suppressWarnings(points <-
sf::st_cast(segments,
"POINT")))

# Remove duplicates

points_unique <- unique(points)

# Draw Voronoi Polygons

voronoi_polygons <-
sf::st_collection_extract(sf::st_voronoi(sf::st_union(points_unique)))

# Dissolve polygons
# Segment these lines and tessellate them

voronoi_polygons <-
voronoi_polygons[unlist(sf::st_intersects(points_unique,
voronoi_polygons))]

# Join points to the polygons

voronoi_polygons <- sf::st_join(sf::st_sf(voronoi_polygons),
points_unique)

# Make geometry valid

voronoi_polygons <- sf::st_make_valid(voronoi_polygons)

voronoi_polygons <- sf::st_buffer(voronoi_polygons,
0)

# Merge polygons

suppressMessages(suppressWarnings(voronoi_polygons <- voronoi_polygons |>
dplyr::group_by(temp_id) |>
dplyr::summarise(geometry = sf::st_union(geometry)) |>
dplyr::ungroup()))
voronoi_polygons <- segment_and_tessellate(x = x,
segment_length = segment_length)

# Clip to boundary

Expand Down Expand Up @@ -180,3 +143,68 @@ st_create_tessellations <-
}

}

#' Segment `LINESTRING` or `POLYGON` geometries and create Voronoi tessellations.
#'
#' @param x a `sf` object with `LINESTRING` or `POLYGON` geometries.
#'
#' @return A `sf` object with Voronoi tessellations
#' @keywords internal
#' @noRd
segment_and_tessellate <- function(x, segment_length) {

# Init vars used further down

temp_id <- geometry <- NULL

# Slice into segments

segments <- sf::st_segmentize(x, segment_length)

# Extract points from the segments

suppressMessages(suppressWarnings(points <-
sf::st_cast(segments,
"POINT")))

# Remove duplicates

points_unique <- unique(points)

# Draw Voronoi Polygons

voronoi_polygons <-
sf::st_collection_extract(sf::st_voronoi(sf::st_union(points_unique)))

# Dissolve polygons

voronoi_polygons <-
voronoi_polygons[unlist(sf::st_intersects(points_unique,
voronoi_polygons))]

# Join points to the polygons

voronoi_polygons <- sf::st_join(sf::st_sf(voronoi_polygons),
points_unique)

# Make geometry valid

voronoi_polygons <- sf::st_make_valid(voronoi_polygons)

voronoi_polygons <- sf::st_buffer(voronoi_polygons,
0)

# Merge polygons

suppressMessages(
suppressWarnings(
voronoi_polygons <- voronoi_polygons |>
dplyr::group_by(temp_id) |>
dplyr::summarise(geometry = sf::st_union(geometry)) |>
dplyr::ungroup()
)
)

return(voronoi_polygons)

}
9 changes: 5 additions & 4 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@ template:
bootstrap: 5
reference:

- subtitle: Extraction
- subtitle: Extract & Process Data
contents:
- st_explode
- st_extract_connected
- st_extract_disconnected
- st_extract_intersections

- subtitle: Spatial Units
- subtitle: Create Spatial Units
contents:
- st_create_proximitybands
- st_create_streetblocks
- st_create_tessellations
- st_merge_spatialunits
- st_survey_spatialunits

- subtitle: Miscellaneous
contents:
- add_unique_id
- st_merge_spatialunits
- st_survey_spatialunits

- subtitle: Datasets
contents:
Expand Down
62 changes: 62 additions & 0 deletions man/st_create_proximitybands.Rd

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

20 changes: 20 additions & 0 deletions tests/testthat/test-st_create_proximitybands.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
test_that("st_create_proximitybands() fails on non `sf` objects", {
expect_error(
st_create_proximitybands(
x = 42,
merge_threshold = NULL,
verbose = FALSE
)
)
})

test_that("st_create_proximitybands() fails on non-linestring objects passed to x",
{
expect_error(
st_create_tessellations(
x = bangalore_buildings,
merge_threshold = NULL,
verbose = FALSE
)
)
})

0 comments on commit f0079f5

Please sign in to comment.