文章目录
- 综述
- 代码实现
- 代码解析
- 结果
综述
在数字孪生或者实景三维的项目中,视频融合和可视域分析,一直都是热点问题。Cesium中,支持对阴影的后处理操作,通过重新编写GLSL代码就能实现视域和视频融合的功能。ArcGIS之前支持的可视域分析只要是通过GP服务的方式去实现,并且只针对地形有相应的效果,并不能直接叠加到建模模型上。
ArcGIS for JavaScript最新发布了4.30版本的api,其中新增了对于模型场景的视域分析,并且提供了很好的交互功能,使其在项目应用中又有了更多的可能性。
代码实现
直接上代码,或者直接去官网看。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Interactive viewshed analysis | Sample | ArcGIS Maps SDK for JavaScript 4.30</title>
<style>
html,
body,
html,
body,
#viewDiv {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#viewshedComponent {
width: 270px;
}
#viewshedComponent calcite-button {
display: flex;
}
#promptText {
margin-top: 0.4rem;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.30/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.30/"></script>
<script type="module" src="https://js.arcgis.com/calcite-components/2.8.5/calcite.esm.js"></script>
<link rel="stylesheet" type="text/css" href="https://js.arcgis.com/calcite-components/2.8.5/calcite.css" />
<script>
require([
"esri/Map",
"esri/views/SceneView",
"esri/geometry/SpatialReference",
"esri/core/promiseUtils",
"esri/core/reactiveUtils",
"esri/views/3d/environment/SunLighting",
"esri/analysis/ViewshedAnalysis",
"esri/analysis/Viewshed"
], function (
Map,
SceneView,
SpatialReference,
promiseUtils,
reactiveUtils,
SunLighting,
ViewshedAnalysis,
Viewshed
) {
const view = new SceneView({
container: "viewDiv",
camera: {
position: {
spatialReference: SpatialReference.WebMercator,
x: -9753837.742627423,
y: 5140806.202422867,
z: 995.4546383377165
},
heading: 1.2311944909542853,
tilt: 70.07900968078631
},
map: new Map({
basemap: "topo-3d",
ground: "world-elevation"
}),
environment: {
lighting: new SunLighting({
date: new Date("January 18, 2024 12:50:00 UTC-6"),
directShadowsEnabled: true
})
}
});
view.when(async () => {
// Hide the 3D basemap's labels layer.
const labelsLayer = view.map.basemap.referenceLayers.find(
(layer) => layer.title === "Places and Labels"
);
labelsLayer.visible = false;
// Create the viewshed shape.
const viewshed = new Viewshed({
observer: {
spatialReference: SpatialReference.WebMercator,
x: -9754426,
y: 5143111,
z: 330
},
farDistance: 900, // In meters
tilt: 84, // Tilt of 0 looks down, tilt of 90 looks parallel to the ground, tilt of 180 looks up to the sky
heading: 63, // Counted clockwise from North
horizontalFieldOfView: 85,
verticalFieldOfView: 52
});
// Initialize viewshed analysis with the created viewshed shape and add it to the view.
const viewshedAnalysis = new ViewshedAnalysis({ viewsheds: [viewshed] });
view.analyses.add(viewshedAnalysis);
// Access the viewshed's analysis view.
const analysisView = await view.whenAnalysisView(viewshedAnalysis);
// Make the existing analysis interactive and select the created viewshed.
analysisView.interactive = true;
analysisView.selectedViewshed = viewshed;
// Add interactivity to the custom UI component's buttons.
const createButton = document.getElementById("createButton");
const cancelButton = document.getElementById("cancelButton");
const promptText = document.getElementById("promptText");
// Controller which allows to cancel an ongoing viewshed creation operation.
let abortController = null;
createButton.addEventListener("click", () => {
// Cancel any pending creation operation.
stopCreating();
// Create a new abort controller for the new operation.
abortController = new AbortController();
updateUI();
// Save current number of viewsheds to track whenever a new one is created.
const viewshedCounter = viewshedAnalysis.viewsheds.length;
// Watch whenever the a new viewshed is created and selected and then stop the creation method.
reactiveUtils.when(
() => viewshedAnalysis.viewsheds.length > viewshedCounter && analysisView.selectedViewshed,
() => {
stopCreating();
updateUI();
}
);
// Pass the controller as an argument to the interactive creation method
// and schedule the updateUI function after creating viewsheds is finished.
analysisView
.createViewsheds(abortController)
.catch((e) => {
// When the operation is cancelled, don't do anything. Any other errors are thrown.
if (!promiseUtils.isAbortError(e)) {
throw e;
}
})
.finally(() => {
// Update the UI to reflect the non-creating mode.
updateUI();
});
});
cancelButton.addEventListener("click", () => {
// Pressing the Cancel button stops the viewshed creation process and updates the UI accordingly.
stopCreating();
updateUI();
});
// Cancel the creation process and updates the UI when ESC is pressed.
view.on("key-down", (event) => {
if ((event.key = "Escape")) {
stopCreating();
updateUI();
}
});
// Cancel any pending viewshed creation operation.
function stopCreating() {
abortController?.abort();
abortController = null;
}
// Update the UI component according to whether there is a pending operation.
function updateUI() {
const creating = abortController != null;
createButton.style.display = !creating ? "flex" : "none";
cancelButton.style.display = creating ? "flex" : "none";
promptText.style.display = creating ? "flex" : "none";
}
// Add the component to the UI.
view.ui.add("viewshedComponent", "top-right");
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<calcite-card id="viewshedComponent">
<calcite-button id="createButton">Create viewshed</calcite-button>
<calcite-button id="cancelButton" style="display:none">Cancel </calcite-button>
<div id="promptText" style="display: none">
<em>Start the analysis by clicking in the scene to place the observer point and set the target.</em>
</div>
</calcite-card>
</body>
</html>
代码解析
ArcGIS for JavaScript提供了新的对象Viewshed和ViewshedAnalysis。
Viewshed定义了Viewshed分析的几何形状。视域由位置、距离、方向(由头部和倾斜定义)和视野角度决定。
ViewshedAnalysis允许在3D SceneView中创建和显示viewshed和view dome类型的可见性分析。该分析功能可以包含多个视图。它们可以以交互方式或编程方式创建,并且可以将分析直接添加到SceneView.analyses中。
const viewshed = new Viewshed({
observer: {
spatialReference: {
latestWkid: 3857,
wkid: 102100
},
x: -9754426,
y: 5143111,
z: 330
},
farDistance: 900,
heading: 64,
tilt: 84,
horizontalFieldOfView: 85,
verticalFieldOfView: 52
});
const viewshedAnalysis = new ViewshedAnalysis({
viewsheds: [viewshed],
});
view.analyses.add(viewshedAnalysis);
···
上面方法是直接去创建可视域的功能,当然也可以通过交互的方式去创建可视域。
```javascript
const analysisView = await view.whenAnalysisView(viewshedAnalysis);
// Make the existing analysis interactive and select the created viewshed.
analysisView.interactive = true;
analysisView.selectedViewshed = viewshed;
analysisView
.createViewsheds(abortController)
.catch((e) => {
// When the operation is cancelled, don't do anything. Any other errors are thrown.
if (!promiseUtils.isAbortError(e)) {
throw e;
}
})
.finally(() => {
// Update the UI to reflect the non-creating mode.
updateUI();
});