From 20959162d975121b5283c4875d761e179a955c2e Mon Sep 17 00:00:00 2001 From: Francesco Nattino Date: Tue, 9 Sep 2025 21:22:08 +0200 Subject: [PATCH 1/3] add test with fully occluded rays this is failing for now --- tests/testthat/test-isovist.R | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/testthat/test-isovist.R b/tests/testthat/test-isovist.R index 7270c2f..ed53ab5 100644 --- a/tests/testthat/test-isovist.R +++ b/tests/testthat/test-isovist.R @@ -186,3 +186,14 @@ test_that("merge_isovists returns the merged polygon, with or without holes", { expected <- sf::st_union(isovists) expect_equal(actual, expected) }) + +test_that("visor works with fully occluded rays", { + viewpoints <- sf::st_sfc(sf::st_point(c(0, 0)), sf::st_point(c(2, 0))) + occluders <- sf::st_sfc( + create_occluder(1, 0, 2, 1), + create_occluder(3, 1, 1, 1) + ) + expect_no_error( + get_isovist(viewpoints, occluders = occluders, ray_length = 1) + ) +}) From 4496f9dad8d9a8ee7345828378c454b20dc6a47f Mon Sep 17 00:00:00 2001 From: Francesco Nattino Date: Wed, 10 Sep 2025 09:32:20 +0200 Subject: [PATCH 2/3] fix issue with fully occluded rays --- R/isovist.R | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/R/isovist.R b/R/isovist.R index 336b6fb..df31816 100644 --- a/R/isovist.R +++ b/R/isovist.R @@ -159,10 +159,16 @@ occlude_rays <- function(rays, occluders = NULL) { # Filter the only features intersecting each others rays_intersect <- ray_geoms[ray_intersects_occluders] occluders_intersect <- occluder_geoms[occluder_intersects_rays] + occluders_intersect <- sf::st_union(occluders_intersect) + + # Determine which rays are fully within the occluders and will thus be dropped + rays_within_occluders <- sf::st_within(rays_intersect, + occluders_intersect, + sparse = FALSE) # Determine the ray segments that do not overlap with the occluders. For each - # ray the segments form (multi)-linestrings - diffs <- sf::st_difference(rays_intersect, sf::st_union(occluders_intersect)) + # ray the segments form (multi)-linestrings. Fully-occluded rays are dropped + diffs <- sf::st_difference(rays_intersect, occluders_intersect) # By casting `diffs` to linestrings, we keep only the first segment of each # ray, which, by construction, runs from the original viewpoint to the first @@ -170,8 +176,12 @@ occlude_rays <- function(rays, occluders = NULL) { rays_occluded <- sf::st_cast(diffs, "LINESTRING", group_or_split = FALSE) |> suppressWarnings() + # Determine the indices of the partially-occluded rays, i.e. the ones that + # intersect the occluders and are not fully within the occluders + idx <- which(ray_intersects_occluders)[!rays_within_occluders] + # Update the occluded ray geometries and return the sf(c) object - ray_geoms[ray_intersects_occluders] <- rays_occluded + ray_geoms[idx] <- rays_occluded sf::st_set_geometry(rays, ray_geoms) } From 3ac03928ef66c52deefef5c92fd8a1592bede2aa Mon Sep 17 00:00:00 2001 From: Francesco Nattino Date: Wed, 10 Sep 2025 09:34:33 +0200 Subject: [PATCH 3/3] update changelog --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index 4efdfd3..13ba062 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added Zenodo and CRAN badges #15 +## Fixed + +- Isovist can now be computed also if some rays are fully occluded #17 + # Version 0.1.0 - 2025-04-03 ## Added