package com.revolsys.elevation.gridded.compactbinary;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import com.revolsys.elevation.gridded.GriddedElevationModel;
import com.revolsys.elevation.gridded.GriddedElevationModelReadFactory;
import com.revolsys.elevation.gridded.GriddedElevationModelWriter;
import com.revolsys.elevation.gridded.GriddedElevationModelWriterFactory;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.editor.GeometryEditor;
import com.revolsys.geometry.model.vertex.Vertex;
import com.revolsys.gis.grid.CustomRectangularMapGrid;
import com.revolsys.gis.grid.RectangularMapGrid;
import com.revolsys.io.AbstractIoFactoryWithCoordinateSystem;
import com.revolsys.spring.resource.PathResource;
import com.revolsys.spring.resource.Resource;
import com.revolsys.util.Exceptions;
public class CompactBinaryGriddedElevation extends AbstractIoFactoryWithCoordinateSystem
implements GriddedElevationModelReadFactory, GriddedElevationModelWriterFactory {
public static final String FILE_EXTENSION = "demcb";
public static final String FILE_EXTENSION_GZ = "demcb.gz";
public static final String FILE_EXTENSION_ZIP = "demcb.zip";
public static final String FILE_FORMAT = "DEMGCB";
public static final byte[] FILE_FORMAT_BYTES = "DEMGCB".getBytes(StandardCharsets.UTF_8);
public static final int HEADER_SIZE = 88;
public static final int RECORD_SIZE = 4;
public static final short VERSION = 2;
public static double getElevationInterpolated(final Resource baseResource,
final int coordinateSystemId, final int gridCellSize, final int gridSize,
final String fileExtension, final double x, final double y) {
final int gridTileSize = gridSize * gridCellSize;
final int tileX = CustomRectangularMapGrid.getGridFloor(0.0, gridTileSize, x);
final int tileY = CustomRectangularMapGrid.getGridFloor(0.0, gridTileSize, y);
final Resource resource = RectangularMapGrid.getTileResource(baseResource, "dem",
coordinateSystemId, Integer.toString(gridTileSize), tileX, tileY, fileExtension);
if (resource.exists()) {
try {
final int gridCellX = GriddedElevationModel.getGridCellX(tileX, gridCellSize, x);
final int gridCellY = GriddedElevationModel.getGridCellY(tileY, gridCellSize, y);
final int elevationByteSize = 4;
final int offset = HEADER_SIZE + (gridCellY * gridSize + gridCellX) * elevationByteSize;
int elevation;
if (resource.isFile()) {
final Path path = resource.toPath();
try (
SeekableByteChannel byteChannel = Files.newByteChannel(path, StandardOpenOption.READ)) {
byteChannel.position(offset);
final ByteBuffer bytes = ByteBuffer.allocate(4);
byteChannel.read(bytes);
elevation = bytes.getInt(0);
} catch (final IOException e) {
throw Exceptions.wrap("Unable to read: " + resource, e);
}
} else {
try (
DataInputStream in = resource.newBufferedInputStream(DataInputStream::new)) {
in.skip(offset);
elevation = in.readInt();
} catch (final IOException e) {
throw Exceptions.wrap("Unable to read: " + resource, e);
}
}
return elevation;
} catch (final ClassCastException e) {
throw new IllegalArgumentException(fileExtension + " not supported");
}
}
return Double.NaN;
}
public static double getElevationNearest(final Resource baseResource,
final int coordinateSystemId, final int gridCellSize, final int gridSize,
final String fileExtension, final double x, final double y) {
final int gridTileSize = gridSize * gridCellSize;
final int tileX = CustomRectangularMapGrid.getGridFloor(0.0, gridTileSize, x);
final int tileY = CustomRectangularMapGrid.getGridFloor(0.0, gridTileSize, y);
final Resource resource = RectangularMapGrid.getTileResource(baseResource, "dem",
coordinateSystemId, Integer.toString(gridTileSize), tileX, tileY, fileExtension);
try {
final int gridCellX = GriddedElevationModel.getGridCellX(tileX, gridCellSize, x);
final int gridCellY = GriddedElevationModel.getGridCellY(tileY, gridCellSize, y);
final int elevationByteSize = 4;
final int offset = HEADER_SIZE + (gridCellY * gridSize + gridCellX) * elevationByteSize;
int elevation;
if (resource.isFile()) {
final Path path = resource.toPath();
try (
FileChannel channel = FileChannel.open(path, StandardOpenOption.READ)) {
final ByteBuffer bytes = ByteBuffer.allocate(4);
channel.read(bytes, offset);
elevation = bytes.getInt(0);
}
} else {
try (
DataInputStream in = resource.newBufferedInputStream(DataInputStream::new)) {
in.skip(offset);
elevation = in.readInt();
}
}
return elevation;
} catch (final NoSuchFileException e) {
return Double.NaN;
} catch (final IOException e) {
throw Exceptions.wrap("Unable to read: " + resource, e);
} catch (final ClassCastException e) {
throw new IllegalArgumentException(fileExtension + " not supported");
}
}
@SuppressWarnings("unchecked")
public static <G extends Geometry> G setElevationNearest(final PathResource baseResource,
final int coordinateSystemId, final int gridCellSize, final int gridSize,
final String fileExtension, final G geometry) {
final GeometryEditor editor = geometry.newGeometryEditor();
editor.setAxisCount(3);
for (final Vertex vertex : geometry.vertices()) {
final double x = vertex.getX();
final double y = vertex.getY();
final double elevation = getElevationNearest(baseResource, coordinateSystemId, gridCellSize,
gridSize, fileExtension, x, y);
if (!Double.isNaN(elevation)) {
final int[] vertexId = vertex.getVertexId();
editor.setZ(elevation, vertexId);
}
}
return (G)editor.newGeometry();
}
public CompactBinaryGriddedElevation() {
super("DEM Compact Binary");
addMediaTypeAndFileExtension("image/x-rs-compact-binary-dem", FILE_EXTENSION);
addFileExtension(FILE_EXTENSION_ZIP);
addFileExtension(FILE_EXTENSION_GZ);
}
@Override
public GriddedElevationModel newGriddedElevationModel(final Resource resource,
final Map<String, ? extends Object> properties) {
try (
CompactBinaryGriddedElevationReader reader = new CompactBinaryGriddedElevationReader(
resource)) {
reader.setProperties(properties);
return reader.read();
}
}
@Override
public GriddedElevationModelWriter newGriddedElevationModelWriter(final Resource resource) {
return new CompactBinaryGriddedElevationWriter(resource);
}
}