/** * Version 1.0 is to handle single variable 2DSquareCelled raster data. * Copyright (C) 2005 Andy Turner, CCG, University of Leeds, UK. * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ package uk.ac.leeds.ccg.andyt.grids.exchange; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.awt.image.MemoryImageSource; import java.io.File; import java.io.Serializable; import java.math.BigDecimal; import java.util.Arrays; import javax.imageio.ImageIO; import uk.ac.leeds.ccg.andyt.grids.core.AbstractGrid2DSquareCell; import uk.ac.leeds.ccg.andyt.grids.core.Grid2DSquareCellDouble; import uk.ac.leeds.ccg.andyt.grids.core.Grid2DSquareCellInt; import uk.ac.leeds.ccg.andyt.grids.core.Grids_Environment; import uk.ac.leeds.ccg.andyt.grids.core.Grids_OutOfMemoryErrorHandler; /** * Class for exporting to images. */ //public class ImageExporter extends ErrorHandler { public class ImageExporter implements Serializable { protected Grids_Environment _Grids_Environment; //private static final long serialVersionUID = 1L; /** Creates a new instance of ImageExporter */ public ImageExporter() { this._Grids_Environment = new Grids_Environment(); } /** Creates a new instance of ImageExporter */ public ImageExporter( Grids_Environment a_Grids_Environment) { this._Grids_Environment = a_Grids_Environment; } /** * Writes this grid as a Grey scale image * @param file The File exported to. * @param type The name of the type of image to be written e.g. "png", "jpeg" */ public void toGreyScaleImage( AbstractGrid2DSquareCell a_Grid2DSquareCell, File file, String type, boolean handleOutOfMemoryError) { try { toGreyScaleImage( a_Grid2DSquareCell, file, type); Grids_OutOfMemoryErrorHandler.ensureThereIsEnoughMemoryToContinue(); } catch (java.lang.OutOfMemoryError a_OutOfMemoryError) { if (handleOutOfMemoryError) { _Grids_Environment.clear_MemoryReserve(); if (_Grids_Environment.swapToFile_Grid2DSquareCellChunks_Account(handleOutOfMemoryError) < 1L) { throw a_OutOfMemoryError; } _Grids_Environment.init_MemoryReserve(handleOutOfMemoryError); toGreyScaleImage( a_Grid2DSquareCell, file, type, handleOutOfMemoryError); } else { throw a_OutOfMemoryError; } } } /** * Writes this grid as a Grey scale image * @param file The File exported to. * @param type The name of the type of image to be written e.g. "png", "jpeg" */ protected void toGreyScaleImage( AbstractGrid2DSquareCell _Grid2DSquareCell, File file, String type) { // Initialisation boolean handleOutOfMemoryError = true; long nrows = _Grid2DSquareCell.get_NRows(handleOutOfMemoryError); long ncols = _Grid2DSquareCell.get_NCols(handleOutOfMemoryError); // Check int precision OK here. if (nrows * ncols > Integer.MAX_VALUE) { System.err.println("Unable to export into a single image using toGreyScaleImage(AbstractGrid2DSquareCell,File,String) as _NRows * _Ncols > Integer.MAXVALUE"); System.err.println("This method either needs development, or another does which should be called instead of this."); System.err.println("The images could be created in chunks by fixing the number range as parameters passed into the method."); return; } int size = (int) (ncols * nrows); long row = Integer.MIN_VALUE; long col = Integer.MIN_VALUE; int iValue = 0; long _long_0 = 0L; BigDecimal iValueBigDecimal = new BigDecimal("0.0"); BigDecimal valueBigDecimal = new BigDecimal("0.0"); BigDecimal aBigDecimal255 = new BigDecimal("255.0"); int scale = 20; int pos = Integer.MIN_VALUE; int gridImageValue = Integer.MIN_VALUE; // Test what writers are available as this may vary on different systems! boolean writerAvailable = IO.isImageWriterAvailable(type); if (!writerAvailable) { System.out.println("Unable to export using toGreyScaleImage(AbstractGrid2DSquareCell,File,String) IO.isImageWriterAvailable(" + type + ") is not available."); String[] writerTypes = ImageIO.getWriterMIMETypes(); System.out.println("WriterTypes:"); for (int i = 0; i < writerTypes.length; i++) { System.out.println(writerTypes[i]); } return; } int[] gridImageArray; try { gridImageArray = new int[size]; Arrays.fill(gridImageArray, 0); } catch (OutOfMemoryError a_OutOfMemoryError) { _Grids_Environment.clear_MemoryReserve(); if (_Grids_Environment.swapToFile_Grid2DSquareCellChunks_Account(handleOutOfMemoryError) < 1L) { throw a_OutOfMemoryError; } _Grids_Environment.init_MemoryReserve(handleOutOfMemoryError); gridImageArray = new int[size]; Arrays.fill(gridImageArray, 0); } if (_Grid2DSquareCell.getClass() == Grid2DSquareCellInt.class) { Grid2DSquareCellInt _Grid2DSquareCellInt = (Grid2DSquareCellInt) _Grid2DSquareCell; int noDataValue = _Grid2DSquareCellInt.getNoDataValue(handleOutOfMemoryError); BigDecimal maxBigDecimal = _Grid2DSquareCellInt.getGridStatistics( handleOutOfMemoryError).getMaxBigDecimal(handleOutOfMemoryError); BigDecimal minBigDecimal = _Grid2DSquareCellInt.getGridStatistics( handleOutOfMemoryError).getMinBigDecimal(handleOutOfMemoryError); BigDecimal rangeBigDecimal = maxBigDecimal.subtract(minBigDecimal); //int max = _Grid2DSquareCellInt.getGridStatistics().getMaxInt( handleOutOfMemoryError ); //int min = _Grid2DSquareCellInt.getGridStatistics().getMinInt( handleOutOfMemoryError ); int value = Integer.MIN_VALUE; for (row = _long_0; row < nrows; row++) { //for ( row = nrows - 1; row > -1; row -- ) { for (col = _long_0; col < ncols; col++) { try { value = _Grid2DSquareCellInt.getCell( row, col, handleOutOfMemoryError); //pos = ( int ) ( ( row * ncols ) + col ); pos = (int) ((((nrows - 1) - row) * ncols) + col); // Construct an RGB integer by byte operation : 32 bytes, first 8 bytes is transparency value // second, third, forth 8 byte is Red, green, blue value, which will be used by MemoryImageSource // class. iValue = 255; // Set noDataValue as blue if (value == noDataValue) { gridImageValue = (255 << 24) | (0 << 16) | (0 << 8) | iValue; gridImageArray[pos] = gridImageValue; } else { valueBigDecimal = new BigDecimal(String.valueOf(value)); if (maxBigDecimal.compareTo(minBigDecimal) != 0) { //if ( max != min ) { // The imprecision in the following calculation may cause problems, if more presision is need then a greater scale can be set. //iValueBigDecimal = aBigDecimal255.multiply( ( valueBigDecimal.subtract( minBigDecimal ) ) ).divide( rangeBigDecimal, scale, BigDecimal.ROUND_HALF_UP ); iValueBigDecimal = aBigDecimal255.multiply((valueBigDecimal.subtract(minBigDecimal))).divide(rangeBigDecimal, scale, BigDecimal.ROUND_HALF_UP); //iValueDouble = 255.0d * ( ( ( double ) value - min ) / ( double ) ( max - min ) ); // The imprecision in the following integerisation may cause problems iValue = iValueBigDecimal.intValue(); //DEBUG if this does happen, iValue should be very close to 255 or 0 if (iValue > 255) { iValue = 255; } else { if (iValue < 0) { iValue = 0; } } //gridImageValue = ( iValue << 24 ) | ( iValue << 16 ) | ( iValue << 8 ) | iValue; gridImageValue = (255 << 24) | (iValue << 16) | (iValue << 8) | iValue; //gridImageValue = ( 255 << 24 ) | ( 255 << 16 ) | ( iValue << 8 ) | iValue; //gridImageValue = ( 255 << 24 ) | ( 255 << 16 ) | ( 255 << 8 ) | iValue; gridImageArray[pos] = gridImageValue; } else { // Set white gridImageValue = (255 << 24) | (255 << 16) | (255 << 8) | 255; gridImageArray[pos] = gridImageValue; } } } catch (OutOfMemoryError a_OutOfMemoryError) { _Grids_Environment.clear_MemoryReserve(); if (_Grids_Environment.swapToFile_Grid2DSquareCellChunks_Account(handleOutOfMemoryError) < 1L) { throw a_OutOfMemoryError; } _Grids_Environment.init_MemoryReserve(handleOutOfMemoryError); value = _Grid2DSquareCellInt.getCell( row, col, handleOutOfMemoryError); //pos = ( int ) ( ( row * ncols ) + col ); pos = (int) ((((nrows - 1) - row) * ncols) + col); // Construct an RGB integer by byte operation : 32 bytes, first 8 bytes is transparency value // second, third, forth 8 byte is Red, green, blue value, which will be used by MemoryImageSource // class. iValue = 255; // Set noDataValue as blue if (value == noDataValue) { gridImageValue = (255 << 24) | (0 << 16) | (0 << 8) | iValue; gridImageArray[pos] = gridImageValue; } else { valueBigDecimal = new BigDecimal(String.valueOf(value)); if (maxBigDecimal.compareTo(minBigDecimal) != 0) { //if ( max != min ) { // The imprecision in the following calculation may cause problems, if more presision is need then a greater scale can be set. //iValueBigDecimal = aBigDecimal255.multiply( ( valueBigDecimal.subtract( minBigDecimal ) ) ).divide( rangeBigDecimal, scale, BigDecimal.ROUND_HALF_UP ); iValueBigDecimal = aBigDecimal255.multiply((valueBigDecimal.subtract(minBigDecimal))).divide(rangeBigDecimal, scale, BigDecimal.ROUND_HALF_UP); //iValueDouble = 255.0d * ( ( ( double ) value - min ) / ( double ) ( max - min ) ); // The imprecision in the following integerisation may cause problems iValue = iValueBigDecimal.intValue(); //DEBUG if this does happen, iValue should be very close to 255 or 0 if (iValue > 255) { iValue = 255; } else { if (iValue < 0) { iValue = 0; } } //gridImageValue = ( iValue << 24 ) | ( iValue << 16 ) | ( iValue << 8 ) | iValue; gridImageValue = (255 << 24) | (iValue << 16) | (iValue << 8) | iValue; //gridImageValue = ( 255 << 24 ) | ( 255 << 16 ) | ( iValue << 8 ) | iValue; //gridImageValue = ( 255 << 24 ) | ( 255 << 16 ) | ( 255 << 8 ) | iValue; gridImageArray[pos] = gridImageValue; } else { // Set white gridImageValue = (255 << 24) | (255 << 16) | (255 << 8) | 255; gridImageArray[pos] = gridImageValue; } } } } } } else { //_Grid2DSquareCell.getClass() == Grid2DSquareCellDouble.class Grid2DSquareCellDouble _Grid2DSquareCellDouble = (Grid2DSquareCellDouble) _Grid2DSquareCell; double noDataValue = _Grid2DSquareCellDouble.get_NoDataValue( handleOutOfMemoryError); // if ( Double.isInfinite( noDataValue ) ) { // System.out.println( // "Warning!!! noDataValue = " + noDataValue + // " in ESRIAsciigridExporter.toGreyScaleImage( AbstractGrid2DSquareCell( " + // _Grid2DSquareCellDouble.toString( handleOutOfMemoryError ) + " ), File( " + // file.toString() + " ) )" ); // } BigDecimal maxBigDecimal = _Grid2DSquareCellDouble.getGridStatistics( handleOutOfMemoryError).getMaxBigDecimal(handleOutOfMemoryError); BigDecimal minBigDecimal = _Grid2DSquareCellDouble.getGridStatistics( handleOutOfMemoryError).getMaxBigDecimal(handleOutOfMemoryError); BigDecimal rangeBigDecimal = maxBigDecimal.subtract(minBigDecimal); // Read all data into an array and scale all values into the range [ 0, 255 ] double value = Double.MIN_VALUE; for (row = _long_0; row < nrows; row++) { //for ( row = nrows - 1; row > -1; row -- ) { for (col = _long_0; col < ncols; col++) { try { value = _Grid2DSquareCellDouble.getCell( row, col, handleOutOfMemoryError); //pos = ( int ) ( ( row * ncols ) + col ); pos = (int) ((((nrows - 1) - row) * ncols) + col); // Construct an RGB integer by byte operation : 32 bytes, first 8 bytes is transparency value // second, third, forth 8 byte is Red, green, blue value, which will be used by MemoryImageSource // class. iValue = 255; // Set noDataValue as blue if (value == noDataValue) { gridImageValue = (255 << 24) | (0 << 16) | (0 << 8) | 255; gridImageArray[pos] = gridImageValue; } else { valueBigDecimal = new BigDecimal(String.valueOf(value)); if (maxBigDecimal.compareTo(minBigDecimal) != 0) { //if ( max != min ) { // The imprecision in the following calculation may cause problems, if more presision is need then a greater scale can be set. //iValueBigDecimal = aBigDecimal255.multiply( ( valueBigDecimal.subtract( minBigDecimal ) ) ).divide( rangeBigDecimal, scale, BigDecimal.ROUND_HALF_UP ); iValueBigDecimal = aBigDecimal255.multiply((valueBigDecimal.subtract(minBigDecimal)).divide(rangeBigDecimal, scale, BigDecimal.ROUND_HALF_UP)); //iValueDouble = 255.0d * ( ( ( double ) value - min ) / ( double ) ( max - min ) ); iValue = iValueBigDecimal.intValue(); //DEBUG if this does happen, iValue should be very close to 255 or 0 if (iValue > 255) { iValue = 255; } else { if (iValue < 0) { iValue = 0; } } //gridImageValue = ( iValue << 24 ) | ( iValue << 16 ) | ( iValue << 8 ) | iValue; gridImageValue = (255 << 24) | (iValue << 16) | (iValue << 8) | iValue; //gridImageValue = ( 255 << 24 ) | ( 255 << 16 ) | ( iValue << 8 ) | iValue; //gridImageValue = ( 255 << 24 ) | ( 255 << 16 ) | ( 255 << 8 ) | iValue; gridImageArray[pos] = gridImageValue; } else { // Set white gridImageValue = (255 << 24) | (255 << 16) | (255 << 8) | 255; gridImageArray[pos] = gridImageValue; } } } catch (OutOfMemoryError a_OutOfMemoryError) { _Grids_Environment.clear_MemoryReserve(); if (_Grids_Environment.swapToFile_Grid2DSquareCellChunks_Account(handleOutOfMemoryError) < 1L) { throw a_OutOfMemoryError; } _Grids_Environment.init_MemoryReserve(handleOutOfMemoryError); //_Grid2DSquareCell.init_MemoryReserve( _Grid2DSquareCell.handleOutOfMemoryErrorTrue ); Moved to after retry value = _Grid2DSquareCellDouble.getCell( row, col, handleOutOfMemoryError); //pos = ( int ) ( ( row * ncols ) + col ); pos = (int) ((((nrows - 1) - row) * ncols) + col); // Construct an RGB integer by byte operation : 32 bytes, first 8 bytes is transparency value // second, third, forth 8 byte is Red, green, blue value, which will be used by MemoryImageSource // class. iValue = 255; // Set noDataValue as blue if (value == noDataValue) { gridImageValue = (255 << 24) | (0 << 16) | (0 << 8) | 255; gridImageArray[pos] = gridImageValue; } else { valueBigDecimal = new BigDecimal(String.valueOf(value)); if (maxBigDecimal.compareTo(minBigDecimal) != 0) { //if ( max != min ) { // The imprecision in the following calculation may cause problems, if more presision is need then a greater scale can be set. //iValueBigDecimal = aBigDecimal255.multiply( ( valueBigDecimal.subtract( minBigDecimal ) ) ).divide( rangeBigDecimal, scale, BigDecimal.ROUND_HALF_UP ); iValueBigDecimal = aBigDecimal255.multiply((valueBigDecimal.subtract(minBigDecimal)).divide(rangeBigDecimal, scale, BigDecimal.ROUND_HALF_UP)); //iValueDouble = 255.0d * ( ( ( double ) value - min ) / ( double ) ( max - min ) ); iValue = iValueBigDecimal.intValue(); //DEBUG if this does happen, iValue should be very close to 255 or 0 if (iValue > 255) { iValue = 255; } else { if (iValue < 0) { iValue = 0; } } //gridImageValue = ( iValue << 24 ) | ( iValue << 16 ) | ( iValue << 8 ) | iValue; gridImageValue = (255 << 24) | (iValue << 16) | (iValue << 8) | iValue; //gridImageValue = ( 255 << 24 ) | ( 255 << 16 ) | ( iValue << 8 ) | iValue; //gridImageValue = ( 255 << 24 ) | ( 255 << 16 ) | ( 255 << 8 ) | iValue; gridImageArray[pos] = gridImageValue; } else { // Set white gridImageValue = (255 << 24) | (255 << 16) | (255 << 8) | 255; gridImageArray[pos] = gridImageValue; } } } } } } try { // Use gridImageArray to create a MemoryImageSource // Construct a BufferedImage using Toolkit // because Image class does not implement the RenderedImage // interface and imageio needs it. MemoryImageSource gridImageSource = new MemoryImageSource( (int) ncols, (int) nrows, gridImageArray, 0, (int) ncols); Image tempImage = Toolkit.getDefaultToolkit().createImage(gridImageSource); BufferedImage gridImage = new BufferedImage( (int) ncols, (int) nrows, BufferedImage.TYPE_INT_RGB); Graphics2D g = (Graphics2D) gridImage.getGraphics(); g.drawImage(tempImage, 0, 0, new java.awt.Panel()); try { javax.imageio.ImageIO.write(gridImage, type, file); } catch (java.io.IOException e1) { e1.printStackTrace(); System.out.println( "Warning!!! Failed to write grid as " + type + " to File(" + file.toString() + ")"); } g.dispose(); gridImage.flush(); tempImage.flush(); } catch (OutOfMemoryError a_OutOfMemoryError) { try { _Grids_Environment.clear_MemoryReserve(); if (_Grids_Environment.swapToFile_Grid2DSquareCellChunks_Account(handleOutOfMemoryError) < 1L) { throw a_OutOfMemoryError; } _Grids_Environment.init_MemoryReserve(handleOutOfMemoryError); MemoryImageSource gridImageSource = new MemoryImageSource( (int) ncols, (int) nrows, gridImageArray, 0, (int) ncols); Image tempImage = Toolkit.getDefaultToolkit().createImage(gridImageSource); BufferedImage gridImage = new BufferedImage( (int) ncols, (int) nrows, BufferedImage.TYPE_INT_RGB); Graphics2D g = (Graphics2D) gridImage.getGraphics(); g.drawImage(tempImage, 0, 0, new java.awt.Panel()); try { javax.imageio.ImageIO.write(gridImage, type, file); } catch (java.io.IOException e1) { e1.printStackTrace(); System.out.println( "Warning!!! Failed to write grid as " + type + " to File(" + file.toString() + ")"); } g.dispose(); gridImage.flush(); tempImage.flush(); } catch (OutOfMemoryError _OutOfMemoryError2) { System.err.println("Insufficient memory to write image."); return; } } } }