三维地图Cesium,加载一个模型,模型沿着给定的一组经纬度路线移动

目录

实现效果

实现思路

功能点

选择移动路线

加载模型和移动路线

重新运行

指定位置(经纬度点)开始移动

视角切换

到站提示

运行

停止

联动接口

完整代码

html

js逻辑

trainOperation.js

sourceData.js

gitee仓库项目代码

疑问解答


实现效果

三维地图Cesium,加载一个模型,模型沿着给定的一组经纬度

实现思路

  1. 车的运行使用czml文件的特性来加载小车模型和小车运行的固定路线,同时定义它们是怎么随时间的变化而变化的。
  2. 路径长度已知,运行速度已知,结合三维cesium的时钟,就可以知道火车移动完成这段路线需要的时间。
  3. 改变运行速度或改变当前移动的位置(经纬度点)发生变化,就重新计算时间,然后重新加载czml文件。

功能点

目前已经实现的功能:

1、加载指定可自定义火车模型

2、移动的路线可以自定义修改,也可以在地图上自己选择移动路线(取路线代码已有)

3、给定指定的移动速度

4、视角切换(第一视角、自由视角、跟随视角)

5、根据指定的经纬度点为起点开始移动

6、停止移动、开始移动、到站提示、从起点重新移动、默认相机的视角

7、根据实际业务,接口获取获取的速度、移动路线、车的状态(前进、静止、后退)来控制模型的状态

选择移动路线

1、方法:at3DMapGetPointTakingRoute()

2、初始化成功后,执行at3DMapGetPointTakingRoute函数

TO.at3DMapGetPointTakingRoute()

3、 打开页面,用鼠标点击需要的路线,页面会以红点的样式进行标注,每标注一个点,控制台会以数组的形式打印所有标注的点

4、 把打印数据赋值给routePath变化,完成了路线的数据采集

// 路线
export const routePath = [
    [116.44411704836098,39.95279202225133],[116.44807033296253,39.956431604742875],
    [116.45386442263022,39.952900124343756],[116.45546340934312,39.951275636111355],
    [116.45567209160625,39.9494096918938],[116.4556296095857,39.94616403625391],
]

加载模型和移动路线

加载模型和路线的方式有好几种:

    1)使用实体单独加载模型和移动路线

    2)使用czml格式描述它们两个的关系

我们这里采用第二种,之前打算用第一种,加载起来是很方便,但是需要计算模型与移动路线的关系等细节,就先不打算了。。。

具体如何使用czml文件描述模型和移动路线的关系,网上有很多,可以自行查阅。

let testModel = window.location.origin + '/Apps/SampleData/models/GroundVehicle/GroundVehicle.glb';
export function modelCzml(useRoutePath,startTime,endTime){
    let minx = startTime+'/'+endTime;
    return [
        {
            "id": "document",
            "version": "1.0"
        },
        {
            "id": "Vehicle",
            "availability": minx ,
            "label": {
                "fillColor": [
                    {
                        "rgba": [0, 0, 0, 255]
                    }
                ],
                "font": "bold 10pt Segoe UI Semibold",
                "horizontalOrigin": "CENTER",
                "outlineColor": {
                    "rgba": [0, 0, 0, 255]
                },
                "pixelOffset": {
                    "cartesian2": [40.0, 50.0]
                },
                "scale": 1.0,
                "show": [
                    {
                        "boolean": true
                    }
                ],
                "style": "FILL",
                "text": "测试火车移动",
                "verticalOrigin": "CENTER"
            },
            "model": {
                "gltf": testModel,
                "minimumPixelSize": 120,
                "maximumScale": 50
            },
            "orientation": {
                "velocityReference": "#position"
            },
            "viewFrom": {
                // "cartesian": [300, 20, 300]
                "cartesian": [500, 2000, 779]
            },
            "properties": {
                "fuel_remaining": {
                    "epoch": startTime,
                    "number": [0, 22.5, 1500, 21.2]
                }
            },
            "path": {
                "material": {
                    "solidColor": {
                        "color": {
                            "rgba": [255, 255, 0, 255]
                        }
                    }
                },
                "width": [
                    {
                        "number": 4.0
                    }
                ],
                "show": [
                    {
                        "boolean": true
                    }
                ]
            },
            "position": {
                "interpolationAlgorithm": "LAGRANGE",
                "interpolationDegree": 1,
                "wrap": false,
                "epoch": startTime,
                "cartographicDegrees": useRoutePath
                // useRoutePath:时间、经度、纬度、高度加载显示的格式为[0, 102.23404378554466, 27.825736605050523, 2500,10, 102.23691954070244, 27.82887625908256, 2500,]
            }
        }
    ]
}

重新运行

1、使用trainLoadRun()函数

2、该方法接收三个参数:

1)targetPoint:起点的经纬度,也就是路线起点

2)currentVelocity:移动速度

3)tractionMethod:火车移动状态(前进 后退 静止)

trainLoadRun(targetPoint,currentVelocity=this.currentVelocity,tractionMethod=this.tractionMethod){
        // 如果当前火车是后退,判断当前火车是否在运行路线上,如果在,则找到当前火车所在点,并反转火车路线数组
        let that = this;
        if (that.latAndLonPosition(targetPoint).exists) {
            let index = that.latAndLonPosition(targetPoint).index
            that.byLonAndLatUpdateTrainPosition(index, currentVelocity, tractionMethod)
        } else {
            console.error('当前经纬度不在轨迹运行路线上:',targetPoint)
        }
    }

指定位置(经纬度点)开始移动

也是用targetPoint方法,改变下targetPoint的位置

注意:给的经纬度点要是不在移动路线上是不做处理的

视角切换

1、第一视角

    1)相机一直在相对小车指定的位置

    2)该模式无法通过鼠标或者键盘操作地图视角

    3)路线方向变化,车的头部以及方向也随着变化

firstPerspectiveTow() {
        let that = this;
        try {
            let center = that.trainModel.position.getValue(
                that.viewer.clock.currentTime
            );
            let orientation = that.trainModel.orientation.getValue(
                that.viewer.clock.currentTime
            )
            let transform = that.Cesium.Matrix4.fromRotationTranslation(that.Cesium.Matrix3.fromQuaternion(orientation), center);
            // viewer.camera.lookAtTransform(transform, new Cesium.Cartesian3(-100, 0, 50))
            that.viewer.camera.lookAtTransform(transform, new that.Cesium.Cartesian3(-60, 0, 50))
        } catch (e) {
            console.log('err in firstPerspectiveTow function')
        }
    }

2、自由视角

    1)将viewer.trackedEntity的值为:undefined即可

viewer.trackedEntity = undefined;

3、跟随视角

viewer.trackedEntity = trainOpera.trainModel;

到站提示

根据当前已经移动的距离与总路线的距离作比较,总路线减去已经移动的路线,误差值在10米为到站,当然,这个误差值可以自己定义

arriveAtStation(clock,callBack){
        let that = this;
        let cDistance = that.walkedThrough();
        let diff = Math.abs(that.fullRoutePathDistance.total - cDistance);
        if (that.tractionMethod == '后退'){
            diff = that.fullRoutePathDistance.total - diff
        }
        if (diff < 9){
            // 可以在这里分发到站广播
            callBack && callBack();
            clock.shouldAnimate = false;
            that.arriveAatStation()
        }
    }

运行

停止

设置时钟的shouldAnimate为false即可

clock.shouldAnimate = false;

联动接口

或者的运行速度,运行状态(前进、静止、后退)等信息,通过接口的形式可以进行控制

接口数据更新的时间根据自己的事迹情况来定

如:获取接口数据的方法

// 通过接口实时获取火车运行的相关信息
    getTrainInfo() {
        let that = this;
        let res = {currentlonLat:'112.938333,40.332444',tractionMethod:'前进',speed:23}; //火车当前前进方式,3种状态:前进 后退 静止
        if (!res || res === '{}') return false;
        let currentSpeed;
        if (res.speed && res.speed.includes('km/h')){
            currentSpeed = res.speed.split('km/h')[0]  //火车运行速度
        }

        // 获取当前火车所在经纬度
        let targetPoint;

        if (res.currentlonLat){
            let point = res.currentlonLat.split(',');
            targetPoint = {lon: point[0],lat: point[1]}
        } else {
            console.log('经纬度坐标丢失');
            return false;
        }

        if (res.tractionMethod == '静止' || currentSpeed == '0'){
            that.tractionMethod = res.tractionMethod;
            that.viewer.clock.shouldAnimate = false;
            return false;
        }

        // 如果页面火车已经到了,但是接口没有返回"静止"状态,前端做停止运行动作
        if (window.isEmit && res.tractionMethod != '静止'){
            window.TO.viewer.clock.shouldAnimate = false;
            return false;
        } else {
            window.isEmit = false;
        }

        that.viewer.clock.shouldAnimate = true;

        // 运行状态不一样,要重新计算
        if (that.tractionMethod != res.tractionMethod){
            that.currentVelocity = currentSpeed;
            that.tractionMethod = res.tractionMethod;
            that.trainLoadRun(targetPoint,that.currentVelocity,that.tractionMethod)
        } else {
            // 运行的速度不变,不做处理
            if (that.currentVelocity == currentSpeed){
                return false;
            }
            let diff = Math.abs(Number(currentSpeed) - Number(that.currentVelocity));
            if (diff < 1 ){
                return false;
            }
            that.currentVelocity = currentSpeed;
            that.tractionMethod = res.tractionMethod;
            that.trainLoadRun(targetPoint,that.currentVelocity,that.tractionMethod)
        }
    }

可以在init()初始化的时候,用定时器的方式去更新数据

init(){
        let that = this;
        that.getTrainInfo();
        window.trainTimer = window.setInterval(function (){
            that.getTrainInfo && that.getTrainInfo();
        }, 4000);

        that.loadComputeData(that.routePath, that.currentVelocity, that.trainRun);
        that.viewer.clock.onTick.addEventListener(function (clock){
            that.handler(clock,that)
        });

    }

完整代码

html

<!DOCTYPE html>
<!--suppress ALL -->
<html lang="en">
<head>
    <!-- Use correct character set. -->
    <meta charset="utf-8"/>
    <!-- Tell IE to use the latest, best version. -->
    <meta content="IE=edge" http-equiv="X-UA-Compatible"/>
    <!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" name="viewport"/>
    <title>Hello World!</title>
    <script src="../Build/Cesium/Cesium.js"></script>
    <style>
        @import url(../Build/Cesium/Widgets/widgets.css);
        html, body, #cesiumContainer {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        .btn{
            position: fixed;
            top: 5px;
            left: 10px;
            z-index: 9999;
            display: flex;
            align-items: center;
        }
        .btn .btn-item{
            margin: 0 4px;
        }
    </style>
</head>
<body>
<div class="btn">
    <div style="display: flex;align-items: center">
        <span style="font-size: 14px;color: white">当前速度:</span>
        <input value="20" placeholder="当前速度" id="currentSuDu" style="width: 100px;height: 20px">
    </div>
    <button class="btn-item" id="runBtn">运行</button>
    <button class="btn-item" id="stopBtn">停止</button>
    <button class="btn-item" id="restart">重新开始</button>
    <button class="btn-item" id="startAtSpecifiedLocation">指定位置开始</button>
    <button class="btn-item" id="firstViewBtn">第一视角</button>
    <button class="btn-item" id="freeViewBtn">自由视角</button>
    <button class="btn-item" id="followViewBtn">跟随视角</button>
</div>
<div id="cesiumContainer"></div>
<script type="module">
    import { routePath } from './trainOperation/sourceData.js';
    import TrainOpera from './trainOperation/trainOperation.js';
    let Cesium = window.Cesium;
    Cesium.Ion.defaultAccessToken = '你自己的token'
    const viewer = new Cesium.Viewer("cesiumContainer", {});
    window.viewer = viewer;
    let TO = new TrainOpera(window.viewer.clock,routePath);
    TO.init();
    TO.addPolyline(TO.routePath);
    window.TO = TO; // 存到全局对象
    TO.at3DMapGetPointTakingRoute();
</script>
</body>
</html>

js逻辑

trainOperation.js
// noinspection LanguageDetectionInspection
import { modelCzml,operaDomStyle } from './sourceData.js';

function operaDom(trainOpera,clock){
    let viewer = window.viewer;
    operaDomStyle()
    // 获取按钮元素
    let currentSuDu = document.getElementById('currentSuDu');
    let runBtn = document.getElementById('runBtn');
    let stopBtn = document.getElementById('stopBtn');
    let startAtSpecifiedLocation = document.getElementById('startAtSpecifiedLocation');
    let restart = document.getElementById('restart');
    let firstViewBtn = document.getElementById('firstViewBtn');
    let freeViewBtn = document.getElementById('freeViewBtn');
    let followViewBtn = document.getElementById('followViewBtn');


    // 添加点击事件处理程序
    currentSuDu.onblur = function (){
        let dom = document.getElementById('currentSuDu');
        if (!dom) return;
        trainOpera.currentVelocity = this.value;
        console.log('当前速度:',trainOpera.currentVelocity+'千米每小时');
    }
    runBtn.addEventListener('click', function (){
        console.log('运行。。。')
        trainOpera.trainRun()
    });
    stopBtn.addEventListener('click', function () {
        console.log('暂停运行。。。')
        clock.shouldAnimate = false;
    });
    startAtSpecifiedLocation.addEventListener('click', function () {
        console.log('指定位置开始');
        let targetPoint = {lon:116.44807033296253,lat:39.956431604742875};
        trainOpera.trainLoadRun(targetPoint)
    });
    restart.addEventListener('click', function () {
        console.log('从新头开始运行');
        let targetPoint = {lon:trainOpera.originalRoute[0][0],lat:trainOpera.originalRoute[0][1]};
        trainOpera.trainLoadRun(targetPoint)
    });
    firstViewBtn.addEventListener('click', function () {
        console.log('已切换为第一视角')
        clock.firstPerspective = true; // 控制第一视角
        viewer.trackedEntity = trainOpera.trainModel;
    });
    freeViewBtn.addEventListener('click', function () {
        console.log('已切换为自由视角')
        clock.firstPerspective = false; // 控制第一视角
        // 关闭视角跟随
        viewer.trackedEntity = undefined;
    });
    followViewBtn.addEventListener('click', function () {
        console.log('已切换为跟随视角')
        clock.firstPerspective = false;
        viewer.trackedEntity = trainOpera.trainModel;
    });
}

export default class TrainOpera {
    constructor(clock,routePath) {
        window.isEmit = false; // 控制火车到站后,是否重新运行
        this.clock = clock;
        this.originalRoute = routePath;
        this.camera = window.viewer.camera;
        this.Cesium = window.Cesium;
        this.viewer = window.viewer;
        this.routePath = this.addInterpolation(routePath);
        operaDom(this,clock);
        clock.firstPerspective = false; // 默认不做第一视角
    }
    originalRoute = null; // 运行的路线,经纬度
    trainModel = null; // 实体模型
    startTime = '2023-10-27T12:00:00Z'; // 开始运行时间
    currentVelocity = 20; //当前速度53千米每小时
    tractionMethod = '前进'   //火车当前前进方式,3种状态:前进 后退 静止
    CameraPositionInfo = {}; // 当前的相机位置参数
    newRoutePathIndex = 0; // 记录当前从那个短路开始计算新的数据
    finishNeedTime = null;// 根据当前的速度运行,到终点需要多少时间
    wagonNumber = '测试火车移动'; // 车号
    discrepancyNum = 37; // 时钟秒速相差实际的37秒,不知道为什么。。。?clock.currentTime.secondsOfDay - 37才是现在时钟运行的秒速时间
    useRoutePath = []; // 全局的加载好的路线
    dataSource = null; // 当前加载的czml
    onTickEvent = null;
    timer = null

    fullRoutePathDistance = {  // 当前路程总长,点与点之间的距离多少米
        total: 0, // 总的距离
        distance:[],
        positionBetween:[]
    }

    // 记录火车轨迹和时间点
    saveLastPathAndTime = {
        timer:[],
        lanLat:[]
    };

    init(){
        let that = this;
        // that.getTrainInfo();
        // window.trainTimer = window.setInterval(function (){
        //     that.getTrainInfo && that.getTrainInfo();
        // }, 4000);

        that.loadComputeData(that.routePath, that.currentVelocity, that.trainRun);
        that.viewer.clock.onTick.addEventListener(function (clock){
            that.handler(clock,that)
        });

    }

    restartTrainRun(tractionMethod){
        // 火车到站后监听重新启动
        if (tractionMethod == '静止'){
            window.TO.viewer.clock.shouldAnimate = false;
        }
        if (!window.isEmit) return;
        if (tractionMethod == window.TO.tractionMethod) return;
        window.isEmit = false;
        console.log('restartTrainRun');
        window.TO.timer = window.setInterval(function (){
            window.TO && window.TO.getTrainInfo && window.TO.getTrainInfo();
        }, 3000);
    }

    // 火车到站
    arriveAatStation(){
        let that = this;
        window.isEmit = true;
        console.log('The train has arrived at the station');
        // that.viewer.clock.currentTime = new that.Cesium.JulianDate(that.viewer.clock.stopTime.dayNumber,that.viewer.clock.stopTime.secondsOfDay - that.discrepancyNum);
        window.clearInterval(that.timer);
        window.clearInterval(window.TO.timer);
        that.viewer.clock.shouldAnimate = false;
        alert('火车到站')
    }

    // 获取指定范围内的随机数
    getRandomInt(min,max){
        return Math.floor(Math.random() * (max - min +1)) + min;
    }

    // 通过接口实时获取火车运行的相关信息
    getTrainInfo() {
        return ;
        let that = this;
        let res = {currentlonLat:'112.938333,40.332444',tractionMethod:'前进',speed:23}; //火车当前前进方式,3种状态:前进 后退 静止
        if (!res || res === '{}') return false;
        let currentSpeed;
        if (res.speed && res.speed.includes('km/h')){
            currentSpeed = res.speed.split('km/h')[0]  //火车运行速度
        }

        // 获取当前火车所在经纬度
        let targetPoint;

        if (res.currentlonLat){
            let point = res.currentlonLat.split(',');
            targetPoint = {lon: point[0],lat: point[1]}
        } else {
            console.log('经纬度坐标丢失');
            return false;
        }

        if (res.tractionMethod == '静止' || currentSpeed == '0'){
            that.tractionMethod = res.tractionMethod;
            that.viewer.clock.shouldAnimate = false;
            return false;
        }

        // 如果页面火车已经到了,但是接口没有返回"静止"状态,前端做停止运行动作
        if (window.isEmit && res.tractionMethod != '静止'){
            window.TO.viewer.clock.shouldAnimate = false;
            return false;
        } else {
            window.isEmit = false;
        }

        that.viewer.clock.shouldAnimate = true;

        // 运行状态不一样,要重新计算
        if (that.tractionMethod != res.tractionMethod){
            that.currentVelocity = currentSpeed;
            that.tractionMethod = res.tractionMethod;
            that.trainLoadRun(targetPoint,that.currentVelocity,that.tractionMethod)
        } else {
            // 运行的速度不变,不做处理
            if (that.currentVelocity == currentSpeed){
                return false;
            }
            let diff = Math.abs(Number(currentSpeed) - Number(that.currentVelocity));
            if (diff < 1 ){
                return false;
            }
            that.currentVelocity = currentSpeed;
            that.tractionMethod = res.tractionMethod;
            that.trainLoadRun(targetPoint,that.currentVelocity,that.tractionMethod)
        }
    }

    trainLoadRun(targetPoint,currentVelocity=this.currentVelocity,tractionMethod=this.tractionMethod){
        // 如果当前火车是后退,判断当前火车是否在运行路线上,如果在,则找到当前火车所在点,并反转火车路线数组
        let that = this;
        if (that.latAndLonPosition(targetPoint).exists) {
            let index = that.latAndLonPosition(targetPoint).index
            that.byLonAndLatUpdateTrainPosition(index, currentVelocity, tractionMethod)
        } else {
            console.error('当前经纬度不在轨迹运行路线上:',targetPoint)
        }
    }


    byLonAndLatUpdateTrainPosition(index,velocity, method){
        let that = this;
        that.newRoutePathIndex = index;
        that.currentVelocity = velocity;
        let useRoutePath = []
        if (method == '前进') {
          useRoutePath = that.routePath.slice(index);
          that.loadComputeData(useRoutePath,that.currentVelocity,that.trainRun);
        }
        if (method == '后退') {
          useRoutePath = that.routePath.slice(0,index).reverse();
          that.loadComputeData(useRoutePath,that.currentVelocity,that.trainRun);
        }
        if (method == '静止')  {
          window.viewer.clock.shouldAnimate = false;
          clearInterval(that.timer)
        }
    }

    // 判断一个经纬度坐标点,是否在某条由经纬度组成的路线,并返回该点所在的位置
    latAndLonPosition(targetPoint){
        // targetPoint:{lon:'83.486845',lat:'42.514477'}
        let that = this;
        let ret;
        let tPoint = that.Cesium.Cartographic.fromDegrees(targetPoint.lon, targetPoint.lat);

        let minDistance = Infinity;
        let positionIndex = 0;

        if (!that.routePath.length){
            return { exists: false }
        }
        for (let i = 0; i < that.routePath.length; i++) {
            const routePoint = that.routePath[i];
            const pointCartographic = that.Cesium.Cartographic.fromDegrees(...routePoint);
            const distance = that.distance(pointCartographic, tPoint);
            if (distance < minDistance) {
                minDistance = distance;
                positionIndex = i;
            }
        }
        let routePathStart = that.Cesium.Cartographic.fromDegrees(...that.routePath[0]);
        let routePathEnd = that.Cesium.Cartographic.fromDegrees(...that.routePath[that.routePath.length - 1]);
        let routeDistance = that.distance(routePathStart, routePathEnd);

        if (minDistance < routeDistance) {
            // 目标点在路线上  positionIndex:所在位置
            ret = { index:positionIndex, exists: true }
        } else {
            // 目标点不在路线上
            ret = { exists: false }
        }
        return ret
    }

    walkedThrough(){
        let that = this;
        let setCTimer = new that.Cesium.JulianDate(that.clock.currentTime.dayNumber,that.clock.currentTime.secondsOfDay - that.discrepancyNum)
        let center = that.trainModel.position.getValue(setCTimer);
        let currentPosition = that.cartesian3_to_WGS84(center);
        let positionIndex = that.latAndLonPosition(currentPosition)
        let cDistance = 0;
        if (!that.fullRoutePathDistance.distance.length) return 0;
        for (let i = 0; i < that.fullRoutePathDistance.distance.length; i++) {
            if (i === positionIndex.index) break;
            cDistance = cDistance + that.fullRoutePathDistance.distance[i]
        }
        return cDistance // 走了多少距离
    }

    computerFullRoutePathDistance(routePath){
        let that = this;
        for (let i = 0; i < routePath.length; i++) {
            if (i < routePath.length-1 ){
                let startPosition = that.Cesium.Cartographic.fromDegrees(...routePath[i]);
                let endPosition = that.Cesium.Cartographic.fromDegrees(...routePath[i+1]);
                let towPosition = that.distance(startPosition,endPosition);
                that.fullRoutePathDistance.distance.push(towPosition);
                that.fullRoutePathDistance.positionBetween.push(routePath[i][0]+'-'+routePath[i+1][0]);
                that.fullRoutePathDistance.total = (that.fullRoutePathDistance.total + towPosition)
            }
        }
    }

    timeFormat(){
        let now = new Date();
        let year = now.getFullYear();
        let month = ("0" + (now.getMonth() + 1)).slice(-2); // 获取两位数的月份
        let day = ("0" + now.getDate()).slice(-2); // 获取两位数的日期
        let hours = ("0" + now.getHours()).slice(-2); // 获取两位数的小时
        let minutes = ("0" + now.getMinutes()).slice(-2); // 获取两位数的分钟
        let seconds = ("0" + now.getSeconds()).slice(-2); // 获取两位数的秒数
        // 返回格式:如2023-10-27T12:00:00Z
        return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}Z`
    }

    //将秒转换为分钟、小时
    convertSecondsToMinutesHours(seconds) {
        let minutes = Math.floor(seconds / 60);
        let hours = Math.floor(minutes / 60);
        return {
            minutes,
            hours
        }
    }


    //加载czml文件
    reloadCzml(czml,callBack) {
        let that = this;
        that.delModelEntity(that.dataSource); // 更新前,需删除之前的数据
        that.viewer.dataSources.add(that.Cesium.CzmlDataSource.load(czml)).then(function (ds){
            that.dataSource = ds
            callBack && callBack(ds)
        })
    }

    // 根据路程和当前速度,计数走完需要多少时间
    countingRate(v,s) {
        // 单位是米和秒
        // 速度(v)、距离(s)和时间(t)之间的关系可以用以下的数学公式表示:s = v × t
        //速度 (km/h) = 速度 (m/s) × 3.6
        let t1 = s / (v*1000/3600); // km
        let t2 = s / v; // m
        return t1
    }

    trainRun(fn){
        let clock = window.viewer.clock;
        // clock.firstPerspective = true;
        clock.shouldAnimate = true;
        clock.multiplier = 1;
        fn && fn()
    }

    // 计算带时间的路径
    createDynamicPositions(positions,totalTime) {
        let that = this;
        // 将秒数,平均分给每个坐标点,每个坐标点应该到的时间
        if (!positions || !positions.length) return;
        let useRoutePath = [];
        let avgT = Number(totalTime)/positions.length;
        let cur = 0;

        // 重新计数
        that.saveLastPathAndTime = { timer: [], lanLat: []};

        for (let i = 0; i < positions.length; i++) {
            cur = cur + avgT;
            useRoutePath.push(cur,positions[i][0],positions[i][1],0);
            that.saveLastPathAndTime.timer.push(cur);
            that.saveLastPathAndTime.lanLat.push(positions[i][0]);
        }
        useRoutePath[0] = 0;
        that.useRoutePath = useRoutePath;
        return {
            useRoutePath,
            saveLastPathAndTime:that.saveLastPathAndTime,
        };
    }

    // 给一个时间格式字符串和一个添加的秒数,返回添加秒数后的最终时间
    addSeconds(timeString, secondsToAdd) {
        // var time = "2012-08-04T15:00:00Z"; console.log(addSeconds(time, 5670)); 输出新的时间字符串
        // 将字符串转化为Date对象
        let date = new Date(timeString);
        // 在Date对象上加上秒数
        date.setSeconds(date.getSeconds() + secondsToAdd);
        return date.toISOString(); // 将新的Date对象转换回字符串
    }

    //修改当前的速度
    setCurrentVelocity(currentVelocity){
        let that = this;

        // 保存当前相机的位置
        that.CameraPositionInfo = that.getCameraPositionInfo();
        if (currentVelocity < 0) currentVelocity = 1;

        let secondEd = that.clock.currentTime.secondsOfDay - that.discrepancyNum;
        let distanceEd = secondEd*(that.currentVelocity*1000 / 3600);


        // 找到移动到的经纬度点
        let atBetweenIndex = 0;
        let totalPathEd = 0;
        let originalDistance = that.fullRoutePathDistance.distance;
        for (let i = 0; i < originalDistance.length; i++) {
            totalPathEd = totalPathEd + originalDistance[i];
            if (totalPathEd > distanceEd){
                atBetweenIndex = i;
                break
            }
        }
        // 已经移动到的经纬度坐标点
        let curMeter = 0;
        let currentLan = that.fullRoutePathDistance.positionBetween[atBetweenIndex];
        for (let i = 0; i < that.routePath.length; i++) {
            if (currentLan.includes(that.routePath[i][0])){
                curMeter = i // 记录当前从那个短路开始计算新的数据
                break
            }
        }

        that.newRoutePathIndex = that.newRoutePathIndex + curMeter;
        let newRoutePath = that.routePath.slice(that.newRoutePathIndex);
        that.loadComputeData(newRoutePath,currentVelocity,that.trainRun)
    }

    // 加载最新数据
    loadComputeData(routePath,Velocity = this.currentVelocity,fn){
        let that = this;
        //routePath:由经纬度组成的路程
        // currentVelocity = 30; // 每秒30米的速度行驶
        let endTime; // 运行结束时间
        that.currentVelocity = Velocity;
        let finishNeedTime; // 走完需要多少时间 单位是秒s
        let distanceMeter; // 起点到终点多少米远
        distanceMeter = that.totalJourney(routePath);
        finishNeedTime = that.countingRate(Velocity,distanceMeter);
        that.finishNeedTime = finishNeedTime;
        endTime = that.addSeconds(this.startTime,finishNeedTime);
        let ret = that.createDynamicPositions(routePath,finishNeedTime);
        that.saveLastPathAndTime = ret.saveLastPathAndTime; // 路程与时间
        that.reloadCzml(modelCzml(ret.useRoutePath,that.startTime,endTime),function (ds){
            let trainModel = ds.entities.getById('Vehicle');
            that.trainModel = trainModel;
            that.viewer.trackedEntity = trainModel;


            // 设置模型可以随路径方向转向
            // trainModel.orientation = new Cesium.VelocityOrientationProperty(trainModel.position);
            // trainModel.model.alignedAxis = new Cesium.VelocityVectorProperty(trainModel.position, true);
            // 更新相机位置(第一视角)
            // tool.firstPerspective()
            fn && fn()
        })
        return finishNeedTime
    }

    // 找出当前的速度改变前,火车在的位置,利用数组的下标的方式来记录,根据当前时间的秒速去找,对比,取下一个地点
    currentMovingLocation(arr, num){
        // 添加数字到数组中
        arr.push(num);
        // 排序数组
        arr.sort((a, b) => a - b);
        // 查找数字在数组中的位置(下标)
        let index = arr.indexOf(num);
        return {
            index: index,
            sortedArray: arr
        };
    }

    // 修改当前时钟的时间,从那个位置开始运动
    setClockCurrentTime(second){
        let that = this;
        that.clock.currentTime = new that.Cesium.JulianDate(that.clock.currentTime.dayNumber, second);
        that.clock.multiplier = 1;
        that.clock.shouldAnimate = true;
    }

    // 删除实体和模型
    delModelEntity(dataSource){
        let that = this;
        if (dataSource){
            that.viewer.dataSources.remove(dataSource);
        }
    }

    // 第一视角
    firstPerspective(){
        let that = this;
        //window.trainModel实体
        const orientation = that.trainModel.orientation;
        const position = that.trainModel.position;
        function main() {
            try {
                if (that.viewer.clock.shouldAnimate === true) {

                    let ori = orientation.getValue(that.viewer.clock.currentTime); // 获取偏向角
                    let center = position.getValue(that.viewer.clock.currentTime); // 获取位置

                    // 1、由四元数计算三维旋转矩阵
                    let mtx3 = that.Cesium.Matrix3.fromQuaternion(ori);

                    // 2、计算四维转换矩阵:
                    let mtx4 = that.Cesium.Matrix4.fromRotationTranslation(mtx3, center);

                    // 3、计算角度:
                    let hpr = that.Cesium.Transforms.fixedFrameToHeadingPitchRoll(mtx4);

                    // 获取角度(弧度)
                    const headingTemp = hpr.heading;
                    const pitchTemp = hpr.pitch;

                    // 调整角度为第一人称
                    const heading = that.Cesium.Math.toRadians(that.Cesium.Math.toDegrees(headingTemp) + 90);
                    const pitch = that.Cesium.Math.toRadians(that.Cesium.Math.toDegrees(pitchTemp) - 12);
                    // 视角高度,根据模型大小调整
                    const range = 140.0;

                    // 动态改变模型视角
                    that.viewer.camera.lookAt(center, new that.Cesium.HeadingPitchRange(heading, pitch, range));
                }
            } catch (e) {
                console.log('err in firstPerspective function')
            }
        }
        that.onTickEvent = that.viewer.clock.onTick.addEventListener(main);
    }

    firstPerspectiveTow() {
        let that = this;
        try {
            let center = that.trainModel.position.getValue(
                that.viewer.clock.currentTime
            );
            let orientation = that.trainModel.orientation.getValue(
                that.viewer.clock.currentTime
            )
            let transform = that.Cesium.Matrix4.fromRotationTranslation(that.Cesium.Matrix3.fromQuaternion(orientation), center);
            // viewer.camera.lookAtTransform(transform, new Cesium.Cartesian3(-100, 0, 50))
            that.viewer.camera.lookAtTransform(transform, new that.Cesium.Cartesian3(-60, 0, 50))
        } catch (e) {
            console.log('err in firstPerspectiveTow function')
        }
    }

    // 根据经纬度坐标,计算总的路程有多少米
    totalJourney(routePaths){
        let that = this;
        if (!routePaths || !routePaths.length) return 0;
        let totalJourney = 0;
        for (let i = 0; i < routePaths.length; i++) {
            if (i < routePaths.length-1 ){
                let startPosition = that.Cesium.Cartographic.fromDegrees(...routePaths[i]);
                let endPosition = that.Cesium.Cartographic.fromDegrees(...routePaths[i+1]);
                let towPosition = that.distance(startPosition,endPosition);
                totalJourney = totalJourney + towPosition
            }
        }
        return totalJourney
    }

    arriveAtStation(clock,callBack){
        let that = this;
        let cDistance = that.walkedThrough();
        let diff = Math.abs(that.fullRoutePathDistance.total - cDistance);
        if (that.tractionMethod == '后退'){
            diff = that.fullRoutePathDistance.total - diff
        }
        if (diff < 9){
            // 可以在这里分发到站广播
            callBack && callBack();
            clock.shouldAnimate = false;
            that.arriveAatStation()
        }
    }

    // 画一条路线
    addPolyline(routePath){
        let that = this;
        if (!routePath || !routePath.length) return;
        let positions = [];
        routePath.forEach( el =>{
            positions.push(that.Cesium.Cartesian3.fromDegrees(...el))
        })
        return that.viewer.entities.add({
            id: 'MY_POLYLINE',
            name: 'MY_POLYLINE',
            polyline: {
                positions: positions,  // 设置轨道的位置点
                material: that.Cesium.Color.YELLOW.withAlpha(1.0),
                width: 4  // 设置轨道的宽度。
            }
        });
    }

    getCameraPositionInfo(){
        let that = this;
        let position = that.viewer.camera.position;  // 获取当前相机位置
        let offsetHeading = that.viewer.camera.heading; // 获取当前相机偏移角度
        let pitch = that.viewer.camera.pitch; // 获取当前相机俯视角
        let roll = that.viewer.camera.roll; // 获取当前相机的滚动角
        // viewer.camera.setView(cameraPositionInfo); // 设置相机位置
        return {
            destination: position,// 经度,纬度,高度
            orientation: that.Cesium.HeadingPitchRoll.fromDegrees(offsetHeading, pitch, roll) // 偏航角,俯仰角,滚动角
        }
    }

    throttle(func, delay) {
        let lastCall = 0;
        return function(...args) {
            const now = new Date().getTime();
            if (now - lastCall < delay) {
                return;
            }
            lastCall = now;
            return func.apply(this, args);
        };
    }

    // 两个经纬度之间的插值,返回相差的经纬度坐标
    interpolation(startPosition,endPosition){
        let that = this;
        // 两个参数接收的类型和值:
        // startPosition:[83.787568,42.919772]
        // endPosition:[83.484661,42.515142]
        let nextPath = [];  // 存放两个经纬度坐标之间的差值

        // nextPath.push(startPosition); // 火车有时候有可能会变成闪动

        let sPosition = new that.Cesium.Cartesian3.fromDegrees(...startPosition);
        let ePosition = new that.Cesium.Cartesian3.fromDegrees(...endPosition);
        let differenceValue = 0;
        // Cesium.Cartesian3.lerp(startPosition, endPosition, differenceValue, new Cesium.Cartesian3())  // 计数两点之间的插值
        while (differenceValue <= 1){
            // 计数两个经纬度之间的差值,分成--份
            differenceValue = differenceValue + 0.05; // 0.05
            let coordinateD = that.Cesium.Cartesian3.lerp(sPosition, ePosition, differenceValue, new that.Cesium.Cartesian3());

            let wgs84Point = that.cartesian3_to_WGS84(coordinateD);
            nextPath.push([wgs84Point.lon, wgs84Point.lat])
        }
        return nextPath
    }

    // 笛卡尔坐标转化为经纬度坐标
    cartesian3_to_WGS84(point) {
        let that = this;
        let cartesian3 = new that.Cesium.Cartesian3(point.x, point.y, point.z);
        let cartographic = that.Cesium.Cartographic.fromCartesian(cartesian3);
        let lat = that.Cesium.Math.toDegrees(cartographic.latitude);
        let lon = that.Cesium.Math.toDegrees(cartographic.longitude);
        let alt = cartographic.height;
        return { lat, lon, alt };
    }

    // 添加插值
    addInterpolation(routePath){
        let that = this;
        if (!routePath || !routePath.length) return;

        /**
        // bd09towgs84  百度坐标转换为wgs84坐标
        let wgs84 = [];
        for (let i = 0; i < routePath.length; i++) {
            let a = window.coordtransform.bd09togcj02(routePath[i][0],routePath[i][1]);
            let b = window.coordtransform.gcj02towgs84(a[0],a[1]);
            wgs84.push(b)
        }
        routePath = wgs84;
        **/

        let arr = [];
        for (let i = 0; i < routePath.length; i++) {
            if (i < routePath.length-1){

                // 做个优化,如何两个点直接的距离小于15米不做差值计算
                let startPosition = that.Cesium.Cartographic.fromDegrees(...routePath[i]);
                let endPosition = that.Cesium.Cartographic.fromDegrees(...routePath[i+1]);
                let ret = that.distance(startPosition,endPosition);
                if (5 > ret) continue;


                let opera = that.interpolation(routePath[i],routePath[i+1]);
                arr = arr.concat(opera)
            }
        }

        // 原路线的起点和终点要保持和源数据一致,不能丢失
        if (arr[0][0] != routePath[0][0]) arr.unshift(routePath[0]);
        if (arr[arr.length -1][0] != routePath[routePath.length -1][0]) arr.push(routePath[routePath.length -1]);

        that.computerFullRoutePathDistance(arr);
        return arr
    }

    // 计数两个经纬度之间距离多少米
    distance(startPosition, endPosition) {
        let that = this;
        // let startPosition = Cesium.Cartographic.fromDegrees(117.270739, 31.84309);
        // let endPosition = Cesium.Cartographic.fromDegrees(117.270739, 31.85309);
        let geodesic = new that.Cesium.EllipsoidGeodesic();
        geodesic.setEndPoints(startPosition, endPosition);
        return geodesic.surfaceDistance; // 返回两点之间的距离
    }

    // 在三维地图上取点,地图上会打印选择的点坐标,可以用于去移动路线
    at3DMapGetPointTakingRoute(){
        window.viewer.entities.add({
            //点的位置
            position : Cesium.Cartesian3.fromDegrees(112.90365587012, 27.810901397823, 0),
            //点
            point : {
                pixelSize : 5,//点的大小
                color : Cesium.Color.RED,//点的颜色
                outlineColor:Cesium.Color.GREEN,//外圈颜色
                outlineWidth:1,//外圈大小
            }
        });
        let coordinate = []
        let str = ''
        let handler = new window.Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
        handler.setInputAction(function(event) {
            let cartesian = window.viewer.camera.pickEllipsoid(event.position);
            let cartographic = window.Cesium.Cartographic.fromCartesian(cartesian);
            let lng = window.Cesium.Math.toDegrees(cartographic.longitude); // 经度
            let lat = window.Cesium.Math.toDegrees(cartographic.latitude); // 纬度
            let alt = cartographic.height; // 高度,椭球面height永远等于0
            // 添加点
            window.viewer.entities.add({
                //点的位置
                position : Cesium.Cartesian3.fromDegrees(lng, lat, 0),
                //点
                point : {
                    pixelSize : 5,//点的大小
                    color : Cesium.Color.RED,//点的颜色
                    outlineColor:Cesium.Color.GREEN,//外圈颜色
                    outlineWidth:1,//外圈大小
                }
            });

            str += '['
            str += lng
            str += ','
            str += lat
            str += ']'
            str += ','
            coordinate.push([lng, lat])
            console.log(coordinate);
            console.log(str);
        }, window.Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }

    handler(clock,TO){
        clock.multiplier = 1;
        if (clock.firstPerspective){
            // tool.throttle(tool.firstPerspectiveTow,50) && tool.throttle(tool.firstPerspectiveTow,50)();
            TO.firstPerspectiveTow()
        }
        clock.shouldAnimate && TO.arriveAtStation(clock);
    }
}
sourceData.js
/* eslint-disable */

// 路线
export const routePath = [
    [116.44411704836098,39.95279202225133],[116.44807033296253,39.956431604742875],
    [116.45386442263022,39.952900124343756],[116.45546340934312,39.951275636111355],
    [116.45567209160625,39.9494096918938],[116.4556296095857,39.94616403625391],
]

// let testModel = window.location.origin + '/Apps/trainOperation/model/scene.gltf';
// let testModel = window.location.origin + '/Apps/SampleData/models/GroundVehicle/GroundVehicle.glb';
let testModel = window.location.origin + '/Apps/SampleData/models/GroundVehicle/GroundVehicle.glb';
export function modelCzml(useRoutePath,startTime,endTime){
    let minx = startTime+'/'+endTime;
    return [
        {
            "id": "document",
            "version": "1.0"
        },
        {
            "id": "Vehicle",
            "availability": minx ,
            "label": {
                "fillColor": [
                    {
                        "rgba": [0, 0, 0, 255]
                    }
                ],
                "font": "bold 10pt Segoe UI Semibold",
                "horizontalOrigin": "CENTER",
                "outlineColor": {
                    "rgba": [0, 0, 0, 255]
                },
                "pixelOffset": {
                    "cartesian2": [40.0, 50.0]
                },
                "scale": 1.0,
                "show": [
                    {
                        "boolean": true
                    }
                ],
                "style": "FILL",
                "text": "测试火车移动",
                "verticalOrigin": "CENTER"
            },
            "model": {
                "gltf": testModel,
                "minimumPixelSize": 120,
                "maximumScale": 50
            },
            "orientation": {
                "velocityReference": "#position"
            },
            "viewFrom": {
                // "cartesian": [300, 20, 300]
                "cartesian": [500, 2000, 779]
            },
            "properties": {
                "fuel_remaining": {
                    "epoch": startTime,
                    "number": [0, 22.5, 1500, 21.2]
                }
            },
            "path": {
                "material": {
                    "solidColor": {
                        "color": {
                            "rgba": [255, 255, 0, 255]
                        }
                    }
                },
                "width": [
                    {
                        "number": 4.0
                    }
                ],
                "show": [
                    {
                        "boolean": true
                    }
                ]
            },
            "position": {
                "interpolationAlgorithm": "LAGRANGE",
                "interpolationDegree": 1,
                "wrap": false,
                "epoch": startTime,
                "cartographicDegrees": useRoutePath
                // useRoutePath:时间、经度、纬度、高度加载显示的格式为[0, 102.23404378554466, 27.825736605050523, 2500,10, 102.23691954070244, 27.82887625908256, 2500,]
            }
        }
    ]
}

export function operaDomStyle(){
    try {
        document.getElementsByClassName('cesium-viewer-animationContainer')[0].style.visibility = 'hidden';
        document.getElementsByClassName('cesium-viewer-bottom')[0].style.visibility = 'hidden';
        document.getElementsByClassName('cesium-viewer-timelineContainer')[0].style.visibility = 'hidden';
        document.getElementsByClassName('cesium-viewer-toolbar')[0].style.visibility = 'hidden';
        document.getElementsByClassName('cesium-viewer-fullscreenContainer')[0].style.visibility = 'hidden';
    } catch (e) {}
}

gitee仓库项目代码

wdn-cesiumCarRoute: 三维地图Cesium,加载一个模型,模型沿着给定的一组经纬度路线、速度、前进状态进行移动

疑问解答

需要解答(项目的运行、代码疑问、新功能开发...),可以给我留言哟。有空随时会解答

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/695838.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

热题系列章节5

169. 多数元素 给定一个大小为 n 的数组&#xff0c;找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1: 输入: [3,2,3] 输出: 3 示例 2: 输入: [2,2,1,1,1,2,2] 输出:…

shell编程(三)—— 运算符

和其他编程语言一样&#xff0c;bash也有多种类型的运算符&#xff0c;本篇对bash的相关运算符做简单介绍。 一、运算符 1.1 算术运算符 常见的算术运算符&#xff0c;如加&#xff08;&#xff09;、减&#xff08;-&#xff09;、乘&#xff08;*&#xff09;、除&#xf…

简单介绍一下vim

简单介绍一下vim 一、vim是什么&#xff1f;二、vim的优点三、vi/vim的使用命令模式输入模式底线命令模式 四、vi/vim 按键说明&#xff08;一&#xff09;命令模式可用的光标移动、复制粘贴、搜索替换等移动光标的方法:搜索替换的方法删除、复制与贴上的方法 &#xff08;二&a…

嵌入式Linux系统编程 — 3.5 utime、utimes、futimens、utimensat函数修改文件时间属性

目录 1 文件的时间属性简介 2 utime()函数 2.1 utime()函数简介 2.2 示例程序 3 utimes()函数 3.1 utimes()函数简介 3.2 示例程序 4 futimens()函数 4.1 futimens()函数简介 4.2 示例程序 5 utimensat()函数 5.1 utimensat()函数简介 5.2 示例程序 1 文件的时间…

如何从SD存储卡恢复已删除的图像

SD卡概述 在日常工作和学习中&#xff0c;SD卡经常被用作许多设备上的存储设备&#xff0c;例如数码相机&#xff0c;手机&#xff0c;摄像机&#xff0c;行车记录仪以及监控设备&#xff0c;用于为我们存储图像&#xff0c;视频&#xff0c;文档&#xff0c;音频和其他数据。…

html5实现个人网站源码

文章目录 1.设计来源1.1 网站首页页面1.2 个人工具页面1.3 个人日志页面1.4 个人相册页面1.5 给我留言页面 2.效果和源码2.1 动态效果2.2 目录结构 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/139564407 ht…

12. Django 第三方功能应用

12. 第三方功能应用 因为Django具有很强的可扩展性, 所以延伸了第三方功能应用. 通过本章的学习, 读者能够在网站开发过程中快速实现API接口开发, 验证码生成与使用, 站内搜索引擎, 第三方网站实现用户注册, 异步任务和定时任务, 即时通信等功能.12.1 Django Rest Framework框…

【YOLOv5进阶】——修改网络结构(以C2f模块为例)

一、站在巨人的肩膀上 这里我们借鉴YOLOv8源码&#xff1a; 上期说到&#xff0c;对于网络模块定义详情在common.py这个文件&#xff0c;如Conv、CrossConv、C3f等。本期要修改的需要参考YOLOv8里的C2f模块&#xff0c;它定义在YOLOv8的module文件夹的block.py文件里&#xf…

js调试过程中修改变量值

1.在想要变更的地方添加断点 2.添加监视表达式 3.执行网页代码&#xff0c;当执行到断点处则会停止 4.点击执行下一步&#xff0c;则会执行监视表达式

新材料正不断推动模具3D打印行业发展

随着工业4.0的浪潮席卷全球&#xff0c;模具制造行业也迎来了技术革新的新纪元。3D打印技术以其独特的制造优势&#xff0c;正逐渐在模具制造领域崭露头角。然而&#xff0c;要实现模具3D打印技术的广泛应用&#xff0c;高性能的打印材料是不可或缺的关键因素。 材料是模具3D打…

一、Socket创建和连接

C网络编程&#xff08;asio&#xff09; 文章目录 C网络编程&#xff08;asio&#xff09;1、Asio概述2、网络编程基本流程2、创建socket3、创建监听socket4、绑定accpet监听套接字5、连接指定的端点6、服务器接收连接 点击查看代码 1、Asio概述 ​ Asio起源于Boost库&#xf…

超详解——python条件和循环——小白篇

目录 1. 缩进和悬挂else 2. 条件表达式 3. 和循环搭配的else 4. 可调用对象 总结&#xff1a; 1. 缩进和悬挂else 在Python中&#xff0c;代码块是通过缩进来表示的。条件判断和循环结构的代码块需要正确缩进。悬挂else指的是else子句和相应的if或循环在同一级别的缩进。 …

AVL树 ---(C++)

本篇讲全面的讲解 AVL 树的插入&#xff0c;旋转以及验证 AVL 树的性能&#xff08;本篇未实现删除代码&#xff09;。至于为什么会有 AVL 树&#xff0c;这是因为简单的二叉搜索树并不能直接的保证搜索的效率&#xff0c;因为当我们在二叉搜索树中插入一段有序的序列的时候&am…

STC90C51驱动LCD1602、LCD12864、OLED

主控芯片&#xff08;STC90C516RDPG5151028&#xff09;介绍 ROM64K,RAM1280字节&#xff0c;40Pin&#xff0c;3个定时器&#xff0c;1个串口&#xff0c;8个中断源&#xff08;分别是&#xff1a;外部中断0(INTO)、外部中断 1(INT1)、外部中断 2(INT2)、外部中断 3(INT3)、定…

【微信小程序开发(从零到一)】——个人中心页面的实战项目(二)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

「动态规划」如何计算能获得多少点数?

740. 删除并获得点数https://leetcode.cn/problems/delete-and-earn/description/ 给你一个整数数组nums&#xff0c;你可以对它进行一些操作。每次操作中&#xff0c;选择任意一个nums[i]&#xff0c;删除它并获得nums[i]的点数。之后&#xff0c;你必须删除所有等于nums[i] …

【网络安全的神秘世界】web应用程序安全与风险

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 第一章&#xff1a;web应用程序安全与风险 web攻击基础知识 1、什么是web应用攻击 web攻击的本质&#xff0c;就是通过http协议篡改应用程序&#xff0…

虚拟机ping不通主机,但是主机可以ping通虚拟机

我在Windows10系统安装了虚拟机&#xff0c;设置的主机与虚拟机的连接方式是桥接&#xff0c;安装好后&#xff0c;发现虚拟机ping不通主机&#xff0c;但是主机可以ping通虚拟机。 我的操作是&#xff1a;关闭防火墙&#xff0c;发现虚拟机可以ping通主机了。说明是Windows10…

python后端结合uniapp与uview组件tabs,实现自定义导航按钮与小标签颜色控制

实现效果&#xff08;红框内&#xff09;&#xff1a; 后端api如下&#xff1a; task_api.route(/user/task/states_list, methods[POST, GET]) visitor_token_required def task_states(user):name_list [待接单, 设计中, 交付中, 已完成, 全部]data []color [#F04864, …

CPP初级:模板的运用!

目录 一.泛型编程 二.函数模板 1.函数模板概念 2.函数模板格式 3.函数模板的原理 三.函数模板的实例化 1.隐式实例化 2.显式实例化 3.模板参数的匹配原则 四.类模板 1.类模板的定义格式 2.类模板的实例化 一.泛型编程 泛型编程&#xff1a;编写与类型无关的通用代码…