产品让在Cesium中绘制迁徙线,把我00后的小同事给蚌埠住了~

大家好,我是日拱一卒的攻城师不浪,致力于技术与艺术的融合。这是2024年输出的第42/100篇文章。

前言

在3D开发过程中,迁徙线这个场景起到了举足轻重的作用。

迁徙线通过直观的动态展示方式,将复杂的空间流动数据形象化,有助于分析和理解空间关系与变化趋势,为决策提供参考。

例如:人口迁徙动向自然灾害响应人口疏散指挥等应急指挥,经济贸易路线演示,病情扩散路线等诸多场景,迁徙线都发挥了重要作用;

今天我们在Cesium中实现迁徙线的功能;

封装Class类

在Cesium中,我们一般使用JS的Class类进行场景大类的封装,这样做有几个好处:

  • 在前端的任何框架中都可使用,与vue或者react无关;

  • 使用方便,通过灵活的参数控制场景的定制化需求;

  • 代码独立,易于后期维护迭代;

构造函数

封装类,一般都会有一个固定参数viewer需要传入,viewer就是Cesium实例化后的场景实例;

扫描二维码关注公众号,回复: 17526049 查看本文章

剩下的场景参数,我们可以用一个对象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 是较大横纵间距。

  • diffLondiffLat 表示起始点和终点的经纬度差异,用来判断抛物线的方向。

  • 返回的 result 数组包含了 Cesium Cartesian3 格式的坐标点,用于构建飞行线的路径。

清除方法

可通过遍历 viewer.entities 中的实体,删除 idodLine_ 开头的实体,从而移除所有迁徙飞线和轨迹线。

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(备注来意),也欢迎数字孪生可视化领域的交流合作。

猜你喜欢

转载自blog.csdn.net/weixin_40580834/article/details/143477567