cesium支持多种地形瓦片数据(GoogleEarthEnterpriseTerrainData、QuantizedMeshTerrainData、HeightmapTerrainData)
一、quantized-mesh-1.0(量化网格-1.0格式)
quantized-mesh-1.0是简单多分辨率四叉树金字塔的高度图。
1.1、切片规则
quantized-mesh的切片规则和Tile Map Service (TMS) 的global-geodetic规则相似。
- 所有图块都具有后缀名.terrain
- 图块大小为65x65像素。每个图块都是经过特殊编码的三角形网格,顶点在图块边缘处与相邻顶点重叠。
- 图块获取URL示例如下:
金字塔的两个ROOT文件:
- (-180度,-90度)-(0度,90度)-http://example.com/tiles/0/0/0.terrain
- (0度,-90度)-(180度,90度)-http://example.com/tiles/0/1/0.terrain
在ROOT URL可以找到下一级的八个图块:
- (-180度,-90度)-(-90度,0度)-http://example.com/tiles/1/0/0.terrain
- ( -90度,-90度)-(0度,0度)-http://example.com/tiles/1/1/0.terrain
- (0度,-90度)-(90度,0度)-http://example.com/tiles/1/2/0.terrain
- (90度,-90度)-(180度,0度)-http://example.com/tiles/1/3/0.terrain
- (-180度,0度)-(-90度,90度)-http://example.com/tiles/1/0/1.terrain
- (-90度,0度)-(0度,90度)-http://example.com/tiles/1/1/1.terrain
- (0度,0度)-(90度,90度)-http://example.com/tiles/1/2/1.terrain
- (90度,0度)-(180度,90度)-http://example.com/tiles/1/3/1.terrain
请求图块时,请确保在请求中包含以下HTTP标头:
Accept: application/vnd.quantized-mesh,application/octet-stream;q=0.9
1.2、根据指定坐标计算瓦片索引
# 根据WGS-84的经纬度获取量化网格的瓦片坐标
def wgs84_to_tile(lon, lat, zoom):
isnum = lambda x: isinstance(x, int) or isinstance(x, float)
if not (isnum(lon) and isnum(lat)):
raise TypeError("lon and lat must be int or float!")
if not isinstance(zoom, int) or zoom < 0 or zoom > 22:
raise TypeError("zoom must be int and between 0 to 22.")
lon = 180 + lon
lon = lon / 360 # make lon to (0,1)
lat = 90 + lat
lat = lat / 180 # make lat to (0,1)
num = 2 ** zoom
x = floor(lon * num * 2)
y = floor(lat * num)
return x, y
二、quantized-mesh-1.0文件格式
2.1、数据头部
该文件的第一部分是具有以下格式的数据头。double是64位浮点数,float是32位浮点数。
struct QuantizedMeshHeader
{
// 瓦片中心在地心坐标系下的坐标
double CenterX;
double CenterY;
double CenterZ;
// 该瓦片覆盖区域的最小和最大高度值
// 最小值可以低于所有顶点,最大值也可以高于任何顶点
// 因为在网格简化(simplificatipn)的过程中可能会有移除最小或最大顶点的情况
// 但是这些是适用于用于分析或可视化的的值
float MinimumHeight;
float MaximumHeight;
// 瓦片的球面边界.
// X,Y,Z 坐标是地心坐标系下的坐标, 半径的单位为米
double BoundingSphereCenterX;
double BoundingSphereCenterY;
double BoundingSphereCenterZ;
double BoundingSphereRadius;
// 地平线遮挡点,以椭球体缩放的地心坐标系表示
// 如果此点低于地平线,则整个图块位于地平线下方。
// 有关更多信息,请参见http://cesiumjs.org/2013/04/25/Horizon-culling/。
double HorizonOcclusionPointX;
double HorizonOcclusionPointY;
double HorizonOcclusionPointZ;
};
struct QuantizedMeshHeade
2.2、顶点数据(vertex data)
头部数据后面紧跟着顶点数据,unsigned int是32位无符号整数,unsigned short是16位无符号整数。
struct VertexData
{
unsigned int vertexCount; // 顶点个数
unsigned short u[vertexCount]; // 顶点横坐标
unsigned short v[vertexCount]; // 顶点纵坐标
unsigned short height[vertexCount]; // 顶点高程值
};
vertexCount字段指示后面三个数组的大小。 这三个数组包含来自前一个值的增量,然后进行zig-zag编码,以便使小整数(无论其符号如何)使用较少比特位。
解码值的过程很简单:
var u = 0;
var v = 0;
var height = 0;
// zig-zag 编码
function zigZagEncode (value) {
return (value >> 31) ^ (value << 1);
}
// zig-zag 解码
function zigZagDecode(value) {
return (value >> 1) ^ (-(value & 1));
}
for (i = 0; i < vertexCount; ++i) {
u += zigZagDecode(uBuffer[i]);
v += zigZagDecode(vBuffer[i]);
height += zigZagDecode(heightBuffer[i]);
uBuffer[i] = u;
vBuffer[i] = v;
heightBuffer[i] = height;
}
2.3、三角形索引
紧跟在顶点数据之后的是索引数据。用来指示顶点如何链接在一起成三角形。如果tile具有超过65536个顶点,则tile使用IndexData32结构对索引进行编码。否则,它使用IndexData16结构。
为了对索引数据强制进行字节对齐,在IndexData之前添加填充字节,以确保IndexData16为2字节对齐和IndexData32为4字节对齐。
struct IndexData16
{
unsigned int triangleCount; // 三角形个数
unsigned short indices[triangleCount * 3]; // 三角形顶点索引
}
struct IndexData32
{
unsigned int triangleCount;
unsigned int indices[triangleCount * 3];
}
索引使用来自 webgl-loader 的 高水位标记(high water mark)编码进行编码。
索引解码如下:
var highest = 0;
for (var i = 0; i < indices.length; ++i) {
var code = indices[i];
indices[i] = highest - code;
if (code === 0) {
++highest;
}
}
索引的每个三元组以逆时针顺序,渲染一个三角形。
2.4、边缘索引
三角索引之后还有四个边缘索引列表,保存了tile边缘上的顶点。
知道哪些顶点在边缘上以添加裙边以隐藏相邻细节层之间的裂缝是有帮助的。
struct EdgeIndices16
{
unsigned int westVertexCount;
unsigned short westIndices[westVertexCount];
unsigned int southVertexCount;
unsigned short southIndices[southVertexCount];
unsigned int eastVertexCount;
unsigned short eastIndices[eastVertexCount];
unsigned int northVertexCount;
unsigned short northIndices[northVertexCount];
}
struct EdgeIndices32
{
unsigned int westVertexCount;
unsigned int westIndices[westVertexCount];
unsigned int southVertexCount;
unsigned int southIndices[southVertexCount];
unsigned int eastVertexCount;
unsigned int eastIndices[eastVertexCount];
unsigned int northVertexCount;
unsigned int northIndices[northVertexCount];
}
更多信息,请参考文档。
2.5、Cesium的解析代码
var data = new Cesium.QuantizedMeshTerrainData({
minimumHeight : -100,
maximumHeight : 2101,
quantizedVertices : new Uint16Array([// order is SW NW SE NE
// longitude
0, 0, 32767, 32767,
// latitude
0, 32767, 0, 32767,
// heights
16384, 0, 32767, 16384]),
indices : new Uint16Array([0, 3, 1,
0, 2, 3]),
boundingSphere : new Cesium.BoundingSphere(new Cesium.Cartesian3(1.0, 2.0, 3.0), 10000),
orientedBoundingBox : new Cesium.OrientedBoundingBox(new Cesium.Cartesian3(1.0, 2.0, 3.0), Cesium.Matrix3.fromRotationX(Cesium.Math.PI, new Cesium.Matrix3())),
horizonOcclusionPoint : new Cesium.Cartesian3(3.0, 2.0, 1.0),
westIndices : [0, 1],
southIndices : [0, 1],
eastIndices : [2, 3],
northIndices : [1, 3],
westSkirtHeight : 1.0,
southSkirtHeight : 1.0,
eastSkirtHeight : 1.0,
northSkirtHeight : 1.0
});