ViewshedSightline

open class ViewshedSightline @JvmOverloads constructor(position: Position, area: ViewshedArea, samplesPerSide: Int = 1024, var attributes: ShapeAttributes = ShapeAttributes()) : AbstractRenderable, Attributable, Highlightable, Movable

Visibility-from-observer overlay computed against the source elevation model rather than the rendered terrain mesh. Sampling resolution is decoupled from the camera's terrain LoD, so far-away coverage areas remain accurate even when the visible terrain tiles are coarse.

Each dirty render frame snapshots inputs and starts a coroutine that samples the elevation grid off the render thread on Dispatchers.Default. When sampling completes, the next render frame populates a DrawableViewshedKernel with the snapshot + elevation array and enqueues it via RenderContext.offerSurfaceDrawable. The drawable runs on the GL thread during that frame's draw phase: uploads the elevations as an R32F texture, runs the per-fragment Amanatides–Woo kernel via earth.worldwind.render.program.ViewshedKernelShaderProgram into an RGBA8 render target, and the SurfaceImage samples that texture in place — no CPU readback round-trip.

Requires GLES 3 / WebGL 2 / GL 3.3 core for R32F sampling and texelFetch. The drawable detects Kgl.supportsSizedTextureFormats == false and silently skips rather than rendering garbage.

GPU memory cost is roughly samplesPerSide² * 12 bytes per shape: 4 B for the RGBA8 output texture + 4 B for the R32F elevation sampler + ~4 B for the FBO depth/scratch state. At the default 1024² that's ~12 MB GPU, plus two pooled CPU FloatArrays of samplesPerSide² * 4 bytes (8 MB) on the host side. Multiple sightlines multiply linearly. Use samplesForResolution to size against the underlying DEM rather than oversampling - kernel work is O(N³) so dropping N saves both memory and GPU time. Call dispose when removing a sightline to reclaim GPU resources before engine teardown.

Constructors

Link copied to clipboard
constructor(position: Position, area: ViewshedArea, samplesPerSide: Int = 1024, attributes: ShapeAttributes = ShapeAttributes())

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
open override var altitudeMode: AltitudeMode

Altitude reference for position. Defaults to ABSOLUTE.

Link copied to clipboard

The geographic footprint analysed by this sightline. Replacing it triggers a recompute.

Link copied to clipboard
open override var attributes: ShapeAttributes

Attributes used to color terrain that is visible from the observer. Only interiorColor is consumed.

Link copied to clipboard
open override var displayName: String?
Link copied to clipboard

Resolution multiplier applied to samplesPerSide while the observer is being dragged. When the position has been settled for settleFrames frames, the kernel re-runs at full resolution. 1.0 disables draft mode (always full-res). Default 0.5 halves both axes during interaction for ~4× faster GPU dispatch.

Link copied to clipboard

Number of consecutive frames with a stable RenderContext.elevationModelTimestamp required before a tile-arrival burst triggers a re-sample. Default ~30 frames (~0.5 s at 60 fps). On Kotlin/JS where the elevation grid sample runs on the event loop, an unfiltered tick-per-tile would stall the page repeatedly during initial map load; debouncing collapses the burst into one sample after tiles settle. Position changes still bypass this and trigger immediately.

Link copied to clipboard

Indicates the shape's highlight attributes.

Link copied to clipboard
open override var isEnabled: Boolean
Link copied to clipboard
open override var isHighlighted: Boolean

Indicates whether the shape is highlighted.

Link copied to clipboard
open override var isPickEnabled: Boolean

Picking flows through the inner per-window surfaceImage (it owns the surface texture drawable the user actually clicks on), so propagate the outer's pick state onto every window's SurfaceImage. Without this, a click on the viewshed overlay returns the internal SurfaceImage as the picked userObject instead of this ViewshedSightline. The outer's render() gate already suppresses pick rendering when isPickEnabled is false, but we still mirror the flag onto each SurfaceImage for defence in depth.

Link copied to clipboard
open override val isPointShape: Boolean

true when this shape's referencePosition coincides with where the user actually sees the shape (Placemark, Label, sightlines — the reference is the rendered location). false for shapes whose reference is computed from extended geometry and may sit far from where the user grabbed (Polygon centroid, Path centroid, Mesh).

Link copied to clipboard

Attributes used to color terrain that is occluded from the observer.

Link copied to clipboard
open override var pickDelegate: Any?
Link copied to clipboard

The observer's geographic position. Resolution honors altitudeMode when the position is sampled. Mutated in place (via Position.copy) and detected by doRender comparing against cachedPosition, so direct field writes (position.latitude = ...) trigger a recompute too.

Link copied to clipboard
open override val referencePosition: Position

A position associated with the object that indicates its aggregate geographic position. The chosen position varies among implementers of this interface. For objects defined by a list of positions, the reference position is typically the first position in the list. For symmetric objects the reference position is often the center of the object. In many cases the object's reference position may be explicitly specified by the application.

Link copied to clipboard

Number of grid samples along each side of the area-of-interest (output raster is samplesPerSide square). Clamped to [2, MAX_SAMPLES_PER_SIDE] (default MAX_SAMPLES_PER_SIDE is 4096) to prevent GPU allocation footguns - at 4096² the output + sampler textures alone are ~64 MB each.

Link copied to clipboard

Number of consecutive frames with no position change required to trigger a full-resolution refresh after a draft-resolution dispatch. Default ~30 frames (~0.5 s at 60 fps).

Link copied to clipboard
open override var zOrder: Double

Functions

Link copied to clipboard
open fun dispose()

Release all GPU resources owned by this sightline (output texture, kernel sampler texture, framebuffer). The actual glDelete* calls run on the next render frame's GL thread - the call itself is safe from any thread. After dispose, doRender is a no-op; reuse requires a fresh instance.

Link copied to clipboard
open override fun <T> getUserProperty(key: Any): T?
Link copied to clipboard
open override fun hasUserProperty(key: Any): Boolean
Link copied to clipboard
open fun invalidate()

Marks the cached visibility raster stale; the next render frame will resample the elevation model and re-run the kernel. Useful when attributes / occludeAttributes colors are mutated in-place.

Link copied to clipboard
open override fun moveTo(globe: Globe, position: Position)

Move the shape over the globe's surface while maintaining its original azimuth, its orientation relative to North.

Link copied to clipboard
open override fun putUserProperty(key: Any, value: Any): Any?
Link copied to clipboard
open override fun removeUserProperty(key: Any): Any?
Link copied to clipboard
open override fun render(rc: RenderContext)
Link copied to clipboard
fun samplesForResolution(metersPerPixel: Double): Int

Compute the samplesPerSide count that yields approximately metersPerPixel resolution given the shape's current area. Useful when the caller wants the kernel grid to match the native resolution of an underlying DEM — for example 30.0 for NASADEM or 90.0 for SRTM3 — so the kernel doesn't oversample beyond what the source data actually provides. Result is clamped to >= 2 and computed against the longer axis of the area.