大家好,我是日拱一卒的攻城师不浪,致力于技术与艺术的融合。这是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(备注来意),也欢迎数字孪生可视化领域
的交流合作。