Cesium贴地车俯仰角跟随地形且动态贴地线

1 实现要求

  • 1 地面车辆按照规定的起点和终点运行
  • 2 地面车辆必须贴地运动
  • 3 地面车辆的必须有俯仰角的变化
  • 4 循环播放,且车辆经过路径动态高亮,下一次循环清除高亮

2 实现效果

在这里插入图片描述

3 实现代码

将如下代码替换到cesium的一个例子中即可:https://sandcastle.cesium.com/index.html?src=Interpolation.html

	var viewer = new Cesium.Viewer("cesiumContainer", {
    infoBox: false, //Disable InfoBox widget
    selectionIndicator: false, //Disable selection indicator
    shouldAnimate: true, // Enable animations
    terrainProvider: Cesium.createWorldTerrain(),
});

//Enable lighting based on the sun position
viewer.scene.globe.enableLighting = true;

//Enable depth testing so things behind the terrain disappear.
viewer.scene.globe.depthTestAgainstTerrain = true;

//Set the random number seed for consistent results.
Cesium.Math.setRandomNumberSeed(3);

let times = [
    Cesium.JulianDate.fromIso8601("2018-07-19T15:18:00Z"),
    Cesium.JulianDate.fromIso8601("2018-07-19T15:24:00Z")
];
let stTime = times[0];
let endTime = times[1];
var start = stTime.clone();
var stop = endTime.clone();

//Make sure viewer is at the desired time.
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; //Loop at the end
viewer.clock.multiplier = 30;

//Set timeline to simulation bounds
viewer.timeline.zoomTo(start, stop);

//Generate a random circular pattern with varying heights.
function computeCirclularFlight(lon, lat, radius) {
    var property = new Cesium.SampledPositionProperty();
    for (var i = 0; i <= 360; i += 45) {
        var radians = Cesium.Math.toRadians(i);
        var time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
        var position = Cesium.Cartesian3.fromDegrees(
            lon + radius * 1.5 * Math.cos(radians),
            lat + radius * Math.sin(radians),
            Cesium.Math.nextRandomNumber() * 500 + 1750
        );
        property.addSample(time, position);
        console.log(time.toString(), " -> ", position.toString());

        //Also create a point for each sample we generate.
        viewer.entities.add({
            position: position,
            point: {
                pixelSize: 8,
                color: Cesium.Color.TRANSPARENT,
                outlineColor: Cesium.Color.YELLOW,
                outlineWidth: 3,
            },
        });
    }
    return property;
}

let positions = [
    // new Cartesian3(1216348.1632364073, -4736348.958775471, 4081284.5528982095),
    // new Cartesian3(1216369.1229444197, -4736377.467107148, 4081240.888485707)
    new Cesium.Cartesian3(
        -2358138.847340281,
        -3744072.459541374,
        4581158.5714175375
    ),
    new Cesium.Cartesian3(
        -2357231.4925370603,
        -3745103.7886602185,
        4580702.9757762635
    ),
];
let stPos = positions[0];
let endPos = positions[1];

// sampled postion's time resolution
let timeOfResolution = 6;

// using sampled property to get sampled data
let oriSamples = new Cesium.SampledProperty(Cesium.Cartesian3);
oriSamples.addSamples(times, positions);

// get sampled data, ervery "distanceOfResolution" we take a sample
let geodesic = new Cesium.EllipsoidGeodesic(
    Cesium.Cartographic.fromCartesian(stPos),
    Cesium.Cartographic.fromCartesian(endPos)
);
let lenInMeters = Math.ceil(geodesic.surfaceDistance); // avoid overflow when take samples
let samplesNum = Math.floor(
    Cesium.JulianDate.secondsDifference(endTime, stTime) / timeOfResolution
);
//let secondsInterval = Math.floor(Cesium.JulianDate.secondsDifference(endTime, stTime) / samplesNum);
console.log(
    "len: ",
    lenInMeters,
    "samplesNum",
    samplesNum,
    "secondsInterval",
    timeOfResolution
);

// get sampled data, ervery "timeOfResolution" passed, we take a sample
let sampledPositions = [];
let sampledTimes = [];
for (let i = 0; i < samplesNum + 1; i++) {
    let sampleTime = Cesium.JulianDate.addSeconds(
        stTime,
        i * timeOfResolution,
        new Cesium.JulianDate()
    );
    let tmpPos = oriSamples.getValue(sampleTime);
    console.log(sampleTime.toString(), " -> || -> ", tmpPos.toString());
    sampledPositions.push(Cesium.Cartographic.fromCartesian(tmpPos));
    sampledTimes.push(sampleTime);
}

let promise = Cesium.sampleTerrainMostDetailed(
    viewer.terrainProvider,
    sampledPositions
).then(() => {
    console.log(
        "start adding!",
        "time and pos size: ",
        sampledTimes.length,
        sampledPositions.length
    );

    let carPositionProperty = new Cesium.SampledPositionProperty();

    // add positions which are clamped to ground to the carPositionProperty
    for (let i = 0; i < samplesNum + 1; i++) {
        carPositionProperty.addSample(
            sampledTimes[i],
            // new Cesium.Cartesian3.fromDegrees( // this way of changing pos is not right, all should be under WGS84
            // sampledPositions[i].longitude,
            // sampledPositions[i].latitude,
            // sampledPositions[i].height));
            Cesium.Ellipsoid.WGS84.cartographicToCartesian(sampledPositions[i])
        );
        // console.log(sampledTimes[i], " ------->>> ", sampledPositions[i]);
    }

    // after the clamped to ground data computed, dynamically show the path
    let isConstant = false;
    let curSegmentNo = 0; // the polyLine are divided into samplesNum's segements
    let lastSegementNo = -1;
    let p2 = Cesium.Ellipsoid.WGS84.cartographicToCartesian(sampledPositions[1]);
    let curPolyline = [stPos]; // represent the polyline
    console.log("init 2 points are: ", stPos.toString(), " ", p2.toString());
    //let timeNow = Cesium.JulianDate.now().clone();

    console.log("starting add entity");
    viewer.entities.add({
        polyline: {
            // This callback updates positions each frame.
            // Ellipsoid.WGS84.cartographicArrayToCartesianArray(sampledPositions),
            positions: new Cesium.CallbackProperty(function (time, result) {
                //console.log("len: ", lenInMeters, "samplesNum", samplesNum, "timeOfResolution", timeOfResolution);

                //let st
                curSegmentNo = Math.floor(
                    Cesium.JulianDate.secondsDifference(time, stTime) / timeOfResolution
                );
                console.log(curSegmentNo);
                if (curSegmentNo !== lastSegementNo) {
                    //console.log("curSegmentNo is: ", curSegmentNo.toString(), "\ncurTime: ", time.toString(), "\nstTime: ", stTime.toString());
                    // tmmP => curPolyine[lastSegementNo+1 : CurSegmentNo]
                    let tmpP = Cesium.Ellipsoid.WGS84.cartographicToCartesian(
                        sampledPositions[curSegmentNo]
                    );
                    //console.log("adding new points: ", tmpP.toString(), "\nsize is:", curPolyline.length);
                    curPolyline.push(tmpP);
                    lastSegementNo = curSegmentNo;
                }
                // if reach the end of sampled positions, clear the polyline's positions
                if (curSegmentNo === samplesNum - 1) {
                    curSegmentNo = 0;
                    curPolyline = [];
                    console.log("cleared!");
                }

                return curPolyline;
            }, isConstant),
            //clampToGround: true,
            width: 5,
            material: Cesium.Color.RED,
            availability: new Cesium.TimeIntervalCollection([
                new Cesium.TimeInterval({
                    start: stTime,
                    stop: endTime,
                }),
            ]),
        },
    });
    console.log("end adding polyline");

    //Compute the entity position property.
    //var position = computeCirclularFlight(-112.110693, 36.0994841, 0.03);
    var position = carPositionProperty;
    //Actually create the entity
    var entity = viewer.entities.add({
        //Set the entity availability to the same interval as the simulation time.
        availability: new Cesium.TimeIntervalCollection([
            new Cesium.TimeInterval({
                start: start,
                stop: stop,
            }),
        ]),

        //Use our computed positions
        position: position,

        //Automatically compute orientation based on position movement.
        orientation: new Cesium.VelocityOrientationProperty(position),

        //Load the Cesium plane model to represent the entity
        model: {
            uri: "../SampleData/models/CesiumAir/Cesium_Air.glb",
            minimumPixelSize: 64,
        },

        //Show the path as a pink line sampled in 1 second increments.
        path: {
            resolution: 1,
            material: new Cesium.PolylineGlowMaterialProperty({
                glowPower: 0.1,
                color: Cesium.Color.YELLOW,
            }),
            width: 1,
        },
    });

    //Add button to view the path from the top down
    Sandcastle.addDefaultToolbarButton("View Top Down", function () {
        viewer.trackedEntity = undefined;
        viewer.zoomTo(
            viewer.entities,
            new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90))
        );
    });

    //Add button to view the path from the side
    Sandcastle.addToolbarButton("View Side", function () {
        viewer.trackedEntity = undefined;
        viewer.zoomTo(
            viewer.entities,
            new Cesium.HeadingPitchRange(
                Cesium.Math.toRadians(-90),
                Cesium.Math.toRadians(-15),
                7500
            )
        );
    });

    //Add button to track the entity as it moves
    Sandcastle.addToolbarButton("View Aircraft", function () {
        // viewer.trackedEntity = entity;
        viewer.zoomTo(
            viewer.entities,
            new Cesium.HeadingPitchRange(
                Cesium.Math.toRadians(15),
                Cesium.Math.toRadians(-45),
                2500
            )
        );
    });

    //Add a combo box for selecting each interpolation mode.
    Sandcastle.addToolbarMenu(
        [
            {
                text: "Interpolation: Linear Approximation",
                onselect: function () {
                    entity.position.setInterpolationOptions({
                        interpolationDegree: 1,
                        interpolationAlgorithm: Cesium.LinearApproximation,
                    });
                },
            },
            {
                text: "Interpolation: Lagrange Polynomial Approximation",
                onselect: function () {
                    entity.position.setInterpolationOptions({
                        interpolationDegree: 5,
                        interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
                    });
                },
            },
            {
                text: "Interpolation: Hermite Polynomial Approximation",
                onselect: function () {
                    entity.position.setInterpolationOptions({
                        interpolationDegree: 2,
                        interpolationAlgorithm: Cesium.HermitePolynomialApproximation,
                    });
                },
            },
        ],
        "interpolationMenu"
    );
});

4 参考

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要让面(plane)地且紧地形,您可以使用Cesium中的Primitive API和GroundPrimitive API。这两个API都可以用于在地球表面上绘制图形,但它们的实现方式略有不同。 以下是使用Primitive API实现面地的步骤: 1. 创建一个Primitive对象,并将其添加到场景中: ```javascript var primitive = new Cesium.Primitive({ geometryInstances: new Cesium.GeometryInstance({ geometry: new Cesium.PlaneGeometry({ vertexFormat: Cesium.VertexFormat.POSITION_ONLY }), modelMatrix: Cesium.Matrix4.IDENTITY }), appearance: new Cesium.MaterialAppearance({ material: Cesium.Material.fromType('Color', { color: Cesium.Color.RED }) }) }); viewer.scene.primitives.add(primitive); ``` 2. 使用Cesium.sampleTerrainMostDetailed函数获取地形高度,并将面的高度设置为地形高度: ```javascript var positions = Cesium.Cartesian3.fromDegreesArray([lon1, lat1, lon2, lat2, lon3, lat3, lon4, lat4]); Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, positions).then(function() { var minHeight = Math.min(positions[0].height, positions[2].height, positions[4].height, positions[6].height); var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(positions[0], Cesium.Ellipsoid.WGS84); Cesium.Matrix4.multiplyByTranslation(modelMatrix, new Cesium.Cartesian3(0.0, 0.0, minHeight), modelMatrix); primitive.geometryInstances.geometry = new Cesium.PlaneGeometry({ vertexFormat: Cesium.VertexFormat.POSITION_ONLY, plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_Z, minHeight) }); primitive.geometryInstances.modelMatrix = modelMatrix; }); ``` 这个例子中,我们使用了一个PlaneGeometry来创建一个平面,并使用sampleTerrainMostDetailed函数获取了顶点的高度,然后将这个高度作为面的高度。最后,我们将这个面的几何实例的模型矩阵设置为一个东北天坐标系到固定坐标系的转换矩阵,并加上地形高度的偏移量。 以下是使用GroundPrimitive API实现面地的步骤: 1. 创建一个GroundPrimitive对象,并将其添加到场景中: ```javascript var primitive = new Cesium.GroundPrimitive({ geometryInstances: new Cesium.GeometryInstance({ geometry: new Cesium.PlaneGeometry({ vertexFormat: Cesium.VertexFormat.POSITION_ONLY }) }), appearance: new Cesium.MaterialAppearance({ material: Cesium.Material.fromType('Color', { color: Cesium.Color.RED }) }) }); viewer.scene.primitives.add(primitive); ``` 2. 设置面的顶点坐标和高度,然后调用primitive.update方法更新几何实例: ```javascript var positions = Cesium.Cartesian3.fromDegreesArray([lon1, lat1, lon2, lat2, lon3, lat3, lon4, lat4]); Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, positions).then(function() { var minHeight = Math.min(positions[0].height, positions[2].height, positions[4].height, positions[6].height); var vertices = [ positions[0].x, positions[0].y, minHeight, positions[1].x, positions[1].y, minHeight, positions[2].x, positions[2].y, minHeight, positions[3].x, positions[3].y, minHeight, positions[4].x, positions[4].y, minHeight, positions[5].x, positions[5].y, minHeight, positions[6].x, positions[6].y, minHeight, positions[7].x, positions[7].y, minHeight ]; primitive.geometryInstances.geometry.attributes.position.values = new Float32Array(vertices); primitive.update(); }); ``` 这个例子中,我们首先使用sampleTerrainMostDetailed函数获取了顶点的高度,并将这个高度作为面的高度。然后,我们将面的顶点坐标设置为经纬度坐标和高度,并使用primitive.update方法更新几何实例。 无论您使用哪种方法,都可以实现面(plane)地且紧地形的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值