Cesium抛物线方程

公司项目上有两个功能模块,进口贸易和出口贸易。属于全球性质的,拿到两个坐标之后,需要有一根抛物连线连接这两个点。有时候一根线需要跨越半个地球,就导致了有些线会出现穿地的现象。

因为使用的是path,有线条动画,没有使用polyline。如果是用polyline的话,不会有穿地的情况发生,而是会近乎贴着地面。

原先项目上的处理方法是:已知两个坐标点,已知设定的动画时间。使用SampledPositionProperty方法来求中间点。然后三个点连成一条线。

// 86400 总时间,43200就是中间时刻
var startTime = viewer.clock.startTime;
var midTime = Cesium.JulianDate.addSeconds(startTime, 43200, new Cesium.JulianDate());
var stopTime = Cesium.JulianDate.addSeconds(startTime, 86400, new Cesium.JulianDate());

// 从地点坐标到终点坐标,在中间时刻就是中点
var property = new Cesium.SampledPositionProperty();
var startPosition = Cesium.Cartesian3.fromDegrees(起点坐标, 起点坐标, 0);
property.addSample(startTime, startPosition);
var stopPosition = Cesium.Cartesian3.fromDegrees(终点坐标, 终点坐标, 0);
property.addSample(stopTime, stopPosition);

// 求中点, 并给予中点一个高度
var midPoint = Cesium.Cartographic.fromCartesian(property.getValue(midTime));
midPoint.height = Cesium.Math.nextRandomNumber() * 100000 + 900000;
var midPosition = viewer.scene.globe.ellipsoid.cartographicToCartesian(midPoint, new Cesium.Cartesian3());

// 三个坐标点
const resultproperty = new Cesium.SampledPositionProperty();
resultproperty .addSample(startTime, startPosition);
resultproperty .addSample(midTime, midPosition);
resultproperty .addSample(stopTime, stopPosition);

// 添加到entity中
const entity = viewer.entities.add({
    position: resultproperty,
    .....  其他配置   ......
})
// 设置插值方式
entity.position.setInterpolationOptions({
    interpolationDegree : 5,
    interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
});

指定两个点的行进动画,当动画时间到一半的时候,所取得的位置就是两个线段的中点。再由三点绘制一条抛物线。但是这条抛物线会有穿地的问题。

 原本最优的结果应该是都从一个点发散出去,然而有些线却没有正确的出发,而是看起来像从地下出发的一样。这种方法只适用于一定区域,当两点的距离可能横跨半个地球的时候,就不太行了。

于是考虑使用第二种方法,自己在网上找了一个抛物线的方程,基于这个方程自己绘制一条抛物线。

绘制方法接收    起始坐标点,终点坐标点,高度,坐标点数量  四个参数。

1.先判断是经度间距大,还是纬度间距大

南北半球直接相减求出绝对值就是纬度的跨度了。东西半球的经度就麻烦一点,也是相减求绝对值,如果这个绝对值大于了180,说明它从另一个方向更近点,跨度就按360-经度绝对值来算。如果不转换一下,绘制出来的线会多绕路。

2.按指点的坐标点数量,求出经度变化量和纬度变化量

于是就有了下面的代码:

//抛物线方程  { pt1: {lon: 114.302312702, lat: 30.598026044}, pt2: {lon: 114.302312702, lat: 30.598026044}, height: height, num: 100 }
function parabolaEquation(options) {
    //方程 y=-(4h/L^2)*x^2+h h:顶点高度 L:横纵间距较大者
    // 设置最低高度
    const h = options.height && options.height > 5000 ? options.height : 5000;
    let latLength = Math.abs(options.pt1.lat - options.pt2.lat);
    let lonLength = Math.abs(options.pt1.lon - options.pt2.lon);
    if (lonLength > 180) {
        lonLength = 360 - lonLength;
    }
    const L = lonLength > latLength ? lonLength : latLength;
    const result = [];
    // 设置坐标点最少50个
    const num = options.num && options.num > 50 ? options.num : 50;
    let dlt = L / num;
    if (lonLength > latLength) {//以lon为基准
        const delLat = (options.pt2.lat - options.pt1.lat) / num;
        // 由西向东递增为正数。由东向西递增为负数
        if (Math.abs(options.pt1.lon - options.pt2.lon) > 180 || options.pt1.lon - options.pt2.lon > 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 lon = options.pt1.lon + dlt * i;
            const lat = options.pt1.lat + delLat * i;
            result.push([lon, lat, tempH]);
        }
    } else {//以lat为基准
        let delLon = (options.pt2.lon - options.pt1.lon) / num;
        if (options.pt1.lat - options.pt2.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 lon = options.pt1.lon + delLon * i;
            const lat = options.pt1.lat + dlt * i;
            result.push([lon, lat, tempH]);
        }
    }
    // 落地
    result.push([options.pt2.lon,options.pt2.lat,options.pt2.height || 0])
    return result;
}

如果坐标点数量传了一个50进去,实际返回的数组有51个。这个数组每一个子项都是数组,如果绘制成polyline还需要遍历展开。

最后再生成动态值

            // 动态路径
            const property = new Cesium.SampledPositionProperty();
            const allTime = 86400;
            const stepTime = 86400 / (坐标点数量+1);
            const startTime = viewer.clock.startTime;
            const stopTime = Cesium.JulianDate.addSeconds(startTime, allTime, new Cesium.JulianDate());
            for (let index = 0; index < positionsArr.length; index++) {
                const point = positionsArr[index];
                if (index === 0) {
                    property.addSample(startTime, Cesium.Cartesian3.fromDegrees(起点坐标, 起点坐标, 0));
                }else if (index === positionsArr.length - 1) {
                    property.addSample(stopTime, Cesium.Cartesian3.fromDegrees(终点坐标, 终点坐标, 0));
                }else {
                    property.addSample(Cesium.JulianDate.addSeconds(startTime, (index)*stepTime, new Cesium.JulianDate()),Cesium.Cartesian3.fromDegrees(point[0],point[1],point[2]))
                }
            }

替换掉原先的resultproperty就行了。

效果图如下

 不再有穿地的了,这是进口效果,起点终点交换一下就是散开来的出口效果了。

猜你喜欢

转载自blog.csdn.net/GhostPaints/article/details/125188187
今日推荐