ArcGIS JS API实现水淹模拟功能
效果展示
废话不多说,国际惯例,让我先来看一看水淹模拟功能动态效果。
实现原理
水淹模拟功能的实现原理其实很简单,创建一个平面的几何图形用来模拟水面,然后不停修改平面的高度,来模拟水面上涨的效果。
实现步骤
1.创建地图场景map和sceneView
首先我们肯定要先引入ArcGIS JS API相关资源,比如说脚本文件和样式文件。
<link
rel="stylesheet"
href="https://js.arcgis.com/4.24/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.24/"></script>
引入相关资源后,编辑JS脚本文件,用require
从API中引入我们需要用到的模块:
require([
'esri/Map',
'esri/views/SceneView',
'esri/layers/GraphicsLayer',
'esri/widgets/Sketch/SketchViewModel',
'esri/Graphic',
], (Map, SceneView, GraphicsLayer, SketchViewModel, Graphic) => {
// ...
})
引入需要用到的模块后,开始创建地图场景,先创建DOM结构:
<body>
<div id="viewDiv"></div>
</body>
然后创建地图和场景:
// 创建地图
const map = new Map({
basemap: 'topo-vector',
ground: 'world-elevation',
});
// 创建视图场景
const view = new SceneView({
container: 'viewDiv',
map: map,
});
注意: map
中一定要配置“ground: ‘world-elevation’”属性,该属性用来配置地面的高程信息,因为我们需要地面高低起伏,在地面凹处的场景下才能更好的展示水面上涨的动画效果。
2.创建水面图层layer
我们需要创建一个图层,用来承载用于表示“水面”的几何图形。决定使用GraphicsLayer
类来创建图层,用该类创建的图层可以包含多个Graphic
图形,每个图形都可以表示一个区域的水面。
layer = new GraphicsLayer({
elevationInfo: {
mode: 'absolute-height',
},
});
view.map.add(layer); // 将图层添加进地图中
需要注意的是,我们在创建layer时,属性中的高程信息一定要设置为绝对高度模式absolute-height
。这样的好处是图层中图形的高度数值都是相对于海平面的高度值,方便后面计算高度,因为后面设置“水面”的高度为水面区域的最低点海拔高度 + 水面区域的积水深度。
3.创建水面图形graphic
创建好图层后,我们就需要创建一个图形graphic来表示水面,然后添加进图层中。graphic主要由geometry、symbol以及attributes等属性组成。geometry
为几何形状,包含了图形中所有点的坐标信息,symbol
为图形的符号信息,让一个平面的材质看起来像水面就是通过这个属性设置,attributes
为图形的属性信息,可以用来存储一些有用的信息,比如水面的海拔高度、水面的积水深度等信息。
①创建几何形状geometry
构建一个geometry对象,我们需要知道所有点的坐标信息。可以通过给场景绑定一个点击事件,每次发生点击时会得到一个事件对象,代码如下:
view.on('click', (e) => {
console.log(e); // 在控制台中查看地图中所点击的点位坐标信息
});
在地图上找到合适位置,点击地图,在控制台中查看打印的信息。
打印对象中的mapPoint
就是地图点位信息,包含x、y、z
坐标信息,经纬度信息。通过该方法获取4个点的点位信息,来构建geometry对象。
geometry = {
type: 'polygon',
rings: [
[
[103.36068616145837, 31.10348639821816, 1500], // 点1坐标信息
[103.32161294109986, 31.037595800850536, 1500], // 点2坐标信息
[103.2378326319481, 31.067370203812192, 1500], // 点3坐标信息
[103.30103440333917, 31.136727926006987, 1500], // 点4坐标信息
[103.36068616145837, 31.10348639821816, 1500], // 点1坐标信息
],
],
spatialReference: view.spatialReference, // 设置为和地图场景一直的坐标系
};
需要注意的是,最后一个点应该是和第一个点重合的,所以坐标应该是一样的,这样才能构成一个完整的面。
现在我们将所有点的高度值都设为了1500,这个值怎么获取来呢?我们可以用刚才打印地图点信息同样的方法,点击4个点所围成区域中的最低点,这样可以得到区域中海拔最低的数值,在后面做水面上涨的动画时,只要在这个高度值的基础上加上一个积水深度值就行了。如下图所示:
②创建图形符号symbol
设置好几何形状后,我们就要来设置材质,也就是图形符号symbol。要想让平面看起来像水面,有那种水波的效果,全靠这个symbols的设置,好在现在ArcGIS JS API中已经有一个现成的WaterSymbol3DLayer
用来将多边形几何图形渲染为逼真的动画水面。
const symbol = {
type: 'polygon-3d', // symbold类型
symbolLayers: [
{
type: 'water',
waveDirection: 180, // 水波传播方向
color: '#5975a3', //
waveStrength: 'moderate', // 水波的形状和强度
waterbodySize: 'large', // 水体的大小
},
],
};
设置完symbol效果如下图所示:
③生成graphic并添加进图层中
有了geometry和symbol我们就可以生成graphic了,attributes可以暂时不设置。
const graphic = new Graphic({ // 创建新的graphic
geometry: geometry, // 几何图形
symbol: symbol, // 符号样式
});
layer.add(graphic); // 添加新的graphic
将图形添加进图层后,我们可能在地图上看不到,这有可能是因为高度设置低了,在地面以下,被挡住了,这时候我们适当调整下高度就可以看到了。
4.创建动画
创建动画时,我们可以选择使用定时器window.setInterval()
或者专门用于动画的window.requestAnimationFrame()
,推荐使用后者,点击查看文档说明。两者的区别大家可以在网上搜一下。
通过改变水面的高度值,来实现水面上涨的效果,那每次更新时,我们应该把高度设置为多少呢?其实我们可以在动画开始时,记录下当时的时间,然后在每次更新时算出此时与动画开始时的时间差值占总动画时长的百分比,再乘以积水总深度就得到了此次更新时的积水深度值,最后再加上一个最低点的海拔高度就得出了此时水面距离海平面的绝对高度值,然后更新graphic,齐活~
function animation() {
const now = new Date().getTime(); // 当前时间戳
const delta = now - animationStartTime; // 时间戳差值
const currentDepth = (depth / duration) * (delta / 1000); // 新水面高度
if (geometry) {
updateWaterSurface(
geometry, // 几何形状
elevation, // 最低点的海拔高度
newDepth > depth ? depth : currentDepth // 积水深度
);
}
if (currentDepth < depth) { // 积水深度不够时,继续执行
animationId = window.requestAnimationFrame(animation);
}
}
总结
完成整个水淹效果实现后,其实可以发现最重要的地方就三点:一是正确设置图层高程模式和图形的高度值,必须要设置为绝对高度模式absolute-height
;二是symbol的正确设置,利用WaterSymbol3DLayer
让平面看起来和真实水面一样;三是使用window.requestAnimationFrame()
API来实现水面的上涨动画。
示例源码
为了方便在不同的地形区域展示水面上涨效果,写了一个示例页面,可以方便的绘制水面区域,以及很方便的设置海拔高度、积水深度、动画时长等参数。
以下视频为完整功能操作演示视频。
水淹模拟功能操作演示
以下为示例中的水面上涨动画效果:
如需查看示例效果可点击下载ArcGIS JS API实现水淹模拟功能(源码+详细注释).zip。附件中包含详细的注释,方便大家的理解。
如果该文章对您有所帮助,请您一定不要吝啬您的鼓励。点赞、评论、分享、收藏、打赏都是您对我的鼓励和支持。
如果您有GitHub
账号,还可以关注我~
最后,感谢大家的阅读,如有错误,还请各位批评指正。