4 min read

viewscape 3.0.0 is on CRAN


R R package viewscape

I am happy to share that viewscape 3.0.0 is now available on CRAN. This version expands the package from a viewshed and viewscape-metrics workflow into a broader toolkit for visibility-based landscape analysis in R.

The package can be installed directly from CRAN:

install.packages("viewscape")
library(viewscape)

The CRAN package page is here: https://CRAN.R-project.org/package=viewscape. The source code and issue tracker are available on GitHub: https://github.com/billbillbilly/viewscape.

What is new in 3.0.0?

The main workflow is still centered on computing viewsheds from a digital surface model (DSM) or digital elevation model and one or more observer locations. The new release adds more tools around that core:

  • field-of-view masking with fov_mask()
  • visual magnitude analysis with visual_magnitude()
  • viewscape configuration metrics, including extent, depth, relief, skyline variation, Sky View Factor, and patch-based metrics
  • land-cover diversity and feature proportions inside the visible area
  • pairwise intervisibility networks with intervis_network()
  • panoramic view generation with pano_view()

These functions are useful when a research question is not only “what is visible?” but also “how much does it matter in the view?”, “which viewpoints are connected?”, and “what would the visible scene look like from the observer position?”

A short example

The package includes example data, so the basic workflow can be tested without downloading any external DSM first.

library(viewscape)
library(terra)
library(sf)

dsm <- terra::rast(system.file("test_dsm.tif", package = "viewscape"))
dtm <- terra::rast(system.file("test_dtm.tif", package = "viewscape"))
viewpoints <- sf::read_sf(system.file("test_viewpoints.shp", package = "viewscape"))

viewpoint <- viewpoints[2, ]

viewshed <- viewscape::compute_viewshed(
  dsm = dsm,
  viewpoints = viewpoint,
  offset_viewpoint = 3,
  r = 800,
  method = "view_tree"
)

viewshed_raster <- viewscape::visualize_viewshed(
  viewshed,
  outputtype = "raster"
)

terra::plot(dsm, axes = FALSE, box = FALSE, legend = FALSE)
terra::plot(viewshed_raster, add = TRUE, col = "red", axes = FALSE, box = FALSE)
terra::plot(viewpoint, add = TRUE, col = "blue", pch = 16)

The result is a standard viewshed object that can be passed to other viewscape functions.

Field of view

In real environments, people rarely experience a full 360-degree view at one time. fov_mask() subsets the viewshed by direction and angle. Angles are measured clockwise from north, where 0 is north and 90 is east.

# North-facing 120-degree field of view
sector <- viewscape::fov_mask(viewshed, c(-60, 60))

terra::plot(dsm, axes = FALSE, box = FALSE, legend = FALSE)
terra::plot(
  viewscape::visualize_viewshed(sector, outputtype = "raster"),
  add = TRUE,
  col = "red",
  axes = FALSE,
  box = FALSE
)
terra::plot(viewpoint, add = TRUE, col = "blue", pch = 16)

Visual magnitude

A binary viewshed tells us whether a cell is visible. Visual magnitude estimates how much each visible cell contributes to the observer’s visual field. Nearby surfaces that face the observer tend to have larger values.

vm <- viewscape::visual_magnitude(viewshed, dsm)

terra::plot(
  dsm,
  axes = FALSE,
  box = FALSE,
  legend = FALSE,
  main = "Visual magnitude over DSM"
)
terra::plot(sqrt(vm), add = TRUE, alpha = 0.8, axes = FALSE, box = FALSE)
terra::plot(viewpoint, add = TRUE, col = "red", pch = 16)

This is especially helpful when visibility needs to be weighted by visual dominance rather than treated as a simple yes/no result.

Intervisibility network

intervis_network() tests whether viewpoints can see one another. The output can be an adjacency matrix or an sf line layer.

network_lines <- viewscape::intervis_network(
  viewpoints = viewpoints,
  dsm = dsm,
  offset_viewpoint = 10,
  r = 800,
  output = "lines"
)

terra::plot(dsm, axes = FALSE, box = FALSE, legend = FALSE)
terra::plot(network_lines["geometry"], add = TRUE, col = "grey60", lwd = 1)
terra::plot(network_lines[network_lines$mutual, "geometry"], add = TRUE, col = "#2171b5", lwd = 2)
terra::plot(viewpoints, add = TRUE, col = "red", pch = 16)

This can support questions about lookout points, scenic routes, landmark visibility, visual corridors, and spatial networks of mutually visible locations.

Panoramic view

The new pano_view() function generates a panoramic representation from a viewpoint. Without a semantic raster, it can create an RGB panorama using imagery. With a land-cover, canopy, or building raster, it can project semantic information into the panoramic view.

pano <- viewscape::pano_view(
  dsm = dsm,
  vpt = viewpoint,
  h = 3,
  pano_dim = c(512, 1024),
  plot = TRUE
)

For land-use or land-cover analysis, a semantic raster can be added:

landuse <- terra::rast(system.file("test_landuse.tif", package = "viewscape"))

pano_landuse <- viewscape::pano_view(
  dsm = dsm,
  vpt = viewpoint,
  h = 5,
  semantic = landuse,
  plot = TRUE
)

This opens up a bridge between raster-based visibility analysis and image-like representations of the observer’s visual scene.

Citation and documentation

The package documentation includes the reference manual and vignette:

If you use viewscape in research, please cite the package and the related methods described in the documentation. The 3.0.0 release builds on viewshed algorithms from Franklin and Ray (1994) and Wang et al. (2000), visual magnitude from Chamberlain and Meitner (2013), and viewscape configuration metrics from Tabrizian et al. (2020).