2021-09-22

在 VS Code 中实现Cesium的代码提示,参照链接:VSCode实现Cesium的自动提示功能_liveanddie的博客-CSDN博客

为了防止这片博文链接失效,特此记录。

在Cesium 的源码中,找到CesiumUnminified文件夹中的Cesium.d.ts文件,将其文件放到项目中去。然后在需要代码提示的文件开头,用以下代码引用:

/// <reference path="../Cesium.d.ts"/>

如果使用npm命令安装失败的,可以直接从github上,将提示文件下载下来,或者复制文件中的所有内容,在本地新建一个同名的文件,将内容粘贴进去。然后像上面一样,直接引用这个提示文件,这样,代码自动提示的更多更全面。

github地址:DefinitelyTyped/index.d.ts at master · DefinitelyTyped/DefinitelyTyped · GitHub

在项目中,两个widget切换的时候,设置了一个相机camera的flyto方法的飞行动画。由于两个widget的视角不同,一个是360的俯视角的A界面,一个是357的倾斜视角的B界面。导致出现一个问题,在flyto方法相机还在飞的过程中,跳转到A界面时,相机视角依旧是B界面的视角。

解决办法:

由于A界面用setView方法对视角进行了调整,因此在调整之前,用官方的completeFlight  方法,完成当前的相机飞行并将相机立即移动到其最终目的地。如果没有进行任何飞行,则此功能不执行任何操作。

2021-09-15

创建地图上的entity时,同时设置了billboard和label,导致部分视角只显示billboard广告牌,而没有出现label标签文字。

出现此问题的原因为,用来做背景的billboard图片遮挡住了文字。

解决办法一:(相对有效)

为label的配置,设置显示背景,并且设置一个背景颜色。

此处由于billboard的image为一个黑色边框的纯白色图标,因此label的backgroundColor设置为了纯白色。可以依据情况,设置一个透明颜色的背景

                    billboard: {image: img,width: 19,height: 19},label: { //文字标签text: imgName,font: '500 30px Helvetica',// 15pt monospacescale: 0.45,style: Cesium.LabelStyle.FILL,fillColor: Cesium.Color.BLACK,pixelOffset: new Cesium.Cartesian2(1, -0), //偏移量showBackground: true,                       //显示label的背景backgroundColor: Cesium.Color.WHITE         //设置label的背景颜色},

解决办法二:

更改广告牌billboard的颜色,使其具有一个透明度,让文字可以看到

                    billboard: {image: img,width: 19,height: 19,color: new Cesium.Color(1.0,1.0,1.0,0.8)},label: { //文字标签text: imgName,font: '500 30px Helvetica',// 15pt monospacescale: 0.45,style: Cesium.LabelStyle.FILL,fillColor: Cesium.Color.BLACK,pixelOffset: new Cesium.Cartesian2(1, -0), //偏移量},

2021/12/30

在项目上有这么一个问题,在天地图以及其他地图服务上,绘制的区域都是和底图符合的没有偏移。这些服务大多是从服务器上加载使用的,没有偏移很正常。但是,天地图使用的是网络服务,百度地图也是使用的网络服务,在天地图上是没有偏移的,但是在百度电子地图上是有严重偏移的。

项目中所使用的百度电子地图,使用的类型是:midnight。深色的电子地图。

项目已经是快要完成的阶段,如果因为一张客户不常使用的底图而去改每个模块的代码,有点得不偿失。测试的工作量也很庞大,只能通过框架源码下手了。由于使用的是火星科技平台的Mars3D框架,官方文档中有专门处理百度地图加载的,但试了没有用。于是从源码下手了。

在源码中,有一个“BaiduImageryProvider”底图加载,在里面使用到了一个方法“WebMercatorTilingScheme”。根据Cesium的官方文档,这个方法是用来确定图片如何在椭圆球体上展开的。

主要是这两个参数:rectangleSouthwestInMeters,rectangleNortheastInMeters

在源码中,BaiduImageryProvider里面专门设置了两个变量width和height,用来控制平铺位置。

偏移相差多少只能手动去更改这两个数值,一直到调整差不多没有多少偏移为止。这也是一个很偏门的法子。有时间还得要多去研究这个官方方法。

2022年1月25日

项目界面进行了大改,负责了一个模块的制作。涉及到了一些地图entity的操作。大致的要求是,第一个层级是全国层级,可以看到全国的所有省份在地图上的边界线。第二个层级是省份层级,点击了某个省份后就可以看到这个省份内所有市区的边界线。

因为还存在着另一个要求,当时遇到了一个报错,因为解决了没有截图到,所以记录一下。

要求就是,点击了某一个省份后,其他省份要变成黑色透明无边界线。所以采用的方法是,创建两个customdatasource来分别存放所有的省份entity,全国所有省份内市区的entity。

然后创建了两个方法,分别操作控制这个customdatasource里面的entity的透明度和边界线以及文字的可见度。

当时想法很简单,我先把所有的省份都变成黑色透明无边界线,然后再单独去显示那个被点击的省份。然后报错了,报错了一个webgl渲染失败的报错,说找不到id。

后面才知道,所有的js都走完后,cesium会统一去渲染地图上的实体,并且是异步的。也就是说,虽然按顺序执行了第一个方法渲染全部和第二个方法单独渲染。实际上这两样会产生严重的问题,因为渲染全部很耗费时间,单独渲染可能更快完成,导致了两个渲染操作都要去锁定那个entity,导致有一个方法无法再获取到entity的id。当一个实体entity接受到了更新的状态,cesium就会像锁表一样,不让其他方法去操作,而其他的渲染操作找不到这个实体entity,它就直接报错说webgl渲染失败。

出现这个情况的条件:这些entity开始是不可见的,有一个方法操作全部这些entity,有一个方法操作其中一个单独的entity。

2022年2月26日

公司另外一个项目有几个需求要求这个礼拜完成,把一些经验记录下来。要求模型放置之后可以进行旋转和缩放。通过完成这个功能,让我有了一些新的理解。

放置模型之后,我使用了mars3d提供的divPoint弹窗,在地图上绘制了一个div界面,里面放了按钮和input[type="range"]类型的拖动条。这些都是H5和JS方面的交互,主要记录 Cesium的一些entity操作。

由于创建model是在entity.model中,在其中有一个scale属性,可以直接设置好缩放比列。

最好在创建entity的时候,就设置model的最小缩放和最大缩放。

接下来就是处理旋转功能了。当时创建的时候,并没有为entity.model设置 orientation这个值。导致后面我想要赋值的时候,直接给我报错了。如果放置的模型后续涉及到旋转操作,这个属性就必须要设置。主要定义俯仰角,官方的示例是这个:

​​​​​​Cesium Sandcastle

我使用的俯仰角和旋转体系都是官方提供的那个方法,在官方文档中还有很多。我使用的是

orientation: Cesium.Transforms.headingPitchRollQuaternion(Cesium.Cartesian3.fromDegrees(x, y, 0.0),new Cesium.HeadingPitchRoll(0, 0, 0)
)

它让模型面朝东。

然后使用下面的代码,让它旋转:

  let entity = viewer.entities.getById(entityId);const rotates = Cesium.Transforms.headingPitchRollQuaternion(Cesium.Cartesian3.clone(entity.position._value),new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(value), 0, 0))entity.orientation.setValue(rotates);

通过id或者其他方式,获取到要操作的entity。然后,把原先的entity的实体的position值克隆一份用作参数。

如果你直接传entity的position作为参数会报错,原因大概是不能调用自身的属性作为参数来赋值给自身的其他属性。会矛盾冲突,cesium官方给了克隆方法,那就用呗。

最好的解决办法,应该不是重新赋值。而是通过一个四元数Quaternion来转换现有的值,可惜四元数我没去学,估计和矩阵转换类似的算法吧。

2月27日    自己写的旋转和缩放的功能遇到了BUG,记录一下解决的办法和问题原因。

bug描述:另一个同事之前写好了一个模型按住可以拖动的效果,拖动了之后模型就没办法通过我那个窗口来调整方向了。看了一下日志是报错了,报错原因是不能从undefind里面获取orientation。

最后找到了bug原因,同事的拖动逻辑直接改了entity的orientation值,让其变成了一个回调方法。

顺便记录一下,如何实现模型的点击拖动。不是我写的逻辑,是同事写的逻辑,在此记录一下。

    mapLeftClick_Down: function (event) {this.pointDraged = viewer.scene.pick(event.position);//选取当前的entitythis.leftDownFlag = true;if (this.pointDraged) {viewer.scene.screenSpaceCameraController.enableRotate = false;//锁定相机}},mapLeftClick_Up: function (event) {this.leftDownFlag = false;this.pointDraged = null;viewer.scene.screenSpaceCameraController.enableRotate = true;//解锁相机},mapMouseMove: function (event) {if (this.leftDownFlag === true && this.pointDraged != null) {var ray = viewer.camera.getPickRay(event.endPosition);var cartesian = viewer.scene.globe.pick(ray, viewer.scene);this.pointDraged.id.position = new Cesium.CallbackProperty(function () {return cartesian;}, false);//此处根据具体entity来处理,也可能是pointDraged.id.position=cartesian;}},

主要是给cesium的地图绑定左键点下事件,左键松开事件,和鼠标移动事件。

        // 左键点击事件
this.handler.setInputAction(this.mapLeftClick_Down, Cesium.ScreenSpaceEventType.LEFT_DOWN);
this.handler.setInputAction(this.mapLeftClick_Up, Cesium.ScreenSpaceEventType.LEFT_UP);// 鼠标移动事件
this.handler.setInputAction(this.mapMouseMove, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

加了两个状态判断。主要逻辑是这样的:

左键点下:用pointDraged记录获取鼠标点击选中的目标。用leftDownFlag记录左键已经被摁下。如果确实选中了实体,则锁定相机。

鼠标移动:同时判断两种状态,如果两种状态都存在。则可以更改实体的position值。

左键松开:将两个状态重置,然后将相机解锁。

主要的逻辑在鼠标移动事件里面:

因为鼠标在地图上的移动,也只能获得一个x和y值,而位置在地球上需要一个三维的笛卡尔坐标值。所以需要用到这个方法。获取射线,然后再找到这个射线和地球的焦点。

同事并没有直接给position赋值,而是使用了一个callback方法,每次获取这个值的时候就去执行里面的方法。我的那套逻辑没有考虑到这个问题,所以通过 entity.position._value 是没办法获得的。

改进之后:

  let entity = viewer.entities.getById(entityId);let rotates;if (entity.position._value) {rotates = Cesium.Transforms.headingPitchRollQuaternion(Cesium.Cartesian3.clone(entity.position._value),new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(value), 0, 0))}else {rotates = Cesium.Transforms.headingPitchRollQuaternion(Cesium.Cartesian3.clone(entity.position.getValue()),new Cesium.HeadingPitchRoll(Cesium.Math.toRadians(value), 0, 0))}entity.orientation.setValue(rotates);

其实就是加了一个判断,回过神来才发现,entity.position.getValue() 不是一样可以获取值,直接用entity.position._value 反而不好。代码不想优化了,就这样吧。

2022/04/02  项目中遇到了新的需求,要在右下角实现一个小地图的功能,主要是嫌弃框架的鹰眼图只显示大区域,显示不到具体的街道和社区。

在实现这个功能的时候,参考了原框架自带的鹰眼图,发现人家是引用的另外一个写好的库。一个专门实现2d平面地图的js。名称叫做 leaflet.js。官网不需要翻墙工具,这点还是挺好的。官方网址:http://leafletjs.com

要实现的功能比较复杂,小地图的视角是固定的,俯视显示某一个区域。这个小地图里面,有一个矩形框,显示大地图的视角。

功能思路了比较简单,我也是根据鹰眼图来修改的。

1.使用leaflet.js生成一个平面2d地图,设置底图,并取消显示自带的缩放按钮。

2.固定视角

3.给大地图设置监听

第一步,可以参考官网的方法。我是这样写的:

// L是那个js文件定义好的全局变量,直接用就行
map = L.map('html里面设置的div的id', {center: [经度, 纬度],zoom: 12,    // 缩放等级zoomControl: false,    // 缩放控件不显示attributionControl: false    // 属性控件不显示});
// 设置2d地图服务
L.tileLayer('2d地图的服务地址', {}).addTo(this.map);

缩放等级要根据自己需要去调整。center就是2d地图加载完成后的视角。总得来说,leaflet有的功能cesium都有,毕竟人家是针对手机端做的2d地图。

然后就是后面两步了

map;
showStyle: {color: "#0000ff",weight: 1,fill: !0,stroke: !0,opacity: 1
},
litterMapRectangle = null;
sceneRenderHandler = function(e) {var litterMap_ext = this.getExtent(this.viewer),litterMap_i = L.latLng(litterMap_ext.ymin, litterMap_ext.xmin),litterMap_s = L.latLng(litterMap_ext.ymax, litterMap_ext.xmax),bounds = L.latLngBounds(litterMap_i, litterMap_s);this.map.setView([22.543563, 114.049363],12);this.litterMapRectangle ? this.litterMapRectangle.setBounds(bounds) :this.litterMapRectangle = L.rectangle(bounds, this.showStyle).addTo(this.map)},
getExtent= function(viewer) {var rectangle = viewer.camera.computeViewRectangle(),result = this.getMinMax(rectangle);if (result.xmax < result.xmin) {var s = result.xmax;result.xmax = result.xmin,result.xmin = s}if (result.ymax < result.ymin) {var s = result.ymax;result.ymax = result.ymin,result.ymin = s}return result},
getMinMax= function(rectangle) {var t = Number(Cesium.Math.toDegrees(rectangle.west)).toFixed(6),i = Number(Cesium.Math.toDegrees(rectangle.east)).toFixed(6),n = Number(Cesium.Math.toDegrees(rectangle.north)).toFixed(6);return {xmin: t,xmax: i,ymin: Number(Cesium.Math.toDegrees(rectangle.south)).toFixed(6),ymax: n}},

getExtent方法,是获取大地图的camera相机视角的矩阵,viewer.camera.computeViewRectangle()直接返回一个对象,里面存储了东西南北四个方向,知道了四个方向,自然就围成了一个矩形。然后使用getMinMax转换这四个方向,把它们换算。

调用两次L.latLng,得到两个坐标点。然后再调用L.latLngBounds传入这个两个坐标点,绘制出矩形。为什么不是四个点呢,因为知道了矩形斜对面的那个点了。

自定义的showStyle变量,就是设置这个在小地图上的矩形是啥颜色,多粗,透明什么的。如果需求说要根据视角大小改变这个矩形的颜色,那就每次都刷新矩形的样式。

知道如何刷新绘制2d小地图上的矩形了,接下来就是绑定大地图上的监听事件了。

这里建议这样绑定:

viewer.scene.postRender.addEventListener(

自定义的方法, 方法的上下文);

这个是Cesium自带的方法,每次渲染地图的时候都会触发这个事件。频率大概是1秒10次。

为什么不用camera的change监听呢?一来,比较卡顿,刷新的不够快。二来呢,因为设置过,所以知道很蛋疼。

蛋疼的原因个人猜测是这样的,cesium的相机视角拖动后,即使你松开鼠标了,它相机视角也会平滑的滑行。估计滑行的时候不会触发change事件吧。

我这里使用的自定义方法是:sceneRenderHandler。所以这样给大地图绑定:

// 绑定
this.viewer.scene.postRender.addEventListener(this.sceneRenderHandler, this);// 解绑
this.viewer.scene.postRender.removeEventListener(this.sceneRenderHandler, this);

leaflet把它看作一个小型的cesium,只能说用的时候还是要多去官网看文档。无用的知识增加了...

2022-04-13  项目上有一个路线漫游的功能,需要有连线。这个飞行对象在飞行的时候,有一根线连着某一个固定的点。左侧显示这些点的详情列表,还要动态变化距离。

两个点形成的连线,一个点是固定的,一个点是动态变化的。主要使用的技术是cesium原生的new Cesium.CallbackProperty (callback, isConstant)。

这是个回调方法,每次要获取值的时候,就调用这个方法去更新。哪些地方能用呢,这个看entity里面的每一个graphic就知道了。拿线段polyline来举例。

只要官方文档说,这个值可以是一个Property或者衍生的MaterialProperty ,用我的理解就是,你官方文档显示的值类型名字里面带了property这个单词,就都可以用这个方法。

viewer.entities.add({polyline: {positions: new Cesium.CallbackProperty(function (time, result) {let cartographic = 某个动态变化点的Cartographic值;// 将这个值转换为笛卡尔坐标const lon = Cesium.Math.toDegrees(cartographic.longitude);const lat = Cesium.Math.toDegrees(cartographic.latitude);// 重新赋值return Cesium.Cartesian3.fromDegreesArrayHeights([item.longitude, item.latitude, self.pointHeight, lon, lat, cartographic.height],Cesium.Ellipsoid.WGS84,result)}, false),width: 5,material: self.arrPolylineColor[index%self.arrPolylineColor.length]}
)

上面的代码,是一个比较简单的使用CallbackProperty方法。Cesium官方的使用案例是下面这个链接,官方案例用到了time这个变化的值,固定增长的。

Cesium Sandcastlehttps://sandcastle.cesium.com/?src=Callback%20Property.html一个完整的动态变化线,并且显示线长度,然后左侧还更新列表里面的DOM数据。大概代码:

let arr = 一个数组,里面每一个对象都包含了左侧列表需要渲染的数据。
// 假设这个arr里面的每一个子项都是长这样的对象
{distance: 距离,name: 名字
}
// 假设列表的容器jquery是:$('#rongqi')
左侧列表大概布局是这样
divdiv 名字 /divdiv 距离 /div
/div// 第一步,遍历这个数组
arr.forEach((item,index) => {// 首先,创建需要加入左侧列表的jquery对象const box = $('<div></div>');// 加入名称box.append($('<div>'+item.name+'</div>'));// 加入距离,这里设置一个变量,方便后续的callbackProperty里面可以直接更改。就不用单独设置一个id了。const distance = $('<div>'+item.distance+'</div>');// 将这个盒子加入左侧列表$('#rongqi').append(box);// 然后创建 entityconst entity = viewer.entities.add({// 由于label 使用的位置是整个entity的位置,所以如果动态线需要显示label,这个entity也要设置为动态变化的position: new Cesium.CallbackProperty(回调方法,false),polyline: {// 点位置是动态的,所以也是动态的positions: 回调方法// 线材质,线颜色,线宽度等等....},label: {// 文本值是变化的,所以也是动态的text: new Cesium.CallbackProperty(function (){const juli = 某种方法求得的距离。distance.html(juli);return '一个字符串,让label显示。'},false)}})
})

基本实现逻辑就是这样。需求里面有一个范围限制,超过某一个固定距离,这条线就要隐藏掉。这里我遇到一个很重要的知识点,也让我头疼了一天。

// 代码逻辑还是上面一样,不一样的是单独把entity拿出来设置。forEach(// 设置好了div// 用一个变量存储设置的entityconst entity = viewer.entities.add({});// 将entity的position单独设置entity.position = new Cesium.CallbackProperty(function() {.........之前的代码逻辑.......const juli = 某个方法获得的距离if(超过了某个设定的值){entity.polyline.show = false;entity.label.show = false;}else{entity.polyline.show = true;entity.label.show = true;}.........之前的代码逻辑.......return .......},false)
)

好了,这样写就会遇到一个bug了。这个bug就是,当一个entity里面的graphic,比如说上面代码设置的polyline,label 里面的show设置为false的时候。cesium就不会去执行callbalProperty方法了。准确的说,当一个entity里面所有的graphic的show都不可见的时候,这个entity里面所有的回调方法都不会去执行。亲测

可能是cesium出于性能方面的考虑,你一个polyline都看不见了,就没必要每毫秒都去执行回调方法来更新你polyline的属性了。

但是需求里面说,左侧列表的距离的值还是要变化啊。不能因为polyline和label看不到了就不更新它。于是,我们就需要创建一个billboard,不需要设置其他值。只需要:

billboard: { show: true }

这样,这个entity一直有一个graphic显示。它的position的回调方法就一直会执行。

根据我的测试结果,如果polyline的show是false,那么为polyline设置的动态回调方法也不会去执行。

2022-5-06   另一个项目上使用到了路线规划,地图上取两点,然后调用接口去获取路径信息。

不仅是国内,也要支持国际。国际得话就使用mapbox,国内的就使用高德和百度的路径规划。

功能实现也不是很难,按步骤解决:

1.实现地图取点,地图点击取点和输入经纬度取点

2.请求接口,判断国际路线还是国内路线

3.将接口返回的路径数据显示在地图上

第一个思路也比较简单:

let startPoint = false;        // 起点取点状态
let endPoint = false;          // 终点取点状态// 设置两个点击按钮,分别改变这个状态。这两个状态是互斥的,一个启用另一个就要关闭
$('起点按钮').click(()=>{startPoint=true;endPoint=false;
})在cesium的地图点击事件里面分别进行判断
.....if(startPoint){拿到点击的坐标点在地图上绘制一个显示的东西重新将startPoint设置为falsereturn}else if(endPoint){拿到点击的坐标点在地图上绘制一个显示的东西重新将endPoint设置为falsereturn}
.....// 如果功能要求支持多路径,意味着有多个起点和多个终点。那就设置一个变量,记录当前是第几条路线
1.建立两个数组,分别存储起点entity和终点entity
这样的话,重复取起点的时候就可以清除上一个了
currentLineIndex = 0;
startArr = [];   endArr = [];// 点击事件逻辑更改一下,加判断
if(startPoint){拿到点击的坐标点在地图上绘制一个显示的东西entity// 判断上一个entity是否有,有的话就移除,并重新设置if(startArr[currentLine]){viewer.entities.remove(startArr[currentLine]);}startArr[currentLine] = entity;重新将startPoint设置为falsereturn}else if(endPoint){拿到点击的坐标点在地图上绘制一个显示的东西
// 判断上一个entity是否有,有的话就移除,并重新设置if(endtArr[currentLine]){viewer.entities.remove(endtArr[currentLine]);}endtArr[currentLine] = entity;重新将endPoint设置为falsereturn}

起点和终点显示的entity分别管理虽然麻烦,但易于分别处理,毕竟可以执行输入经纬度取点,就意味着起点终点并不是一样都有。

路线绘制看个人,高德和百度返回的路径规划需要转换,用数组的map方法转换一下再绘制比较好。也用数组来存储绘制好的路线。

这样,有一个公共的变量存储当前的路线,只要取下标就能拿到想要的路线。只有在接口请求成功之后,再改变currentLineIndex的值。

问题来了,路线上需要有一辆车动起来。功能就像场景漫游一样,Cesium官方也有自己的案例:

Cesium Sandcastlehttps://sandcastle.cesium.com/?src=Interpolation.html大概的设计思路是差不多的:

1.时间轴设置开始时间,设置结束时间。

2.entity的position设置

3.entity的label文字的改变。

// 假设接口返回的path路径数据为一个数组,数组每一个成员为一个经纬度数组  [经度,纬度]
pathArr = [........]var property = new Cesium.SampledPositionProperty();
let startTime = new Cesium.JulianDate.fromDate(new Date());    // 当前时间为开始时间
let endiTime = null;
// 初始坐标,用于监听变化
let lastLon = pathArr[0][0];
let lastLat = pathArr[0][1];
// 总距离
let lastDis = 0;// 花费固定的时间,假设是60秒
const timeStep = 60 / pathArr.length;循环遍历
for(let i=0;i<pathArr.length;i++){const lon = pathArr[i];const lat = pathArr[i];// 时间let currentTime = Cesium.JulianDate.addSeconds(startTime .clone(), (i-1 || 0) * timesel, new Cesium.JulianDate());currentTime = Cesium.JulianDate.addSeconds(currentTime , j * timesel, new Cesium.JulianDate());// 位置let position = null;if (lng && lat) position = Cesium.Cartesian3.fromDegrees(lng, lat, 0);if (position && time)property.addSample(time, position);// 记录结束时间if(i == pathArr.length - 1){endiTime = currentTime;}
}let changText = '需要动态变化的文本'
function getText(){return changText ;
}// 创建entityconst entity = viewer.entities.add({positions: property,label: {text: new Cesium.CallbackProperty(getText, false),......其他配置},model:{模型数据}});// 设置时间轴viewer.clock.startTime = startTime .clone();viewer.clock.stopTime = endiTime .clone();viewer.clock.currentTime = startTime .clone();viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;viewer.clock.multiplier = 1;// 设置时间轴监听事件var timeClockTickCallBackFun=function (clocktick) {// 确保时间轴的时间,小于我们设置好的停止时间if (Cesium.JulianDate.lessThanOrEquals(clocktick.currentTime, stop.clone())) {// 当前时间点,entity的坐标let cat=entity.position.getValue(clocktick.currentTime)let ellipsoid = viewer.scene.globe.ellipsoidlet cartographic = ellipsoid.cartesianToCartographic(cat);let clat = Cesium.Math.toDegrees(cartographic.latitude);let clon = Cesium.Math.toDegrees(cartographic.longitude);// 计算离上一个坐标的距离    算法根据需要来变let  dis=Math.sqrt(Math.pow((clon-lastlon),2)+Math.pow((clat-lastlat),2));dis+=lastDis ;lastDis =dis;lastLon =clon;lastLat =clat;changText =  '有了dis距离,想要变化的文字';// 时间回到了开始时间,达成重置条件if (Cesium.JulianDate.equalsEpsilon(clocktick.currentTime, endiTime .clone(),0.1)){lastlon=lon;lastlat=lat;lastdis=0;}}}viewer.clock.onTick.addEventListener(timeClockTickCallBackFun);

因为timeClockTickCallBackFun方法会持有textChange变量,而entity的label也是动态获取的,获取的值也是textChange变量。当textChange被改变的时候,entity上label显示的文字也会改变。

如果是多条路线,那时间轴的起点startTime就使用固定值,不需要每次创建的时候取当前时间为开始时间了。

5月19日

遇到了一个非常奇怪的bug,从一个json文件里面获取了所有公司的位置,里面也有properties存储了这些公司的一些信息描述。然后呢,通过下拉框过滤条件,在地图上显示这些点位。

bug报错是一个渲染报错,“Range Error: Invalid array length”。最后发现这个问题出现在label的text文字上。报这个错误的原因暂时不清楚,只能记录一下。

登录页面需要做一个地球,用夜色底图,加上json文件里面的线条,流动效果。

参考的效果是按github首页上面的地球来做,还要求自转。

cesium地球自转的代码如下:

class GlobeRotate {constructor(viewer, options) {this._viewer = viewer;this._options = options;}// 根据国际天体参考系计算旋转矩阵_icrf() {if (this._viewer.scene.mode !== Cesium.SceneMode.SCENE3D) {return ture;}let icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(this._viewer.clock.currentTime);if (icrfToFixed) {let camera = this._viewer.camera;let offset = Cesium.Cartesian3.clone(camera.position);let transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed);// 偏移相机,否则会场景旋转而地球不转camera.lookAtTransform(transform, offset);}}// 绑定事件_bindEvent() {// 转动的速度设置this._viewer.clock.multiplier = this._options.time * 1000;// 初始化为单位矩阵this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);this._viewer.scene.preRender.addEventListener(this._icrf, this);}// 解除绑定_unbindEvent() {this._viewer.clock.multiplier = 1;this._viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);this._viewer.scene.preRender.removeEventListener(this._icrf, this);}// 开始旋转start() {this._viewer.clock.shouldAnimate = true;this._unbindEvent();this._bindEvent();return this;}// 停止旋转stop() {this._unbindEvent();return this;}
}let globeRotate = new GlobeRotate(viewer,{time: 5});
globeRotate.start();

很早以前在某个网站上找到的,忘记是哪里了,效果很不错。唯一有点问题的地方在于,它是通过设置时间轴clock来实现的。所以当设置的entity里面的某些属性用的是callbackProperty方法时候,参数time有时候会变成undefined。

比如,有需求要求,这个自转的地球需要每隔五秒显示一个点的信息。

思路是这样的:数据来源于json文件,使用datasource创建数据集,将里面的entity的show都设置为false不可见。拿到里面的entities,每五秒根据随机数按下标取值,显示,并记录当前显示的entity作为showEntity。每过五秒将showEntity设置为不可见,将新的随机entity赋值给showEntity。第二步,创建用于显示的entity,里面只包含label,每过五秒就将当前的showEntity的position赋值给它。

思路代码大致如下

  let showEntity = 新建好label样式的entity;let entities = null;let lastEntity = null;let currentClock = viewer.clock.currentTime.clone();
// 在加载完成后,再添加监听事件viewer.dataSources.add(Cesium.GeoJsonDataSource.load('data/globegangtie.json',{markerSize: 20,markerColor: Cesium.Color.GREEN,markerSymbol: 'city'})).then(ds => {ds.entities.values.forEach(entity => {entity.show = false;});entities = ds.entities.values;viewer.entities.add(labelEntity);
// 也可通过clock的onTrick监听viewer.camera.changed.addEventListener(function (percentage) {showLabelEntity();    // 变化显示});})function showLabelEntity() {
// 时间判断超过5秒if (Cesium.JulianDate.secondsDifference(viewer.clock.currentTime,currentClock)/10000 > 5) {currentClock = viewer.clock.currentTime.clone();lastEntity && (lastEntity.show = false);lastEntity = entities[Number.parseInt(entities.length * Math.random())];lastEntity.show = true;let str = '';str += `${lastEntity.properties.project.getValue()}\n${lastEntity.properties.num.getValue()}\n${lastEntity.properties.len.getValue()}`labelEntity.position = lastEntity.position.getValue();labelEntity.label.text = str;}}

Cesium 日常问题整理相关推荐

  1. Oracle存储过程日常工作整理写法实例

    Oracle存储过程日常工作整理写法实例 create or replace package body CUX_HRMIS_SYNC_PKG is /*  *-- Creation time:2016 ...

  2. JavaScript 经典实例日常收集整理(常用经典)

    作者:阿讯小飞 原文来自:脚本之家 跨浏览器添加事件 //跨浏览器添加事件 function addEvent(obj,type,fn){ if(obj.addEventListener){ obj. ...

  3. oracle 处理英文日期格式,日常收集整理oracle trunc 函数处理日期格式(很实用)

    本文给大家分享的oracle trunc 函数处理日期格式的相关知识,非常具有参考价值,具体请看下文说明吧. 复制代码 代码如下: select to_char(sysdate,'yyyy-mm-dd ...

  4. 前端插件日常问题 整理

    1)luara图片轮播 <script type="text/javascript" src="/js/jquery.luara.js"></ ...

  5. linux每日命令,Linux日常命令整理

    一.查看磁盘使用情况 [root@jerrik gc]# df -h 文件系统 容量 已用 可用 已用% 挂载点 /dev/vda1 40G 3.9G 34G 11% / devtmpfs 1.9G ...

  6. 72.日常问题整理[2023/04/06]sql查询年度,月度,上个月数据

    例子: SELECTcreate_time FROMt 如果查询的时间是data类型 //查询年度数据 WHERE YEAR(t.create_time) = YEAR(NOW()) //查询月度数据 ...

  7. HP-UX日常工作整理

    一.             网卡地址配置 1.      配置IP地址 修改/etc/rc.config.d/netconf文件. INTERFACE_NAME[0]=lan0            ...

  8. 62.日常问题整理[2023/02/20]excel获取列个数 getPhysicalNumberOfCells 和 getLastCellNum

    getPhysicalNumberOfCells 与 getLastCellNum的区别 hssfSheet.getLastRowNum()://最后一行行标,比行数小1 hssfSheet.getR ...

  9. linux每日命令,日常整理linux常用命令大全(收藏)

    这篇文章是小编给大家日常收集整理的有关linux命令知识,非常不错,具有参考借鉴价值,需要的朋友可以参考下 这篇文章是小编给大家日常收集整理的有关linux命令知识,非常不错,具有参考借鉴价值,具体内 ...

最新文章

  1. python selenium error “Geckodriver executable needs to be in PATH”
  2. vue cli3.3 以上版本配置vue.config.js
  3. 疫情过后,未来只有三种企业:华为、海尔和腾讯!
  4. Python脚本如何生成Windows可执行文件.exe
  5. POJ1177矩形面积并(矩形切割+括号匹配)
  6. 超低延迟实时流媒体传输技术
  7. 发了篇paper,双非二本的她直博浙大
  8. php是静态语言,Thinkphp静态缓存多语言切换
  9. 互联网传真 传真指令_传真的完整形式是什么?
  10. Oracle 练习题P256
  11. html flex 表单样式,Flexbox 布局的最简单表单的实现
  12. 解决问题:swiper动态加载图片后无法滑动
  13. js ajax 表单异步提交
  14. 知识都是公开的,程序员水平相差巨大的因素有哪些
  15. 安卓打开rpm文件_android 添加文件打开方式,找了很久终于找到了,收藏起来吧...
  16. 单片机原理与接口技术(ESP8266/ESP32)机器人类草稿
  17. Unity酱~ 卡通渲染技术分析(一)
  18. Unity Timeline的使用
  19. 湖北武汉施工员报考施工员安全意识的建立方法建筑七大员报考
  20. php手机界面设计,浅析手机界面设计

热门文章

  1. FTP上传下载端口号
  2. 计算机副教授要求,计算机科学与技术学院副教授一二级岗申报评审条件
  3. 力扣比赛 5454. 统计全 1 子矩形
  4. ENVI 5.1设置影像背景值透明显示
  5. 晶体和晶振(晶体晶振区别、频率误差、负载电容)
  6. 应届毕业生:关于户口、档案、报到证、派遣证、就业协议书、劳动合同、社保等
  7. Code Review Checklist
  8. 层次分析法(AHP)基础概念整理+步骤总结
  9. MySQL 更改root密码
  10. Django搭建简单网站