Short answer

A viewshed is a binary (or cumulative) raster that marks every cell an observer can see from one or more vantage points, computed by tracing line-of-sight rays across an elevation surface. The result is only as good as four inputs: the surface model (DEM vs DSM), the observer and target heights, the search radius, and whether you correct for Earth curvature and refraction. Most "wrong" viewsheds come from leaving observer height at zero or running the analysis on a smoothed bare-earth DEM when the real obstructions are trees and buildings.

How a viewshed is actually computed

For each candidate target cell, the algorithm constructs the straight 3D line from the observer position (cell elevation + observer offset) to the target (cell elevation + target offset). It then samples the terrain profile between the two points. The geometric test is an angle comparison: walking outward from the observer, it tracks the maximum vertical angle reached so far. A target is visible if its own vertical angle to the observer exceeds every terrain angle encountered along the ray; otherwise the intervening ground occludes it.

Two implementation families dominate:

  • R2 / R3 line-of-sight tracing (ArcGIS Viewshed and Viewshed 2, ESRI's classic approach) samples along each ray.
  • R.LOS / sweep methods such as GRASS r.viewshed, which implements the Haverkort–Toma–Zhuang external-memory sweep and scales well to large DEMs.

The distinction matters for very large rasters: a naive per-cell ray trace is O(n) rays each O(√n) long, while sweep algorithms reorganise the work to be far more cache- and IO-efficient.

The parameters that decide whether your result is trustworthy

Observer and target height

These are offsets added to the DEM elevation at the observer and target cells. A standing person is roughly 1.6–1.8 m; a radar mast or antenna might be 30 m; a fire lookout cab sits at its own structural height. If you leave the observer offset at 0, you are asking "what can a flat eye on the bare ground see," which almost always understates real visibility. In ArcGIS these are the OFFSETA (observer) and OFFSETB (target) fields on the observer feature class; in GRASS r.viewshed they are observer_elevation and target_elevation.

Search radius and angular limits

A maximum radius caps how far rays are traced. For a 1 m LiDAR DSM, tracing to 50 km is both meaningless and slow; for a microwave link study it may be essential. Constrain it. ArcGIS exposes RADIUS1/RADIUS2 (inner/outer) plus AZIMUTH1/AZIMUTH2 and VERT1/VERT2 to clip the field of view to a sector and elevation band, which is how you model a directional antenna or a window's view cone.

DEM versus DSM

This is the single most consequential choice. A DEM is bare earth; a DSM includes vegetation and structures (first-return LiDAR or photogrammetric surface). For telecom, security sightlines, or "can residents see the turbine," use a DSM. For geological outcrop visibility or a deliberately conservative terrain-only horizon, use a DEM. Mixing them silently — say a bare-earth observer on a canopy-height target surface — produces results no one can defend.

Curvature and refraction

Over short distances a flat-Earth assumption is fine. Past roughly 2–5 km it is not. The drop of a target below a tangent horizon is approximately:

drop ≈ d² / (2R)

where d is distance and R is Earth's radius (~6,371,000 m). At 10 km that is about 7.8 m of geometric drop before refraction. Atmospheric refraction bends the ray slightly downward; a standard correction uses an effective radius of R / (1 − k) with a refractivity coefficient k ≈ 0.13 (ESRI's default REFRACTIVITY_COEFFICIENT is 0.13). Both ArcGIS and GRASS let you toggle curvature/refraction (CURVATURE/r.viewshed -c and the refraction coefficient). Enable them for long-range studies; the difference at the horizon is the difference between "visible" and "hidden."

Worked example: cumulative viewshed in GRASS GIS

Suppose you have a DSM at 2 m resolution in EPSG:32631 (UTM 31N) and three proposed lookout positions, and you want a cumulative viewshed (how many vantage points see each cell).

# import the DSM
r.import input=dsm_2m.tif output=dsm

# set the computational region to the DSM
g.region raster=dsm -p

# one viewshed per observer, observer at 25 m mast, target a 1.7 m person,
# 8 km radius, with curvature and refraction on
r.viewshed input=dsm output=vs_p1 coordinates=482300,5630100 \
  observer_elevation=25 target_elevation=1.7 max_distance=8000 \
  refraction_coeff=0.14286 -c

# repeat for vs_p2, vs_p3 with their coordinates...

# binary-ise (visible=1) and sum to a cumulative viewshed
r.mapcalc "vs_sum = (vs_p1 > 0) + (vs_p2 > 0) + (vs_p3 > 0)"

vs_sum now ranges 0–3: cells seen by all three masts score 3. That cumulative layer is what you hand a planner, not the raw per-point rasters.

The QGIS equivalent uses the GRASS r.viewshed algorithm exposed in the Processing toolbox, or the standalone Visibility Analysis plugin (Zoran Čučković), which adds intervisibility and "horizon" outputs.

Common pitfalls and why they happen

  • Observer height left at 0. The default offset is often zero, so the analyst forgets it. Visibility collapses to whatever the bare ground touches.
  • Wrong surface model. A bare-earth DEM hides the fact that a forest would block the view; the map looks optimistic and gets challenged on site.
  • Vertical and horizontal units mismatched. If the DEM is in feet but the project CRS is metric, or a geographic CRS (degrees) is used so the algorithm cannot reconcile horizontal distance with vertical elevation, the angle test is nonsense. Always run viewshed in a projected CRS with consistent units.
  • Resolution treated as accuracy. A 1 m DSM is not 1 m accurate; LiDAR noise and interpolation artefacts create spurious blockers and pinholes. Light smoothing or void-filling first often gives a more defensible result.
  • Ignoring curvature at range. A 30 km microwave path computed flat-Earth will report clear line of sight that does not exist.

QA and validation

  • Field-check a sample. Visit (or street-view) two or three "visible" and "hidden" cells and confirm. A viewshed that disagrees with reality on the easy cases is wrong everywhere.
  • Sensitivity test the offset. Re-run at observer height ±2 m. If the visible area swings wildly, your result is fragile and the height assumption must be stated explicitly.
  • Confirm the CRS and units with gdalinfo dsm_2m.tif before you start; look for a projected coordinate system and metres.
  • Inspect the radius edge. A perfect circle of visibility at the radius boundary is the clip artefact, not real terrain — label it.

Bathyl perspective

We treat a viewshed as an explicitly parameterised model, not a fact. Every visibility layer we deliver records the surface model, observer/target offsets, radius, and curvature settings, because those four lines are what let a reviewer reproduce — or challenge — the map. Visibility is a hypothesis the field can test, and we write it so that the test is easy.

Related reading

Sources