/*
* Copyright (C) 2012 Dr. John Lindsay <jlindsay@uoguelph.ca>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package whitebox.geospatialfiles;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
/**
* The whiteboxRaster is used to manipulate Whitebox GAT raster files (.dep and
* .tas).
*
* @author Dr. John Lindsay <jlindsay@uoguelph.ca>
*/
public class WhiteboxRaster extends WhiteboxRasterBase {
// ************************
// Fields
// ************************
private double[] grid;
private int blockSize = 0;
private long blockStartingCell = 0;
private long blockEndingCell = -1;
private double initialValue;
private boolean isDirty = false;
/**
* Set to false if the header and data files (.dep and .tas) should be
* deleted when the object is closed.
*/
public boolean isTemporaryFile = false;
/**
* Set to true when the getValue function should reflect beyond the edges.
*/
public boolean isReflectedAtEdges = false;
// ************************
// Constructors
// ************************
/**
* Class constructor. Notice that the data file name will also be set based
* on the specified header file name.
*
* @param HeaderFile The name of the WhiteboxRaster header file.
* @param FileAccess Sets the file access. Either "r" (read-only) or "rw"
* (read/write).
*/
public WhiteboxRaster(String HeaderFile, String FileAccess) {
// set the header file and data file.
headerFile = HeaderFile;
dataFile = headerFile.replace(".dep", ".tas");
statsFile = headerFile.replace(".dep", ".wstat");
setFileAccess(FileAccess);
readHeaderFile();
setBlockData();
}
/**
* Class constructor. Notice that the data file name will also be set based
* on the specified header file name.
*
* @param HeaderFile The name of the WhiteboxRaster header file.
* @param FileAccess Sets the file access. Either "r" (read-only) or "rw"
* (read/write).
* @param BufferSize Determines the how much data can be stored in memory.
*/
public WhiteboxRaster(String HeaderFile, String FileAccess, double BufferSize) {
// set the header file and data file.
headerFile = HeaderFile;
dataFile = headerFile.replace(".dep", ".tas");
statsFile = headerFile.replace(".dep", ".wstat");
setFileAccess(FileAccess);
setBufferSize(BufferSize);
readHeaderFile();
setBlockData();
}
/**
* Class constructor. Notice that the data file name will also be set based
* on the specified header file name.
*
* @param HeaderFile The name of the WhiteboxRaster header file.
* @param FileAccess Sets the file access. Either "r" (read-only) or "rw"
* (read/write).
* @param BaseRasterHeader The name of a WhiteboxRaster header file to base
* this new object on.
* @param dataType The data type of the new WhiteboxRaster. Can be 'double',
* 'float', 'integer', or 'byte'
* @param InitialValue Double indicating the value used to initialize the
* grid. It is recommended to use the noDataValue.
*/
public WhiteboxRaster(String HeaderFile, String FileAccess, String BaseRasterHeader, DataType dataType, double InitialValue) {
// set the header file and data file.
headerFile = HeaderFile;
dataFile = headerFile.replace(".dep", ".tas");
statsFile = headerFile.replace(".dep", ".wstat");
File f1 = new File(this.headerFile);
f1.delete();
f1 = new File(this.dataFile);
f1.delete();
f1 = new File(this.statsFile);
f1.delete();
initialValue = InitialValue;
setFileAccess(FileAccess);
setPropertiesUsingAnotherRaster(BaseRasterHeader, dataType);
setBlockData();
}
/**
* Class constructor. Notice that the data file name will also be set based
* on the specified header file name.
*
* @param HeaderFile The name of the WhiteboxRaster header file.
* @param FileAccess Sets the file access. Either "r" (read-only) or "rw"
* (read/write).
* @param BaseRasterHeader The name of a WhiteboxRaster header file to base
* this new object on.
* @param dataType The data type of the new WhiteboxRaster. Can be 'double',
* 'float', 'integer', or 'byte'
* @param InitialValue Double indicating the value used to initialize the
* grid. It is recommended to use the noDataValue.
* @param BufferSize Determines how much data can be stored in memory.
*/
public WhiteboxRaster(String HeaderFile, String FileAccess, String BaseRasterHeader, DataType dataType, double InitialValue, double BufferSize) {
// set the header file and data file.
headerFile = HeaderFile;
dataFile = headerFile.replace(".dep", ".tas");
statsFile = headerFile.replace(".dep", ".wstat");
File f1 = new File(this.headerFile);
f1.delete();
f1 = new File(this.dataFile);
f1.delete();
f1 = new File(this.statsFile);
f1.delete();
initialValue = InitialValue;
setFileAccess(FileAccess);
setBufferSize(BufferSize);
setPropertiesUsingAnotherRaster(BaseRasterHeader, dataType);
setBlockData();
}
public WhiteboxRaster(String HeaderFile, double north, double south, double east, double west, int rows, int cols, DataScale dataScale, DataType dataType, double initialValue, double noData) {
// set the header file and data file.
headerFile = HeaderFile;
dataFile = headerFile.replace(".dep", ".tas");
statsFile = headerFile.replace(".dep", ".wstat");
File f1 = new File(this.headerFile);
f1.delete();
f1 = new File(this.dataFile);
f1.delete();
f1 = new File(this.statsFile);
f1.delete();
this.north = north;
this.south = south;
this.east = east;
this.west = west;
this.numberRows = rows;
this.numberColumns = cols;
this.dataScale = dataScale;
setDataType(dataType);
this.noDataValue = noData;
writeHeaderFile();
this.initialValue = initialValue;
setFileAccess("rw");
setBlockData();
//createNewDataFile();
}
// ***********************************
// Property getter and setter methods.
// ***********************************
private long bufferSize = Runtime.getRuntime().maxMemory() / 5; //100 * 1048576; //in bytes
/**
* Retrieves the maximum memory usage for this Whitebox grid in megabytes.
*
* @return Maximum memory.
*/
public double getBufferSize() {
return bufferSize / 1048576;
}
/**
* Sets maximum memory usage for this Whitebox grid in megabytes.
*
* @param BufferSize maximum memory usage.
*/
private void setBufferSize(double BufferSize) {
bufferSize = (long) (BufferSize * 1048576);
}
/**
* Retrieves the block size contained in memory.
*
* @return Long containing block size
*/
public long getBlockSize() {
return blockSize;
}
private long numberOfDataFileReads = 0;
/**
* The number of times that the data file (.tas) has been read by this
* object.
*
* @return long stating the number of reads.
*/
public long getNumberOfDataFileReads() {
return numberOfDataFileReads;
}
private long numberOfDataFileWrites = 0;
/**
* The number of times that the data file (.tas) has been written by this
* object.
*
* @return long stating the number of reads.
*/
public long getNumberOfDataFileWrites() {
return numberOfDataFileWrites;
}
//********************************************
// Available methods.
// *******************************************
// /**
// * This method should be used when you need to access an entire row of data
// * at a time. It has less overhead that the getValue method and can be used
// * to efficiently scan through a raster image row by row. It will read the
// * specified row from disk and will not store it internally within the
// * WhiteboxRaster. As such, this method is appropriate when each of the cells
// * in the raster need to be accessed sequentially one time only. This is the
// * case, for example, when the raster is displayed as an image.
// * @param row An int stating the zero-based row to be returned.
// * @return An array of doubles containing the values store in the specified row.
// */
//
// public double[] getRowValues(int row) {
// if (row < 0 || row >= numberRows) { return null; }
//
// double[] retVals = new double[numberColumns];
// RandomAccessFile rIn = null;
// ByteBuffer buf = null;
// FileChannel inChannel = null;
// try {
//
// // See if the data file exists.
// File file = new File(dataFile);
// if (!file.exists()) {
// createNewDataFile();
// }
//
// // what is the starting and ending cell?
// long startingCell = row * numberColumns;
// long endingCell = startingCell + numberColumns - 1;
// int readLengthInCells = (int)(endingCell - startingCell + 1);
//
// buf = ByteBuffer.allocateDirect((int) (readLengthInCells * cellSizeInBytes));
// rIn = new RandomAccessFile(dataFile, "r");
//
// inChannel = rIn.getChannel();
// inChannel.position(startingCell * cellSizeInBytes);
// inChannel.read(buf);
//
// // Check the byte order.
// buf.order(byteOrder);
//
//
// if (dataType == DataType.DOUBLE) { //.equals("double")) {
// buf.rewind();
// DoubleBuffer db = buf.asDoubleBuffer();
// retVals = new double[readLengthInCells];
// db.get(retVals);
// db = null;
// buf = null;
// } else if (dataType == DataType.FLOAT) { //.equals("float")) {
// buf.rewind();
// FloatBuffer fb = buf.asFloatBuffer();
// float[] fa = new float[readLengthInCells];
// fb.get(fa);
// fb = null;
// buf = null;
// retVals = new double[readLengthInCells];
// for (int j = 0; j < readLengthInCells; j++) {
// retVals[j] = fa[j];
// }
// fa = null;
// } else if (dataType == DataType.INTEGER) { //.equals("integer")) {
// buf.rewind();
// ShortBuffer ib = buf.asShortBuffer();
// short[] ia = new short[readLengthInCells];
// ib.get(ia);
// ib = null;
// buf = null;
// retVals = new double[readLengthInCells];
// for (int j = 0; j < readLengthInCells; j++) {
// retVals[j] = ia[j];
// }
// ia = null;
// } else if (dataType == DataType.BYTE) { //.equals("byte")) {
// buf.rewind();
// byte[] ba = new byte[readLengthInCells];
// buf.get(ba);
// buf = null;
// retVals = new double[readLengthInCells];
// for (int j = 0; j < readLengthInCells; j++) {
// retVals[j] = ba[j];
// }
// ba = null;
// }
//
// } catch (Exception e) {
// System.err.println("Caught exception: " + e.toString());
// System.err.println(e.getStackTrace());
// } finally {
// if (rIn != null) {
// try { rIn.close(); } catch (Exception e) {}
// }
// if (inChannel != null) {
// try { inChannel.close(); } catch (Exception e) {};
// }
// numberOfDataFileReads++;
// return retVals.clone();
// }
//
//
// }
/**
* This method should be used when you need to set an entire row of data at
* a time. It has less overhead that the setValue method (which works on a
* pixel-by-pixel basis) and can be used to efficiently scan through a
* raster image row by row.
*
* @param row An int stating the zero-based row to be returned.
* @param vals An array of doubles containing the values store in the
* specified row.
*/
public void setRowValues(int row, double[] vals) {
if (!saveChanges) {
return;
}
if (vals.length != numberColumns) {
return;
}
// update the minimum and maximum values
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (int i = 0; i < numberColumns; i++) {
if (vals[i] < min && vals[i] != noDataValue) {
min = vals[i];
}
if (vals[i] > max && vals[i] != noDataValue) {
max = vals[i];
}
}
if (max > maximumValue) {
maximumValue = max;
}
if (min < minimumValue) {
minimumValue = min;
}
RandomAccessFile rOut = null;
FileChannel outChannel = null;
ByteBuffer buf = null;
try {
// See if the data file exists.
File file = new File(dataFile);
if (!file.exists()) {
createNewDataFile();
}
long startingCell = row * numberColumns;
long endingCell = startingCell + numberColumns - 1;
rOut = new RandomAccessFile(dataFile, "rw");
outChannel = rOut.getChannel();
outChannel.position(startingCell * cellSizeInBytes);
int writeLengthInCells = (int) (endingCell - startingCell + 1);
if (dataType == DataType.DOUBLE) { //.equals("double")) {
buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLengthInCells);
buf.order(byteOrder);
DoubleBuffer db = buf.asDoubleBuffer();
db.put(vals);
db = null;
outChannel.write(buf);
} else if (dataType == DataType.FLOAT) { //.equals("float")) {
float[] fa = new float[writeLengthInCells];
for (int j = 0; j < writeLengthInCells; j++) {
fa[j] = (float) vals[j];
}
buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLengthInCells);
buf.order(byteOrder);
FloatBuffer fb = buf.asFloatBuffer();
fb.put(fa);
fb = null;
fa = null;
outChannel.write(buf);
} else if (dataType == DataType.INTEGER) { //.equals("integer")) {
short[] ia = new short[writeLengthInCells];
for (int j = 0; j < writeLengthInCells; j++) {
ia[j] = (short) vals[j];
}
buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLengthInCells);
buf.order(byteOrder);
ShortBuffer ib = buf.asShortBuffer();
ib.put(ia);
ib = null;
ia = null;
outChannel.write(buf);
} else if (dataType == DataType.BYTE) { //.equals("byte")) {
byte[] ba = new byte[writeLengthInCells];
for (int j = 0; j < writeLengthInCells; j++) {
ba[j] = (byte) vals[j];
}
buf = ByteBuffer.wrap(ba);
ba = null;
outChannel.write(buf);
}
//outChannel.close();
} catch (Exception e) {
System.err.println("Caught exception: " + e.toString());
System.err.println(e.getStackTrace());
} finally {
buf = null;
if (rOut != null) {
try {
rOut.close();
} catch (Exception e) {
}
}
if (outChannel != null) {
try {
outChannel.force(false);
outChannel.close();
} catch (Exception e) {
}
}
numberOfDataFileWrites++;
}
}
private int previousRow = 0;
private int currentReadDirection = -1;
private int numSwitchReadDirections = 0;
private int numReads = 0;
private double switchRatio = 0;
private int halfBlockSize = 0;
//private int readDirectionTendency = 0;
/**
* Retrieves the value contained at a specified cell in the raster grid.
*
* @param row The zero-based row number.
* @param column The zero-based column number.
* @return The value contained in the raster grid at the specified grid
* cell.
*/
public double getValue(int row, int column) {
//try {
if (column >= 0 && column < numberColumns && row >= 0 && row < numberRows) {
if (blockEndingCell < 0) {
readDataBlock();
}
if (grid == null) {
readDataBlock();
}
// what is the cell number?
long cellNum = (long) (row) * numberColumns + column;
// check to see if it is within the current block
if ((cellNum > blockEndingCell) || (cellNum < blockStartingCell)) {
if (saveChanges && isDirty) {
writeDataBlock();
}
numReads++;
// Figure out a new blockstartingcell
if (previousRow < row) { // reading downward
if (currentReadDirection == -1) {
currentReadDirection = 0;
}
if (currentReadDirection != 0) {
currentReadDirection = 0;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - halfBlockSize * switchRatio); //10 * numberColumns);
} else { // reading upward
if (currentReadDirection == -1) {
currentReadDirection = 1;
}
if (currentReadDirection != 1) {
currentReadDirection = 1;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - (blockSize - (switchRatio * halfBlockSize))); //+ (blockSize / 2) * ((double)upReadDirection / downReadDirection)); // + 10 * numberColumns - blockSize);
}
previousRow = row;
//blockStartingCell = (int)(cellNum - blockSize / 2);
if (blockStartingCell < 0) {
blockStartingCell = 0;
}
readDataBlock();
}
return grid[(int) (cellNum - blockStartingCell)];
} else {
if (!isReflectedAtEdges) {
return noDataValue;
}
// if you get to this point, it is reflected at the edges
if (row < 0) {
row = -row - 1;
}
if (row >= numberRows) {
row = numberRows - (row - numberRows) - 1;
}
if (column < 0) {
column = -column - 1;
}
if (column >= numberColumns) {
column = numberColumns - (column - numberColumns) - 1;
}
if (column >= 0 && column < numberColumns && row >= 0 && row < numberRows) {
return getValue(row, column);
} else {
// it was too off grid to be reflected.
return noDataValue;
}
}
// } catch (Exception e) {
// if (communicator != null) {
// communicator.logException("WhiteboxRaster error", e);
// }
// return noDataValue;
// }
}
/**
* Sets the value of a specified cell in the raster grid.
*
* @param row The zero-based row number.
* @param column The zero-based column number.
* @param value The value to place in the grid cell.
*/
public void setValue(int row, int column, double value) {
// try {
if (saveChanges && column >= 0 && column < this.numberColumns
&& row >= 0 && row < this.numberRows) {
if (Double.isNaN(value)) {
value = noDataValue;
}
// what is the cell number?
long cellNum = (long) (row) * numberColumns + column;
if ((cellNum > blockEndingCell) || (cellNum < blockStartingCell)) {
if (isDirty) {
writeDataBlock();
}
numReads++;
// Figure out a new blockstartingcell
if (previousRow < row) {
if (currentReadDirection == -1) {
currentReadDirection = 0;
}
if (currentReadDirection != 0) {
currentReadDirection = 0;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - halfBlockSize * switchRatio);
} else {
if (currentReadDirection == -1) {
currentReadDirection = 1;
}
if (currentReadDirection != 1) {
currentReadDirection = 1;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - (blockSize - (switchRatio * halfBlockSize)));
}
previousRow = row;
if (blockStartingCell < 0) {
blockStartingCell = 0;
}
readDataBlock();
}
if (grid == null) {
setBlockData();
}
grid[(int) (cellNum - blockStartingCell)] = value;
isDirty = true;
}
// } catch (Exception e) {
// if (communicator != null) {
// communicator.logException("WhiteboxRaster error", e);
// }
// }
}
/**
* Increments the value of a specified cell in the raster grid.
*
* @param row The zero-based row number.
* @param column The zero-based column number.
* @param value The value to increment the grid cell by.
*/
public void incrementValue(int row, int column, double value) {
if (saveChanges && column >= 0 && column < this.numberColumns
&& row >= 0 && row < this.numberRows) {
// what is the cell number?
long cellNum = (long) (row) * numberColumns + column;
if ((cellNum > blockEndingCell) || (cellNum < blockStartingCell)) {
if (isDirty) {
writeDataBlock();
}
numReads++;
// Figure out a new blockstartingcell
if (previousRow < row) {
if (currentReadDirection == -1) {
currentReadDirection = 0;
}
if (currentReadDirection != 0) {
currentReadDirection = 0;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - halfBlockSize * switchRatio);
} else {
if (currentReadDirection == -1) {
currentReadDirection = 1;
}
if (currentReadDirection != 1) {
currentReadDirection = 1;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - (blockSize - (switchRatio * halfBlockSize)));
}
previousRow = row;
if (blockStartingCell < 0) {
blockStartingCell = 0;
}
readDataBlock();
}
grid[(int) (cellNum - blockStartingCell)] += value;
isDirty = true;
}
}
/**
* Increments the value of a specified cell in the raster grid by one.
*
* @param row The zero-based row number.
* @param column The zero-based column number.
*/
public void incrementValue(int row, int column) {
if (saveChanges && column >= 0 && column < this.numberColumns
&& row >= 0 && row < this.numberRows) {
// what is the cell number?
long cellNum = (long) (row) * numberColumns + column;
if ((cellNum > blockEndingCell) || (cellNum < blockStartingCell)) {
if (isDirty) {
writeDataBlock();
}
numReads++;
// Figure out a new blockstartingcell
if (previousRow < row) {
if (currentReadDirection == -1) {
currentReadDirection = 0;
}
if (currentReadDirection != 0) {
currentReadDirection = 0;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - halfBlockSize * switchRatio);
} else {
if (currentReadDirection == -1) {
currentReadDirection = 1;
}
if (currentReadDirection != 1) {
currentReadDirection = 1;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - (blockSize - (switchRatio * halfBlockSize)));
}
previousRow = row;
if (blockStartingCell < 0) {
blockStartingCell = 0;
}
readDataBlock();
}
grid[(int) (cellNum - blockStartingCell)]++;
isDirty = true;
}
}
/**
* Decrements the value of a specified cell in the raster grid.
*
* @param row The zero-based row number.
* @param column The zero-based column number.
* @param value The value to decrement the grid cell by.
*/
public void decrementValue(int row, int column, double value) {
if (saveChanges && column >= 0 && column < this.numberColumns
&& row >= 0 && row < this.numberRows) {
// what is the cell number?
long cellNum = (long) (row) * numberColumns + column;
if ((cellNum > blockEndingCell) || (cellNum < blockStartingCell)) {
if (isDirty) {
writeDataBlock();
}
numReads++;
// Figure out a new blockstartingcell
if (previousRow < row) {
if (currentReadDirection == -1) {
currentReadDirection = 0;
}
if (currentReadDirection != 0) {
currentReadDirection = 0;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - halfBlockSize * switchRatio);
} else {
if (currentReadDirection == -1) {
currentReadDirection = 1;
}
if (currentReadDirection != 1) {
currentReadDirection = 1;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - (blockSize - (switchRatio * halfBlockSize)));
}
previousRow = row;
if (blockStartingCell < 0) {
blockStartingCell = 0;
}
readDataBlock();
}
grid[(int) (cellNum - blockStartingCell)] -= value;
isDirty = true;
}
}
/**
* Decrements the value of a specified cell in the raster grid by one.
*
* @param row The zero-based row number.
* @param column The zero-based column number.
*/
public void decrementValue(int row, int column) {
if (saveChanges && column >= 0 && column < this.numberColumns
&& row >= 0 && row < this.numberRows) {
// what is the cell number?
long cellNum = (long) (row) * numberColumns + column;
if ((cellNum > blockEndingCell) || (cellNum < blockStartingCell)) {
if (isDirty) {
writeDataBlock();
}
numReads++;
// Figure out a new blockstartingcell
if (previousRow < row) {
if (currentReadDirection == -1) {
currentReadDirection = 0;
}
if (currentReadDirection != 0) {
currentReadDirection = 0;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - halfBlockSize * switchRatio);
} else {
if (currentReadDirection == -1) {
currentReadDirection = 1;
}
if (currentReadDirection != 1) {
currentReadDirection = 1;
numSwitchReadDirections++;
switchRatio = (double) numSwitchReadDirections / numReads;
}
blockStartingCell = (long) (cellNum - (blockSize - (switchRatio * halfBlockSize)));
}
previousRow = row;
if (blockStartingCell < 0) {
blockStartingCell = 0;
}
readDataBlock();
}
grid[(int) (cellNum - blockStartingCell)]--;
isDirty = true;
}
}
private boolean forceAllDataInMemory = false;
public void setForceAllDataInMemory(boolean value) {
forceAllDataInMemory = value;
setBlockData();
}
public boolean isForceAllDataInMemory() {
return forceAllDataInMemory;
}
private void setBlockData() {
try {
// see if the data can be comfortably contained in memory, keeping in
// mind that it is always stored as doubles.
//System.gc();
long availableMemory = Runtime.getRuntime().freeMemory();
long gridMemoryRequirements = (long) numberRows * (long) numberColumns * 8L;
if ((availableMemory / 3) > gridMemoryRequirements || forceAllDataInMemory) {
// store the entire grid in memory.
blockSize = numberRows * numberColumns;
bufferSize = gridMemoryRequirements;
} else if (((double) gridMemoryRequirements / (availableMemory / 3)) > 2) {
// the data doesn't come close to fitting in the available memory.
bufferSize = 100 * 1048576;
blockSize = (int) (Math.round(Math.floor(bufferSize / 8))) / 3;
} else {
blockSize = (numberRows * numberColumns) / 2;
bufferSize = ((numberRows * numberColumns) / 2) * 8;
}
halfBlockSize = blockSize / 2;
blockStartingCell = 0;
//readDataBlock();
} catch (Exception e) {
if (communicator != null) {
communicator.logException("WhiteboxRaster error", e);
} else {
System.out.println(e.toString());
}
}
}
public void reinitialize(double initialValue) {
this.initialValue = initialValue;
// See if the data file exists.
File file = new File(dataFile);
file.delete();
createNewDataFile();
}
public void createNewDataFile() {
RandomAccessFile rOut = null;
ByteBuffer buf = null;
FileChannel outChannel = null;
try {
long numberCells = (long) ((long) (numberRows) * numberColumns);
int writeLength = 2000000;
if (writeLength > numberCells) {
writeLength = (int) numberCells;
}
long numCellsWritten = 0;
rOut = new RandomAccessFile(dataFile, "rws");
outChannel = rOut.getChannel();
outChannel.position(0);
buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLength);
buf.order(byteOrder);
if (dataType == DataType.DOUBLE) { //.equals("double")) {
double[] da;
DoubleBuffer db = buf.asDoubleBuffer();
do {
if ((numCellsWritten + writeLength) > numberCells) {
writeLength = (int) (numberCells - numCellsWritten);
buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLength);
buf.order(byteOrder);
db = buf.asDoubleBuffer();
}
da = new double[writeLength];
if (initialValue != 0) {
Arrays.fill(da, initialValue);
}
//buf = ByteBuffer.allocate(cellSizeInBytes * writeLength);
//buf.order(byteOrder);
buf.clear();
db.clear();
db.put(da);
outChannel.write(buf);
numCellsWritten += writeLength;
} while (numCellsWritten < numberCells);
db = null;
da = null;
} else if (dataType == DataType.FLOAT) { //.equals("float")) {
//buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLength);
//buf.order(byteOrder);
float[] fa;
FloatBuffer fb = buf.asFloatBuffer();
do {
if ((numCellsWritten + writeLength) > numberCells) {
writeLength = (int) (numberCells - numCellsWritten);
buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLength);
buf.order(byteOrder);
fb = buf.asFloatBuffer();
}
fa = new float[writeLength];
if (initialValue != 0) {
Arrays.fill(fa, (float) initialValue);
}
//buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLength);
//buf.order(byteOrder);
buf.clear();
fb.clear();
fb.put(fa);
outChannel.write(buf);
numCellsWritten += writeLength;
} while (numCellsWritten < numberCells);
fb = null;
fa = null;
} else if (dataType == DataType.INTEGER) { //.equals("integer")) {
short[] ia;
ShortBuffer ib = buf.asShortBuffer();
do {
if ((numCellsWritten + writeLength) > numberCells) {
writeLength = (int) (numberCells - numCellsWritten);
buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLength);
buf.order(byteOrder);
ib = buf.asShortBuffer();
}
ia = new short[(int) writeLength];
if (initialValue != 0) {
Arrays.fill(ia, (short) initialValue);
}
//buf = ByteBuffer.allocate(cellSizeInBytes * writeLength);
//buf.order(byteOrder);
buf.clear();
ib.clear();
ib.put(ia);
outChannel.write(buf);
numCellsWritten += writeLength;
} while (numCellsWritten < numberCells);
ib = null;
ia = null;
} else if (dataType == DataType.BYTE) { //.equals("byte")) {
byte[] ba;
do {
if ((numCellsWritten + writeLength) > numberCells) {
writeLength = (int) (numberCells - numCellsWritten);
buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLength);
buf.order(byteOrder);
}
ba = new byte[writeLength];
if (initialValue != 0) {
Arrays.fill(ba, (byte) initialValue);
}
buf = ByteBuffer.wrap(ba);
buf.flip();
outChannel.write(buf);
numCellsWritten += writeLength;
} while (numCellsWritten < numberCells);
ba = null;
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
if (outChannel != null) {
try {
outChannel.force(false);
outChannel.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
buf = null;
if (rOut != null) {
try {
rOut.close();
} catch (Exception e) {
}
}
}
}
private void readDataBlock() {
RandomAccessFile rIn = null;
FileChannel inChannel = null;
ByteBuffer buf = null;
try {
// See if the data file exists.
File file = new File(dataFile);
if (!file.exists()) {
createNewDataFile();
}
// What is the ending cell?
long endCell = (long) (blockStartingCell) + blockSize;
if (endCell > ((long) (numberRows) * numberColumns - 1)) {
endCell = (long) (numberRows) * numberColumns - 1;
}
blockEndingCell = endCell;
int readLengthInCells = (int) (blockEndingCell - blockStartingCell + 1);
buf = ByteBuffer.allocate((int) (readLengthInCells * cellSizeInBytes));
rIn = new RandomAccessFile(dataFile, "r");
inChannel = rIn.getChannel();
inChannel.position(blockStartingCell * cellSizeInBytes);
inChannel.read(buf);
// Check the byte order.
buf.order(byteOrder);
grid = new double[readLengthInCells];
if (dataType == DataType.DOUBLE) {
buf.rewind();
DoubleBuffer db = buf.asDoubleBuffer();
db.get(grid);
} else if (dataType == DataType.FLOAT) {
buf.rewind();
FloatBuffer fb = buf.asFloatBuffer();
float[] fa = new float[readLengthInCells];
fb.get(fa);
for (int j = 0; j < readLengthInCells; j++) {
grid[j] = fa[j];
}
} else if (dataType == DataType.INTEGER) {
buf.rewind();
ShortBuffer ib = buf.asShortBuffer();
short[] ia = new short[readLengthInCells];
ib.get(ia);
for (int j = 0; j < readLengthInCells; j++) {
grid[j] = ia[j];
}
} else if (dataType == DataType.BYTE) {
buf.rewind();
for (int j = 0; j < readLengthInCells; j++) {
grid[j] = whitebox.utilities.Unsigned.getUnsignedByte(buf, j);
}
}
} catch (Exception e) {
if (communicator != null) {
communicator.logException("WhiteboxRaster error", e);
} else {
System.out.println(e.toString());
}
} catch (Throwable t) {
if (communicator != null) {
communicator.logThrowable("WhiteboxRaster error", t);
} else {
System.err.println(t.getMessage());
}
} finally {
if (rIn != null) {
try {
rIn.close();
} catch (Exception e) {
}
}
if (inChannel != null) {
try {
inChannel.close();
} catch (Exception e) {
}
}
numberOfDataFileReads++;
}
}
/**
* Used to dump any data contained in memory to disk.
*/
public void flush() {
writeDataBlock();
}
/**
* Dumps the data block currently in memory to the data file.
*/
private void writeDataBlock() {
try {
if (!saveChanges) {
return;
}
// update the minimum and maximum values
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (int i = 0; i < grid.length; i++) {
if (grid[i] < min && grid[i] != noDataValue) {
min = grid[i];
}
if (grid[i] > max && grid[i] != noDataValue) {
max = grid[i];
}
}
if (max > maximumValue) {
maximumValue = max;
}
if (min < minimumValue) {
minimumValue = min;
}
RandomAccessFile rOut = null;
ByteBuffer buf = null;
FileChannel outChannel = null;
try {
// See if the data file exists.
File file = new File(dataFile);
if (!file.exists()) {
createNewDataFile();
}
rOut = new RandomAccessFile(dataFile, "rw");
outChannel = rOut.getChannel();
outChannel.position(blockStartingCell * cellSizeInBytes);
int writeLengthInCells = (int) (blockEndingCell - blockStartingCell + 1);
/*long startPos = blockStartingCell * cellSizeInBytes;
FileChannel fc = new RandomAccessFile(dataFile, "rw").getChannel();
FloatBuffer fb = fc.map(FileChannel.MapMode.READ_WRITE, startPos,
writeLengthInCells).asFloatBuffer();
for(int j = 0; j < writeLengthInCells; j++) {
fb.put((float)grid[j]);
}
fc.force(true);
fc.close();*/
if (dataType == DataType.DOUBLE) { //.equals("double")) {
buf = ByteBuffer.allocate(cellSizeInBytes * writeLengthInCells);
buf.order(byteOrder);
DoubleBuffer db = buf.asDoubleBuffer();
db.put(grid);
db = null;
outChannel.write(buf);
} else if (dataType == DataType.FLOAT) { //.equals("float")) {
float[] fa = new float[writeLengthInCells];
for (int j = 0; j < writeLengthInCells; j++) {
fa[j] = (float) grid[j];
}
buf = ByteBuffer.allocateDirect(cellSizeInBytes * writeLengthInCells);
buf.order(byteOrder);
FloatBuffer fb = buf.asFloatBuffer();
fb.put(fa);
fb = null;
fa = null;
outChannel.write(buf);
} else if (dataType == DataType.INTEGER) { //.equals("integer")) {
short[] ia = new short[writeLengthInCells];
for (int j = 0; j < writeLengthInCells; j++) {
ia[j] = (short) grid[j];
}
buf = ByteBuffer.allocate(cellSizeInBytes * writeLengthInCells);
buf.order(byteOrder);
ShortBuffer ib = buf.asShortBuffer();
ib.put(ia);
ib = null;
ia = null;
outChannel.write(buf);
} else if (dataType == DataType.BYTE) { //.equals("byte")) {
byte[] ba = new byte[writeLengthInCells];
for (int j = 0; j < writeLengthInCells; j++) {
ba[j] = (byte) grid[j];
}
buf = ByteBuffer.wrap(ba);
ba = null;
outChannel.write(buf);
}
} catch (Exception e) {
System.err.println("Caught exception: " + e.toString());
System.err.println(e.getStackTrace());
} finally {
buf = null;
if (rOut != null) {
try {
rOut.close();
} catch (Exception e) {
}
}
if (outChannel != null) {
try {
outChannel.force(false);
outChannel.close();
} catch (Exception e) {
}
}
isDirty = false;
numberOfDataFileWrites++;
}
} catch (Exception e) {
if (communicator != null) {
communicator.logException("WhiteboxRaster error", e);
}
}
}
/**
* Used to perform closing functionality when a whiteboxRaster is no longer
* needed.
*/
@Override
public void close() {
if (this.isTemporaryFile) {
File f1 = new File(this.headerFile);
f1.delete();
f1 = new File(this.dataFile);
f1.delete();
} else {
if (saveChanges) {
if (isDirty) {
writeDataBlock();
}
findMinAndMaxVals();
writeHeaderFile();
}
}
grid = null;
}
}