产品让在Cesium中绘制迁徙线,把我00后的小同事给蚌埠住了~
大家好,我是日拱一卒的攻城师不浪,致力于技术与艺术的融合。这是2024年输出的第42/100篇文章。
前言
在3D开发过程中,迁徙线
这个场景起到了举足轻重的作用。
迁徙线通过直观的动态展示方式,将复杂的空间流动数据
形象化,有助于分析和理解空间关系与变化趋势,为决策提供参考。
例如:人口迁徙动向
,自然灾害响应
,人口疏散指挥
等应急指挥,经济贸易路线
演示,病情扩散路线
等诸多场景,迁徙线都发挥了重要作用;
今天我们在Cesium中实现迁徙线的功能;
封装Class类
在Cesium中,我们一般使用JS的Class类
进行场景大类的封装,这样做有几个好处:
-
在前端的任何框架中都可使用,与
vue
或者react
无关; -
使用方便,通过灵活的参数控制场景的定制化需求;
-
代码独立,易于后期维护迭代;
构造函数
封装类,一般都会有一个固定参数viewer
需要传入,viewer就是Cesium实例化后的场景实例;
剩下的场景参数,我们可以用一个对象options进行承接,代码如下:
constructor(viewer, options = {}) {this.viewer = viewer;this.options = options;
}
飞线效果的初始化
先分析一下飞线的形状,我们都知道它就是从一个中心点向四周进行扩散,所以入参需要有一个中心点centerPos
以及需要扩散到相应位置的点,这里我们用一个数组otherPosArr
, 如下:
let centerPos = [120.3642, 36.0668];
let otherPosArr = [[120.3644, 36.1117],[119.9393, 36.7814],[120.4206, 36.185],[120.5732, 36.1893],
];
然后就是飞线的其它参数,例如线的宽度,线的数量等,我们都可以支持通过参数灵活绘制;
还有,就是一条迁徙线是由2条
线组成,一条固定的轨迹线,一条飞动的线,我们都可以选择entity
去绘制,其中飞动的线需要自定义材质;
// 创建飞线
viewer.entities.add({id: `odLine_${index}_${i}`,polyline: {width,positions: _siglePositions,material: new LineFlowMaterialProperty({color: Color.fromAlpha(Color.AQUA, 0.8),speed: 8 * Math.random(),percent: 0.1,gradient: 0.01,}),},
});// 创建轨迹线
viewer.entities.add({id: `odLine_${index}`,polyline: {width,positions: _siglePositions,material: Color.fromAlpha(Color.AQUA, 0.3),},
})
自定义材质
自定义材质的glsl:
Material.LineFlowMaterialSource = `uniform vec4 color;uniform float speed;uniform float percent;uniform float gradient;czm_material czm_getMaterial(czm_materialInput materialInput){czm_material material = czm_getDefaultMaterial(materialInput);vec2 st = materialInput.st;float t =fract(czm_frameNumber * speed / 1000.0);t *= (1.0 + percent);float alpha = smoothstep(t- percent, t, st.s) * step(-t, -st.s);alpha += gradient;material.diffuse = color.rgb;material.alpha = alpha;return material;}`
抛物线算法
定义一个函数,通过抛物线方程
计算从起点到终点的高度,生成一个抛物线形的坐标数组。
parabolaCalculate(startPosition, endPosition, height = 0, count = 50) {let result = [];height = Math.max(+height, 100);count = Math.max(+count, 50);let diffLon = Math.abs(startPosition[0] - endPosition[0]);let diffLat = Math.abs(startPosition[1] - endPosition[1]);let L = Math.max(diffLon, diffLat);let dlt = L / count;if (diffLon > diffLat) {let delLat = (endPosition[1] - startPosition[1]) / count;if (startPosition[0] - endPosition[0] > 0) {dlt = -dlt;}for (let i = 0; i < count; i++) {let h = height - (Math.pow(-0.5 * L + Math.abs(dlt) * i, 2) * 4 * height) / Math.pow(L, 2);let lon = startPosition[0] + dlt * i;let lat = startPosition[1] + delLat * i;let point = new Cartesian3.fromDegrees(lon, lat, h);result.push(point);}}return result;
}
抛物线方程为
其中 h
是高度,L
是较大横纵间距。
-
diffLon
和diffLat
表示起始点和终点的经纬度差异,用来判断抛物线的方向。 -
返回的
result
数组包含了 CesiumCartesian3
格式的坐标点,用于构建飞行线的路径。
清除方法
可通过遍历 viewer.entities
中的实体,删除 id
以 odLine_
开头的实体,从而移除所有迁徙飞线和轨迹线。
clear() {const entities = this.viewer.entities.values;// 遍历并删除带有 'custom-line' 标识符的实体for (let i = entities.length - 1; i >= 0; i--) {if (entities[i].id && entities[i].id.startsWith("odLine_")) {this.viewer.entities.remove(entities[i]);}}
}
最后
以上就是Cesium中绘制迁徙线的核心代码,如果想系统学习Cesium,可以了解下作者的Cesium系列教程《Cesium从入门到实战》
,将Cesium的知识点进行串联,让不了解Cesium的小伙伴拥有一个完整的学习路线,学完后直接上手做项目,+作者:brown_7778(备注来意)了解课程细节。
另外有需要进
可视化&Webgis交流群
可以加我:brown_7778(备注来意),也欢迎数字孪生可视化领域
的交流合作。