大道废

一切有为法,如梦幻泡影,如露亦如电,当作如是观.

0%

cesium学习笔记

2018.05.28

至少在Chrome浏览器中,Cesium 的 Double_Click 事件触发之前会触发两次 Click 事件,所以并不推荐使用 Double_Click 事件来处理操作

2018.05.21

Geometry 不是地形的一部分,但 3DTileset 的 model 是

Geometry的缺省boundingsphere 是整个地球

Viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin) 添加后会造成

1
2
3
4
5
6
7
8
9
10
viewer.screenSpaceEventHandler.setInputAction((event) => {
let picked = viewer.scene.pick(event.endPosition);
if (picked !== prePicked) {
if (prePicked) prePicked.color = preStyle;
if (picked) {
prePicked = picked, preStyle = picked.color;
picked.color = Cesium.Color.YELLOW.withAlpha(0.5);
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

这段代码出问题,它好像会改变 preStyle 的颜色

2018.05.14

https://cesium.com/blog/2018/05/01/cesium-version-1.45-released/

测试线段长度

1
2
3
4
5
6
7
8
9
10
11
12
let hierarchy = entity.polyline.positions._value;
hierarchy = Cesium.PolylinePipeline.generateArc({
positions: hierarchy,
});
let vector = new Cesium.Cartesian3();
let distance = 0;
for (let i = 3; i < hierarchy.length; i += 3) {
vector.x = hierarchy[i] - hierarchy[i - 3];
vector.y = hierarchy[i + 1] - hierarchy[i - 2];
vector.z = hierarchy[i + 2] - hierarchy[i - 1];
distance += Cesium.Cartesian3.magnitude(vector);
}

测试polygon的面积

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Get the polygon from your "entity"
var polygon = theEntity.polygon;
var hierarchy = polygon.hierarchy._value;
// "indices" here defines an array, elements of which defines the indice of a vector
// defining one corner of a triangle. Add up the areas of those triangles to get
// an approximate area for the polygon
var indices = Cesium.PolygonPipeline.triangulate(hierarchy.positions, hierarchy.holes);
var area = 0; // In square kilometers
for (var i = 0; i < indices.length; i += 3) {
  var vector1 = hierarchy.positions[indices[i]];
  var vector2 = hierarchy.positions[indices[i+1]];
  var vector3 = hierarchy.positions[indices[i+2]];
  // These vectors define the sides of a parallelogram (double the size of the triangle)
  var vectorC = Cesium.Cartesian3.subtract(vector2, vector1, new Cesium.Cartesian3());
  var vectorD = Cesium.Cartesian3.subtract(vector3, vector1, new Cesium.Cartesian3());
  // Area of parallelogram is the cross product of the vectors defining its sides
  var areaVector = Cesium.Cartesian3.cross(vectorC, vectorD, new Cesium.Cartesian3());
  
  // Area of the triangle is just half the area of the parallelogram, add it to the sum.
  area += Cesium.Cartesian3.magnitude(areaVector)/2.0;
}

来自 https://groups.google.com/forum/#!topic/cesium-dev/EimmL-poCDI

3DTileset不是一次性加入全部tile,所以要定位到某个tile,必须在外部保存tile的某个属性和它的位置地址,把视角平移过去后,在3DTileset加载完成后。才可以通过 3DTileset.tileVisible 找到有这个属性的 feature 然后着色,或者通过 Cesium3DTileStyle 着色。

根据建筑物坐标与高度,定位并调整视角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let long = -1.2917727072831369, lat = 0.7105749513910979, height = 547.7591871983744;
let heading = viewer.camera.heading, pitch = viewer.camera.pitch;

let position = viewer.scene.globe.ellipsoid.cartographicToCartesian(new Cesium.Cartographic(long, lat, 0.5 * height));
let offset = offsetFromHeadingPitchRange(heading, pitch, height * 2.0);
let transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
Cesium.Matrix4.multiplyByPoint(transform, offset, position);

function offsetFromHeadingPitchRange(heading, pitch, range) {
pitch = Cesium.Math.clamp(pitch, -Cesium.Math.PI_OVER_TWO, Cesium.Math.PI_OVER_TWO);
heading = Cesium.Math.zeroToTwoPi(heading) - Cesium.Math.PI_OVER_TWO;
var pitchQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Y, -pitch);
var headingQuat = Cesium.Quaternion.fromAxisAngle(Cesium.Cartesian3.UNIT_Z, -heading);
var rotQuat = Cesium.Quaternion.multiply(headingQuat, pitchQuat, headingQuat);
var rotMatrix = Cesium.Matrix3.fromQuaternion(rotQuat);
var offset = Cesium.Cartesian3.clone(Cesium.Cartesian3.UNIT_X);
Cesium.Matrix3.multiplyByVector(rotMatrix, offset, offset);
Cesium.Cartesian3.negate(offset, offset);
Cesium.Cartesian3.multiplyByScalar(offset, range, offset);
return offset;
}

例子:
http://www.virtualcitysystems.de/en/

http://nrw.virtualcitymap.de/#/

http://www.3dcitydb.net/3dcitydb/fileadmin/3DWebClient/index.html

https://cesiumjs.org/demos/

http://cybercity3d.s3-website-us-east-1.amazonaws.com/?city=WashingtonDC

2018.05.08

设置Home的位置

1
2
Cesium.Camera.DEFAULT_VIEW_FACTOR = -0.3;
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = tileset._root._boundingVolume.rectangle;

Camera.lookAt 会锁住 camera 的视点,让鼠标不能移动 camera,需要 viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)

距离 3DTileset 1.5倍 boundingSpere.radius,
高度为 1.5 * distance / Math.tan(Cesium.Math.toRadians(70.0)),
heading: Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-20.0),
roll: 0.0,
时候,角度比较好

通过 Camera.pickEllipsoid 得到的坐标是 WGS84 坐标系下的,在有地形信息的情况下就会偏移,就算通过 sampleTerrianMostDetailed 转化过也一样

1
2
3
4
5
6
7
8
9
10
11
12
13
let coordinate = viewer.camera.pickEllipsoid(event.position, viewer.scene.globe.ellipsoid);
coordinate = Cesium.Cartographic.fromCartesian(coordinate);
Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [coordinate]).then((samples) => {
for (let sample of samples) sample.height += 10.0;
viewer.entities.add({
position: Cesium.Cartographic.toCartesian(samples[0], viewer.scene.globe.ellipsoid),
point: {
pixelSize: 5,
color: Cesium.Color.GREEN,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
});
});

通过下面这个 Ray 相交得到的坐标是可以有地形高度的,取决于得到是有没有加载地形信息,如果有则有高度信息,没有就没有

1
2
3
4
5
6
7
8
9
10
let ray = viewer.camera.getPickRay(event.position);
let positon = viewer.scene.globe.pick(ray, viewer.scene);
viewer.entities.add({
position: positon,
point: {
pixelSize: 5,
color: Cesium.Color.LIME,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
});

screenSpaceEventHandler.setInputAction 居然一个类型只可以有一个事件监听

得到两点方向的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//Add button to View Aircraft at a Fixed Angle relative to aircraft
Sandcastle.addToolbarButton('View Aircraft Fixed Angle', function () {
viewer.trackedEntity = undefined;
viewer.clock.onTick.addEventListener(function (clock) {

//get 2 positions close together timewise
var CC3 = Cesium.Cartesian3;
var position1 = entity.position.getValue(clock.currentTime, new CC3());
var position2 = entity.position.getValue(Cesium.JulianDate.addSeconds(clock.currentTime, 1 / 60, new Cesium.JulianDate()), new CC3());

//velocity in terms of Earth Fixed
var Wvelocity = CC3.subtract(position2, position1, new CC3());
CC3.normalize(Wvelocity, Wvelocity);
var Wup = new CC3(); var Weast = new CC3(); var Wnorth = new CC3();
Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(position1, Wup);
CC3.cross({ x: 0, y: 0, z: 1 }, Wup, Weast);
CC3.cross(Wup, Weast, Wnorth);

//velocity in terms of local ENU
var Lvelocity = new CC3();
Lvelocity.x = CC3.dot(Wvelocity, Weast);
Lvelocity.y = CC3.dot(Wvelocity, Wnorth);
Lvelocity.z = CC3.dot(Wvelocity, Wup);

//angle of travel
var Lup = new CC3(0, 0, 1); var Least = new CC3(1, 0, 0); var Lnorth = new CC3(0, 1, 0);
var x = CC3.dot(Lvelocity, Least);
var y = CC3.dot(Lvelocity, Lnorth);
var z = CC3.dot(Lvelocity, Lup);
var angle = Math.atan2(x, y);//math: y b4 x, heading: x b4 y
var pitch = Math.asin(z);//make sure Lvelocity is unitized

//angles offsets
angle += 0 / 180 * Math.PI;
pitch += -20 / 180 * Math.PI;

var range = 80;
var offset = new Cesium.HeadingPitchRange(angle, pitch, range);
viewer.scene.camera.lookAt(entity.position.getValue(clock.currentTime), offset);
}); //end event listener
}); //end button

orientation : new Cesium.VelocityOrientationProperty(position) 可以得到某点的方向,但 position 需要是 Cesium.SampledProperty 根据时间的采样属性

2018.05.07

Globe 上的 ClippingPlane 定一个法向量就可以了,distance是相对与 ClippingPlaneCollection 的 ModelMatrix 的,它是以笛卡尔坐标系的中心建立了一个穿过整个坐标系的平面组,所以会在地球表面形成两个相对的切面。

如何计算一个离某个点距离为 radius 的圆的坐标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let n = 180, points = [];
let center = tileset.boundingSphere.center, radius = tileset.boundingSphere.radius;
let R = viewer.scene.globe.ellipsoid.maximumRadius; //地球半径
center = Cesium.Cartographic.fromCartesian(center); //转成经纬度坐标
//latB = latA-((radius * Math.sin(Math.PI * i / n)) * 180) / (Math.PI * ellipsoid.R)
//latB = LatA-(Y*180)/(Math.PI * e)
//loB = loA - ((rdius * Math.cost(Math.PI * i / n)) * 180) / (Math.PI * ellipsoid.R * Math.cos((LatA + LatB) / 2))
//loB = loA - (x*180)/(math.pi * r * cost((latA + latB) / 2))
for (let i = -n; i <= n; i++) {
let x = Math.cos(Math.PI * i / n) * radius;
let y = Math.sin(Math.PI * i / n) * radius;
//let LatB = center.latitude - (y * 180) / (Math.PI * R);
let LatB = center.latitude - (y / R);
//let LonB = center.longitude - (x * 180) / (Math.PI * R * Math.cos((center.latitude + LatB) / 2));
let LonB = center.longitude - (x / (R * Math.cos((center.latitude + LatB) / 2)));
let point = new Cesium.Cartographic(LonB, LatB);
point = viewer.scene.globe.ellipsoid.cartographicToCartesian(point);
points.push(point);
}

这个是不精确的版本,在小范围误差可接受,参考自地理空间距离计算优化

Polyline 这个 Geometry 不支持 heightReference,于是不会自动贴地。必须通过 Cesium.sampleTerrainMostDetailed 这个方法吧 positions 中的点进行高度调整,其中 pointsR 为高度为零的 Cartographic 对象。

1
2
3
4
5
6
7
8
9
10
Cesium.when(Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, pointsR), (samples) => {
for (let sample of samples) sample.height += 10.0;
viewer.entities.add({
polyline: {
positions: Cesium.Ellipsoid.WGS84.cartographicArrayToCartesianArray(samples),
width: 5,
material: Cesium.Color.WHITE,
},
});
});

2018.05.03

不支持 KHR_materials_pbrSpecularGlossiness

Entity 保存了 Graphic
Graphic 保存 Geometry 对象的描述属性,但没有定义怎么画出这个对象

Billboard:

Billboard

  • @performance Reading a property, e.g., {@link Billboard#show}, is constant time.
  • Assigning to a property is constant time but results in
  • CPU to GPU traffic when {@link BillboardCollection#update} is called. The per-billboard traffic is
  • the same regardless of how many properties were updated. If most billboards in a collection need to be
  • updated, it may be more efficient to clear the collection with {@link BillboardCollection#removeAll}
  • and add new billboards instead of modifying each one.
    照这么说来,当 BillboardCollection.update 的时候才会处理成 GPU 的数据发送至 GPU

BillboradGraphics
BillboardGraphicsSpec

BillboardCollection
BillboardCollectionSpec

BillboardVisualizer
BillboardVisualizerSpec

BillboardCollectionFS.glsl
BillboardCollectionVS.glsl

createBillboardPointCallback

Entity 不指定方向的情况下用 eastNorthUpToFixedFrame

2018.04.27

Cesium 画geometry的效率
Billboard 》polyline > box = plane 》 polygon(不带挤压高度) > polygon(带挤压高度)

一万个 ellipse ellipsoid cylinder corrdior 会有内存不足的问题

画一个带指示线的标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var viewer = new Cesium.Viewer('cesiumContainer',
{timeline: false, animation: false});
var scene = viewer.scene;
var offsetX = 50, offsetY = -80;
var pos = Cesium.Cartesian3.fromDegrees(-75.1641667, 29.9522222);
var labels = scene.primitives.add(new Cesium.LabelCollection());
labels.add({
position: pos,
text: 'Another label',
pixelOffset: new Cesium.Cartesian2(offsetX, offsetY)
});
var canvas = document.createElement('canvas');
canvas.width = Math.abs(offsetX);
canvas.height = Math.abs(offsetY);
var context2D = canvas.getContext('2d');
context2D.beginPath();
context2D.lineWidth = 3;
context2D.strokeStyle = '#ffffff';
context2D.moveTo((offsetX < 0) ? -offsetX : 0, (offsetY < 0) ? -offsetY : 0);
context2D.lineTo((offsetX < 0) ? 0 : offsetX, (offsetY < 0) ? 0 : offsetY);
context2D.stroke();
var billboards = scene.primitives.add(new Cesium.BillboardCollection());
var billboard = billboards.add({
color : Cesium.Color.RED,
image : canvas,
pixelOffset: new Cesium.Cartesian2(offsetX * 0.5, offsetY * 0.5),
position : pos
});

https://stackoverflow.com/questions/32716118/cesium-js-how-draw-line-binding-a-label-to-a-position

2018.04.26

Primitive 对象有定点着色器与片元着色器的源代码入口

Viewer.scene.drillPick 返回的 pickedObjects 数组对象
pickedObject.id 为 entity
pickedObject.primitive 为相应的 primitive 对象

添加X,Y,Z三轴线

1
2
3
4
5
6
var modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(position, hprRollZero, Cesium.Ellipsoid.WGS84, converter);
scene.primitives.add(new Cesium.DebugModelMatrixPrimitive({
modelMatrix : modelMatrix,
length : 300.0,
width : 10.0
}));

2018.04.24

Geometry画出来的图形是以地球坐标系来画的, Model是以模型自身的坐标系来画的,3DTileset是以地球坐标系来画的

3DTileset.debugWireframe可以显示骨架

2018.04.16

Cesium Pick有BUG,它是根据3DTiles的boundingSphere加上geometryError来确定模型占地面积,由maximumHeight的两倍来确定模型的高度。模型在这个范围内浮动,不会有明显的问题,超出这个范围就会有点选不中,显示异常的问题。

3DTileset的modelMatrix是整个set共用的,pickedFeature里的