package com.revolsys.elevation.tin.compactbinary; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.charset.StandardCharsets; import com.revolsys.elevation.tin.IntArrayScaleTriangulatedIrregularNetwork; import com.revolsys.elevation.tin.TriangleConsumer; import com.revolsys.elevation.tin.TriangulatedIrregularNetwork; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.io.BaseCloseable; import com.revolsys.io.Buffers; import com.revolsys.spring.resource.Resource; import com.revolsys.util.Exceptions; public class CompactBinaryTinReader implements BaseCloseable { private static final int BUFFER_RECORD_COUNT = 1000; private static final int RECORD_SIZE = 36; public static TriangulatedIrregularNetwork read(final Resource resource) { try ( final CompactBinaryTinReader compactBinaryTinReader = new CompactBinaryTinReader(resource)) { final TriangulatedIrregularNetwork tin = compactBinaryTinReader .newTriangulatedIrregularNetwork(); return tin; } } private final Resource resource; private int triangleCount; private GeometryFactory geometryFactory; private ByteBuffer buffer = ByteBuffer.allocateDirect(RECORD_SIZE * BUFFER_RECORD_COUNT); private BoundingBox boundingBox; private ReadableByteChannel in; private double scaleFactorXY; private double scaleFactorZ; public CompactBinaryTinReader(final Resource resource) { this.resource = resource; } @Override public void close() { try { this.in.close(); } catch (final IOException e) { throw Exceptions.wrap(e); } finally { this.boundingBox = null; this.buffer = null; this.in = null; this.triangleCount = 0; } } public void forEachTriangle(final TriangleConsumer action) { open(); final ByteBuffer buffer = this.buffer; final double scaleFactorXY = this.scaleFactorXY; final double scaleFactorZ = this.scaleFactorZ; try { int triangleToReadCount = this.triangleCount; while (triangleToReadCount > 0) { final int readCount = readBuffer(triangleToReadCount); for (int readIndex = 0; readIndex < readCount; readIndex++) { final double x1 = getDouble(buffer, scaleFactorXY); final double y1 = getDouble(buffer, scaleFactorXY); final double z1 = getDouble(buffer, scaleFactorZ); final double x2 = getDouble(buffer, scaleFactorXY); final double y2 = getDouble(buffer, scaleFactorXY); final double z2 = getDouble(buffer, scaleFactorZ); final double x3 = getDouble(buffer, scaleFactorXY); final double y3 = getDouble(buffer, scaleFactorXY); final double z3 = getDouble(buffer, scaleFactorZ); action.accept(x1, y1, z1, x2, y2, z2, x3, y3, z3); } triangleToReadCount -= readCount; } } catch (final IOException e) { throw Exceptions.wrap("Unable to read: " + this.resource, e); } } private double getDouble(final ByteBuffer buffer, final double scaleFactor) { final int intValue = buffer.getInt(); if (intValue == Integer.MIN_VALUE) { return Double.NaN; } else { return intValue / scaleFactor; } } public TriangulatedIrregularNetwork newTriangulatedIrregularNetwork() { open(); final ByteBuffer buffer = this.buffer; try { final int[] triangleXCoordinates = new int[this.triangleCount * 3]; final int[] triangleYCoordinates = new int[this.triangleCount * 3]; final int[] triangleZCoordinates = new int[this.triangleCount * 3]; int triangleToReadCount = this.triangleCount; int coordinateIndex = 0; while (triangleToReadCount > 0) { final int readCount = readBuffer(triangleToReadCount); for (int readIndex = 0; readIndex < readCount; readIndex++) { for (int i = 0; i < 3; i++) { triangleXCoordinates[coordinateIndex] = buffer.getInt(); triangleYCoordinates[coordinateIndex] = buffer.getInt(); triangleZCoordinates[coordinateIndex] = buffer.getInt(); coordinateIndex++; } } triangleToReadCount -= readCount; } return new IntArrayScaleTriangulatedIrregularNetwork(this.geometryFactory, this.boundingBox, this.triangleCount, triangleXCoordinates, triangleYCoordinates, triangleZCoordinates); } catch (final IOException e) { throw Exceptions.wrap("Unable to read: " + this.resource, e); } } public void open() { if (this.in == null) { this.in = this.resource.newReadableByteChannel(); readHeader(); } } private int readBuffer(final int triangleToReadCount) throws IOException { final ByteBuffer buffer = this.buffer; buffer.clear(); int readCount; if (triangleToReadCount < BUFFER_RECORD_COUNT) { readCount = triangleToReadCount; } else { readCount = BUFFER_RECORD_COUNT; } buffer.limit(readCount * RECORD_SIZE); Buffers.readAll(this.in, buffer); return readCount; } private void readHeader() { try { this.buffer.limit(CompactBinaryTin.HEADER_SIZE); Buffers.readAll(this.in, this.buffer); final byte[] fileTypeBytes = new byte[6]; this.buffer.get(fileTypeBytes); @SuppressWarnings("unused") final String fileType = new String(fileTypeBytes, StandardCharsets.UTF_8); // File // type @SuppressWarnings("unused") final short version = this.buffer.getShort(); final int coordinateSystemId = this.buffer.getInt(); // Coordinate System // ID this.scaleFactorXY = this.buffer.getDouble(); this.scaleFactorZ = this.buffer.getDouble(); this.geometryFactory = GeometryFactory.fixed(coordinateSystemId, 3, this.scaleFactorXY, this.scaleFactorXY, this.scaleFactorZ); final double minX = this.buffer.getDouble(); final double minY = this.buffer.getDouble(); final double maxX = this.buffer.getDouble(); final double maxY = this.buffer.getDouble(); this.boundingBox = this.geometryFactory.newBoundingBox(2, minX, minY, maxX, maxY); this.triangleCount = this.buffer.getInt(); } catch (final IOException e) { throw Exceptions.wrap("Unable to read: " + this.resource, e); } } }