车辆按照轨迹进行移动,按照cesium官方例子使用timeLine很容易实现出来,如下官网例子

Cesium Sandcastle

android animator轨迹移动 按轨迹移动_javascript

        但是实际项目开发中,要求一次多个车辆运行,运行的轨迹不同,速度不同,且起点不在轨迹的两端……

(前提:觉的timeline方式车辆运行的效果很好,所以是在这个的基础上想的办法)

最后效果:

android animator轨迹移动 按轨迹移动_html_02

 

思路如下:

(1)多个车辆运行,且运行的轨迹不同,速度不同

        单个车辆运行时,就是车辆整个运行时间为clock的开始时间和结束时间,循环执行时候以保证车辆跑完整个轨迹,来来回回的跑,但是多个车辆,速度不同,轨迹总长度不同。

尝试1:算出所有车辆每个运行总时长,找到最大的,以这个时间为clock的开始时间和结束时间

问题:时间最长的的确可以循环跑,但是时间短的,跑完就模型消失了,只能等到下次时间循环再出来跑,肯定是不行的

尝试2:由于对cesium也是初学,想着能不能有多个clock,每个车辆自己有自己的clock,这样就循环自己的啊

问题:貌似不能,找了好多久没有设置多个clock的资料,小白实在无能为力,只能放弃了……(若有知道怎么设置多个clock的,求告知!!!感谢!!!)

尝试3:最后还是在第一种方法的基础上,找到最长运行时间,其他运行时间短的车辆,就不是一个entity了,按照时间叠加,超出时间无所谓,反正到时间了也消失了……

主要代码如下

…………var maxTimeLine -->一系列操作找到的最大的timeLine
var extraLineList = [] //为了保证短时间车辆一直跑,多加出来的timeLine 最终要的就是这个数据
//小于maxTimeLine的timeLine集合循环 添加多个短实体
tempLineList.filter(item=>item.timeLine!=maxTimeLine).forEach(line=> {
    //临时运行结束时间(用于和最大时间判断)
    var tempTime = line[line.length-1].time
    var tempList = []
    //stop 为maxTimeLine的时间 也是clock的停止时间
    while(Cesium.JulianDate.secondsDifference(tempTime,stop)<0){
        var extraLine = getExtraRouteTimeData(line,20,tempTime)
        tempList.push(extraLine)
        tempTime = extraLine[extraLine.length-1].time
    }
    extraLineList.push(tempList)
});

function getExtraRouteTimeData(tempLine,speed,startTime){
    var finalData = []
    //整理数据根据速度求出时间
    for(var i=0;i<tempLine.length;i++)
    {
        if(i==0){
            var temp = {
                lon:tempLine[i].lon,
                lat:tempLine[i].lat,
                height:0,
                time:startTime
            }
            finalData.push(temp)
        }else{
            var distanceValue = getDistance([tempLine[i-1].lon,tempLine[i-1].lat],[tempLine[i].lon,tempLine[i].lat])//应该是米
            var interval = distanceValue/(speed/3.6)*10//缓冲速度 找到在界面上运行最好看的
            const stop = Cesium.JulianDate.addSeconds(finalData[i-1].time,interval,new Cesium.JulianDate());
            var temp = {
                lon:tempLine[i].lon,
                lat:tempLine[i].lat,
                height:0,
                time:stop
            }
            finalData.push(temp)
        }
    }
    return finalData
}

(2)车辆运行起点不同,不在轨迹的两端(基本肯定在轨迹上)

        这种就是在把轨迹,按照车辆的初始位置,拆成两段,之后先加后半段,再加前半段,短轨迹的按照上面的方法,在循环,也是先后半段再前半段的逻辑。

        但是这样遇到一个新的问题,车辆起始点是在轨迹上,但是不一定是轨迹的折点啊,那我要如何将轨迹拆成,【轨迹起点--车辆起始点】【车辆起始点--轨迹终端】两段呢??

        问题一个接着一个……

        对cesium真的不了解,也不知道有啥函数方法,绞尽脑汁只能用笨方法了,先找到与车辆起始点最近的折点,再求车辆起始点与【最近折点--(最近折点-1)]】和【最近折点--(最近折点+1)]】的垂直距离,哪个等于0车辆起始点就在哪段,都为0那很巧车辆起始点就是轨迹的折点,最后整理除两个轨迹【轨迹起点--车辆起始点】【车辆起始点--轨迹终端】

最后所有的代码汇总如下:

//调用方法
function carMoveFun(){
    var tempLineList = []
    //最短那个
    var line2 = [
        [111.5203687977757 ,36.097127118816125],
        [111.52047737873862,36.09728325930151 ],
        [111.52052010896203,36.09743563643246 ],
        [111.52058361872002,36.09758688254787 ],
        [111.52069077711863,36.09779312535372 ],
        [111.5207788133968 ,36.09792260005298 ],
        [111.52088010227294,36.09806827444043 ],
        [111.52104009048006,36.0982866401076  ],
    ]
    addRoute(line2)//添加轨迹路线
    var tempLine2s = getRouteTimeData(line2,30,line2[3])//轨迹终端的起始点line2[3]
    tempLineList.push({
        timeLine:tempLine2s,
        speed:30
    })

    //拐弯那个
    var line1 = [
        [111.51412650892415,36.100095052017245],
        [111.51483676831587,36.10003220657785 ],
        [111.51548449399205,36.09999317694337 ],
        [111.51612500512361,36.09994492029591 ],
        [111.51667870787678,36.09995489144349 ],
        [111.51717214970127,36.099882556847476],
        [111.51759516113012,36.09971712743759 ],
        [111.51824037887503,36.099499697683804],
        [111.51856786970123,36.099362781274316],
        [111.51865735859423,36.09923607421321 ],
        [111.51850574587014,36.0989972875293  ],
        [111.51817889032279,36.09841697029737 ],
        [111.51806446114564,36.09815881884296 ],
        [111.51786244633958,36.09777823518002 ]
    ]
    addRoute(line1)//添加轨迹路线
    var tempLine1s = getRouteTimeData(line1,50,line1[4])//轨迹终端的起始点line1[4]
    tempLineList.push({
        timeLine:tempLine1s,
        speed:50
    })

    //最长那个
    var line3 = [
        [111.51410636094,36.10013702162214],
        [111.51435543060107,36.1001271290815],
        [111.51460364312089,36.100125262904],
        [111.51490347270231,36.1000720242845],
        [111.51531727865796,36.100055969725545],
        [111.51592899080998,36.10002315266753],
        [111.51624774901323,36.09999874841982],
        [111.51656718362959,36.100005140162736],
        [111.51684636302151,36.09995851264969],
        [111.51706075555333,36.09995586024776],
        [111.51723991188527,36.099902452446194],
        [111.51758726520708,36.099799140758144],
        [111.51815036751158,36.09958807678223],
        [111.518581663695,36.09941140462744],
        [111.51885197336928,36.09931721770163],
        [111.51927816021275,36.09916931119326],
        [111.5197500964415,36.09896801084476],
        [111.52021349828564,36.09882758020317],
        [111.52078560640292,36.098625251983506],
        [111.52124920962186,36.09842661126232],
        [111.52171931152586,36.09826077409688],
        [111.52214966650816,36.098092142452714],
        [111.52247956672582,36.098002144461866],
        [111.5228237783227,36.097833928163844],
        [111.5231072311855,36.097766533143925],
        [111.52350930036965,36.097637947989234],
        [111.52367924880824,36.097583596559474]
    ]
    addRoute(line3)//添加轨迹路线
    var tempLine3s = getRouteTimeData(line3,60,line3[5])//轨迹终端的起始点line3[5]
    tempLineList.push({
        timeLine:tempLine3s,
        speed:60
    })

    var tempLine4s = getRouteTimeData(line3,80,line3[8])//轨迹终端的起始点line3[8]
    tempLineList.push({
        timeLine:tempLine4s,
        speed:80
    })

    //获取到最大时间
    var maxTimeLine = tempLineList[0].timeLine[1].length>0?tempLineList[0].timeLine[1]:tempLineList[0].timeLine[0]
    tempLineList.forEach(element => {
        if(element.timeLine[1].length>0){
            if(Cesium.JulianDate.secondsDifference(maxTimeLine[maxTimeLine.length-1].time,element.timeLine[1][element.timeLine[1].length-1].time)<0){
                maxTimeLine = element.timeLine[1]
            }
        }else{
            if(Cesium.JulianDate.secondsDifference(maxTimeLine[maxTimeLine.length-1].time,element.timeLine[0][element.timeLine[0].length-1].time)<0){
                maxTimeLine = element.timeLine[0]
            }
        }
    });
    var start = Cesium.JulianDate.fromDate(new Date(2000, 1, 1, 1))// 开始时间
    var stop = maxTimeLine[maxTimeLine.length-1].time//停止时间
    var extraLineList = []
    //整理最大TimeLine 循环添加多个短实体
    tempLineList.filter(item=>item.timeLine[1]!=maxTimeLine).forEach(tempTmeLines => {//小于maxTimeLine的timeLine集合循环
        var line01 = tempTmeLines.timeLine[0]
        var line02 = tempTmeLines.timeLine[1]
        //临时运行结束时间(用于和最大时间判断)
        var tempTime = line02.length>0?line02[line02.length-1].time:line01[line01.length-1].time
        var tempList = []
        while(Cesium.JulianDate.secondsDifference(tempTime,stop)<0){
            var extraLine = getExtraRouteTimeData(line01,tempTmeLines.speed,tempTime)
            if(extraLine.length>0){
                tempList.push(extraLine)
            }
            var tempEnd = extraLine.length>0?extraLine[extraLine.length-1].time:tempTime
            if(Cesium.JulianDate.secondsDifference(tempEnd,stop)<0){
                var extraLine1 = getExtraRouteTimeData(line02,tempTmeLines.speed,tempEnd)
                if(extraLine1.length>0){
                    tempList.push(extraLine1)
                }
                tempTime = extraLine1.length>0?extraLine1[extraLine1.length-1].time:tempEnd
            }else{
                break
            }
        }
        extraLineList.push({
            timeLine:tempList,
            speed:tempTmeLines.speed
        })
    });

    viewer.clock.startTime = start.clone(); // 设置始时钟始时间
    viewer.clock.currentTime = start.clone();// 设置时钟当前时间
    viewer.clock.multiplier = 10;// 时间速率,数字越大时间过的越快
    viewer.clock.stopTime = stop.clone();
    viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;// 循环执行

    tempLineList.concat(extraLineList).forEach(element => {
        element.timeLine.forEach(item => {
            if(item.length>0){
                terminalRun(item)
            }
        });
    });
}

function getExtraRouteTimeData(tempLine,speed,startTime){
    var finalData = []
    //整理数据根据速度求出时间
    for(var i=0;i<tempLine.length;i++)
    {
        if(i==0){
            var temp = {
                lon:tempLine[i].lon,
                lat:tempLine[i].lat,
                height:0,
                time:startTime
            }
            finalData.push(temp)
        }else{
            var distanceValue = getDistance([tempLine[i-1].lon,tempLine[i-1].lat],[tempLine[i].lon,tempLine[i].lat])//应该是米
            var interval = distanceValue/(speed/3.6)*10//缓冲速度
            const stop = Cesium.JulianDate.addSeconds(finalData[i-1].time,interval,new Cesium.JulianDate());
            var temp = {
                lon:tempLine[i].lon,
                lat:tempLine[i].lat,
                height:0,
                time:stop
            }
            finalData.push(temp)
        }
    }
    return finalData
}

function terminalRun(finalData){
    var myOwnStopTime = finalData[finalData.length-1].time//获取最后一个点的时间 // 停止时间

    //位置信息
    var property = new Cesium.SampledPositionProperty();
    // 取样位置 相当于一个集合
    for (var z = 0; z < finalData.length; z++) {
        var item = finalData[z];
        var thisTime = item.time//Cesium.JulianDate.fromDate(new Date(item.time));
        var position = Cesium.Cartesian3.fromDegrees(
            finalData[z].lon,
            finalData[z].lat,
            finalData[z].height
        );
        // 添加每一个链接点的信息,到达的时间以及坐标位置
        property.addSample(thisTime, position);
    }
    const pinBuilder = new Cesium.PinBuilder();
    var tempId = 'terminalEntity_'+guid()
    var terminalEntity = {
        id:tempId,
        // // // 和时间轴关联
        // // availability: new Cesium.TimeIntervalCollection([
        // //     new Cesium.TimeInterval({
        // //         start: start,
        // //         stop: stop,//短于时间轴会突然消失,直到时间轴循环
        // //     }),
        // // ]),
        position: property,
        // 根据所提供的速度计算模型的朝向
        orientation: new Cesium.VelocityOrientationProperty(property),
        label: { //文字标签
            text: "文字标签"+'\n'+'test',
            font: '500 30px Helvetica',// 15pt monospace
            scale: 0.5,
            style: Cesium.LabelStyle.FILL,
            fillColor: Cesium.Color.WHITE,
            pixelOffset: new Cesium.Cartesian2(-8, -35),   //偏移量
            showBackground: true,
            backgroundColor: new Cesium.Color(38/255, 38/255, 38/255, 0.75)
        },
        // 模型数据
        model: {
            uri: './data/car/scene.gltf',
            scale: 2,
        },
    }

    viewer.entities.add(terminalEntity);
    terminalMcLineRunEntityIds.push(tempId);
}

//获取随机数
function guid() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0,
            v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

// 添加轨迹
function addRoute(list){
    var cartesian3List = []
    list.forEach((item) =>{
        var ellipsoid=viewer.scene.globe.ellipsoid;
        var cartographic=Cesium.Cartographic.fromDegrees(item[0],item[1],0);
        var cartesian3=ellipsoid.cartographicToCartesian(cartographic);
        cartesian3List.push(cartesian3)
    })
    var tempId = 'line_'+guid()
    var lineEntity = {
        id:tempId,
        polyline: {
            //位置
            positions: cartesian3List,//Cesium.Cartesian3.fromDegrees(item[0], item[1], 0),
            //线宽
            width: 2,
            //路线颜色
            material: Cesium.Color.BLUE,
            //是否贴地
            clampToGround: true,
        },
    }
    viewer.entities.add(lineEntity);
    terminalMcLineRunEntityIds.push(tempId)
}

//整理数据根据速度求出时间(这个估计是后台实现)
function getRouteTimeData(tempLine,speed,startPosition){
    //根据终端起始点,将tempLine分成两段
    var middleIndex = getRouteTerminalStartPoint(tempLine,startPosition)
    var firstLine = []//第一段轨迹
    var secondLine = []//第二段轨迹
    //--->起始点就在轨迹折点上
    var ifExit = tempLine.filter(item=>item==startPosition)
    if(ifExit.length>0){
        if(middleIndex==0){//是第一个点
            secondLine = tempLine
        }
        else if((middleIndex+1)==tempLine.length){//是最后一个点
            firstLine = tempLine
        }else{
            firstLine = tempLine.slice(0,middleIndex+1);
            secondLine = tempLine.slice(middleIndex,tempLine.length);
        }
    }
    //--->起始点不在折点上,需要算距离形成两个轨迹
    else{
        //起始点与轨迹第一个点点最近
        if(middleIndex==0){
            firstLine = [tempLine[0],startPosition]
            secondLine = tempLine
            secondLine[0] = startPosition
        }
        //起始点与轨迹最末尾点最近
        else if((middleIndex+1)==tempLine.length){
            firstLine = tempLine.slice(0,middleIndex+1);
            firstLine.push(startPosition)
            secondLine = tempLine.slice(middleIndex,tempLine.length);
            secondLine.unshift(startPosition);
        }
        //起始点与轨迹中间某折点距离最近
        else{
            firstLine = tempLine.slice(0,middleIndex+1);
            secondLine = tempLine.slice(middleIndex,tempLine.length);

            //最近点与前折点线段
            var tempLine1 = [firstLine[firstLine.length-2],firstLine[firstLine.length-1]]
            //最近点与后折点线段
            var tempLine2 = [secondLine[0],secondLine[1]]

            //起始点与【最近点与前折点线段】垂直距离
            var line1Distance = getDistanceByLineAndPoint(tempLine1,startPosition)
            //起始点与【最近点与后折点线段】垂直距离
            var line2Distance = getDistanceByLineAndPoint(tempLine2,startPosition)

            //都为0 则此点正好是轨迹点的折点
            if(line1Distance==line2Distance){
                //应用原截取的就可以
            }
            //起始点在后半段【最近点与后折点线段】
            if(line1Distance>line2Distance){
                firstLine.push(startPosition)
                secondLine[0] = startPosition
            }
            //起始点在前半段【最近点与前折点线段】
            if(line1Distance<line2Distance){
                firstLine[firstLine.length-1] = startPosition
                secondLine.unshift(startPosition);
            }
        }
    }

    //先运行后半段,再运行前半段
    var timeLine01 = []
    //整理数据根据速度求出时间
    for(var i=0;i<secondLine.length;i++)
    {
        if(i==0){
            var temp = {
                lon:secondLine[i][0],
                lat:secondLine[i][1],
                height:0,
                time:Cesium.JulianDate.fromDate(new Date(2000, 1, 1, 1))
            }
            timeLine01.push(temp)
        }else{
            var distanceValue = getDistance(secondLine[i-1],secondLine[i])//应该是米
            var interval = distanceValue/(speed/3.6)*10//缓冲速度
            const stop = Cesium.JulianDate.addSeconds(timeLine01[i-1].time,interval,new Cesium.JulianDate());
            var temp = {
                lon:secondLine[i][0],
                lat:secondLine[i][1],
                height:0,
                time:stop
            }
            timeLine01.push(temp)
        }
    }

    var timeLine02 = []
    //整理数据根据速度求出时间
    for(var i=0;i<firstLine.length;i++)
    {
        if(i==0){
            var temp = {
                lon:firstLine[i][0],
                lat:firstLine[i][1],
                height:0,
                time:timeLine01.length>0?timeLine01[timeLine01.length-1].time:Cesium.JulianDate.fromDate(new Date(2000, 1, 1, 1))
            }
            timeLine02.push(temp)
        }else{
            var distanceValue = getDistance(firstLine[i-1],firstLine[i])//应该是米
            var interval = distanceValue/(speed/3.6)*10//缓冲速度
            const stop = Cesium.JulianDate.addSeconds(timeLine02[i-1].time,interval,new Cesium.JulianDate());
            var temp = {
                lon:firstLine[i][0],
                lat:firstLine[i][1],
                height:0,
                time:stop
            }
            timeLine02.push(temp)
        }
    }
    return [timeLine01,timeLine02]
}

//获取轨迹终端在轨迹上最近的点位置(返回Index)
function getRouteTerminalStartPoint(coors,terminalPosition){
    var num = 10;
    var index = -1
    while(true){
        for(var j=0;j<coors.length;j++) {
            let temp = getDistance(terminalPosition,coors[j])
            if(temp<num){
                index = j
                break;
            }
        }
        if(index == -1){
            num = num+num
        }else{
            return index
        }
    }
}

//获取两点之间距离(经纬度)
function getDistance (first, last) {
    var start = Cesium.Cartographic.fromDegrees(first[0], first[1])
    var end = Cesium.Cartographic.fromDegrees(last[0], last[1])
    var geodesic = new Cesium.EllipsoidGeodesic();
    geodesic.setEndPoints(start, end);
    var distance = geodesic.surfaceDistance
    return distance
}

//获取点到线(两点成线)的垂直距离
function getDistanceByLineAndPoint(lineList,point){
    var linePoint01 = Cesium.Cartesian3.fromDegrees(lineList[0][0], lineList[0][1], 0);
    let x1 = linePoint01.x
    let y1 = linePoint01.y
    var linePoint02 = Cesium.Cartesian3.fromDegrees(lineList[1][0], lineList[1][1], 0);
    let x2 = linePoint02.x
    let y2 = linePoint02.y
    var pointTemp = Cesium.Cartesian3.fromDegrees(point[0], point[1], 0);
    let x = pointTemp.x
    let y = pointTemp.y
    // let [[x1, y1], [x2, y2]] = list;
    // let [x, y] = point;

    let b = Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
    let c = Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
    let a = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    
    if(a === 0) return b;	//	如果选取两点为同一个点,则返回已知点和比较点的距离即可

    //  原理:通过半周长和各边长度来计算三角形面积,即海伦公式
    let p = (a + b + c) / 2;  //  半周长,halfPerimeter
    //  根据海伦公式求三角形面积
    let areaSize = Math.abs(Math.sqrt(p * (p - a) * (p - b) * (p - c)));
    //  根据三角形面积公式求证点到直线距离
    return (2 * areaSize) / a;
}