在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线效果,这些效果不仅增强了数据的可视化表现力,也提升了用户体验。

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

相关推荐
约定Da于配置1 小时前
uniapp封装websocket
前端·javascript·vue.js·websocket·网络协议·学习·uni-app
山楂树の1 小时前
xr-frame 模型摆放与手势控制,支持缩放旋转
前端·xr·图形渲染
LBJ辉2 小时前
1. 小众但非常实用的 CSS 属性
前端·css
milk_yan2 小时前
Docker集成onlyoffice实现预览功能
前端·笔记·docker
村口蹲点的阿三4 小时前
Spark SQL 中对 Map 类型的操作函数
javascript·数据库·hive·sql·spark
m0_748255024 小时前
头歌答案--爬虫实战
java·前端·爬虫
noravinsc5 小时前
python md5加密
前端·javascript·python
ac-er88885 小时前
Yii框架优化Web应用程序性能
开发语言·前端·php
cafehaus5 小时前
抛弃node和vscode,如何用记事本开发出一个完整的vue前端项目
前端·vue.js·vscode
HoneyMoose6 小时前
可以自己部署的微博 Mastodon
前端