把Cesium项目中一些代码总结和经验记在这里,遇到了一个记一个,可能会乱,但会定时梳理一下结构。
1. 坐标系转换
Cesium中常用有四种坐标系,Cartesian2(屏幕坐标)、Cartesian3(投影坐标)、Cartography(弧度坐标)、Degree(WGS84坐标系)。
① Cartesian2:就是显示器的屏幕坐标,从左上角开始是(0,0),右下角根据显示器的分辨率而定
② Cartesian3:笛卡尔空间直角坐标系 (Cesium中几乎所有需要输坐标的地方都是用Cartesian3坐标)
③ Cartography弧度坐标就是三角函数坐标,比如一个地方的经纬度是(30°N,120°W)转换成Cartography坐标就是(30°/π,-120°/π)结果为(0.167,-0.667),此时的π不是3.14那个圆周率,是三角函数里面的180°的那个π
④ Degree:即WGS84坐标,Cesium中并没有Degree这个对象,但一些函数也是用Degree来表示经纬度坐标,所以就用degree来代称经纬度坐标吧。
四者之间的相互转换如下
1. Cartesian2的获取方法(只能从Cartesian3转换)
var cartesian2 = Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene,cartesian3)
2. Cartesian3的获取方法(可以从其他三个坐标系转换)
var cartesian3 = scene.globe.pick(scene.camera.getPickRay(cartesian2), scene); // 从屏幕坐标
var cartesian3 = Cesium.Cartesian3.fromDegrees(lng,lat,height); // 从经纬度
var cartesian3 = Cesium.Cartesian3.fromRadians(lng_radians, latitude_radians, height); // 从弧度_1
var cartesain3 = Cesium.Cartographic.toCartesian(cartographic); // 从弧度_2
var cartesain3 = viewer.globe.ellipsoid.cartographicToCartesian(cartographic); // 从弧度_3
3. Cartographic的获取方法(只能从Cartesian3和degree转换)
var lng_radians = Cesium.Math.toRadians(lng) // 从单个经度或纬度
var cartographic = Cesium.Cartographic.fromDegrees(lng,lat,height); // 从经纬度
var cartograhpic = Cesium.Cartographic.fromCartesian(cartesian3) // 从投影_1
var cartograhpi = Cesium.Ellipsoid.WGS84.cartesianToCartographic(cartesian3); // 从投影_2
4. Degree的获取方法(只能从cartography转换,自己都能算:就用180°来乘角弧度)
var lantitude = Cesium.Math.toDegrees(cartographic.lantitude) // cartography.lantitude * 180
var longitude = Cesium.Math.toDegress(cartographic.longitude)
var height = cartographic.height;
2. Entiy和Primitive接口加载三维模型
(1)模型的数据格式
Cesium虽然可以直接加载glb等格式的人工建模的三维模型数据,但还是建议转换为gltf或者3DTiles这两个官方推荐数据格式,加载起来更快,用CesiumLab可以免费转格式。
(2)模型的加载方法
① 是通过官方demo的viewer.entities.add的方法添加一个entity。该方法优势是代码易懂不需要很多的专业知识,只需要输入地理位置信息、模型文件地址等就可以加载,劣势是大批量添加几十上百个模型的性能问题会比较明显。
② 是通过primitive的隐藏方法添加。该方法类似于Three.js和WebGL的结合来添加一个物体,使用Geometry和Attributes(相当于Three的material)来实现一个GeometryInstance(相当于Three的Mesh),并通过矩阵来控制物体的位置信息(像WebGL的matrix一样控制位置),很多个GeometryInstance组合成一个Primitive。优势是性能出色一次添加上百个模型渲染效率都很高,劣势是需要一些WebGL和数学知识,门槛会高一点。
两种添加的代码如下:
【demo 1】通过entity添加 , entity可以通过ModelInstanceCollection大批量添加模型,但是官方没有公开,而且有bug所以就不写了,大批量推荐用下面demo3的primitiveCollection
let longitude = 30
let latitude = 120
let height = 500
let heading = 90 // 头向北方旋转
let pitch = 0
let roll = 0
let model_name = "test_model" // 模型的名字
let model_uri = "./test_model.gltf" // 模型地址
let position = Cesium.Cartesian3.fromDegrees(longtitude, latitude, height); // 地理位置
let hpr = new Cesium.HeadingPitchRoll(
Cesium.Math.toRadians(heading),
Cesium.Math.toRadians(pitch),
Cesium.Math.toRadians(roll)) ;
let orientation = Cesium.Transforms.headingPitchRollQuaternion(position,hpr); // 朝向
viewer.entities.add({
model_name,
position,
orientation,
model: {
uri: model_uri,
scale: 1, // 模型缩放倍数
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 贴地,无论上面第三行的高度数据是多少,都会自动贴到地面
}
});
【demo 2】通过primitive添加
/**
* 通过primitive来加载模型
* @param {Cesium.Viewer} viewer Cesium.Viewer
* @param {*} option 模型配置信息{ position, uri, scale, hpr, name }
* @param {*} clampToGround 是否贴地,默认为true
* position : [lng, lat, height]的数组
* uri: 模型的地址
* scale: 缩放大小
* hpr: Cesium.HeadingPitchRoll
* name: 模型的名字
*/
export async function addModelAsPrimitive(viewer, option, clampToGround) {
let scene = viewer.scene;
let { position, uri, scale, hpr, name } = option;
let height;
if(clampToGround) // 找到某点的地形高度
{
let carto = Cesium.Cartographic.fromDegrees(position[0],position[1]);
const updatedCarto = await Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [carto]);
height = updatedCarto[0].height;
}
else
{
height = position[2]
}
let cartesian3 = Cesium.Cartesian3.fromDegrees(
position[0],
position[1],
height
);
let headingPitchRoll = new Cesium.HeadingPitchRoll(
Cesium.Math.toRadians(hpr[0]),
Cesium.Math.toRadians(hpr[1]),
Cesium.Math.toRadians(hpr[2])
);
let matrix4 = Cesium.Transforms.headingPitchRollToFixedFrame(
cartesian3,
headingPitchRoll
);
let model = Cesium.Model.fromGltf({
id: name,
url: uri,
modelMatrix: matrix4,
scale,
});
scene.primitives.add(model);
}
③ 通过primitiveCollection大批量添加
/**
* 批量添加模型
* @param {*} viewer cesium.viewer
* @param {*} positionArray 位置信息数组, 位置信息结构:{position:[lng , lat , height], hpr:[h,p,r]}
* @param {*} modelUrl 模型的地址
* @param {*} scale 模型缩放大小
* @param {*} clampToGround 是否贴地,默认为true
*/
export async function batchAddModelAsPrimitive(
viewer,
positionArray,
modelUrl,
scale,
clampToGround = true
) {
let matrix4Collection = [];
for (const item of positionArray) {
const { position, hpr } = item;
let height;
if (clampToGround)
{
let cartographic = Cesium.Cartographic.fromDegrees(position[0],position[1]);
const updatedCartographics = await Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [cartographic]);
height = updatedCartographics[0].height;
}
else{
height = position[2]
}
let cartesian3 = Cesium.Cartesian3.fromDegrees(
position[0],
position[1],
height
);
let headingPitchRoll = new Cesium.HeadingPitchRoll(
Cesium.Math.toRadians(hpr[0]),
Cesium.Math.toRadians(hpr[1]),
Cesium.Math.toRadians(hpr[2])
);
let matrix4 = Cesium.Transforms.headingPitchRollToFixedFrame(
cartesian3,
headingPitchRoll
);
Cesium.Matrix4.multiplyByUniformScale(
matrix4,
scale, // 缩放大小
matrix4
);
matrix4Collection.push({
modelMatrix: matrix4,
});
}
viewer.scene.primitives.add(
new Cesium.ModelInstanceCollection({ // 模型实例集合,隐藏的API
url: modelUrl,
instances: matrix4Collection,
})
);
}
* primitive的组织结构 :
|- primitives
|-- globe(terrain imagery)
|-- model
|-- billboard | label | point | polyline
|-- primitive(geometry,apperance)
|-- viewportquad
|- groundPrimitives
|-- ...
viewer.scene.primitives(PrimitiveCollection)返回的primitives里面还可以再添加 PrimitiveCollection,形成多级层次的primitives。
3. Datasource接口加载geojson/topojson文件并标注文字
用Cesium.GeoJsonDatasource.load加载geojson/topojson数据后,返回值中的entities属性是没有position数据的,导致给这些这些entities设置label标签之类的操作都无效,因为没有位置信息,所以必须要吧entity的postion算出来,具体加载 + 显示标注代码如下:
function loadGeoJson(viewer, url){
const load_promise = new Cesium.GeoJsonDatasource.load(url,{
clampToGround = true; // 自动贴地
});
load_promise.then(datasource=>{
const entities = datasource.entities.value;
for(let index = 0 ; index < entities.lenght ; index ++){
const entity = entities[index];
entity.position = computeEntityPolygonInnerPoint(entity); // 算出中心点位置
entity.label = { // 有position属性,设置label标注才有效果
text: $"polygon_{index}",
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
}
viewer.entities.add(entity);
}
})
}
/**
* 根据polygon的形状算出中心点位置
* @para {Cesium.Entity} polygonEntity 一个poygon的entity
* @return {Cesium.Cartesian3}
*/
function computeEntityPolygonInnerPoint(polygonEntity){
let polyPositions = polygonEntity.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions; //polygon的节点集合
let polyCenter = Cesium.BoundingSphere.fromPoints(polyPositions).center; //算出几何中心点
polyCenter = Cesium.Ellipsoid.WGS84.scaleToGeodeticSurface(polyCenter); // 转为大地坐标
return polyCenter;
}
4. Cesium的生命周期
Cesium也有类似Vue和React的生命周期钩子函数,下面以Vue的钩子函数来做一个类比:
mounted: scene.postRender.addEventListener(()=>{});
/**
可实现同样效果的还有:
① viewer.clock.onTick.addEventListener((clock)=>{});
② 回调:给回调传个Julian时间,如果result发生变化,就触发
Cesium.CallbackProperty( (JulianDate,result)=>{}, false);
*/
beforeMount: scene.preRender
beforeUpdate: scene.preUpdate
Updated: scene.postUpdate
5. 获取位置/物体
5.1 点击拾取坐标
new Cesium.ScreenSpaceEventHandler(scene.canvas).setInputAction(event=>{
let cartesian2 = event.position;
let obj_cartesian3 = scene.pickPosition(cartesian3) // 获取点击物体的坐标
let terrain_cartesian3 = scene.globe.pick(viewer.camera.getPickRay(cartesian2),scene); // 地形坐标
},Cesium.ScreenSpaceEventType.LEFT_CLICK)
5.2 获取指定坐标
// 获取二维点的相对位置
let entity = viewer.entities.add(
position:cartesian3_position,
model:{
url:'....'
});
let cartographic = Cesium.Cartographic.fromCartesian(entity.position)
let height = scene.sampleHeight(cartograpy,[entity]); // 获得点cartograpy相对于entity的高度
let cartesian3 = scene.clampToHeight(entity.position, [entity]); // 获得
// 获取某二维点的地形高度
viewer.scene.globe.getHeight(cartographic); // ① 适合实时获取(同步)
Cesium.sampleTerrain(terrainProvider, level, positions) ; // ② 获取高程(异步),level精度级别
Cesium.sampleTerrainMostDetailed(terrainProvider, positions) ; ③ 最高精度获取高程(异步)
5.3 获取点击的物体
new Cesium.ScreenSpaceEventHandler(scene.canvas).setInputAction(event=>{
let cartesian2 = event.position;
let object = scene.pick(cartesian2) // 获取点击的物体
},Cesium.ScreenSpaceEventType.LEFT_CLICK)
5.4 视角坐标
let camera = viewer.camera;
let cartesian3 = camera.position;
let {heading, pitch, roll} = camera;
6. CZML
Cesium Language的缩写。不用代码就可以执行一些动画。下面链接为官方czml的demo,可以实现一个CZML动画。
详细参考Cesium的官方demo Multi-part CZML - Cesium Sandcastle , demo调用的CZML文件路径为 https://sandcastle.cesium.com/SampleData/MultipartVehicle_part1.czml
7. Billboard Label Point PolyLine等
以billboard举例,其他差不多。
(1)以entity形式添加,直接往viewer.entities里面怼
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
billboard: {
image: "../images/Cesium_Logo_overlay.png", // default: undefined
show: true, // default
pixelOffset: new Cesium.Cartesian2(0, -50), // default: (0, 0)
eyeOffset: new Cesium.Cartesian3(0.0, 0.0, 0.0), // default
horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // default
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // default: CENTER
scale: 2.0, // default: 1.0
color: Cesium.Color.LIME, // default: WHITE
rotation: Cesium.Math.PI_OVER_FOUR, // default: 0.0
alignedAxis: Cesium.Cartesian3.ZERO, // default
width: 100, // default: undefined
height: 25, // default: undefined
},
})
(2)以primitive形式添加,先创建billboardCollection的primitive,然后往primitive里面添加
let billboards = scene.primitives.add(new Cesium.BillboardCollection());
billboards.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(new Cesium.Cartesian3.fromDegree(105,30,500)); // 设置ENU位置矩阵
billboards.add({
image:'xxx',
position:new Cesium.Cartesian3(1,2,3) // east,north,up的相对位置
image:'xxx.png' || "xxx.gif"
sizeInMeters:true , // 近大远小
scaleByDistance:Cesium.NearFarScale , // 实现类似切片z级别的显示大小效果
translucencyByDistance:Cesium.NearFarScale, // 远了就可以控制不显示
pixelOffset: new Cesium.Cartesian2, // 偏移
pixelOffsetByDistance: NearFarScale, // 动态偏移
})
8. Primitive详解
8.1 primitive的详细结构
primitive可以看作是three.js里面的mesh的集合,mesh由geometry和material构成,primitive由geometry数组和material的超集apperance构成,结构如下:
primitive
{
geometryInstances:
[
new Cesium.GeometryInstance
{
id,
geometry: demensions,
vertex,
// 几何平面没有modelMatrix,需要抬升高度就用extrudedHeight属性
modelMatrix: cartesian4,
attributes: color, ...
},
new Cesium.GeometryInstance{...},
...
]
appearance:
{
material: ... // 主要是material属性,其他都是着色器等的配置
}
})
8.2 手动添加一个primitive
以BoxGeometry为例,可以参考第7点的第(2)部分,是一样的流程:
const boxGeo = new Cesium.BoxGeometry.fromDemensions({
vertex,
demensions:new Cesium.Cartesian3(x,y,z) // 大小
});
const geoInstance_1 = new Cesium.GeometryInstance({
geometry: boxGeo,
modelMatrix: matrix4, // 位置
attributes: {
color:xxx
}
});
let primitive = new Cesium.Primitive({
geometryInstances: [geoInstance_1,.....,geoInstance_x],
appearance: xxxx
})
viewer.scene.primitives.add(primitive);
其他的Primitive用法和之前的BillboardCollection 一样,往 scene.primitives 里面添加后返回实例,往实例里面 add 一些对象就可以了。
8.3 Primitive评价
优势:速度快,没有特殊的炫酷效果可以使用。
劣势:做不出酷炫的效果,如粒子效果等。
8.4 补充知识
① geometry 的 arctype : GEODESIC 球面线(最短距离) ; none 从地下传过去 ; rhumb 罗盘方位(线段每一处的切线都指向一个位置,和经纬度线类似)
9. 3DTiles
3dtiles是一个统称并没有 *.3dtiles这个数据格式,其具体的数据格式是由.b3dm .i3dm pnts cmpt和.json组成的
9.1 加载
let 3dtiles = new Cesium.Cesium3Dtiles({
url:'xxxx.json',
// 剖切分析
clippingPlanes: new Cesium.ClippingPlaneCollection({
planes:[
new Cesium.ClippingPlant(
new Cesium.Cartesian3(0,0,-1), // 剖面法向朝向
0 // 离模型中心点的距离
)
]
edgeWidth:1
}),
});
viewer.primitives.add(3dtiels).then(datasource=>{...})
9.2 单体化
详见我的另外一篇博客 Cesium纯代码实现3DTiles单体化点击查询_ckyyy~的博客-CSDN博客_cesium查询
9.3 补充
① Cesium.PostProgressStageLibrary // 后置操作,cesium 封装的 webgl 的东西,可以给拾取的 3dtiels 对象外轮廓加样式
② Cesium.Cesium3DTilesStyle // 设置加载样式,不同高度的楼颜色不一样
③ 3DTiles Inspector 的 demo,展示了各种底层调试渲染 3dtiles 数据的性能控制的东西,如:显示的精细度、冻结某区域不进行渲染、查看帧数、显存占用等
10. Appearance
作用:给primitive设置外观
10. 基本使用
primitive.appearance.material = new Ceisum.Material({
....
})
10.2 PerInstanceColorAppearance 的使用
new Cesium.Primitive({
geometryInstances:[
new Cesium.GeometryInstance(
geometry:new Cesium.xxxGeomrtry({
position:...,
}),
matrix: ... ,
attributes:{
// 这里设置color的时候,primitive的appearance一般都是PerInstanceColor那个
color: Cesium.ColorGeometryInstanceAttribute.xxxx
}
),
..] ,
appearance: new Cesium.PerInstannceColorAppearance({
...
})
})
11. property 时间状态管理
这个属性是进行时空数据可视化控制,是一个暂时没有被重视的属性,但是随着GIS不断往时空方向发展的趋势,这个属性会逐渐被重视
含义:根据时间关联,在不同的时间编辑展示不同的属性
解释:使用起来和 css 的 transform 差不多,主要是设置关键时间的状态,然后自动执行动画。
参考: https://blog.csdn.net/u012833166/article/details/103614546
12. 与Three.js, Echarts融合
12.1 与three.js融合
参考文档:CesiumJs+ThreeJs实测_李小喵Y的博客-CSDN博客_cesium three
实现原理:统一坐标系、渲染频率、相机位置
可以直接封成 api 使用,封装好的API源代码如下:
var ThreeContainer = document.getElementById("threediv");
var cesium = {
viewer: null,
}; // cesium的viwer初始化完以后放到这里
var three = {
renderer: null,
camera: null,
scene: null,
}; // three的环境启动完放在这里
class _3DObject { // three的对象实例化后用这个类包装一下
threeMesh = null;
minWGS84 = null;
maxWGS84 = null;
}
var _3Dobjects = []; // 保存上面的_3DObject的数组
/**
* 这下面的函数都不需要改,著需要根据需求改上面的,但是要注意“核心函数1”的调用时机。
*/
// 核心函数1:把threejs物体添加到场景。
// 如果_3DObject或_3DObjects有变化则还需调用该函数
function render3DObjects(){
var cartToVec = function (cart) {
return new THREE.Vector3(cart.x, cart.y, cart.z);
};
for (var id in _3Dobjects) {
minWGS84 = _3Dobjects[id].minWGS84;
maxWGS84 = _3Dobjects[id].maxWGS84;
var center = Cesium.Cartesian3.fromDegrees(
(minWGS84[0] + maxWGS84[0]) / 2,
(minWGS84[1] + maxWGS84[1]) / 2
);
var centerHigh = Cesium.Cartesian3.fromDegrees(
(minWGS84[0] + maxWGS84[0]) / 2,
(minWGS84[1] + maxWGS84[1]) / 2,
1
);
// use direction from bottom left to top left as up-vector
var bottomLeft = cartToVec(
Cesium.Cartesian3.fromDegrees(minWGS84[0], minWGS84[1])
);
var topLeft = cartToVec(
Cesium.Cartesian3.fromDegrees(minWGS84[0], maxWGS84[1])
);
var latDir = new THREE.Vector3()
.subVectors(bottomLeft, topLeft)
.normalize();
// configure entity position and orientation
_3Dobjects[id].threeMesh.position.copy(center);
_3Dobjects[id].threeMesh.lookAt(centerHigh);
_3Dobjects[id].threeMesh.up.copy(latDir);
}
}
// 核心函数2:three.js的gl环境和Cesium统一。不用单独调用,在loop里面调
function bindingThreeToCesium(three,_3Dobjects,ThreeContainer,viewer) {
// 渲染threejs物体
var width = ThreeContainer.clientWidth;
var height = ThreeContainer.clientHeight;
three.renderer.setSize(width, height);
three.renderer.render(three.scene, three.camera);
// 调整视场角
three.camera.fov = Cesium.Math.toDegrees(cesium.viewer.camera.frustum.fovy);
three.camera.updateProjectionMatrix();
// 渲染相机
three.camera.matrixAutoUpdate = false;
var cvm = viewer.camera.viewMatrix;
var civm = viewer.camera.inverseViewMatrix;
three.camera.lookAt(new THREE.Vector3(0, 0, 0));
three.camera.matrixWorld.set(
civm[0],civm[4],civm[8],civm[12],
civm[1],civm[5],civm[9],civm[13],
civm[2],civm[6],civm[10],civm[14],
civm[3],civm[7],civm[11],civm[15]
);
three.camera.matrixWorldInverse.set(
cvm[0], cvm[4],cvm[8],cvm[12],
cvm[1],cvm[5],cvm[9],cvm[13],
cvm[2],cvm[6],cvm[10],cvm[14],
cvm[3],cvm[7],cvm[11],cvm[15]
);
// three.camera.lookAt(new THREE.Vector3(0, 0, 0));
var aspect = width / height;
three.camera.aspect = aspect;
three.camera.updateProjectionMatrix();
}
// 核心函数3:同步cesiumContainer和threeContainer的canvas渲染频率。只用最开始调一次。
function loop() {
requestAnimationFrame(loop);
cesium.viewer.render();
bindingThreeToCesium(three,_3Dobjects,ThreeContainer,cesium.viewer);
}
// 调用
render3DObjects();
loop();
12.2 与echarts融合
也是封装好以后使用,使用的代码如下:
let echartlayer1 = new EchartsLayer(viewer, option)
echartlayer1.show()|hide() // 控制显隐
引用的代码如下:
var GLMapCoordSys = function (GLMap, api) {
(this._GLMap = GLMap),
(this.dimensions = ["lng", "lat"]),
(this._mapOffset = [0, 0]),
(this._api = api);
};
GLMapCoordSys.prototype.dimensions = ["lng", "lat"];
GLMapCoordSys.prototype.setMapOffset = function (mapOffset) {
this._mapOffset = mapOffset;
};
GLMapCoordSys.prototype.getBMap = function () {
return this._GLMap;
};
GLMapCoordSys.prototype.dataToPoint = function (data) {
var e = [99999, 99999],
i = Cesium.Cartesian3.fromDegrees(data[0], data[1]);
if (!i) return e;
var n = this._GLMap.cartesianToCanvasCoordinates(i);
if (!n) return e;
return (
!(
Cesium.Cartesian3.angleBetween(this._GLMap.camera.position, i) >
Cesium.Math.toRadians(75)
) && [n.x - this._mapOffset[0], n.y - this._mapOffset[1]]
);
};
GLMapCoordSys.prototype.pointToData = function (pt) {
var mapOffset = this._mapOffset;
pt = this._bmap.project([pt[0] + mapOffset[0], pt[1] + mapOffset[1]]);
return [pt.lng, pt.lat];
};
GLMapCoordSys.prototype.getViewRect = function () {
var api = this._api;
return new echarts.graphic.BoundingRect(
0,
0,
api.getWidth(),
api.getHeight()
);
};
GLMapCoordSys.prototype.getRoamTransform = function () {
return echarts.matrix.create();
};
GLMapCoordSys.dimensions = GLMapCoordSys.prototype.dimensions;
GLMapCoordSys.create = function (ecModel, api) {
var coordSys;
ecModel.eachComponent("GLMap", function (GLMapModel) {
var viewportRoot = api.getZr().painter.getViewportRoot();
var GLMap = echarts.glMap;
coordSys = new GLMapCoordSys(GLMap, api);
coordSys.setMapOffset(GLMapModel.__mapOffset || [0, 0]);
GLMapModel.coordinateSystem = coordSys;
});
ecModel.eachSeries(function (seriesModel) {
if (seriesModel.get("coordinateSystem") === "GLMap") {
seriesModel.coordinateSystem = coordSys;
}
});
};
var EchartsLayer = function (map, options) {
this._map = map;
this._overlay = this._createChartOverlay();
if (options) {
this._registerMap();
}
this._overlay.setOption(options || {});
};
EchartsLayer.prototype._registerMap = function () {
if (!this._isRegistered) {
echarts.registerCoordinateSystem("GLMap", GLMapCoordSys),
echarts.registerAction(
{
type: "GLMapRoam",
event: "GLMapRoam",
update: "updateLayout",
},
function (t, e) {}
),
echarts.extendComponentModel({
type: "GLMap",
getBMap: function () {
return this.__GLMap;
},
defaultOption: { roam: !1 },
}),
echarts.extendComponentView({
type: "GLMap",
init: function (t, e) {
(this.api = e),
echarts.glMap.postRender.addEventListener(this.moveHandler, this);
},
moveHandler: function (t, e) {
this.api.dispatchAction({ type: "GLMapRoam" });
},
render: function (t, e, i) {},
dispose: function (t) {
echarts.glMap.postRender.removeEventListener(this.moveHandler, this);
},
});
this._isRegistered = true;
}
};
EchartsLayer.prototype._createChartOverlay = function () {
var scene = this._map.scene;
scene.canvas.setAttribute("tabIndex", 0);
const ele = document.createElement("div");
return (
(ele.style.position = "absolute"),
(ele.style.top = "0px"),
(ele.style.left = "0px"),
(ele.style.zIndex = 999),
(ele.style.width = scene.canvas.parentNode.offsetWidth + "px"),
(ele.style.height = scene.canvas.parentNode.offsetHeight + "px"),
(ele.style.pointerEvents = "none"),
ele.setAttribute("id", "echarts"),
ele.setAttribute("class", "echartMap"),
this._map.container.appendChild(ele),
(this._echartsContainer = ele),
(echarts.glMap = scene),
(this._chart = echarts.init(ele))
);
this.resize();
};
EchartsLayer.prototype.dispose = function () {
this._echartsContainer &&
(this._map.container.removeChild(this._echartsContainer),
(this._echartsContainer = null)),
this._overlay && (this._overlay.dispose(), (this._overlay = null));
};
EchartsLayer.prototype.updateOverlay = function (t) {
this._overlay && this._overlay.setOption(t);
};
EchartsLayer.prototype.getMap = function () {
return this._map;
};
EchartsLayer.prototype.getOverlay = function () {
return this._overlay;
};
EchartsLayer.prototype.show = function () {
this._echartsContainer &&
(this._echartsContainer.style.visibility = "visible");
};
EchartsLayer.prototype.hide = function () {
this._echartsContainer &&
(this._echartsContainer.style.visibility = "hidden");
};
EchartsLayer.prototype.remove = function () {
this._chart.clear();
if (this._echartsContainer.parentNode)
this._echartsContainer.parentNode.removeChild(this._echartsContainer);
this._map = undefined;
};
EchartsLayer.prototype.resize = function () {
const me = this;
window.onresize = function () {
const scene = me._map.scene;
me._echartsContainer.style.width = scene.canvas.style.width;
me._echartsContainer.style.height = scene.canvas.style.height;
me._chart.resize();
};
};
13. turf.js
cesium并没有携带GIS的空间分析能力,如缓冲区分析、叠加分析、拓扑分析、聚类分析等,turf.js库携带了这个能力,让不需要访问服务器就可以在前端实现空间分析功能,参考网站如下:GET START | Turf.js中文网 (fenxianglu.cn)
14. 深度测试
【深度】: 该像素点在 3D 世界中距离摄像机的距离 Z 值,范围在[0,1]之间,值越小表示越靠近观察者,值越⼤表示远离观察者。
【深度缓冲区】:就是一块内存区域,专门存储着每个像素点(绘制在屏幕上的)深度值.深度值(Z 值)越大, 则离摄像机就越远。
【深度测试】:深度缓冲区(DepthBuffer)和颜色缓存区(ColorBuffer)是对应的.颜⾊缓存区存储像素的颜色信息,而深度缓冲区存储像素的深度信息. 在决定是否绘制一个物体表面时, ⾸先要将表面对应的像素的深度值与当前深度缓冲区中的值进行⽐较. 如果大于深度缓冲区中的值,则丢弃这部分.否则利用这个像素对应的深度值和颜色值.分别更新深度缓冲区和颜⾊缓存区. 这个过程称为”深度测试。
【意义】:开启深度测试后,会在缓存中进行对比部分更新画面,不会完全重绘画面,最终导致两个深度值很接近的面会出现闪烁。关闭该功能后,会重新绘制画面。
15. 视角与飞行控制
1. 飞行控制
viewer.flyto() 飞行到某个位置
viewer.camera.flyto() 飞到某个物体上,里面有个flyOverLongitude是飞行途中经过某点
viewer.camera.setview() 直接过去
2. 视角控制
viewer.camera.lookAt() 锁定视角中心位置
camera.lookAtTransform(Cesium.Matrix4.IDENTITY); 取消锁定某位置(锁定到地球球心)
上面两个例举的是常用的视角控制方法,更详细的看这篇文章Cesium中Camera视角飞行或者缩放到指定点并居中的方法_怡暘的博客-CSDN博客_cesium模型随视角缩放
16. Scene常用设置
// 改变背景颜色
scene.skybox.show = false ;
scene.backgroundColor = Color.Red;
// 三维效果优化
logarithmicDepthBuffer //防止面片闪烁(webgl的概念)
// 精确高度值获取
clampToHeightMostDetailed(cartesain3,object(3dtiles,primitive,entities),accuracy(0.1))
// pick
pick(windowPosition,witdh,height) // 缓冲区pick
pickPostion(windowPosition,result) // 点精确pick点位离视角最近的一个
drillPick(windowPosition,limit,width,height) // pick那个点位所有的对象,如那个点位有个3dtile又叠了个model,则返回这两个东西的组成的数组
scene在底层其实是放在cesiumwidget中的,viewer.scene其实是快捷方式访问,真正的结构如下图所示: