在利用Cesium加载点云数据时,我们需要对点云数据进行切片,生成pnts格式。该类型切片文件是二进制文件,我们该如何读取其内容呢?该篇文章简单介绍一下我的理解。
1.pnts二进制文件组织格式
首先我们需要知道pnts二进制文件的组织格式,先贴一个官网链接:3dTiles-PointCloud、BatchTable
一个点云数据切片由header(28个字节,固定的,每个字段占四个字节)+body两部分组成,每一部分数据的组织形式也是固定的,如下图所示。
点云切片数据body中的数据组织形式如下图所示。
要素表中存储的是该切片中每个点的位置信息和颜色信息;批处理表中存储的是切片中点的特定属性信息,例如name,classification,intensity等等(生成点云数据切片时可以自己定义存储哪些属性信息)。
2.pnts二进制文件读取(代码)
// 获取点击位置的点云切片 let content = cesiumViewer.scene.pick(event.position).content; let url = content.url; this.axios.get(url, { // 返回数据类型是arraybuffer responseType: “arraybuffer“ }).then(res => { this.processPntsData(res.data); }) processPntsData(arrayBuffer) { let uint8Array = new Uint8Array(arrayBuffer); // 获取magic let magic = Cesium.getMagic(uint8Array); // header中每个字段占四个字节 let sizeOfUint32 = 4; let byteOffset = 0; byteOffset += sizeOfUint32; // Skip magic // 将二进制转换成DataView,可以读写 let view = new DataView(arrayBuffer); let version = view.getUint32(byteOffset, true); // 继续往下移动 byteOffset += sizeOfUint32; // Skip version //得到byteLength的长度 let byteLength = view.getUint32(byteOffset, true); // 继续往下移动 byteOffset += sizeOfUint32; // Skip byteLength // 得到featuretableJSON字节长度 let featureTableJsonByteLength = view.getUint32(byteOffset, true); // 继续往下移动 byteOffset += sizeOfUint32; // 得到featuretable 二进制字节长度 let featureTableBinaryByteLength = view.getUint32(byteOffset, true); // 得到bathTableJson字节长度 let batchTableJSONByteLength = view.getUint32(20, true); // 得到bathTable 二进制字节长度 let batchTableBinaryByteLength = view.getUint32(24, true); // 下面开始获取body的内容,从第28个字节开始,因为pnts的头部长度为28 // 获得featuretable中的featuretablejson let featureTableArray = new Uint8Array(arrayBuffer, 28, featureTableJsonByteLength); // Uint8ArrayToString二进制转String let featureTableStr = this.Uint8ArrayToString(featureTableArray); let featureTableJSON = JSON.parse(featureTableStr); // 获取pnts中存储的该切片所有点的位置值 let featureTableBinary = new Uint8Array(arrayBuffer, 28 + featureTableJsonByteLength, featureTableBinaryByteLength); let featureTable = new Cesium.Cesium3DTileFeatureTable(featureTableJSON, featureTableBinary); // Cesium执行getPropertyArray进行二进制文件读取必须要传入点云的总长度!但featureTable中该值是0!所以我们必须手动设置! // 利用了点云切片pnts的全局语义值(所有点都具有的全局属性)。 let pointsLength = featureTable.getGlobalProperty(“POINTS_LENGTH“); featureTable.featuresLength = pointsLength; let positions = featureTable.getPropertyArray('POSITION', Cesium.ComponentDatatype.FLOAT, 3); // 获取batchtablejSON let batchTableArray = new Uint8Array(arrayBuffer, 28 + featureTableJsonByteLength + featureTableBinaryByteLength, batchTableJSONByteLength); let batchTableStr = this.Uint8ArrayToString(batchTableArray); let batchTableJSON = JSON.parse(batchTableStr); // 获取batchtable存储的属性值 let batchTableBinary = new Uint8Array(arrayBuffer, 28 + featureTableJsonByteLength + featureTableBinaryByteLength + batchTableJSONByteLength, batchTableBinaryByteLength); },
3. 点云数据的全局语义
这些语义定义了所有点的全局属性,我们可以直接调用这些属性,例如上方代码中我们获取点云中点的数量,可以使用:
let pointsLength = featureTable.getGlobalProperty(“POINTS_LENGTH“);
![]()
源码链接![]()
4.注意事项:Padding!!!
二进制属性应以字节偏移量(byteoffset)开始,该字节偏移量是属性组件类型componentType大小(以字节为单位)的倍数。例如,组件类型为FLOAT的属性每个元素有4字节,因此应该从4的倍数开始偏移。前面的二进制属性需要填充任何值的额外字节,以满足此要求。
如果不满足上述要求,利用Cesium相关函数获取batchTable中的属性值时是会报错的!
上述就是读取二进制文件的流程,下期更新点云切片数据坐标转换成经纬度坐标的流程~欢迎大家多多交流~