/* * Copyright (C) 2011-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.*; import java.nio.*; import java.nio.channels.FileChannel; import java.util.ArrayList; import whitebox.interfaces.Communicator; /** * The base class serving the WhiteboxRaster and WhiteboxRasterInfo * subclasses. * * @author Dr. John Lindsay <jlindsay@uoguelph.ca> */ public class WhiteboxRasterBase { // *********************************** // Fields // *********************************** protected boolean isDirty = false; /** * Parameterless constructor */ public WhiteboxRasterBase() { } /** * 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. */ public WhiteboxRasterBase(String HeaderFile) { // set the header file and data file. headerFile = HeaderFile; dataFile = headerFile.replace(".dep", ".tas"); statsFile = headerFile.replace(".dep", ".wstat"); setFileAccess("rw"); readHeaderFile(); } // *********************************** // Property getter and setter methods. // *********************************** protected String headerFile; protected String shortHeaderName = null; protected Communicator communicator = null; /** * Gets the header file (*.dep) name for this Whitebox raster grid. Notice * that the header file name is set during object creation. * * @return A string containing the reference to the header file. */ public String getHeaderFile() { return headerFile; } /** * Returns the file name with no directory path for the header file (*.dep). * * @return A string containing the reference to the header file. */ public String getShortHeaderFile() { if (shortHeaderName == null) { File file = new File(headerFile); shortHeaderName = file.getName(); shortHeaderName = shortHeaderName.replace(".dep", ""); } return shortHeaderName; } protected String dataFile; /** * Gets the data file (*.tas) name for this Whitebox raster grid. Notice * that the data file name is set during object creation. * * @return A string containing the reference to the data file. */ public String getDataFile() { return dataFile; } /** * Used to determine the size of the data file (.tas). * * @return long containing the size of the data file in bytes. */ public long getDataFileSize() { File file = new File(dataFile); return file.length(); } protected String statsFile; /** * Gets the statistical distribution file (*.wstat) name for this Whitebox * raster grid. Notice that the stats file name is set during object * creation. * * @return A string containing the reference to the stats file. */ public String getStatsFile() { return statsFile; } protected final double largeValue = Double.POSITIVE_INFINITY; protected final double smallValue = Double.NEGATIVE_INFINITY; protected double minimumValue = largeValue; /** * Retrieves the minimum value in the Whitebox grid. * * @return The minimum value. */ public double getMinimumValue() { return this.minimumValue; } /** * Sets the minimum value in the Whitebox grid. * * @param MinimumValue The minimum value. */ public void setMinimumValue(double MinimumValue) { this.minimumValue = MinimumValue; } protected double maximumValue = smallValue; /** * Retrieves the maximum value in the Whitebox grid. * * @return The maximum value. */ public double getMaximumValue() { return maximumValue; } /** * Sets the maximum value in the Whitebox grid. * * @param MaximumValue The minimum value. */ public void setMaximumValue(double MaximumValue) { maximumValue = MaximumValue; } protected double north; /** * Retrieves the coordinate of the northern edge. * * @return The coordinate of the northern edge. */ public double getNorth() { return north; } /** * Sets the coordinate of the northern edge. * * @param North The coordinate of the northern edge. */ public void setNorth(float North) { north = North; } protected double south; /** * Retrieves the coordinate of the southern edge. * * @return The coordinate of the southern edge. */ public double getSouth() { return south; } /** * Sets the coordinate of the southern edge. * * @param South The coordinate of the southern edge. */ public void setSouth(float South) { south = South; } protected double west; /** * Retrieves the coordinate of the western edge. * * @return The coordinate of the western edge. */ public double getWest() { return west; } /** * Sets the coordinate of the western edge. * * @param West The coordinate of the western edge. */ public void setWest(float West) { west = West; } protected double east; /** * Retrieves the coordinate of the eastern edge. * * @return The coordinate of the eastern edge. */ public double getEast() { return east; } /** * Sets the coordinate of the eastern edge. * * @param East The coordinate of the eastern edge. */ public void setEast(float East) { east = East; } protected int numberColumns; /** * Retrieves the number of columns in the grid. * * @return The number of columns. */ public int getNumberColumns() { return numberColumns; } protected int numberRows; /** * Retrieves the number of rows in the grid. * * @return The number of rows. */ public int getNumberRows() { return numberRows; } protected int numberStacks = 1; /** * Retrieves the number of stacks in the grid. Stacks are used for 3D, voxel * data, * * @return The number of columns. */ public int getNumberStacks() { return numberStacks; } public enum DataScale { CONTINUOUS, CATEGORICAL, BOOLEAN, RGB; } protected DataScale dataScale = DataScale.CONTINUOUS; /** * Retrieves the data scale for this Whitebox grid. Data scale may be * <b><i>CONTINUOUS</i></b>, * <i><b>CATEGORICAL</i></b>, <i><b>BOOLEAN</i></b>, or <i><b>RGB</i></b>. * * @return DataScale Data scale. */ public DataScale getDataScale() { return dataScale; } /** * Sets the data scale for this Whitebox grid. Data scale may be * <b><i>CONTINUOUS</i></b>, * <i><b>CATEGORICAL</i></b>, <i><b>BOOLEAN</i></b>, or <i><b>RGB</i></b>. * * @param dataScale The specified data type. */ public void setDataScale(DataScale dataScale) { this.dataScale = dataScale; } public enum DataType { DOUBLE, FLOAT, INTEGER, BYTE; } protected int cellSizeInBytes = 8; protected DataType dataType = DataType.DOUBLE; /** * Retrieves the data type for this Whitebox grid. Data type may be * <b><i>DataType.DOUBLE</i></b>, <b><i>DataType.FLOAT</i></b> (decimal * numbers), <i><b>DataType.INTEGER</i></b> (whole numbers from -32,768 to * 32,767), or <i><b>DataType.BYTE</i></b> * (whole number from 0 to 255). * * @return Data type. */ public DataType getDataType() { return dataType; } /** * Sets the data type for this Whitebox grid. Data type may be * <b><i>DataType.DOUBLE</i></b>, <b><i>DataType.FLOAT</i></b> (decimal * numbers), <i><b>DataType.INTEGER</i></b> (whole numbers from -32,768 to * 32,767), or <i><b>DataType.BYTE</i></b> (whole number from 0 to 255). * * @param DataType The specified data type. */ public void setDataType(DataType dataType) { switch (dataType) { case DOUBLE: this.dataType = DataType.DOUBLE; cellSizeInBytes = 8; break; case FLOAT: this.dataType = DataType.FLOAT; cellSizeInBytes = 4; break; case INTEGER: this.dataType = DataType.INTEGER; cellSizeInBytes = 2; break; case BYTE: this.dataType = DataType.BYTE; cellSizeInBytes = 1; break; } } protected String zUnits = "not specified"; /** * Retrieves the Z units for this Whitebox grid. * * @return Z Units. */ public String getZUnits() { return zUnits; } /** * Sets units of the attribute data, i.e. the z-values in the raster image. * * @param ZUnits The specified data type. */ public void setZUnits(String ZUnits) { zUnits = ZUnits.toLowerCase(); } protected String xyUnits = "not specified"; /** * Retrieves the XY units for this Whitebox grid. * * @return XY Units. */ public String getXYUnits() { return xyUnits; } /** * Sets units of the attribute data, i.e. the xy-values in the raster image. * * @param XYUnits The specified data type. */ public void setXYUnits(String XYUnits) { xyUnits = XYUnits.toLowerCase(); } protected String projection = "not specified"; /** * Retrieves the projection for this Whitebox grid. * * @return Projection. */ public String getProjection() { return projection; } /** * Sets projection the raster image. * * @param Projection The specified projection. */ public void setProjection(String Projection) { projection = Projection; } protected double displayMinimum = largeValue; /** * Retrieves the display minimum for this Whitebox grid. * * @return Display minimum. */ public double getDisplayMinimum() { return displayMinimum; } /** * Sets display minimum. * * @param DisplayMinimum The specified projection. */ public void setDisplayMinimum(double DisplayMinimum) { displayMinimum = DisplayMinimum; } protected double displayMaximum = smallValue; /** * Retrieves the display maximum for this Whitebox grid. * * @return Display maximum. */ public double getDisplayMaximum() { return displayMaximum; } /** * Sets display maximum. * * @param DisplayMaximum The specified projection. */ public void setDisplayMaximum(double DisplayMaximum) { displayMaximum = DisplayMaximum; } protected double nonlinearity = 1.0; /** * Returns the palette non-linearity value (gamma). * * @return double palette non-linearity */ public double getNonlinearity() { return nonlinearity; } /** * Sets the palette non-linearity value (gamma) * * @param value */ public void setNonlinearity(double value) { this.nonlinearity = value; } protected String preferredPalette = "grey.pal"; /** * Retrieves the preferred palette for this Whitebox grid. * * @return Preferred palette. */ public String getPreferredPalette() { return preferredPalette; } /** * Sets the preferred palette used to display the raster image. * * @param PreferredPalette The default palette used to display the image, * e.g. <b><i>earthtones.pal</b></i> or <b><i>spectrum.pal</b></i>. */ public void setPreferredPalette(String PreferredPalette) { PreferredPalette = PreferredPalette.replace(".plt", ".pal"); if (PreferredPalette.lastIndexOf(File.separator) > -1) { String[] str = PreferredPalette.split(File.separator); PreferredPalette = str[str.length - 1]; } preferredPalette = PreferredPalette; } protected double noDataValue = -32768d; /** * Retrieves the numeric value used to specify a grid cell containing no * data or void. * * @return float NoData value. */ public double getNoDataValue() { return noDataValue; } /** * Sets the NoData value used in this raster image. * * @param value A float specifying the value used. Default value is -32768. */ public void setNoDataValue(double value) { if (Double.isNaN(value)) { noDataValue = -32768; } noDataValue = value; } protected double cellSizeX = 0; /** * The grid resolution in the X direction. * * @return float containing the x-direction grid resolution */ public double getCellSizeX() { if (cellSizeX == 0) { cellSizeX = (this.east - this.west) / this.numberColumns; } return cellSizeX; } protected double cellSizeY = 0; /** * The grid resolution in the Y direction. * * @return float containing the y-direction grid resolution */ public double getCellSizeY() { if (cellSizeY == 0) { cellSizeY = (this.north - this.south) / this.numberRows; } return cellSizeY; } protected ByteOrder byteOrder = java.nio.ByteOrder.nativeOrder(); // "LITTLE_ENDIAN"; /** * Gets the file byte order (either LITTLE_ENDIAN or BIG_ENDIAN). * * @return */ public String getByteOrder() { return byteOrder.toString(); } /** * Sets the file byte order (either LITTLE_ENDIAN or BIG_ENDIAN). * * @param value */ public void setByteOrder(String value) { if ((value.toLowerCase().contains("little")) || (value.toLowerCase().contains("lsb")) || (value.toLowerCase().contains("least")) || (value.toLowerCase().contains("intel"))) { byteOrder = ByteOrder.LITTLE_ENDIAN; } else { byteOrder = ByteOrder.BIG_ENDIAN; } } protected boolean saveChanges = true; /** * Used to determine the file access mode set during object construction. * * @return "rw" (read/write) or "r" (read-only). */ public String getFileAccess() { if (saveChanges) { return "rw"; } else { return "r"; } } protected final void setFileAccess(String value) { if (value.toLowerCase().contains("w")) { saveChanges = true; } else { saveChanges = false; } } public void setCommunicator(Communicator communicator) { this.communicator = communicator; } protected ArrayList<String> metadata = new ArrayList<>(); /** * Adds a metadata entry to the header file. * * @param value String containing the metadata entry. */ public void addMetadataEntry(String value) { metadata.add(value.replaceAll(";", ":")); } /** * Retrieves an ArrayList containing all of the metadata entries for this * raster. * * @return ArrayList of metadata strings. */ public ArrayList<String> getMetadata() { return metadata; } /** * Used to delete a metadata entry * * @param i The entry number in the metadata arraylist to delete. */ public void deleteMetadataEntry(int i) { if (i < metadata.size()) { metadata.remove(i); } } protected double stdDeviation = noDataValue; public double getStandardDeviation() { if (stdDeviation == noDataValue) { readStatsFile(); } return stdDeviation; } protected double mode = noDataValue; public double getMode() { if (mode == noDataValue) { readStatsFile(); } return mode; } protected double mean = noDataValue; public double getMean() { if (mean == noDataValue) { readStatsFile(); } return mean; } protected double median = noDataValue; public double getMedian() { if (median == noDataValue) { readStatsFile(); } return median; } protected long[] histo = null; public long[] getHisto() { if (mean == noDataValue) { readStatsFile(); } return histo; } protected double binWidth = noDataValue; public double getHistoBinWidth() { if (binWidth == noDataValue) { readStatsFile(); } return binWidth; } protected long numValidCells = (long) noDataValue; public long getNumValidCells() { if (numValidCells == (long) noDataValue) { readStatsFile(); } return numValidCells; } protected boolean containsFractionalData = false; protected boolean containsFractionalDataChecked = false; /* * This property can be used to assess whether the data contained in this * Whitebox raster possesses fractional parts, i.e. if there are any * pixels with values that are decimals. If the data type is integer or byte, * this method will always return false. If the data type is either float or * decimal, then each pixel value will be assessed to identify the existence of * a fractional part. If a cell is found with a fractional part, the method will * return a value of true. This function is necessary because it is common * for integer type data to be stored as decimal type data. */ public boolean doesDataContainFractionalParts() { if (!containsFractionalDataChecked) { checkContainsFractionalData(); } return containsFractionalData; } //******************************************** // Available methods. // ******************************************* // checks to see if the data contains decimal values. protected void checkContainsFractionalData() { if (dataType == DataType.INTEGER || dataType == DataType.BYTE) { containsFractionalDataChecked = true; containsFractionalData = false; return; } double[] data = null; double z; containsFractionalDataChecked = true; containsFractionalData = false; for (int stack = 0; stack < numberStacks; stack++) { for (int row = 0; row < numberRows; row++) { data = getRowValues(row, stack); for (int col = 0; col < numberColumns; col++) { if (data[col] != noDataValue) { z = Math.floor(data[col]); if ((data[col] - z) > 0.001) { // you have to deal with rounding issues containsFractionalData = true; return; } } } } } } /** * Reads the contents of the header file and fills the properties of the * Whitebox grid. */ protected final void readHeaderFile() { DataInputStream in = null; BufferedReader br = null; boolean byteOrderRead = false; String delimiter = "\t"; try { // Open the file that is the first command line parameter FileInputStream fstream = new FileInputStream(this.headerFile); // Get the object of DataInputStream in = new DataInputStream(fstream); br = new BufferedReader(new InputStreamReader(in)); if (this.headerFile != null) { String line; String[] str; //Read File Line By Line while ((line = br.readLine()) != null) { str = line.split(delimiter); if (str.length == 1) { delimiter = " "; str = line.split(delimiter); if (str.length == 1) { delimiter = ";"; str = line.split(delimiter); } } int dataCol = str.length - 1; if (str[0].toLowerCase().contains("min:") && (!str[0].toLowerCase().contains("display"))) { this.minimumValue = Float.parseFloat(str[dataCol]); } else if (str[0].toLowerCase().contains("max:") && (!str[0].toLowerCase().contains("display"))) { this.maximumValue = Float.parseFloat(str[dataCol]); } else if (str[0].toLowerCase().contains("north")) { this.north = Double.parseDouble(str[dataCol]); } else if (str[0].toLowerCase().contains("south")) { this.south = Double.parseDouble(str[dataCol]); } else if (str[0].toLowerCase().contains("west")) { this.west = Double.parseDouble(str[dataCol]); } else if (str[0].toLowerCase().contains("east")) { this.east = Double.parseDouble(str[dataCol]); } else if (str[0].toLowerCase().contains("cols")) { this.numberColumns = Integer.parseInt(str[dataCol]); } else if (str[0].toLowerCase().contains("rows")) { this.numberRows = Integer.parseInt(str[dataCol]); } else if (str[0].toLowerCase().contains("stacks")) { this.numberStacks = Integer.parseInt(str[dataCol]); } else if (str[0].toLowerCase().contains("data type") || (str[0].toLowerCase().contains("data") && str[1].toLowerCase().contains("type") && str.length > 2)) { //|| //str[0].toLowerCase().contains("data:")) { //this.setDataType(str[1]); if (str[dataCol].toLowerCase().contains("double")) { this.setDataType(DataType.DOUBLE); } else if (str[dataCol].toLowerCase().contains("float")) { this.setDataType(DataType.FLOAT); } else if (str[dataCol].toLowerCase().contains("integer")) { this.setDataType(DataType.INTEGER); } else if (str[dataCol].toLowerCase().contains("byte")) { this.setDataType(DataType.BYTE); } } else if (str[0].toLowerCase().contains("data scale")) { if (str[dataCol].toLowerCase().contains("continuous")) { this.setDataScale(DataScale.CONTINUOUS); //DATA_SCALE_CONTINUOUS); } else if (str[dataCol].toLowerCase().contains("categorical")) { this.setDataScale(DataScale.CATEGORICAL); //DATA_SCALE_CATEGORICAL); } else if (str[dataCol].toLowerCase().contains("bool")) { this.setDataScale(DataScale.BOOLEAN); //DATA_SCALE_BOOLEAN); } else if (str[dataCol].toLowerCase().contains("rgb")) { this.setDataScale(DataScale.RGB); //DATA_SCALE_RGB); } } else if (str[0].toLowerCase().contains("z units")) { this.setZUnits(str[dataCol]); } else if (str[0].toLowerCase().contains("xy units")) { this.setXYUnits(str[dataCol]); } else if (str[0].toLowerCase().contains("projection")) { this.projection = str[dataCol]; } else if (str[0].toLowerCase().contains("display min")) { this.displayMinimum = Float.parseFloat(str[dataCol]); } else if (str[0].toLowerCase().contains("display max")) { this.displayMaximum = Float.parseFloat(str[dataCol]); } else if (str[0].toLowerCase().contains("preferred palette")) { this.preferredPalette = str[dataCol].replace(".plt", ".pal"); } else if (str[0].toLowerCase().contains("byte order")) { this.setByteOrder(str[dataCol]); byteOrderRead = true; } else if (str[0].toLowerCase().contains("nodata")) { this.noDataValue = Float.parseFloat(str[dataCol]); if (Double.isNaN(this.noDataValue)) { this.noDataValue = -32768.0; } } else if (str[0].toLowerCase().contains("metadata entry")) { if (str.length > 1) { this.addMetadataEntry(str[dataCol]); } } else if (str[0].toLowerCase().contains("palette nonlinearity")) { this.nonlinearity = Double.parseDouble(str[dataCol]); } } if (this.displayMinimum == Float.POSITIVE_INFINITY) { this.displayMinimum = this.minimumValue; } if (this.displayMaximum == Float.NEGATIVE_INFINITY) { this.displayMaximum = this.maximumValue; } //Close the input stream in.close(); br.close(); if (!byteOrderRead) { this.byteOrder = ByteOrder.LITTLE_ENDIAN; } } } catch (java.io.IOException e) { System.err.println("Error: " + e.getMessage()); } catch (Exception e) { //Catch exception if any System.err.println("Error: " + e.getMessage()); } finally { try { if (in != null || br != null) { in.close(); br.close(); } } catch (java.io.IOException ex) { } } } public void resetDisplayMinMaxValues() { this.displayMaximum = this.maximumValue; this.displayMinimum = this.minimumValue; } /** * Writes the whiteboxRaster header file (.dep) to disc. */ public void writeHeaderFile() { String str1 = null; File file = new File(this.headerFile); FileWriter fw = null; BufferedWriter bw = null; PrintWriter out = null; try { if (this.displayMaximum == smallValue) { this.displayMaximum = this.maximumValue; } if (this.displayMinimum == largeValue) { this.displayMinimum = this.minimumValue; } if (this.displayMaximum < this.displayMinimum || this.displayMaximum == this.displayMinimum) { if (this.maximumValue < this.minimumValue) { findMinAndMaxVals(); } this.displayMinimum = this.minimumValue; this.displayMaximum = this.maximumValue; } fw = new FileWriter(file, false); bw = new BufferedWriter(fw); out = new PrintWriter(bw, true); str1 = "Min:\t" + Double.toString(this.minimumValue); out.println(str1); str1 = "Max:\t" + Double.toString(this.maximumValue); out.println(str1); str1 = "North:\t" + Double.toString(this.north); out.println(str1); str1 = "South:\t" + Double.toString(this.south); out.println(str1); str1 = "East:\t" + Double.toString(this.east); out.println(str1); str1 = "West:\t" + Double.toString(this.west); out.println(str1); str1 = "Cols:\t" + Integer.toString(this.numberColumns); out.println(str1); str1 = "Rows:\t" + Integer.toString(this.numberRows); out.println(str1); str1 = "Stacks:\t" + Integer.toString(this.numberStacks); out.println(str1); str1 = "Data Type:\t" + this.dataType; out.println(str1); str1 = "Z Units:\t" + this.zUnits; out.println(str1); str1 = "XY Units:\t" + this.xyUnits; out.println(str1); str1 = "Projection:\t" + this.projection; out.println(str1); switch (this.dataScale) { case CONTINUOUS: str1 = "Data Scale:\tcontinuous"; break; case CATEGORICAL: str1 = "Data Scale:\tcategorical"; break; case BOOLEAN: str1 = "Data Scale:\tboolean"; break; case RGB: str1 = "Data Scale:\trgb"; break; } out.println(str1); str1 = "Display Min:\t" + Double.toString(this.displayMinimum); out.println(str1); str1 = "Display Max:\t" + Double.toString(this.displayMaximum); out.println(str1); str1 = "Preferred Palette:\t" + this.preferredPalette.replace(".plt", ".pal"); out.println(str1); str1 = "NoData:\t" + Double.toString(this.noDataValue); out.println(str1); str1 = "Byte Order:\t" + this.byteOrder; out.println(str1); str1 = "Palette Nonlinearity:\t" + this.nonlinearity; out.println(str1); // Write the metadata entries to the file if (metadata.size() > 0) { for (int i = 0; i < metadata.size(); i++) { str1 = "Metadata Entry:\t" + metadata.get(i).replaceAll(":", ";"); out.println(str1); } } } catch (java.io.IOException e) { System.err.println("Error: " + e.getMessage()); } catch (Exception e) { //Catch exception if any System.err.println("Error: " + e.getMessage()); } finally { if (out != null || bw != null) { out.flush(); out.close(); } } } protected void setPropertiesUsingAnotherRaster(String BaseRasterHeader, DataType dataType) { setDataType(dataType); String delimiter = "\t"; // Set the properties of this WhiteboxRaster to those of the base raster. DataInputStream in = null; BufferedReader br = null; try { // Open the file that is the first command line parameter FileInputStream fstream = new FileInputStream(BaseRasterHeader); // Get the object of DataInputStream in = new DataInputStream(fstream); br = new BufferedReader(new InputStreamReader(in)); if (BaseRasterHeader != null) { String line; String[] str; //Read File Line By Line while ((line = br.readLine()) != null) { str = line.split(delimiter); if (str.length == 1) { delimiter = " "; str = line.split(delimiter); if (str.length == 1) { delimiter = ";"; str = line.split(delimiter); } } int dataCol = str.length - 1; if (str[0].toLowerCase().contains("north")) { this.north = Double.parseDouble(str[dataCol]); } else if (str[0].toLowerCase().contains("south")) { this.south = Double.parseDouble(str[dataCol]); } else if (str[0].toLowerCase().contains("west")) { this.west = Double.parseDouble(str[dataCol]); } else if (str[0].toLowerCase().contains("east")) { this.east = Double.parseDouble(str[dataCol]); } else if (str[0].toLowerCase().contains("cols")) { this.numberColumns = Integer.parseInt(str[dataCol]); } else if (str[0].toLowerCase().contains("rows")) { this.numberRows = Integer.parseInt(str[dataCol]); } else if (str[0].toLowerCase().contains("data scale")) { if (str[dataCol].toLowerCase().contains("continuous")) { this.setDataScale(DataScale.CONTINUOUS); //this.setDataScale(DATA_SCALE_CONTINUOUS); } else if (str[dataCol].toLowerCase().contains("categorical")) { this.setDataScale(DataScale.CATEGORICAL); //this.setDataScale(DATA_SCALE_CATEGORICAL); } else if (str[dataCol].toLowerCase().contains("bool")) { this.setDataScale(DataScale.BOOLEAN); //this.setDataScale(DATA_SCALE_BOOLEAN); } else if (str[dataCol].toLowerCase().contains("rgb")) { this.setDataScale(DataScale.RGB); //this.setDataScale(DATA_SCALE_RGB); } } else if (str[0].toLowerCase().contains("xy units")) { this.xyUnits = str[dataCol]; } else if (str[0].toLowerCase().contains("projection")) { this.projection = str[dataCol]; } else if (str[0].toLowerCase().contains("nodata")) { this.noDataValue = Double.parseDouble(str[dataCol]); } else if (str[0].toLowerCase().contains("palette")) { this.preferredPalette = str[dataCol]; } } } } catch (java.io.IOException e) { System.out.println("Error: " + e.getMessage()); } catch (Exception e) { //Catch exception if any System.out.println("Error: " + e.getMessage()); } finally { try { if (in != null || br != null) { in.close(); br.close(); } } catch (java.io.IOException ex) { } } // Save the header file. this.writeHeaderFile(); } /** * 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. * * @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) { return getRowValues(row, 0); } /** * 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. * * @param row An int stating the zero-based row to be returned. * @param stack An int stating the zero-based stack in which the row is located. * @return An array of doubles containing the values store in the specified * row. */ public double[] getRowValues(int row, int stack) { double[] retVals = new double[numberColumns]; if (row < 0 || row >= numberRows) { for (int i = 0; i < numberColumns; i++) { retVals[i] = noDataValue; } return retVals; } RandomAccessFile rIn = null; ByteBuffer buf = null; try { // See if the data file exists. File file = new File(dataFile); if (!file.exists()) { return null; } // what is the starting and ending cell? long startingCell = (long) (row) * numberColumns; long endingCell = (long) (startingCell) + numberColumns - 1; int readLengthInCells = (int) (endingCell - startingCell + 1); buf = ByteBuffer.allocate((int) (readLengthInCells * cellSizeInBytes)); rIn = new RandomAccessFile(dataFile, "r"); FileChannel inChannel = rIn.getChannel(); long numCellsPerStack = numberColumns * numberRows; inChannel.position(startingCell * cellSizeInBytes + (numCellsPerStack * stack)); 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(); retVals = new double[readLengthInCells]; for (int j = 0; j < readLengthInCells; j++) { retVals[j] = whitebox.utilities.Unsigned.getUnsignedByte(buf, j); //ba[j]); } } } catch (IOException e) { System.err.println("Caught exception: " + e.toString()); System.err.println(e.getStackTrace()); } finally { if (rIn != null) { try { rIn.close(); } catch (IOException e) { } } return retVals.clone(); } } /** * This method should be used when you need to access an entire column of * data at a time. It has less overhead that the getValue method and can be * used to efficiently scan through a raster image column by column. * * @param col An int stating the zero-based column to be returned. * @return An array of doubles containing the values store in the specified * row. */ public double[] getColumnValues(int col) { double[] retVals = new double[numberRows]; // fill the return with nodata values if (col < 0 || col >= numberColumns) { for (int i = 0; i < numberRows; i++) { retVals[i] = noDataValue; } return retVals; } RandomAccessFile rIn = null; ByteBuffer buf = null; try { // See if the data file exists. File file = new File(dataFile); if (!file.exists()) { return null; } rIn = new RandomAccessFile(dataFile, "r"); buf = ByteBuffer.allocate((int) (cellSizeInBytes)); FileChannel inChannel = rIn.getChannel(); long pos; // Check the byte order. buf.order(byteOrder); switch (dataType) { case DOUBLE: for (int i = 0; i < numberRows; i++) { pos = i * cellSizeInBytes * numberColumns + cellSizeInBytes * col; inChannel.read(buf, pos); buf.rewind(); DoubleBuffer db = buf.asDoubleBuffer(); retVals[i] = db.get(0); } break; case FLOAT: for (int i = 0; i < numberRows; i++) { pos = i * cellSizeInBytes * numberColumns + cellSizeInBytes * col; inChannel.read(buf, pos); buf.rewind(); FloatBuffer fb = buf.asFloatBuffer(); retVals[i] = (double) fb.get(0); } break; case INTEGER: for (int i = 0; i < numberRows; i++) { pos = i * cellSizeInBytes * numberColumns + cellSizeInBytes * col; inChannel.read(buf, pos); buf.rewind(); ShortBuffer sb = buf.asShortBuffer(); retVals[i] = (double) sb.get(0); } break; case BYTE: for (int i = 0; i < numberRows; i++) { pos = i * cellSizeInBytes * numberColumns + cellSizeInBytes * col; inChannel.position(pos); inChannel.read(buf); buf.rewind(); retVals[i] = whitebox.utilities.Unsigned.getUnsignedByte(buf, 0); } break; } } 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) { } } return retVals.clone(); } } /** * This method returns all of the pixel data contained in a raster as a double array. * @return An array of doubles containing the values store in the specified. */ public double[] getPixelValues() { return getPixelValues(0); } /** * This method returns all of the pixel data contained in a raster for a * specified stack as a double array. * @param stack An int stating the zero-based stack in which the row is located. * @return An array of doubles containing the values store in the specified. */ public double[] getPixelValues(int stack) { double[] retVals = new double[numberRows * numberColumns]; RandomAccessFile rIn = null; ByteBuffer buf = null; try { // See if the data file exists. File file = new File(dataFile); if (!file.exists()) { return null; } // what is the starting and ending cell? long startingCell = 0; long endingCell = (long) numberRows * numberColumns - 1; int readLengthInCells = (int) (endingCell - startingCell + 1); buf = ByteBuffer.allocate((int) (readLengthInCells * cellSizeInBytes)); rIn = new RandomAccessFile(dataFile, "r"); FileChannel inChannel = rIn.getChannel(); long numCellsPerStack = numberColumns * numberRows; inChannel.position(startingCell * cellSizeInBytes + (numCellsPerStack * stack)); 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(); retVals = new double[readLengthInCells]; for (int j = 0; j < readLengthInCells; j++) { retVals[j] = whitebox.utilities.Unsigned.getUnsignedByte(buf, j); //ba[j]); } } } catch (IOException e) { System.err.println("Caught exception: " + e.toString()); System.err.println(e.getStackTrace()); } finally { if (rIn != null) { try { rIn.close(); } catch (IOException e) { } } return retVals.clone(); } } /** * This is a lightweight method of setting individual pixel values. It * writes values directly to the file without the use of a buffer. As such * it is only useful for setting small numbers of pixels. The setValue * method of the WhiteboxRaster class offers a buffered means of setting * individual pixel values and is far better suited to setting larger * numbers of pixels. This method should only be used for existing files. * * @param row Pixel zero-based row number. * @param column Pixel zero-based column number. * @param value Pixel value to set. */ public void setPixelValue(int row, int column, double value) { // update the minimum and maximum values if (value < minimumValue && value != noDataValue) { minimumValue = value; displayMinimum = value; writeHeaderFile(); } if (value > maximumValue && value != noDataValue) { maximumValue = value; displayMaximum = value; writeHeaderFile(); } RandomAccessFile rOut = null; ByteBuffer buf = null; FileChannel outChannel = null; try { rOut = new RandomAccessFile(dataFile, "rw"); outChannel = rOut.getChannel(); long cellNum = (long) (row) * numberColumns + column; outChannel.position(cellNum * cellSizeInBytes); int writeLengthInCells = 1; if (dataType == DataType.DOUBLE) { buf = ByteBuffer.allocate(cellSizeInBytes); buf.order(byteOrder); DoubleBuffer db = buf.asDoubleBuffer(); db.put(value); db = null; outChannel.write(buf); } else if (dataType == DataType.FLOAT) { float fa = (float) value; buf = ByteBuffer.allocateDirect(cellSizeInBytes); buf.order(byteOrder); FloatBuffer fb = buf.asFloatBuffer(); fb.put(fa); fb = null; outChannel.write(buf); } else if (dataType == DataType.INTEGER) { short ia = (short) value; buf = ByteBuffer.allocate(cellSizeInBytes); buf.order(byteOrder); ShortBuffer ib = buf.asShortBuffer(); ib.put(ia); ib = null; outChannel.write(buf); } else if (dataType == DataType.BYTE) { byte[] ba = {(byte) value}; 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) { } } } } /** * Used to find the minimum and maximum values in the raster. NoDataValues * are ignored. Minimum and maximum values are stored in the minimumValue * and maximumValue fields. */ public void findMinAndMaxVals() { double[] data; double min = Double.MAX_VALUE; double max = -Double.MAX_VALUE; double z; for (int stack = 0; stack < numberStacks; stack++) { for (int row = 0; row < numberRows; row++) { data = getRowValues(row, stack); for (int col = 0; col < numberColumns; col++) { z = data[col]; if (z != noDataValue) { if (z < min) { min = z; } if (z > max) { max = z; } } } } } maximumValue = max; minimumValue = min; } protected double[] cumulativeHisto = null; public double getPercentileValue(double percentile) { if (mean == noDataValue || mean == -32768d) { readStatsFile(); } percentile = percentile / 100; double retVal = 0; double x1, x2; double y1, y2; if (cumulativeHisto == null) { cumulativeHisto = new double[histo.length]; cumulativeHisto[0] = histo[0]; for (int i = 1; i < histo.length; i++) { cumulativeHisto[i] = histo[i] + cumulativeHisto[i - 1]; } for (int i = 0; i < histo.length; i++) { cumulativeHisto[i] = cumulativeHisto[i] / numValidCells; } } for (int i = 0; i < histo.length; i++) { if (cumulativeHisto[i] >= percentile) { // find the first bin with a value greater than percentile. if (i > 0) { x1 = minimumValue + (i - 1) * binWidth; x2 = minimumValue + i * binWidth; y1 = cumulativeHisto[i - 1]; y2 = cumulativeHisto[i]; } else { x1 = minimumValue + (i - 1) * binWidth; x2 = minimumValue + i * binWidth; y1 = 0; y2 = cumulativeHisto[i]; } retVal = x1 + (percentile - y1) / (y2 - y1) * binWidth; break; } } if (retVal < minimumValue) { retVal = minimumValue; } if (retVal > maximumValue) { retVal = maximumValue; } return retVal; } public void deleteStatsFile() { File file = new File(statsFile); if (file.exists()) { file.delete(); } } public void readStatsFile() { File file = new File(statsFile); if (!file.exists()) { createStatsFile(); return; } DataInputStream in = null; BufferedReader br = null; boolean statsFlag = false; boolean histoFlag = false; int i = 0; long histoVal = 0; try { // Open the file that is the first command line parameter FileInputStream fstream = new FileInputStream(statsFile); // Get the object of DataInputStream in = new DataInputStream(fstream); br = new BufferedReader(new InputStreamReader(in)); if (statsFile != null) { String line; String[] str; //Read File Line By Line while ((line = br.readLine()) != null) { str = line.split("\t"); if (str[0].toLowerCase().contains("start_stats")) { statsFlag = true; } if (str[0].toLowerCase().contains("end_stats")) { statsFlag = false; } if (str[0].toLowerCase().contains("start_histo")) { histoFlag = true; } if (str[0].toLowerCase().contains("end_histo")) { histoFlag = false; } if (statsFlag) { if (str[0].toLowerCase().contains("mean")) { this.mean = Double.parseDouble(str[1]); } else if (str[0].toLowerCase().contains("median")) { this.median = Double.parseDouble(str[1]); } else if (str[0].toLowerCase().contains("mode")) { this.mode = Double.parseDouble(str[1]); } else if (str[0].toLowerCase().contains("std_dev")) { this.stdDeviation = Double.parseDouble(str[1]); } else if (str[0].toLowerCase().contains("num_valid_cells")) { this.numValidCells = Long.parseLong(str[1]); } } else if (histoFlag) { if (str[0].toLowerCase().contains("bin_width")) { this.binWidth = Double.parseDouble(str[1]); } else if (str[0].toLowerCase().contains("num_bins")) { histo = new long[Integer.parseInt(str[1])]; i = 0; } else if (str[0].toLowerCase().contains("start_histo") == false) { histo[i] = Long.parseLong(str[0]); i++; } } } //Close the input stream in.close(); br.close(); } } catch (java.io.IOException e) { System.err.println("Error: " + e.getMessage()); } catch (Exception e) { //Catch exception if any System.err.println("Error: " + e.getMessage()); } finally { try { if (in != null || br != null) { in.close(); br.close(); } } catch (java.io.IOException ex) { } } } /** * Creates a .wst file to store information about the statistical * distribution of the raster, including the min, max, mean, mode, stdev, * and the histogram. These data are used for clipping the tails of the * distribution for enhanced visualization. */ public void createStatsFile() { File file = new File(statsFile); if (file.exists()) { file.delete(); } mean = 0; mode = 0; long n = 0; double[] data; double imageTotalDeviation = 0; double min = Double.MAX_VALUE; double max = -Double.MAX_VALUE; double z; double[] rowMedians = new double[numberRows]; binWidth = 0; int binNum = 0; int numberOfBins = 0; if (dataScale != DataScale.RGB) { //DATA_SCALE_RGB) { // calculate the mean, min and max. for (int stack = 0; stack < numberStacks; stack++) { for (int row = 0; row < numberRows; row++) { data = getRowValues(row, stack); for (int col = 0; col < numberColumns; col++) { z = data[col]; if (z != noDataValue) { mean += z; n++; if (z < min) { min = z; } if (z > max) { max = z; } } } } } maximumValue = max; minimumValue = min; mean = mean / n; numValidCells = n; if (dataType == DataType.INTEGER) { //.equals("integer")) { numberOfBins = (int) (max - min + 1); binWidth = 1; } else if (dataType == DataType.FLOAT || dataType == DataType.DOUBLE) { //.equals("float") || dataType.equals("double")) { if ((max - min) < 512) { numberOfBins = 512; } else if ((max - min) < 1024) { numberOfBins = 1024; } else if ((max - min) < 2048) { numberOfBins = 2048; } else if ((max - min) < 4096) { numberOfBins = 4096; } else { numberOfBins = 8196; } binWidth = (max - min) / (numberOfBins - 1); } histo = new long[numberOfBins]; // figure out how many bins should be in the histogram for (int stack = 0; stack < numberStacks; stack++) { for (int row = 0; row < numberRows; row++) { data = getRowValues(row, stack); for (int col = 0; col < numberColumns; col++) { z = data[col]; if (z != noDataValue) { imageTotalDeviation += (z - mean) * (z - mean); binNum = (int) (Math.floor((z - min) / binWidth)); histo[binNum]++; } } } } stdDeviation = Math.sqrt(imageTotalDeviation / (n - 1)); long highestVal = 0; int highestBin = 0; for (int i = 0; i < histo.length; i++) { if (histo[i] > highestVal) { highestVal = histo[i]; highestBin = i; } } mode = highestBin * binWidth; median = getPercentileValue(50.0d); String str = null; FileWriter fw = null; BufferedWriter bw = null; PrintWriter out = null; try { fw = new FileWriter(file, false); bw = new BufferedWriter(fw); out = new PrintWriter(bw, true); str = "START_STATS:"; out.println(str); str = "MIN: \t" + Double.toString(this.minimumValue); out.println(str); str = "MAX: \t" + Double.toString(this.maximumValue); out.println(str); str = "MEAN: \t" + Double.toString(mean); out.println(str); str = "MEDIAN: \t" + Double.toString(median); out.println(str); str = "MODE: \t" + Double.toString(mode); out.println(str); str = "STD_DEV: \t" + Double.toString(stdDeviation); out.println(str); str = "NUM_VALID_CELLS: \t" + Long.toString(n); out.println(str); str = "END_STATS"; out.println(str); str = "START_HISTO"; out.println(str); str = "BIN_WIDTH: \t" + binWidth; out.println(str); str = "NUM_BINS: \t" + numberOfBins; out.println(str); for (int i = 0; i < histo.length; i++) { str = String.valueOf(histo[i]); out.println(str); } str = "END_HISTO"; out.println(str); } catch (java.io.IOException e) { System.err.println("Error: " + e.getMessage()); } catch (Exception e) { //Catch exception if any System.err.println("Error: " + e.getMessage()); } finally { if (out != null || bw != null) { out.flush(); out.close(); } } } else { numberOfBins = 256; } } private double halfCellSizeX = -1; private double EWRange = -1; public int getColumnFromXCoordinate(double x) { if (halfCellSizeX < 0 || EWRange < 0) { getCellSizeX(); halfCellSizeX = cellSizeX / 2.0; EWRange = east - west - cellSizeX; } return (int) (Math.round((numberColumns - 1) * (x - west - halfCellSizeX) / EWRange)); } private double halfCellSizeY = -1; private double NSRange = -1; public int getRowFromYCoordinate(double y) { if (halfCellSizeY < 0 || NSRange < 0) { getCellSizeY(); halfCellSizeY = cellSizeY / 2.0; NSRange = north - south - cellSizeY; } return (int) (Math.round((numberRows - 1) * (north - halfCellSizeY - y) / NSRange)); } private double[] xCoordsByColumn; public double getXCoordinateFromColumn(int column) { if (halfCellSizeX < 0 || EWRange < 0) { getCellSizeX(); halfCellSizeX = cellSizeX / 2.0; EWRange = east - west - cellSizeX; } if (xCoordsByColumn == null) { xCoordsByColumn = new double[numberColumns]; for (int i = 0; i < numberColumns; i++) { xCoordsByColumn[i] = west + halfCellSizeX + i * cellSizeX; } } if (column >= 0 && column < numberColumns) { return xCoordsByColumn[column]; } else { return west + halfCellSizeX + column * cellSizeX; } } private double[] yCoordsByRow; public double getYCoordinateFromRow(int row) { if (halfCellSizeY < 0 || NSRange < 0) { getCellSizeY(); halfCellSizeY = cellSizeY / 2.0; NSRange = north - south - cellSizeY; } if (yCoordsByRow == null) { yCoordsByRow = new double[numberRows]; for (int i = 0; i < numberRows; i++) { yCoordsByRow[i] = north - halfCellSizeY - i * cellSizeY; } } if (row >= 0 && row < numberRows) { return yCoordsByRow[row]; } else { return north - halfCellSizeY - row * cellSizeY; } } public void close() { if (saveChanges) { if (isDirty) { writeHeaderFile(); } } } }