Skip to main content

Viewshed Analysis Example

This example demonstrates how to perform viewshed analysis using the DMap3D SDK, determining the areas visible from an observation point.

Preview

The user clicks in the scene to set an observation point. Visible areas are displayed in green, and non-visible areas are displayed in red.

Complete Code

src/components/ViewshedAnalysis.tsx
import { useEffect, useRef, useState } from 'react'
import * as Cesium from 'cesium'
import DMap3D from 'dmap3d'
import './ViewshedAnalysis.css'

function ViewshedAnalysis() {
const containerRef = useRef<HTMLDivElement>(null)
const viewerRef = useRef<Cesium.Viewer | null>(null)
const toolRef = useRef<any>(null)
const [isActive, setIsActive] = useState(false)

useEffect(() => {
if (!containerRef.current) return

Cesium.Ion.defaultAccessToken = 'your-cesium-ion-token'

const viewer = new Cesium.Viewer(containerRef.current, {
animation: false,
fullscreenButton: false,
geocoder: false,
homeButton: false,
sceneModePicker: false,
selectionIndicator: false,
timeline: false,
navigationHelpButton: false,
infoBox: false,
baseLayerPicker: false,
terrain: Cesium.Terrain.fromWorldTerrain(),
})
viewerRef.current = viewer

// Load 3DTiles model (viewshed analysis requires occluders)
const loadTileset = async () => {
const tileset = await Cesium.Cesium3DTileset.fromUrl(
'/3DTiles/model/tileset.json'
)
viewer.scene.primitives.add(tileset)
viewer.zoomTo(tileset)
}
loadTileset()

// Create viewshed analysis tool
const tool = new DMap3D.analysis.viewshed(viewer, {
visibleLineColor: '#00ff00', // Visible area color (green)
invisibleLineColor: '#ff0000', // Non-visible area color (red)
})
toolRef.current = tool

// Listen for analysis completion
tool.onAnalysisEnd(() => {
setIsActive(false)
})

return () => {
tool.destroy()
viewer.destroy()
}
}, [])

const handleStart = () => {
const tool = toolRef.current
if (!tool) return
tool.activate()
setIsActive(true)
}

const handleStop = () => {
const tool = toolRef.current
if (!tool) return
tool.deactivate()
setIsActive(false)
}

const handleClear = () => {
const tool = toolRef.current
if (!tool) return
tool.clear()
setIsActive(false)
}

return (
<div className="viewshed-analysis">
<div ref={containerRef} className="cesium-container" />

<div className="control-panel">
<h3>Viewshed Analysis</h3>
<p>Click to set observation point, drag to adjust direction and range</p>

<div className="buttons">
<button onClick={handleStart} disabled={isActive}>
{isActive ? 'Analyzing...' : 'Start Analysis'}
</button>
<button onClick={handleStop} disabled={!isActive}>Stop</button>
<button onClick={handleClear}>Clear</button>
</div>

<div className="legend">
<h4>Legend</h4>
<div className="legend-item">
<span className="color-box visible"></span>
<span>Visible Area</span>
</div>
<div className="legend-item">
<span className="color-box invisible"></span>
<span>Non-visible Area</span>
</div>
</div>

<div className="tip">
<h4>Instructions</h4>
<ol>
<li>Click in the scene to set observation point</li>
<li>Move the mouse to adjust observation direction</li>
<li>Click again to confirm the analysis range</li>
</ol>
</div>
</div>
</div>
)
}

export default ViewshedAnalysis

Stylesheet

src/components/ViewshedAnalysis.css
.viewshed-analysis {
position: relative;
width: 100%;
height: 100vh;
}

.cesium-container {
width: 100%;
height: 100%;
}

.control-panel {
position: absolute;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.95);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
min-width: 250px;
}

.control-panel h3 {
margin: 0 0 10px 0;
font-size: 18px;
}

.control-panel p {
margin: 0 0 15px 0;
color: #666;
font-size: 14px;
}

.buttons {
display: flex;
gap: 8px;
margin-bottom: 15px;
}

.buttons button {
flex: 1;
padding: 8px 12px;
border: none;
border-radius: 4px;
background: #1890ff;
color: white;
cursor: pointer;
font-size: 14px;
}

.buttons button:hover {
background: #40a9ff;
}

.buttons button:disabled {
background: #d9d9d9;
cursor: not-allowed;
}

.legend {
border-top: 1px solid #e8e8e8;
padding-top: 15px;
margin-bottom: 15px;
}

.legend h4 {
margin: 0 0 8px 0;
font-size: 14px;
color: #666;
}

.legend-item {
display: flex;
align-items: center;
gap: 8px;
margin: 6px 0;
font-size: 14px;
}

.color-box {
width: 20px;
height: 12px;
border-radius: 2px;
}

.color-box.visible {
background: #00ff00;
}

.color-box.invisible {
background: #ff0000;
}

.tip {
border-top: 1px solid #e8e8e8;
padding-top: 15px;
}

.tip h4 {
margin: 0 0 8px 0;
font-size: 14px;
color: #666;
}

.tip ol {
padding-left: 20px;
margin: 0;
}

.tip li {
font-size: 13px;
margin: 4px 0;
color: #666;
}

Key Code Explanation

1. Load 3DTiles Model

const tileset = await Cesium.Cesium3DTileset.fromUrl('/3DTiles/model/tileset.json')
viewer.scene.primitives.add(tileset)
viewer.zoomTo(tileset)

Viewshed analysis requires occluders (buildings, terrain, etc.), so 3DTiles models typically need to be loaded.

2. Create Viewshed Analysis Tool

const tool = new DMap3D.analysis.viewshed(viewer, {
visibleLineColor: '#00ff00', // Visible area color
invisibleLineColor: '#ff0000', // Non-visible area color
})

3. Analysis Workflow

// Activate analysis tool, enter interactive mode
tool.activate()

// User operations:
// 1. Left-click to set observation point
// 2. Move mouse to determine observation direction
// 3. Click again to complete analysis

// Deactivate analysis
tool.deactivate()

// Clear analysis results
tool.clear()

Extended Features

  1. Parameter Adjustment - Dynamically adjust viewshed heading, pitch, and distance
  2. Multiple Observation Points - Set multiple observation points for comprehensive analysis
  3. Area Viewshed - Use DMap3D.analysis.viewshedArea for area viewshed analysis
  4. Result Export - Export viewshed analysis results