package com.revolsys.elevation.tin.compactbinary;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import com.revolsys.elevation.tin.TriangulatedIrregularNetwork;
import com.revolsys.elevation.tin.TriangulatedIrregularNetworkWriter;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.Triangle;
import com.revolsys.io.Buffers;
import com.revolsys.properties.BaseObjectWithProperties;
import com.revolsys.spring.resource.Resource;
import com.revolsys.util.Exceptions;
public class CompactBinaryTinWriter extends BaseObjectWithProperties
implements TriangulatedIrregularNetworkWriter {
private static final int BUFFER_SIZE = 1000;
private Resource resource;
private double scaleXY;
private double scaleZ;
private ByteBuffer buffer = ByteBuffer.allocateDirect(36 * 1000);
private WritableByteChannel out;
private int bufferTriangleCount;
public CompactBinaryTinWriter(final Resource resource) {
this.resource = resource;
}
@Override
public void close() {
super.close();
this.resource = null;
if (this.out != null) {
if (this.out.isOpen()) {
try {
this.out.close();
} catch (final IOException e) {
throw Exceptions.wrap(e);
}
}
}
this.out = null;
this.buffer = null;
}
@Override
public void flush() {
}
@Override
public void open() {
}
@Override
public void write(final TriangulatedIrregularNetwork tin) {
try {
this.out = this.resource.newWritableByteChannel();
final BoundingBox tinBoundingBox = tin.getBoundingBox();
final GeometryFactory geometryFactory = tin.getGeometryFactory();
final int coordinateSystemId = geometryFactory.getCoordinateSystemId();
this.scaleXY = geometryFactory.getScaleXY();
if (this.scaleXY <= 0) {
this.scaleXY = 1000;
}
this.scaleZ = geometryFactory.getScaleZ();
if (this.scaleZ <= 0) {
this.scaleZ = 1000;
}
final int triangleCount = tin.getTriangleCount();
this.buffer.put(CompactBinaryTin.FILE_TYPE_BYTES);
this.buffer.putShort(CompactBinaryTin.VERSION);
this.buffer.putInt(coordinateSystemId); // Coordinate System ID
this.buffer.putDouble(this.scaleXY); // Scale XY
this.buffer.putDouble(this.scaleZ); // Scale Z
this.buffer.putDouble(tinBoundingBox.getMinX()); // minX
this.buffer.putDouble(tinBoundingBox.getMinY()); // minY
this.buffer.putDouble(tinBoundingBox.getMaxX()); // maxX
this.buffer.putDouble(tinBoundingBox.getMaxY()); // maxY
this.buffer.putInt(triangleCount);
Buffers.writeAll(this.out, this.buffer);
tin.forEachTriangle(this::writeTriangle);
Buffers.writeAll(this.out, this.buffer);
} catch (final IOException e) {
throw Exceptions.wrap("Unable to write: " + this.resource, e);
}
}
private void writeTriangle(final Triangle triangle) {
final ByteBuffer buffer = this.buffer;
final double scaleXy = this.scaleXY;
final double scaleZ = this.scaleZ;
for (int i = 0; i < 3; i++) {
final double x = triangle.getX(i);
final double y = triangle.getY(i);
final double z = triangle.getZ(i);
if (Double.isFinite(x)) {
final int intX = (int)Math.round(x * scaleXy);
buffer.putInt(intX);
} else {
buffer.putInt(Integer.MIN_VALUE);
}
if (Double.isFinite(y)) {
final int intX = (int)Math.round(y * scaleXy);
buffer.putInt(intX);
} else {
buffer.putInt(Integer.MIN_VALUE);
}
if (Double.isFinite(z)) {
final int intX = (int)Math.round(z * scaleZ);
buffer.putInt(intX);
} else {
buffer.putInt(Integer.MIN_VALUE);
}
}
this.bufferTriangleCount++;
if (this.bufferTriangleCount == BUFFER_SIZE) {
try {
Buffers.writeAll(this.out, buffer);
} catch (final IOException e) {
throw Exceptions.wrap("Unable to write: " + this.resource, e);
}
this.bufferTriangleCount = 0;
}
}
}