public byte[] getPntsBytes(List pointList, List attributeTransferList) {
XYZModel offsetModel = calculateQuantizedVolumeOffset(pointList);
XYZModel maxOffset = calculateQuantizedVolumeOffsetMax(pointList);
XYZModel scaleModel = calculateQuantizedVolumeScale(offsetModel, maxOffset);
//组装头文件
String magic = "pnts";
int version = 1;
int byteLength = 0;
int featureTableJSONByteLength = 0;
int featureTableBinaryByteLength = 0;
int batchTableJSONByteLength = 0;
int batchTableBinaryByteLength = 0;
//体文件
//量化后坐标
List quantizedList = calculatePositionQuantized(pointList, offsetModel, scaleModel);
//坐标
byte[] positionBinary = new byte[0];
for (PointModel xyzModel : quantizedList) {
positionBinary = byteMergerByList(positionBinary, short2byte_Little((short) xyzModel.x), short2byte_Little((short) xyzModel.y), short2byte_Little((short) xyzModel.z));
}
positionBinary = fillBytesWithZero(positionBinary, 8);
//颜色
byte[] colorByteRGB = {(byte) 0, (byte) 0, (byte) 0};
byte[] colorBinary = new byte[0];
for (PointModel xyzModel : pointList) {
colorBinary = byteMergerByList(colorBinary, colorByteRGB);
}
//要素表
FeatureTable featureTable = new FeatureTable();
featureTable.POINTS_LENGTH = pointList.size();
featureTable.POSITION_QUANTIZED = new OffsetModel();
featureTable.POSITION_QUANTIZED.byteOffset = 0;
//偏移
if (!ObjectUtils.isEmpty(offsetModel)) {
featureTable.QUANTIZED_VOLUME_OFFSET = offsetModel.toMatrixArray();
}
//缩放
if (!ObjectUtils.isEmpty(scaleModel)) {
featureTable.QUANTIZED_VOLUME_SCALE = scaleModel.toMatrixArray();
}
//RGB
featureTable.RGB = new OffsetModel();
featureTable.RGB.byteOffset = positionBinary.length;
String featureTableJson = JSON.toJSONString(featureTable);
byte[] featureTableJsonBytes = featureTableJson.getBytes();
featureTableJsonBytes = fillBytesWithEmpty(featureTableJsonBytes);
featureTableJSONByteLength = featureTableJsonBytes.length;
//批量表
BatchTable batchTable = transferBatchTableItem(pointList, attributeTransferList);
byte[] batchTableBinary = new byte[0];
int byteOffsetIndex = 0;
//分布式处理+ForkJoin并行处理生成pnts
List processList = new PNTSProcess().process(batchTable.items);
for (BatchTableItem batchItem : batchTable.items) {
PNTSProcessModel pntsProcessModel = processList.stream().filter(it -> it.batchTableItem.name.equals(batchItem.name)).findFirst().orElse(null);
if (!ObjectUtils.isEmpty(pntsProcessModel)) {
batchTableBinary = byteMergerByList(batchTableBinary, pntsProcessModel.batchTableBinary);
batchItem.batchTableBaseItem.byteOffset = byteOffsetIndex;
byteOffsetIndex += pntsProcessModel.byteOffsetIndex;
}
}
String batchTableJSONString = batchTable.toBatchTableJSONString();
byte[] batchTableJSONBytes = batchTableJSONString.getBytes();
batchTableJSONBytes = fillBytesWithEmpty(batchTableJSONBytes);
batchTableJSONByteLength = batchTableJSONBytes.length;
//batchTableBinary
batchTableBinary = fillBytesWithZero(batchTableBinary, 8);
batchTableBinaryByteLength = batchTableBinary.length;
//计算填充量
int fillNum = 0;
int i = 28 + positionBinary.length + colorBinary.length;
if (i % 8 != 0) {
fillNum = 8 - i % 8;
}
if (fillNum != 0) {
colorBinary = fillInputBytesWithZero(colorBinary, fillNum);
}
//合并
byte[] featureTableBinary = byteMergerByList(positionBinary, colorBinary);
featureTableBinaryByteLength = featureTableBinary.length;
byteLength = 28 + featureTableJSONByteLength + featureTableBinaryByteLength + batchTableJSONByteLength + batchTableBinaryByteLength;
//组装pnts的二级制文件
byte[] bytes1 = magic.getBytes();
byte[] bytes2 = int32ToLHByte(version);
byte[] bytes3 = int32ToLHByte(byteLength);
byte[] bytes4 = int32ToLHByte(featureTableJSONByteLength);
byte[] bytes5 = int32ToLHByte(featureTableBinaryByteLength);
byte[] bytes6 = int32ToLHByte(batchTableJSONByteLength);
byte[] bytes7 = int32ToLHByte(batchTableBinaryByteLength);
byte[] bytes8 = featureTableJsonBytes;
byte[] bytes9 = featureTableBinary;
byte[] bytes10 = batchTableJSONBytes;
byte[] bytes11 = batchTableBinary;
//合并bytes
byte[] pntsBinaryBytes = byteMergerByList(bytes1, bytes2, bytes3, bytes4, bytes5, bytes6, bytes7, bytes8, bytes9, bytes10, bytes11);
return pntsBinaryBytes;
}
public class PointModel {
public double x;
public double y;
public double z;
public double value;
}
public class PointAttributeTransferModel {
/**
* input文件对应输入字段名称
*/
public String inputKey;
/**
* PointModel对应字段
*/
public Field field;
/**
* Pnts输出字段名称
*/
public String outputKey;
public PointAttributeTransferModel(String outputKey, Field field, String inputKey){
this.outputKey = outputKey;
this.field = field;
this.inputKey = inputKey;
}
}
public static XYZModel calculateQuantizedVolumeOffset(List points) {
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double minZ = Double.MAX_VALUE;
// Find minimum values for each dimension
for (PointModel point : points) {
minX = Math.min(minX, point.x);
minY = Math.min(minY, point.y);
minZ = Math.min(minZ, point.z);
}
// Calculate quantized volume offset
XYZModel offsetModel = new XYZModel(minX, minY, minZ);
return offsetModel;
}
public static XYZModel calculateQuantizedVolumeOffsetMax(List points) {
double maxX = -Double.MAX_VALUE;
double maxY = -Double.MAX_VALUE;
double maxZ = -Double.MAX_VALUE;
// Find minimum values for each dimension
for (PointModel point : points) {
maxX = Math.max(maxX, point.x);
maxY = Math.max(maxY, point.y);
maxZ = Math.max(maxZ, point.z);
}
// Calculate quantized volume offset
XYZModel offsetModel = new XYZModel(maxX, maxY, maxZ);
return offsetModel;
}
public static XYZModel calculateQuantizedVolumeScale(XYZModel quantizedVolumeOffset, XYZModel maxOffset) {
double[] range = {maxOffset.x - quantizedVolumeOffset.x, maxOffset.y - quantizedVolumeOffset.y, maxOffset.z - quantizedVolumeOffset.z};
double offsetQuantizedX = range[0] > 1 ? range[0] / 1 : 1;
double offsetQuantizedY = range[1] > 1 ? range[1] / 1 : 1;
double offsetQuantizedZ = range[2] > 1 ? range[2] / 1 : 1;
// Calculate quantized volume scale
XYZModel scaleModel = new XYZModel(offsetQuantizedX, offsetQuantizedY, offsetQuantizedZ);
return scaleModel;
}
public List calculatePositionQuantized(List points, XYZModel quantizedVolumeOffset, XYZModel quantizedScaleModel) {
List positionQuantizedList = new ArrayList<>();
for (PointModel point : points) {
int xQuantized = (int) Math.floor(((point.x - quantizedVolumeOffset.x) / quantizedScaleModel.x * 65535));
int yQuantized = (int) Math.floor(((point.y - quantizedVolumeOffset.y) / quantizedScaleModel.y * 65535));
int zQuantized = (int) Math.floor(((point.z - quantizedVolumeOffset.z) / quantizedScaleModel.z * 65535));
//short xQuantized = (short) ((point.x - quantizedVolumeOffset.x) / quantizedScaleModel.x * 65535);
//short yQuantized = (short) ((point.y - quantizedVolumeOffset.y) / quantizedScaleModel.y * 65535);
//short zQuantized = (short) ((point.z - quantizedVolumeOffset.z) / quantizedScaleModel.z * 65535);
PointModel positionQuantized = new PointModel();
positionQuantized.x = xQuantized;
positionQuantized.y = yQuantized;
positionQuantized.z = zQuantized;
positionQuantizedList.add(positionQuantized);
}
return positionQuantizedList;
}
public static byte[] byteMergerByList(byte[]... bytes) {
int totalLength = 0;
for (byte[] array : bytes) {
totalLength += array.length;
}
ByteBuffer buffer = ByteBuffer.allocate(totalLength);
for (byte[] array : bytes) {
buffer.put(array);
}
return buffer.array();
}
public static byte[] fillBytesWithZero(byte[] featureTableBinary, int fillBoundary) {
int length = featureTableBinary.length;
int remain = length % fillBoundary;
Short s = 0;
if (remain != 0) {
int fillNum = fillBoundary - remain;
for (int i = 0; i < fillNum; i++) {
featureTableBinary = byteMergerByList(featureTableBinary, int8ToLHByte(s));
}
}
return featureTableBinary;
}
public static byte[] fillInputBytesWithZero(byte[] featureTableBinary, int fillNum) {
Short s = 0;
for (int i = 0; i < fillNum; i++) {
featureTableBinary = byteMergerByList(featureTableBinary, int8ToLHByte(s));
}
return featureTableBinary;
}
public static byte[] int8ToLHByte(int n) {
byte[] b = new byte[1];
b[0] = (byte) (n & 0xff);
return b;
}
public class FeatureTable {
public int POINTS_LENGTH;
public OffsetModel POSITION_QUANTIZED;
public List QUANTIZED_VOLUME_OFFSET;
public List QUANTIZED_VOLUME_SCALE;
public OffsetModel RGB;
}
public static byte[] fillBytesWithEmpty(byte[] featureTableJsonBytes) {
int length = featureTableJsonBytes.length;
int remain = length % 8;
String emptyStr = " ";
if (remain != 0) {
int fillNum = 8 - remain;
for (int i = 0; i < fillNum; i++) {
featureTableJsonBytes = byteMergerByList(featureTableJsonBytes, emptyStr.getBytes());
}
}
return featureTableJsonBytes;
}
public BatchTable transferBatchTableItem(List pointList, List attributeTransferList) {
BatchTable batchTable = new BatchTable();
batchTable.items = new ArrayList<>();
List pointModelFiledNames = getFiledName(new PointModel());
for (PointAttributeTransferModel pointAttributeTransferModel : attributeTransferList) {
String enumKey = pointAttributeTransferModel.field.getName().toLowerCase();
List collect = pointModelFiledNames.stream().filter(it -> it.filedName.equals(enumKey)).collect(Collectors.toList());
BatchTableItem batchTableItem = new BatchTableItem();
batchTableItem.batchTableBaseItem = new BatchTableBaseItem();
batchTableItem.batchTableBaseItem.byteOffset = 0;
batchTableItem.batchTableBaseItem.componentType = ObjectUtils.isEmpty(collect) ? "DOUBLE" : collect.get(0).filedType.toUpperCase();
batchTableItem.batchTableBaseItem.type = "SCALAR";
batchTableItem.name = pointAttributeTransferModel.outputKey;
List