Purpose: Compute the viewshed — the set of terrain cells visible from an observer — over a dense elevation grid, and render it as a visibility map.
Business problem: Line-of-sight analysis drives real decisions: cell-tower and radar placement, wildfire lookout siting, scenic-view real-estate valuation, and defilade planning.
Scope: Ray-based viewshed over generated DEMs. Earth-curvature and atmospheric-refraction corrections and GeoTIFF/COG ingestion are future work.
Algorithm
Ray-casting viewshed: For every target cell a ray is walked from the observer; the maximum vertical angle to intervening terrain is tracked, and the target is visible only if its own angle clears that running maximum.
Observer height: The eye sits at the observer cell's elevation plus a configurable height, which materially changes coverage — a key thing to demonstrate interactively.
Deterministic sampling: Each ray takes max(|dx|, |dy|) steps and samples the nearest cell, keeping the native and managed passes numerically aligned.
Complexity: O(n · √n) for an n-cell grid — cheap enough for interactive grids, and an obvious SIMD/tiling candidate for large DEMs.
API Layer
Controller:ViewshedController is versioned at /api/v1/viewshed and allows anonymous demo access.
Response:ViewshedResultDto returns a row-major visibility grid (1 = visible, 0 = hidden) plus a visible-cell count and the echoed observer position.
Validation: The service enforces grid bounds, a cell cap, a positive cell size, an in-bounds observer, and a finite observer height.
Services and Native Boundary
Service:ViewshedService validates the request and dispatches to ViewshedNativeBridge when the kernel is present.
Native bridge:ViewshedNativeBridge passes a dense elevation array and a preallocated visibility buffer into C++ and maps the result to the DTO.
C++ library:native/viewshed_kernel exposes Viewshed_Compute, returning the visible-cell count.
Why C++ here: The per-cell ray walk is dense pointer arithmetic over contiguous elevation memory — exactly the workload where cache locality and vectorization matter.
Managed fallback:ViewshedService mirrors the ray-casting logic in C#, keeping the demo portable on Render with no native artifact.
Engineering Decisions and Tradeoffs
Ray-per-cell over sweep: The R2 ray method is easy to reason about and parallelize; a sweep-line/reference-plane method would be faster but harder to demonstrate.
Generated DEMs: Keeps the demo self-contained but skips georeferencing, nodata handling, and reprojection that a production pipeline needs.
Nearest-cell sampling: Simple and parity-friendly; bilinear sampling along the ray would reduce aliasing at a modest cost.
Future improvements: Earth-curvature correction, multi-observer cumulative viewsheds, GDAL-backed DEM ingestion, and tile/PNG output for map overlays.
Interview Discussion Points
How does observer height change coverage, and why is it non-linear over rough terrain?
What accuracy do you trade away with nearest-cell ray sampling versus interpolation?
How would earth curvature and refraction enter the angle computation at long range?
Where would you parallelize — per target cell, per ray, or per tile — and why?