org.jgrasstools.gears.io.grasslegacy.io.CompressesRasterWriter Maven / Gradle / Ivy
/*
* JGrass - Free Open Source Java GIS http://www.jgrass.org
* (C) HydroloGIS - www.hydrologis.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
package org.jgrasstools.gears.io.grasslegacy.io;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.BitSet;
import java.util.zip.Deflater;
import org.jgrasstools.gears.io.grasslegacy.utils.Window;
/**
*
* Write compressed JGrass rasters to disk
*
*
* @author Andrea Antonello - www.hydrologis.com
* @since 1.1.0
*/
public class CompressesRasterWriter {
private static final String ERROR_IN_WRITING_RASTER = "Error in writing raster: ";
private int outputToDiskType = 0;
private double[] range = null;
private long pointerInFilePosition = 0;
private long[] rowaddresses = null;
private Window dataWindow = null;
/**
* Preparing the environment for compressing and writing the map to disk
*
* @param _outputToDiskType
* @param _novalue
* @param _jump
* @param _range
* @param _pointerInFilePosition
* @param _rowaddresses
* @param _dataWindow
*/
public CompressesRasterWriter( int _outputToDiskType, double[] _range,
long _pointerInFilePosition, long[] _rowaddresses, Window _dataWindow ) {
outputToDiskType = _outputToDiskType;
range = _range;
range[0] = Double.POSITIVE_INFINITY;
range[1] = Double.NEGATIVE_INFINITY;
pointerInFilePosition = _pointerInFilePosition;
rowaddresses = _rowaddresses;
dataWindow = _dataWindow;
}
/**
* Passing the object after defining the type of data that will be written
*
* @param theCreatedFile
* @param theCreatedNullFile
* @param dataObject
* @return
* @throws RasterWritingFailureException
*/
public boolean compressAndWriteObj( RandomAccessFile theCreatedFile, RandomAccessFile theCreatedNullFile, Object dataObject )
throws RasterWritingFailureException {
if (dataObject instanceof double[][]) {
compressAndWrite(theCreatedFile, theCreatedNullFile, (double[][]) dataObject);
} else {
throw new RasterWritingFailureException("Raster type not supported.");
}
return true;
}
/**
* compress and write double[][] matrix this method converts every single row of the matrix of
* values to bytes, as needed by the deflater. Then the byterows are compressed and then written
* to file. Every rows first byte carries the information about compression (0 = not compressed,
* 1 = compressed). At the begin the place for the header is written to file, in the end the
* header is re-written with the right rowaddresses (at the begin we do not know how much
* compression will influence).
*
* @param theCreatedFile - handler for the main map file
* @param theCreatedNullFile - handler for the file of the null map (in cell_misc)
* @param rastermatrix - the data matrix
* @return successfull or not
* @throws RasterWritingFailureException
*/
private boolean compressAndWrite( RandomAccessFile theCreatedFile, RandomAccessFile theCreatedNullFile,
double[][] rastermatrix ) throws RasterWritingFailureException {
try {
// set the number of bytes needed for the values to write to disk
int numberofbytes = outputToDiskType * 4;
/*
* the underlying byte array is needed as input to the deflater create it with the size
* of the column * numberofbytes (8 for double, 4 for float), which is made to define
* how we write to disk
*/
byte[] rowAsBytes = new byte[rastermatrix[0].length * numberofbytes];
ByteBuffer rowAsByteBuffer = ByteBuffer.wrap(rowAsBytes);
/*
* let's get started with the serious stuff. The null file is a bitmap representing with
* 0's the nulls and with 1's existing values. Important is that for example 12 numbers
* in a row will use 2 bytes in the nulls file, but fill only 12 n=bits. Therefore we
* need to padd it.
*/
int numberOfValuesPerRow = rastermatrix[0].length;
int rest = numberOfValuesPerRow % 8;
int paddings = 0;
if (rest != 0) {
paddings = 8 - rest;
}
BitSet nullbits = new BitSet(numberOfValuesPerRow + paddings);
// iterate over the number of rows to compress every row and
// write the result to disk
// if (logger.isDebugEnabled())
// logger.debug("rows x cols = " + rastermatrix.length + " x "
// + rastermatrix[0].length);
int k = 0;
for( int i = 0; i < rastermatrix.length; i++ ) {
for( int j = 0; j < rastermatrix[0].length; j++ ) {
// if it is NOT an NAN or if it is NOT a Novalue, then write the
// value
if (!Double.isNaN(rastermatrix[i][j])) {
// since we have to reread all the values, let's get the
// range
if (rastermatrix[i][j] < range[0])
range[0] = rastermatrix[i][j];
if (rastermatrix[i][j] > range[1])
range[1] = rastermatrix[i][j];
// convert the double row in a sequence of byte as needed by
// the
// deflater
if (numberofbytes == 8) {
rowAsByteBuffer.putDouble(rastermatrix[i][j]);
} else {
// the check on other formats is no longer needed, since
// it
// would jump out at the begin of this method
rowAsByteBuffer.putFloat((float) rastermatrix[i][j]);
}
/*
* ...and create the bitarray for the nullmap (in this case 0 is ok, so we
* just increment the counter k
*/
k++;
}
// if it is a novalue, set the value and add the set the bit in
// the null-bitmap to true
else {
// put in the map the placeholder = 0.0 ...
if (numberofbytes == 8) {
rowAsByteBuffer.putDouble(0.0);
} else {
// the check on other formats is no longer needed, since
// it
// would jump out at the begin of this method
rowAsByteBuffer.putFloat(0f);
}
// ...and set the bit for the nullmap
nullbits.set(k);
k++;
}
}
/*
* now the bitset is complete... just need to write it to disk to create in one time
* the row (in cell_misc)
*/
int l = 0;
byte[] bytearray = new byte[(numberOfValuesPerRow + paddings) / 8];
for( int e = 0; e < (numberOfValuesPerRow + paddings) / 8; e++ ) {
bytearray[e] = (byte) 0;
for( int f = 0; f < 8; f++ ) {
if (nullbits.get(l)) {
bytearray[e] += (byte) Math.pow(2.0, (double) (7 - f));
}
l++;
}
}
theCreatedNullFile.write(bytearray);
nullbits.clear();
k = 0;
/*
* now the row is converted to an array of bytes (ByteBuffer's method wrap applies
* changes on the ByteBuffer to the bytearray and vice versa. We can start with the
* deflater.
*/
byte[] output = new byte[rowAsBytes.length * 2];
/* lenght *2 since not always compressing gives the needed result :) */
Deflater compresser = new Deflater();
compresser.setInput(rowAsBytes);
compresser.finish();
int compressedDataLength = compresser.deflate(output);
/*
* now write to file the compressed row and set the right rowaddress.
*/
theCreatedFile.seek(pointerInFilePosition);
/*
* jgrass always uses compression, so the first byte of the row will always be 49,
* i.e. 1 which means that the row is compressed
*/
theCreatedFile.write(49);
theCreatedFile.write(output, 0, compressedDataLength);
rowaddresses[i + 1] = pointerInFilePosition = theCreatedFile.getFilePointer();
rowAsByteBuffer.clear();
}
/*
* now that all the compressed rows are written to file, we have to write their
* addresses in the header
*/
theCreatedFile.seek(1);
for( int i = 0; i < rowaddresses.length; i++ ) {
theCreatedFile.writeInt((int) rowaddresses[i]);
}
} catch (Exception e) {
throw new RasterWritingFailureException(ERROR_IN_WRITING_RASTER + e.getLocalizedMessage());
}
return true;
}
public Window getDataWindow() {
return dataWindow;
}
public int getOutputToDiskType() {
return outputToDiskType;
}
public long getPointerInFilePosition() {
return pointerInFilePosition;
}
public double[] getRange() {
return range;
}
public long[] getRowaddresses() {
return rowaddresses;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy