/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.jgrasstools.gears.io.grasslegacy.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.BitSet;
import java.util.LinkedHashMap;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import org.jgrasstools.gears.i18n.GearsMessageHandler;
import org.jgrasstools.gears.io.grasslegacy.map.attribute.AttributeTable;
import org.jgrasstools.gears.io.grasslegacy.map.color.ColorMapBuffer;
import org.jgrasstools.gears.io.grasslegacy.utils.FileUtilities;
import org.jgrasstools.gears.io.grasslegacy.utils.GrassLegacyConstans;
import org.jgrasstools.gears.io.grasslegacy.utils.JlsTokenizer;
import org.jgrasstools.gears.io.grasslegacy.utils.Window;
import org.jgrasstools.gears.libs.monitor.IJGTProgressMonitor;
/**
* <p>
* This reads any native Raster map format. It supports integer, float and double and the
* transformation of any of those into int, float and double matrixes, as well as in the ByteBuffers
* of the same tipes.
* </p>
*
* @author Andrea Antonello - www.hydrologis.com
* @since 1.1.0
*/
public class GrassRasterReader extends MapReader {
/*
* 0 for 1-byte integer, 1 for 2-byte integer and so on, -1 for float, -2 for double
*/
private int rasterMapType = -9999;
private int numberOfBytesPerValue = -9999;
/* 1 for compressed, 0 for not compressed */
private int compressed = -9999;
private Object novalue = new Double(Double.NaN);
/**
* Comment for <code>matrixType</code> this defines the tipe of matrix to return: 0 = normal
* type[][] matrix 1 = type[][] matrix indexed from [1][1] 2 = transposed of the original
* type[][] matrix 3 = transposed of the original type[][] matrix and indexed by [1][1]
*/
private int matrixType = 0;
/**
* Comment for <code>theFilePath</code> this is the complete path including the filename
*/
// private String theFilePath = null;
// private String theNullFilePath = null;
private RandomAccessFile cellFile = null;
private RandomAccessFile nullFile = null;
private String filename = null;
private String locationPath = null;
private String mapsetPath = null;
private String reclassPath = null;
private Vector<Object> reclassTable = null;
/**
* Comment for <code>addressesofrows</code> the pointers to access in the map file the begin
* of every row
*/
private long[] addressesofrows = null;
private boolean moreData = false;
private boolean hasChanged = true;
private boolean isOldIntegerMap = false;
// private ByteBuffer rowCache = null;
private int rowCacheRow = -1;
private int firstDataRow = -1;
private final double[] range = new double[]{1000000.0, -1000000.0}; // min,
// max
private final double[] dataRange = new double[]{Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY};
/* Storage for cell category descriptive information */
private AttributeTable attTable = null;
private AttributeTable legendAttribTable = null;
private double[][] outputData;
/** Creates a new instance of GrassRasterReader */
public GrassRasterReader() {
super(MapReader.RASTER_READER);
}
/*
* (non-Javadoc)
*
* @see eu.hydrologis.jgrass.libs.io.MapReader#open(java.lang.String)
*/
public boolean open( String mapPath ) {
File file = new File(mapPath);
if (!file.exists()) {
return false;
}
File mapsetFile = file.getParentFile().getParentFile();
File locationFile = mapsetFile.getParentFile();
return this.open(file.getName(), locationFile.getAbsolutePath(), mapsetFile.getName());
}
/*
* (non-Javadoc)
*
* @see jgrass.io.MapReader#open(java.lang.String, jgrass.map.Mapset) this opens checks for the
* file existence, sets the active and map regions, defines the maptype, opens the map and
* extracts the header to extract the rowaddresse and check if everything is alright
*/
public boolean open( String fileName, String locationPath, String mapsetName ) {
cellFile = nullFile = null;
filename = fileName;
this.locationPath = locationPath;
this.mapsetPath = locationPath + File.separator + mapsetName;
if (hasChanged) {
/* Test the type of the map. */
if (rasterMapType == -9999) {
if (!getRasterMapTypes(filename, mapsetPath)) {
return false;
}
}
if (compressed == 1) {
/*
* Read the header of the map and the addresses of the compressed rows.
*/
try {
addressesofrows = getRowAddressesFromHeader(readHeader(cellFile));
} catch (IOException e) {
return false;
}
}
/* Ok. Get ready to read data */
moreData = true;
}
hasChanged(false);
return true;
}
public void close() {
try {
cellFile.close();
if (nullFile != null)
nullFile.close();
} catch (IOException e) {
e.printStackTrace();
}
hasChanged(true);
moreData = true;
}
/**
*
*/
/**
*
*/
public AttributeTable loadAttributeTable( String themapsetPath, String thefilename ) {
File ds1 = null;
try {
/*
* File is a standard file where the categories values are stored in the cats directory.
*/
ds1 = new File(themapsetPath + File.separator + GrassLegacyConstans.CATS + File.separator + thefilename);
if (!ds1.exists()) {
return null;
}
BufferedReader rdr = new BufferedReader(new FileReader(ds1));
/* Instantiate attribute table */
attTable = new AttributeTable();
/* Ignore first 4 lines. */
rdr.readLine();
rdr.readLine();
rdr.readLine();
rdr.readLine();
/* Read next n lines */
String line;
while( (line = rdr.readLine()) != null ) {
/* All lines other than '0:no data' are processed */
if (line.indexOf("0:no data") == -1) { //$NON-NLS-1$
JlsTokenizer tk = new JlsTokenizer(line, ":"); //$NON-NLS-1$
if (tk.countTokens() == 2) {
float f = Float.parseFloat(tk.nextToken());
String att = tk.nextToken().trim();
attTable.addAttribute(f, att);
} else if (tk.countTokens() == 3) {
float f0 = Float.parseFloat(tk.nextToken());
float f1 = Float.parseFloat(tk.nextToken());
String att = tk.nextToken().trim();
attTable.addAttribute(f0, f1, att);
}
}
}
return attTable;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/*
* (non-Javadoc)
*
* @see jgrass.io.MapReader#hasMoreData() checks if there are more data and if there are not, it
* stops
*/
public boolean hasMoreData( IJGTProgressMonitor monitor ) throws Exception {
try {
if (dataWindow != null && moreData == true) {
outputData = new double[dataWindow.getRows()][dataWindow.getCols()];
/* Allocate the space for the map data. */
int bufferSize = dataWindow.getRows() * dataWindow.getCols() * numberOfBytesPerValue;
// System.out.println("retrieveing data in window: " +
// dataWindow.toString());
try {
/* Byte array that will hold a complete null row */
byte[] nullRow = null;
/* The rowDataArray holds the unpacked row data */
byte[] rowDataCache = new byte[dataWindow.getCols() * numberOfBytesPerValue];
/* The rowColorDataArray holds the unpacked row color data */
byte[] rowColorDataCache = new byte[dataWindow.getCols() * 4];
/* Reset row cache */
// rowCache = ByteBuffer.allocate(fileWindow.getCols() *
// ((rasterMapType == -2) ? 8 : 4));
// if (logger.isDebugEnabled())
// logger.debug("allocating rowDataArray["+rowDataArray.length+
// "], rowCache["+rowCache.limit()+"]");
rowCacheRow = -1;
firstDataRow = -1;
int rowindex = -1;
/* Get a local reference to speed things up */
int filerows = fileWindow.getRows();
double filenorth = fileWindow.getNorth();
double filensres = fileWindow.getNSResolution();
double datanorth = dataWindow.getNorth();
double datansres = dataWindow.getNSResolution();
// double datansres2 = datansres / 2;
/* Iterate through all the rows of the data window. */
// int lastpos = 0;
monitor.beginTask(GearsMessageHandler.getInstance().message("grass.legacy.reading") + filename, dataWindow //$NON-NLS-1$
.getRows());
int stepRowsForPercentage = dataWindow.getRows() / 100;
for( double row = 0; row < dataWindow.getRows(); row++ ) {
if (row % stepRowsForPercentage == 0.0)
monitor.worked(stepRowsForPercentage);
/*
* Calculate the map file row for the current data window row.
*/
double filerow = (filenorth - (datanorth - (row * datansres))) / filensres;
filerow = Math.floor(filerow);
// int filerow = (int) ((filenorth - (datanorth - (row *
// datansres + datansres2))) / filensres);
// System.out.print("FILENORTH="+filenorth+",
// DATANORTH="+datanorth+", FILEROW="+row);
if (filerow < 0 || filerow >= filerows) {
// System.out.println(", NULL ROW");
/*
* If no data has been read yet, then increment first data row counter
*/
if (firstDataRow == -1)
rowindex++;
/*
* Write a null row to the raster buffer. To speed things up the first
* time this is called it instantiates the buffer and fills it with null
* values that are reused the other times.
*/
if (nullRow == null)
nullRow = initNullRow();
ByteBuffer wrap = ByteBuffer.wrap(nullRow);
for( int col = 0; col < dataWindow.getCols(); col++ ) {
outputData[(int) row][col] = wrap.getDouble();
}
} else {
// System.out.println(", DATA ROW");
if (firstDataRow == -1)
firstDataRow = rowindex + 1;
/* Read row and put in raster buffer */
if (filerow == rowCacheRow) {
ByteBuffer wrap = ByteBuffer.wrap(rowDataCache);
for( int col = 0; col < dataWindow.getCols(); col++ ) {
outputData[(int) row][col] = wrap.getDouble();
}
} else {
readRasterRow((int) filerow, rowDataCache, rowColorDataCache);
rowCacheRow = (int) filerow;
ByteBuffer wrap = ByteBuffer.wrap(rowDataCache);
for( int col = 0; col < dataWindow.getCols(); col++ ) {
outputData[(int) row][col] = wrap.getDouble();
}
}
}
// System.out.println("FILEROWS="+filerows+",
// FILEROW="+filerow+", ROWCACHEROW="+rowCacheRow+",
// ROW_COUNTER="+row);
}
monitor.done();
rowDataCache = null;
rowColorDataCache = null;
rowCacheRow = -1;
nullRow = null;
System.gc();
} catch (IOException e) {
moreData = false;
} catch (DataFormatException e) {
moreData = false;
}
}
} catch (Exception e) {
e.printStackTrace();
throw new RasterReadingFailureException("Problems reading raster: " + e.getLocalizedMessage());
}
return moreData;
}
/*
* (non-Javadoc)
*
* @see jgrass.io.MapReader#getNextData() returns the data in the required format
*/
public Object getNextData() {
moreData = false;
if (dataObject instanceof double[][]) {
return outputData;
} else {
throw new RuntimeException();
}
}
public ColorMapBuffer getNextDataColor( int attColor ) {
return null;
}
/**
* Create a string that defines how the legend will look.
* @throws IOException
*/
public String getLegendString() throws IOException {
return null;
}
/**
*
*/
public void hasChanged( boolean _haschanged ) {
// if (_haschanged) {
// novalue = new Double(Double.NaN);
// matrixType = 0;
// // theFilePath = null;
// cellFile = nullFile = null;
// filename = null;
// mapsetPath = null;
// locationPath = null;
// rasterByteBuffer = null;
// moreData = false;
// // fileWindow = null;
// // dataWindow = null;
// rasterMapType = -9999;
// numberOfBytesPerValue = -9999;
// compressed = -9999;
// }
// hasChanged = _haschanged;
}
/**
* utility to set particular parameters
*/
public void setParameter( String key, Object obj ) {
if (key.equals("novalue")) { //$NON-NLS-1$
novalue = obj;
} else if (key.equals("matrixtype")) { //$NON-NLS-1$
Integer dmtype = (Integer) obj;
matrixType = dmtype.intValue();
}
}
public void setOutputDataObject( Object _dataObject ) {
/* Call parent class to store data object */
super.setOutputDataObject(_dataObject);
}
/**
* Returns the path to the map relative to the mapset.
*/
public String getMapPath() {
return GrassLegacyConstans.CELL;
}
/**
* Reads the header part of the file into memory
*/
private ByteBuffer readHeader( RandomAccessFile ds ) throws IOException {
/*
* the first byte defines the number of bytes are used to describe the row addresses in the
* header (once it was sizeof(long) in grass but then it was turned to an offset (that
* brought to reading problems in JGrass whenever the offset was != 4).
*/
int first = ds.read();
ByteBuffer fileHeader = ByteBuffer.allocate(1 + first * fileWindow.getRows() + first);
ds.seek(0);
/* Read header */
ds.read(fileHeader.array());
return fileHeader;
}
/**
* Extract the row addresses from the header information of the file
*/
private long[] getRowAddressesFromHeader( ByteBuffer header ) {
/*
* Jump over the no more needed first byte (used in readHeader to define the header size)
*/
byte firstbyte = header.get();
/* Read the data row addresses inside the file */
long[] adrows = new long[fileWindow.getRows() + 1];
if (firstbyte == 4) {
for( int i = 0; i <= fileWindow.getRows(); i++ ) {
adrows[i] = header.getInt();
}
} else if (firstbyte == 8) {
for( int i = 0; i <= fileWindow.getRows(); i++ ) {
adrows[i] = header.getLong();
}
} else {
// problems
}
return adrows;
}
/**
* Determines the map type given a file and its mapset. It reads the information from the header
* file in the cellhd directory and determines the geographic limits, format of the data, etc
* from the file. A Typical file header looks like: proj: 1 zone: 13 north: 4928000 south:
* 4914000 east: 609000 west: 590000 cols: 950 rows: 700 e-w resol: 20 n-s resol: 20 format: 0
* compressed: 1 If the first line is 'reclass' then this file is a reclassified file and the
* original data file is given by the next two lines: reclass name: soils mapset: PERMANENT #1 5
* 3 8 .... .... We also check the cell_misc directory to determine the raster map binary format
* and type, the file f_format, which holds type, exists only if the map is floating point map.
*/
private boolean getRasterMapTypes( String fname, String mapsetPath ) {
LinkedHashMap<String, String> fileMapHeader = new LinkedHashMap<String, String>();
/* Read contents of 'cellhd/name' file from the current mapset */
String line;
BufferedReader cellhead;
String reclassFile = null;
String reclassMapset = null;
reclassTable = null;
try {
File ds4 = new File(mapsetPath + File.separator + GrassLegacyConstans.CELLHD + File.separator + fname);
cellhead = new BufferedReader(new FileReader(ds4));
cellhead.mark(128);
/*
* Read first line to determine if file is a reclasses file. If it is then open the data
* file and continue as per usual.
*/
if ((line = cellhead.readLine()) == null)
return false;
if (line.trim().equalsIgnoreCase("reclass")) { //$NON-NLS-1$
/* The next two lines hold the orginal map file amd mapset */
for( int i = 0; i < 2; i++ ) {
if ((line = cellhead.readLine()) == null)
return false;
JlsTokenizer tk = new JlsTokenizer(line, ":"); //$NON-NLS-1$
if (tk.countTokens() == 2) {
String s = tk.nextToken();
if (s.equalsIgnoreCase("name")) //$NON-NLS-1$
reclassFile = tk.nextToken().trim();
else if (s.equalsIgnoreCase("mapset")) //$NON-NLS-1$
reclassMapset = tk.nextToken().trim();
}
}
/* Instantiate the reclass table */
reclassTable = new Vector<Object>();
/* The next line holds the start value for categories */
if ((line = cellhead.readLine()) == null)
return false;
if (line.charAt(0) == '#') {
int reclassFirstCategory = Integer.parseInt(line.trim().substring(1));
/* Pad reclass table until the first reclass category */
for( int i = 0; i < reclassFirstCategory; i++ ) {
reclassTable.addElement(""); //$NON-NLS-1$
}
} else {
/* Add an empty element for the 0th category */
reclassTable.addElement(""); //$NON-NLS-1$
}
/* Now read the reclass table */
while( (line = cellhead.readLine()) != null ) {
reclassTable.addElement(new Integer(line));
}
/* Construct original data file path */
reclassPath = locationPath + File.separator + reclassMapset + File.separator;
/* Test for its existence */
ds4 = new File(reclassPath + GrassLegacyConstans.CELLHD + File.separator + reclassFile);
if (!ds4.exists()) {
return false;
}
cellhead = new BufferedReader(new FileReader(ds4));
// if (logger.isDebugEnabled())
// logger.debug("map is a reclassed map, original="
// + reclassPath + reclassFile
// + ", reclassFirstCategory=" + reclassFirstCategory);
} else {
/* Push first line back onto buffered reader stack */
cellhead.reset();
}
while( (line = cellhead.readLine()) != null ) {
line = line.replaceFirst(":", "@@@@");
StringTokenizer tok = new StringTokenizer(line, "@@@@"); //$NON-NLS-1$
if (tok.countTokens() == 2) {
String key = tok.nextToken().trim();
String value = tok.nextToken().trim();
/* If key is 'ew resol' or 'ns resol' then store 'xx res' */
if (key.indexOf("resol") != -1) //$NON-NLS-1$
fileMapHeader.put(key.replaceAll("resol", "res"), value); //$NON-NLS-1$ //$NON-NLS-2$
else
fileMapHeader.put(key, value);
}
}
/*
* Setup file window object that holds the geographic limits of the file data.
*/
fileWindow = null;
if (fileMapHeader.containsKey("n-s res")) { //$NON-NLS-1$
fileWindow = new Window(fileMapHeader.get("west"), //$NON-NLS-1$
fileMapHeader.get("east"), //$NON-NLS-1$
fileMapHeader.get("south"), //$NON-NLS-1$
fileMapHeader.get("north"), //$NON-NLS-1$
fileMapHeader.get("e-w res"), fileMapHeader.get("n-s res")); //$NON-NLS-1$
} else if (fileMapHeader.containsKey("cols")) { //$NON-NLS-1$
fileWindow = new Window(fileMapHeader.get("west"), //$NON-NLS-1$
fileMapHeader.get("east"), //$NON-NLS-1$
fileMapHeader.get("south"), //$NON-NLS-1$
fileMapHeader.get("north"), //$NON-NLS-1$
Integer.parseInt(fileMapHeader.get("rows")), //$NON-NLS-1$
Integer.parseInt(fileMapHeader.get("cols"))); //$NON-NLS-1$
} else {
throw new IllegalArgumentException(
"Missing infor in the header file. Row/cols or resolution info have to be available.");
}
// if (logger.isDebugEnabled())
// logger.debug("map file window: " + fileWindow.toString());
if (!fileMapHeader.get("format").equals("")) { //$NON-NLS-1$ //$NON-NLS-2$
rasterMapType = new Integer(fileMapHeader.get("format")).intValue(); //$NON-NLS-1$
if (rasterMapType > -1) {
rasterMapType++;
/*
* In Grass integers can be from 1 to 4 bytes. Jgrass will convert them all
* directly into an intger (4-bytes) at reding and decompressing time. Therefore
* the numberofbytespervalue is always 4.
*/
numberOfBytesPerValue = 4;
/* Instantiate cell file object. */
File ds = null;
if (reclassPath == null)
ds = new File(mapsetPath + File.separator + GrassLegacyConstans.CELL + File.separator + fname);
else
ds = new File(reclassPath + GrassLegacyConstans.CELL + File.separator + reclassFile);
if (ds.exists()) {
cellFile = new RandomAccessFile(ds, "r"); //$NON-NLS-1$
if (cellFile == null) {
// if (logger.isDebugEnabled())
// logger
// .debug("cannot open integer map file, file="
// + ds.toString());
return false;
} else {
// if (logger.isDebugEnabled())
// logger.debug("opening integer map file: "
// + ds.toString());
}
/* Check if null file exists. */
nullFile = null;
if (reclassPath == null)
ds = new File(mapsetPath + File.separator + GrassLegacyConstans.CELL_MISC + File.separator + filename
+ File.separator + GrassLegacyConstans.CELLMISC_NULL);
else
ds = new File(reclassPath + GrassLegacyConstans.CELL_MISC + File.separator + reclassFile + File.separator
+ GrassLegacyConstans.CELLMISC_NULL);
if (ds.exists()) {
nullFile = new RandomAccessFile(ds, "r"); //$NON-NLS-1$
if (nullFile == null) {
isOldIntegerMap = false;
// if (logger.isDebugEnabled())
// logger.debug("cannot open null file: "
// + ds.toString());
} else {
isOldIntegerMap = true;
// if (logger.isDebugEnabled())
// logger.debug("opening null file: "
// + ds.toString());
}
}
} else {
// if (logger.isDebugEnabled())
// logger.error("integer map file doesn't exist, map="
// + filename);
return false;
}
} else if (rasterMapType < 0) {
/*
* Read contents of 'cell_misc/name/f_format' file from the current mapset
*/
File ds5 = null;
if (reclassPath == null)
ds5 = new File(mapsetPath + File.separator + GrassLegacyConstans.CELL_MISC + File.separator + fname
+ File.separator + GrassLegacyConstans.CELLMISC_FORMAT);
else
ds5 = new File(reclassPath + GrassLegacyConstans.CELL_MISC + File.separator + reclassFile + File.separator
+ GrassLegacyConstans.CELLMISC_FORMAT);
if (ds5.exists()) {
/*
* if the file f_format exists, then we are talking about floating maps
*/
BufferedReader cellmiscformat = new BufferedReader(new FileReader(ds5));
while( (line = cellmiscformat.readLine()) != null ) {
StringTokenizer tokk = new StringTokenizer(line, ":"); //$NON-NLS-1$
if (tokk.countTokens() == 2) {
String key = tokk.nextToken().trim();
String value = tokk.nextToken().trim();
fileMapHeader.put(key, value);
}
}
// assign the values
if (!fileMapHeader.get("type").equals("")) { //$NON-NLS-1$ //$NON-NLS-2$
if ((fileMapHeader.get("type")) //$NON-NLS-1$
.equalsIgnoreCase("double")) { //$NON-NLS-1$
rasterMapType = -2;
numberOfBytesPerValue = 8;
} else if ((fileMapHeader.get("type")) //$NON-NLS-1$
.equalsIgnoreCase("float")) { //$NON-NLS-1$
rasterMapType = -1;
numberOfBytesPerValue = 4;
} else {
// if (logger.isDebugEnabled())
// logger.error("Type in the "
// + GrassLocation.CELLMISC_FORMAT
// + " file is not consistent");
return false;
}
} else {
// if (logger.isDebugEnabled())
// logger
// .error("FileSystem inconsistent. Floating type
// not defined");
return false;
}
cellmiscformat.close();
} else {
// if (logger.isDebugEnabled())
// logger
// .debug("floating point format file not found, file="
// + ds5.toString());
return false;
}
isOldIntegerMap = false;
/* Instantiate cell file and null file objects */
if (reclassPath == null)
ds5 = new File(mapsetPath + File.separator + GrassLegacyConstans.FCELL + File.separator + fname);
else
ds5 = new File(reclassPath + GrassLegacyConstans.FCELL + File.separator + reclassFile);
if (ds5.exists()) {
cellFile = new RandomAccessFile(ds5, "r"); //$NON-NLS-1$
if (cellFile == null) {
// if (logger.isDebugEnabled())
// logger
// .error("cannot open floating point map file,
// file: "
// + ds5.toString());
return false;
} else {
// if (logger.isDebugEnabled())
// logger
// .debug("opening floating point cell file: "
// + ds5.toString());
}
nullFile = null;
if (reclassPath == null)
ds5 = new File(mapsetPath + File.separator + GrassLegacyConstans.CELL_MISC + File.separator + filename
+ File.separator + GrassLegacyConstans.CELLMISC_NULL);
else
ds5 = new File(reclassPath + GrassLegacyConstans.CELL_MISC + File.separator + reclassFile + File.separator
+ GrassLegacyConstans.CELLMISC_NULL);
if (ds5.exists()) {
nullFile = new RandomAccessFile(ds5, "r"); //$NON-NLS-1$
if (nullFile == null) {
// if (logger.isDebugEnabled())
// logger.debug("cannot open null file: "
// + ds5.toString());
} else {
// if (logger.isDebugEnabled())
// logger.debug("opening null file: "
// + ds5.toString());
}
}
} else {
// if (logger.isDebugEnabled())
// logger.debug("cannot open file , file="
// + ds5.toString());
return false;
}
}
} else {
// if (logger.isDebugEnabled())
// logger
// .error("FileSystem inconsistent. Fileformat not recognized");
return false;
}
if (!fileMapHeader.get("compressed").equals("")) { //$NON-NLS-1$ //$NON-NLS-2$
compressed = new Integer(fileMapHeader.get("compressed")).intValue(); //$NON-NLS-1$
} else {
// if (logger.isDebugEnabled())
// logger
// .error("FileSystem inconsistent. Compression not defined");
return false;
}
cellhead.close();
} catch (FileNotFoundException e) {
// if (logger.isDebugEnabled())
// logger.error(DebugPanel.FormatExceptionTrace(e));
return false;
} catch (IOException e) {
// if (logger.isDebugEnabled())
// logger.error(DebugPanel.FormatExceptionTrace(e));
return false;
}
return true;
}
/**
* reads a row of data from the file into a byte array.
*
* @param currentfilerow the current row to be extracted from the file
* @param rowDataCache the byte array to store the unpacked row data
* @return boolean TRUE for success, FALSE for failure.
* @throws IOException
* @throws DataFormatException
*/
private boolean readRasterRow( int currentfilerow, byte[] rowDataCache, byte[] rowColorDataCache ) throws IOException,
DataFormatException {
ByteBuffer rowBuffer = ByteBuffer.wrap(rowDataCache);
ByteBuffer rowColorBuffer = ByteBuffer.wrap(rowColorDataCache);
/*
* Read the correct approximated row from the file. The row contents as saved in a cache for
* along with the row number. If the row requested is the row in the cache then we do not
* ned to read from the file.
*/
// int currentfilecol;
boolean iscompressed = (compressed == 1 ? true : false);
/* Data window geographic boundaries */
double activeewres = dataWindow.getWEResolution();
// double activeewres2 = activeewres / 2;
double activewest = dataWindow.getWest();
/* Map file geographic limits */
double filewest = fileWindow.getWest();
double fileewres = fileWindow.getWEResolution();
// System.out.println("currentfilerow="+currentfilerow+",
// fileWindow.getRows()="+fileWindow.getRows());
/* Reset row cache and read new row data */
ByteBuffer rowCache = ByteBuffer.allocate(fileWindow.getCols() * ((rasterMapType == -2) ? 8 : 4));
// rowCache.rewind();
getMapRow(currentfilerow, rowCache, iscompressed);
// rowCacheRow = currentfilerow;
// if the northing is inside the file boundaries, calculate the values
// for (double col = activewest; col < activeeast; col += activeewres)
for( double col = 0; col < dataWindow.getCols(); col++ ) {
/*
* Calculate the column value of the data to be extracted from the row
*/
double x = (((activewest + (col * activeewres)) - filewest) / fileewres);
x = Math.floor(x);
// currentfilecol = (int) (((activewest + (col * activeewres +
// activeewres2)) - filewest) / fileewres);
if (x < 0 || x >= fileWindow.getCols()) {
// System.out.println("COL="+col+", X="+x+", NULL VALUE(1)");
/*
* Depending on the map type we store a different 'NO VALUE' value.
*/
if (rasterMapType > 0) {
/*
* For integers the NaN pattern doesn't seem to so we we use the positive
* infinite value.
*/
rowBuffer.putInt(Integer.MAX_VALUE);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, Integer.MAX_VALUE);
} else if (rasterMapType == -1) {
/* For floats we use the Not A Number (NAN) value. */
rowBuffer.putFloat(Float.NaN);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, Float.NaN);
} else if (rasterMapType == -2) {
/* For double values we use the NAN value. */
rowBuffer.putDouble(Double.NaN);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, Double.NaN);
} else {
/* Don't know what to do. Probably throw some exception? */
}
} else if (readNullValueAtRowCol(currentfilerow, (int) x)) {
// System.out.println("COL="+col+", X="+x+", NULL VALUE(2)");
/*
* Depending on the map type we store a different 'NO VALUE' value.
*/
if (rasterMapType > 0) {
/*
* For integers the NaN pattern doesn't seem to so we we use the positive
* infinite value.
*/
rowBuffer.putInt(Integer.MAX_VALUE);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, Integer.MAX_VALUE);
} else if (rasterMapType == -1) {
/* For floats we use the Not A Number (NAN) value. */
rowBuffer.putFloat(Float.NaN);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, Float.NaN);
} else if (rasterMapType == -2) {
/* For double values we use the NAN value. */
rowBuffer.putDouble(Double.NaN);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, Double.NaN);
} else {
/* Don't know what to do. Probably throw some exception? */
}
} else {
// System.out.println("COL="+col+", X="+x+", DATA VALUE");
rowCache.position((int) x * numberOfBytesPerValue);
if (rasterMapType > 0) {
/* Integers */
int cell = rowCache.getInt();
// System.out.println("x="+easthing+", y="+northing+",
// cell="+cell);
// System.out.print(cell+" ");
/* File is an integer map file with 0 = novalue */
if (cell == 0 && isOldIntegerMap) {
rowBuffer.putInt(Integer.MAX_VALUE);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, Integer.MAX_VALUE);
} else {
/* If map is a reclass then get the reclassed value */
if (reclassTable != null) {
cell = ((Integer) reclassTable.elementAt(cell)).intValue();
}
rowBuffer.putInt(cell);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, cell);
/* Update data range value */
if (cell < dataRange[0])
dataRange[0] = cell;
else if (cell > dataRange[1])
dataRange[1] = cell;
}
} else if (rasterMapType == -1) {
/* Floating point map with float values. */
float cell = rowCache.getFloat();
if (reclassTable != null) {
cell = ((Integer) reclassTable.elementAt((int) cell)).floatValue();
}
rowBuffer.putFloat(cell);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, cell);
/* Update data range value */
if (cell < dataRange[0])
dataRange[0] = cell;
else if (cell > dataRange[1])
dataRange[1] = cell;
} else if (rasterMapType == -2) {
/* Floating point map with double values. */
/*
* if (logger.isDebugEnabled()) logger.debug("RASTERREAD: rowCache size = " +
* rowCache.capacity() / 8 + " position = " + rowCache.position() / 8 + " total
* columns to read = " + dataWindow.getCols() + " actual column = " + col);
*/
double cell = rowCache.getDouble();
/*
* if (logger.isDebugEnabled()) logger.debug("RASTERREAD: cell = " + cell);
*/
if (reclassTable != null) {
cell = ((Integer) reclassTable.elementAt((int) cell)).doubleValue();
}
rowBuffer.putDouble(cell);
if (colorTable != null)
colorTable.interpolateColorValue(rowColorBuffer, cell);
/* Update data range value */
if (cell < dataRange[0])
dataRange[0] = cell;
else if (cell > dataRange[1])
dataRange[1] = cell;
} else {
/* Don't know what to do. Probably throw some exception? */
}
}
}
// System.out.println();
return true;
}
/**
* SwingUtilities
*/
private byte[] initNullRow() {
int len = dataWindow.getCols() * numberOfBytesPerValue;
byte[] nrow = new byte[len];
if (rasterMapType > 0) {
ByteBuffer src = ByteBuffer.allocate(4);
src.putInt(Integer.MAX_VALUE);
byte[] arr = src.array();
for( int i = 0; i < len; i += 4 )
System.arraycopy(arr, 0, nrow, i, 4);
} else if (rasterMapType == -1) {
ByteBuffer src = ByteBuffer.allocate(4);
src.putFloat(Float.NaN);
byte[] arr = src.array();
for( int i = 0; i < len; i += 4 )
System.arraycopy(arr, 0, nrow, i, 4);
} else if (rasterMapType == -2) {
ByteBuffer src = ByteBuffer.allocate(8);
src.putDouble(Double.NaN);
byte[] arr = src.array();
for( int i = 0; i < len; i += 8 )
System.arraycopy(arr, 0, nrow, i, 8);
}
return nrow;
}
/**
* read a row of the map from the active region
*
* @param currentrow
* @param iscompressed
* @return
* @throws IOException
* @throws DataFormatException
*/
private void getMapRow( int currentrow, ByteBuffer rowdata, boolean iscompressed ) throws IOException, DataFormatException {
// if (logger.isDebugEnabled())
// {
// logger.debug("ACCESSING THE FILE at row: " + currentrow +
// ", rasterMapType = " + rasterMapType +
// ", numberOfBytesPerValue = " + numberOfBytesPerValue +
// ", iscompressed = " + iscompressed);
// }
if (iscompressed) {
/* Compressed maps */
if (rasterMapType == -2) {
/* Compressed double map */
readCompressedFPRowByNumber(rowdata, currentrow, addressesofrows, cellFile, numberOfBytesPerValue);
} else if (rasterMapType == -1) {
/* Compressed floating point map */
readCompressedFPRowByNumber(rowdata, currentrow, addressesofrows, cellFile, numberOfBytesPerValue);
} else if (rasterMapType > 0) {
/* Compressed integer map */
readCompressedIntegerRowByNumber(rowdata, currentrow, addressesofrows, cellFile);
} else {
// if (logger.isDebugEnabled())
// logger.error("format not double nor float");
}
} else {
if (rasterMapType < 0) {
/* Uncompressed floating point map */
readUncompressedFPRowByNumber(rowdata, currentrow, cellFile, numberOfBytesPerValue);
} else if (rasterMapType > 0) {
/* Uncompressed integer map */
readUncompressedIntegerRowByNumber(rowdata, currentrow, cellFile);
} else {
// if (logger.isDebugEnabled())
// logger.error("Unknown case, iscompressed=" + iscompressed
// + ", compressed=" + compressed + ", rasterMapType="
// + rasterMapType);
}
}
return;
}
/**
* read a row of data from a compressed floating point map
*
* @param rn
* @param adrows
* @param outFile
* @param typeBytes
* @return the ByteBuffer containing the data
* @throws IOException
* @throws DataFormatException
*/
private void readCompressedFPRowByNumber( ByteBuffer rowdata, int rn, long[] adrows, RandomAccessFile thefile, int typeBytes )
throws DataFormatException, IOException {
int offset = (int) (adrows[rn + 1] - adrows[rn]);
/*
* The fact that the file is compressed does not mean that the row is compressed. If the
* first byte is 0 (49), then the row is compressed, otherwise (first byte = 48) the row has
* to be read in simple XDR uncompressed format.
*/
byte[] tmp = new byte[offset - 1];
thefile.seek(adrows[rn]);
int firstbyte = (thefile.read() & 0xff);
if (firstbyte == 49) {
/* The row is compressed. */
// thefile.seek((long) adrows[rn] + 1);
thefile.read(tmp, 0, offset - 1);
Inflater decompresser = new Inflater();
decompresser.setInput(tmp, 0, tmp.length);
decompresser.inflate(rowdata.array());
decompresser.end();
} else if (firstbyte == 48) {
/* The row is NOT compressed */
// thefile.seek((long) (adrows[rn]));
// if (thefile.read() == 48)
// {
// thefile.seek((long) (adrows[rn] + 1));
thefile.read(rowdata.array(), 0, offset - 1);
// }
}
}
/**
* read a row of data from an uncompressed floating point map
*
* @param rn
* @param outFile
* @param typeBytes
* @return the ByteBuffer containing the data
* @throws IOException
* @throws DataFormatException
*/
private void readUncompressedFPRowByNumber( ByteBuffer rowdata, int rn, RandomAccessFile thefile, int typeBytes )
throws IOException, DataFormatException {
int datanumber = fileWindow.getCols() * typeBytes;
thefile.seek((rn * datanumber));
thefile.read(rowdata.array());
}
/**
* read a row of data from a compressed integer point map
*
* @param rn
* @param adrows
* @param outFile
* @return the ByteBuffer containing the data
* @throws IOException
*/
private void readCompressedIntegerRowByNumber( ByteBuffer rowdata, int rn, long[] adrows, RandomAccessFile thefile )
throws IOException, DataFormatException {
int offset = (int) (adrows[rn + 1] - adrows[rn]);
thefile.seek(adrows[rn]);
/*
* Read how many bytes the values are ex 1 => if encoded: 1 byte for the value and one byte
* for the count = 2 2 => if encoded: 2 bytes for the value and one byte for the count = 3
* etc... etc
*/
int bytespervalue = (thefile.read() & 0xff);
ByteBuffer cell = ByteBuffer.allocate(bytespervalue);
int cellValue = 0;
/* Create the buffer in which read the compressed row */
byte[] tmp = new byte[offset - 1];
thefile.read(tmp);
ByteBuffer tmpBuffer = ByteBuffer.wrap(tmp);
tmpBuffer.order(ByteOrder.nativeOrder());
/*
* Create the buffer in which read the decompressed row. The final decompressed row will
* always contain 4-byte integer values
*/
if ((offset - 1) == (bytespervalue * fileWindow.getCols())) {
/* There is no compression in this row */
for( int i = 0; i < offset - 1; i = i + bytespervalue ) {
/* Read the value */
tmpBuffer.get(cell.array());
/*
* Integers can be of 1, 2, or 4 bytes. As rasterBuffer expects 4 byte integers we
* need to pad them with 0's. The order of the padding is determined by the
* ByteOrder of the buffer.
*/
if (bytespervalue == 1) {
cellValue = (cell.get(0) & 0xff);
} else if (bytespervalue == 2) {
cellValue = cell.getShort(0);
} else if (bytespervalue == 4) {
cellValue = cell.getInt(0);
}
// if (logger.isDebugEnabled()) logger.debug("tmpint=" + tmpint
// );
rowdata.putInt(cellValue);
}
} else {
/*
* If the row is compressed, then the values appear in pairs (like couples a party). The
* couple is composed of the count and the value value (WARNING: this can be more than
* one byte). Therefore, knowing the length of the compressed row we can calculate the
* number of couples.
*/
int couples = (offset - 1) / (1 + bytespervalue);
for( int i = 0; i < couples; i++ ) {
/* Read the count of values */
int count = (tmpBuffer.get() & 0xff);
/* Read the value */
tmpBuffer.get(cell.array());
/*
* Integers can be of 1, 2, or 4 bytes. As rasterBuffer expects 4 byte integers we
* need to pad them with 0's. The order of the padding is determined by the
* ByteOrder of the buffer.
*/
if (bytespervalue == 1) {
cellValue = (cell.get(0) & 0xff);
} else if (bytespervalue == 2) {
cellValue = cell.getShort(0);
} else if (bytespervalue == 4) {
cellValue = cell.getInt(0);
}
/*
* Now write the cell value the required number of times to the raster row data
* buffer.
*/
for( int j = 0; j < count; j++ ) {
// // if (logger.isDebugEnabled()) logger.debug(" " +
// tmpint);
rowdata.putInt(cellValue);
}
}
}
}
/**
* read a row of data from an uncompressed integer map
*
* @param rn
* @param thefile
* @return
* @throws IOException
* @throws DataFormatException
*/
private void readUncompressedIntegerRowByNumber( ByteBuffer rowdata, int rn, RandomAccessFile thefile ) throws IOException,
DataFormatException {
int cellValue = 0;
ByteBuffer cell = ByteBuffer.allocate(rasterMapType);
/* The number of bytes that are inside a row in the file. */
int filerowsize = fileWindow.getCols() * rasterMapType;
/* Position the file pointer to read the row */
thefile.seek((rn * filerowsize));
/* Read the row of data from the file */
ByteBuffer tmpBuffer = ByteBuffer.allocate(filerowsize);
thefile.read(tmpBuffer.array());
/*
* Transform the rasterMapType-size-values to a standard 4 bytes integer value
*/
while( tmpBuffer.hasRemaining() ) {
// read the value
tmpBuffer.get(cell.array());
/*
* Integers can be of 1, 2, or 4 bytes. As rasterBuffer expects 4 byte integers we need
* to pad them with 0's. The order of the padding is determined by the ByteOrder of the
* buffer.
*/
if (rasterMapType == 1) {
cellValue = (cell.get(0) & 0xff);
} else if (rasterMapType == 2) {
cellValue = cell.getShort(0);
} else if (rasterMapType == 4) {
cellValue = cell.getInt(0);
}
// if (logger.isDebugEnabled()) logger.debug("tmpint=" + cellValue
// );
rowdata.putInt(cellValue);
}
}
/**
* read the null value from the null file (if it exists) and returns the information about the
* particular cell (true if it is novalue, false if it is not a novalue
*
* @param currentfilerow
* @param currentfilecol
* @return
*/
private boolean readNullValueAtRowCol( int currentfilerow, int currentfilecol ) throws IOException {
/*
* If the null file doesn't exist and the map is an integer, than it is an old integer-map
* format, where the novalues are the cells that contain the values 0
*/
if (nullFile != null) {
long byteperrow = (long) Math.ceil(fileWindow.getCols() / 8.0); // in the
// null
// map of
// cell_misc
long currentByte = (long) Math.ceil((currentfilecol + 1) / 8.0); // in the
// null
// map
// currentfilerow starts from 0, so it is the row before the one we
// need
long byteToRead = (byteperrow * currentfilerow) + currentByte;
nullFile.seek(byteToRead - 1);
int bitposition = (currentfilecol) % 8;
byte[] thetmp = new byte[1];
thetmp[0] = nullFile.readByte();
BitSet tmp = FileUtilities.fromByteArray(thetmp);
boolean theBit = tmp.get(7 - bitposition);
/*
* if (theBit) { System.out.println("1 at position: " + (7-bitposition) + " due to
* bitposition: " + bitposition); } else { System.out.println("0 at position: " +
* (7-bitposition) + " due to bitposition: " + bitposition); }
*/
return theBit;
}
// else
// {
// /* There is no null file around */
// if (rasterMapType > 0)
// {
// // isOldIntegerMap = true;
// return false;
// }
// else
// {
// //throw some exception
// return false;
// }
//
// }
return false;
}
/**
* @param tmp
*/
private void setRange( double tmp, int i, int k ) {
// set the range
// if (logger.isDebugEnabled()) logger.debug("RANGEVALUE = " + tmp);
if (i == 0 && k == 0 && tmp == tmp) {
range[0] = tmp;
range[1] = tmp;
} else {
if (tmp < range[0] && tmp == tmp)
range[0] = tmp;
if (tmp > range[1] && tmp == tmp)
range[1] = tmp;
}
}
/**
* retrieve the range values of the map
*/
public double[] getRange() {
return range;
}
}