在Cesium中加载OD线

前言

在地理信息系统(GIS)和3D地图应用中,"OD线"(Origin-Destination Line)是一种重要的视觉工具,用于表示两点之间的各种关系,例如航班线路、人口迁徙、交通流量和经济往来。

随着大数据和可视化技术的发展,OD线的应用越来越广泛,对可视化手段也提出了新的要求,包括二维和三维、静态和动态、以及不同数据量的处理。

实现

1. 北京公交动态OD线

为了实现动态OD线的渲染,我们需要自定义MaterialProperty,因为Cesium提供的内置MaterialProperty类型无法满足需求。

以下是北京公交动态OD线的自定义MaterialProperty实现:

javascript 复制代码
// 导入线材质图片
import lineImage from './images/shortFlowColor.png';

/**
 * SpriteLineMaterialProperty构造函数
 * @param {Object} options - 构造对象
 */
export default class SpriteLineMaterialProperty {
    constructor(options) {
        options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT);

        // 定义变化事件
        this._definitionChanged = new Cesium.Event();
        // 初始化颜色属性
        this._color = undefined;
        // 颜色属性订阅
        this._colorSubscription = undefined;
        // 设置颜色
        this.color = options.color;
        // 设置持续时间,默认1000毫秒
        this.duration = Cesium.defaultValue(options.duration, 1000);
        // 设置当前时间
        this._time = (new Date()).getTime();
    }
}

// 定义属性
Object.defineProperties(SpriteLineMaterialProperty.prototype, {
    // 是否常量
    isConstant: {
        get: function () {
            return false;
        }
    },
    // 定义变化事件
    definitionChanged: {
        get: function () {
            return this._definitionChanged;
        }
    },
    // 颜色属性
    color: Cesium.createPropertyDescriptor('color')
});

/**
 * 获取材质类型
 * @param {Number} time - 时间
 * @returns {String} 材质类型
 */
SpriteLineMaterialProperty.prototype.getType = function (time) {
    return 'SpriteLine';
};

/**
 * 获取材质值
 * @param {Number} time - 时间
 * @param {Object} result - 结果对象
 * @returns {Object} 材质值
 */
SpriteLineMaterialProperty.prototype.getValue = function (time, result) {
    if (!Cesium.defined(result)) {
        result = {};
    }
    // 获取颜色值
    result.color = Cesium.Property.getValueOrClonedDefault(this._color, time, Cesium.Color.WHITE, result.color);
    // 设置线材质图片
    result.image = Cesium.Material.SpriteLineImage;
    // 计算时间比例
    result.time = (((new Date()).getTime() - this._time) % this.duration) / this.duration;
    return result;
};

/**
 * 判断是否相等
 * @param {SpriteLineMaterialProperty} other - 另一个对象
 * @returns {Boolean} 是否相等
 */
SpriteLineMaterialProperty.prototype.equals = function (other) {
    return this === other ||
        (other instanceof SpriteLineMaterialProperty && Cesium.Property.equals(this._color, other._color))
};

// 注册材质属性和材质类型
Cesium.SpriteLineMaterialProperty = SpriteLineMaterialProperty;
Cesium.Material.SpriteLineType = 'SpriteLine';
Cesium.Material.SpriteLineImage = lineImage;
Cesium.Material.SpriteLineSource = `czm_material czm_getMaterial(czm_materialInput materialInput)
{
    czm_material material = czm_getDefaultMaterial(materialInput);
    vec2 st = materialInput.st;
    vec4 colorImage = texture(image, vec2(fract(st.s - time), st.t));
    material.alpha = colorImage.a * color.a;
    material.diffuse = color.rgb * 1.5;
    return material;
}
`;
Cesium.Material._materialCache.addMaterial(Cesium.Material.SpriteLineType, {
    fabric: {
        type: Cesium.Material.SpriteLineType,
        uniforms: {
            color: new Cesium.Color(1.0, 0.0, 0.0, 0.5),
            image: Cesium.Material.SpriteLineImage,
            time: 0,
        },
        source: Cesium.Material.SpriteLineSource
    },
    translucent: function (material) {
        return true;
    }
});

2. 三维OD线

三维OD线用于在三维场景下展示OD数据,例如网络攻击、全球资本流动和全球航班轨迹等。以下是实现三维OD线的步骤:

根据起终点获取弧线

javascript 复制代码
/**
 * 根据起终点获取弧线
 * @param {Object} startPoint - 起点坐标
 * @param {Object} endPoint - 终点坐标
 * @param {Number} ratio - 弧度比率,默认0.5
 * @returns {Array} 弧线坐标数组
 */
function getParabolaLine(startPoint, endPoint, ratio = 0.5) {
    // 方程 y=-(4h/L^2)*x^2+h h:顶点高度 L:横纵间距较大者
    const h = Cesium.Cartesian3.distance(
        Cesium.Cartesian3.fromDegrees(startPoint.lng, startPoint.lat),
        Cesium.Cartesian3.fromDegrees(endPoint.lng, endPoint.lat),
    ) * ratio;
    const L = Math.abs(startPoint.lng - endPoint.lng) > Math.abs(startPoint.lat - endPoint.lat)
        ? Math.abs(startPoint.lng - endPoint.lng)
        : Math.abs(startPoint.lat - endPoint.lat);

    const num = 99;
    const result = [];
    let dlt = L / num;
    if (Math.abs(startPoint.lng - endPoint.lng) > Math.abs(startPoint.lat - endPoint.lat)) {
        //以 lng 为基准
        const delLat = (endPoint.lat - startPoint.lat) / num;
        if (startPoint.lng - endPoint.lng > 0) dlt = -dlt;
        for (let i = 0; i < num; i++) {
            const tempH = h - (Math.pow(-0.5 * L + Math.abs(dlt) * i, 2) * 4 * h) / Math.pow(L, 2);
            const lng = startPoint.lng + dlt * i;
            const lat = startPoint.lat + delLat * i;
            result.push({ lng, lat, height: tempH });
        }
    } else {
        //以 lat 为基准
        let delLng = (endPoint.lng - startPoint.lng) / num;
        if (startPoint.lat - endPoint.lat > 0) dlt = -dlt;
        for (let i = 0; i < num; i++) {
            const tempH = h - (Math.pow(-0.5 * L + Math.abs(dlt) * i, 2) * 4 * h) / Math.pow(L, 2);
            const lng = startPoint.lng + delLng * i;
            const lat = startPoint.lat + dlt * i;
            result.push({ lng, lat, height: tempH });
        }
    }
    // 落地
    result.push({lng: endPoint.lng, lat: endPoint.lat, height: endPoint.height || 0});

    return result;
}

自定义三维OD线材质

javascript 复制代码
/**
 * ODLineMaterialProperty构造函数
 * @param {Object} options - 构造对象
 */
export default class ODLineMaterialProperty {
    constructor(options) {
        options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT);

        this._definitionChanged = new Cesium.Event();
        this._color = undefined;
        this._speed = undefined;
        this._percent = undefined;
        this._gradient = undefined;
        this._gradientColor = undefined;
        this.color = options.color;
        this.speed = options.speed;
        this.percent = options.percent;
        this.gradient = options.gradient;
        this.gradientColor = options.gradientColor;
    }
}
Object.defineProperties(ODLineMaterialProperty.prototype, {
    // 是否常量
    isConstant: {
        get: function () {
            return false;
        }
    },
    // 定义变化事件
    definitionChanged: {
        get: function () {
            return this._definitionChanged;
        }
    },
    // 颜色属性
    color: Cesium.createPropertyDescriptor('color'),
    speed: Cesium.createPropertyDescriptor('speed'),
    percent: Cesium.createPropertyDescriptor('percent'),
    gradient: Cesium.createPropertyDescriptor('gradient'),
    gradientColor: Cesium.createPropertyDescriptor('gradientColor')
});

/**
 * 获取材质类型
 * @param {Number} time - 时间
 * @returns {String} 材质类型
 */
ODLineMaterialProperty.prototype.getType = function (time) {
    return 'ODLine';
};

/**
 * 获取材质值
 * @param {Number} time - 时间
 * @param {Object} result - 结果对象
 * @returns {Object} 材质值
 */
ODLineMaterialProperty.prototype.getValue = function (time, result) {
    if (!Cesium.defined(result)) {
        result = {};
    }
    result.color = Cesium.Property.getValueOrDefault(this._color, time, Cesium.Color.RED, result.color);
    result.speed = Cesium.Property.getValueOrDefault(this._speed, time, 5.0, result.speed);

总结

本文介绍了在 Cesium 中实现 OD 线(Origin-Destination Line)的两种方法:动态OD线和三维OD线。

通过自定义 MaterialProperty,我们能够创建出满足特定需求的OD线效果,这些效果不仅增强了数据的可视化表现力,也提升了用户体验。

-- 欢迎点赞、关注、转发、收藏【我码玄黄】,各大平台同名。

相关推荐
汝即来归1 分钟前
如何实现对象的克隆?如何实现单例模式?
开发语言·javascript·jvm·c++
暴躁的白菜6 分钟前
Fiddler简单使用
前端·测试工具·fiddler
我爱学习_zwj9 分钟前
Web APIs - 第5章笔记
开发语言·前端·javascript
m0_7482451730 分钟前
vue登录成功之后的token处理
前端·javascript·vue.js
PP东40 分钟前
ES6学习模板字符串、解构赋值(三)
javascript·学习·es6
S-X-S1 小时前
HTML - 蛆(畅享版)
前端·html
beyondjxx1 小时前
纯css 实现呼吸灯效果
前端·css·css3
等一场春雨1 小时前
CSS3 实现火焰-小火苗效果
前端·css·css3
m0_748254091 小时前
vue3前端组件库的搭建与发布(一)
前端
Irises`2 小时前
flex自适应页面
开发语言·前端·javascript