/* * Geotools 2 - OpenSource mapping toolkit * (C) 2003, Geotools Project Management Committee (PMC) * (C) 2001, Institut de Recherche pour le Développement * * 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 * * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.gc; // J2SE dependencies import java.awt.Color; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.util.Arrays; import javax.media.jai.iterator.RectIter; import javax.media.jai.iterator.RectIterFactory; import org.geotools.ct.MathTransform1D; import org.geotools.cv.Category; import org.geotools.cv.ColorInterpretation; import org.geotools.cv.SampleDimension; import org.geotools.cv.SampleDimensionType; import org.geotools.gp.Hints; import org.geotools.resources.ClassChanger; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Errors; import org.geotools.units.Unit; import org.geotools.util.NumberRange; /** * Describes the band values for a grid coverage. * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux * * @deprecated Replaced by {@link org.geotools.coverage.grid.GridSampleDimension}. */ final class GridSampleDimension extends SampleDimension { /** * The range of sample values after a transformation from * integer geophysics value to 8 bits indexed image. */ private static final NumberRange INTEGER_TO_UBYTE; /** * The range of sample values after a transformation from * integer geophysics value to 16 bits indexed image. */ private static final NumberRange INTEGER_TO_USHORT; /** * The range of sample values after a transformation from floating point geophysics * value to 8 bits indexed image. Index 0 is reserved for {@link Category#NODATA}, * which maps to {@link Float#NaN} values. */ private static final NumberRange FLOAT_TO_UBYTE; /** * The range of sample values after a transformation from floating point geophysics * value to 16 bits indexed image. Index 0 is reserved for {@link Category#NODATA}, * which maps to {@link Float#NaN} values. */ private static final NumberRange FLOAT_TO_USHORT; static { final Integer ZERO = new Integer(0); final Integer ONE = new Integer(1); final Integer U08 = new Integer(255); final Integer U16 = new Integer(65535); INTEGER_TO_UBYTE = new NumberRange(Integer.class, ZERO, U08); INTEGER_TO_USHORT = new NumberRange(Integer.class, ZERO, U16); FLOAT_TO_UBYTE = new NumberRange(Integer.class, ONE, U08); FLOAT_TO_USHORT = new NumberRange(Integer.class, ONE, U16); } /** * Band number for this sample dimension. */ private final int band; /** * The number of bands in the {@link GridCoverage} who own this sample dimension. */ private final int numBands; /** * The grid value data type. */ private final SampleDimensionType type; /** * Construct a sample dimension with a set of categories from an other sample dimension. * * @param band The originating sample dimension. * @param image The image to be wrapped by {@link GridCoverage}. * @param bandNumber The band number. */ private GridSampleDimension(final SampleDimension band, final RenderedImage image, final int bandNumber) { super(band); final SampleModel model = image.getSampleModel(); this.band = bandNumber; this.numBands = model.getNumBands(); this.type = SampleDimensionType.getEnum(model, bandNumber); } /** * Create a set of sample dimensions for the given image. The array length of both * arguments must matches the number of bands in the supplied image. * * @param name The name for data (e.g. "Elevation"). * @param image The image for which to create a set of sample dimensions. * @param src User-provided sample dimensions, or null if none. * @param dst The array where to put sample dimensions. * @return true if all sample dimensions are geophysics, or false * if all sample dimensions are not geophysics. * @throws IllegalArgumentException if geophysics and non-geophysics dimensions are mixed. */ static boolean create(final String name, final RenderedImage image, final SampleDimension[] src, final SampleDimension[] dst) { final int numBands = image.getSampleModel().getNumBands(); if (src!=null && src.length!=numBands) { throw new IllegalArgumentException(Errors.format( ErrorKeys.NUMBER_OF_BANDS_MISMATCH_$3, new Integer(numBands), new Integer(src.length), "SampleDimension")); } if (dst.length != numBands) { throw new IllegalArgumentException(Errors.format( ErrorKeys.NUMBER_OF_BANDS_MISMATCH_$3, new Integer(numBands), new Integer(dst.length), "SampleDimension")); } int nGeo = 0; int nInt = 0; SampleDimension[] defaultSD = null; for (int i=0; inull for computing it * automatically. * @param max The maximal value for each bands, or null for computing it * automatically. * @param units The units of sample values, or null if unknow. * @param colors The colors to use for values from min to max * for each bands, or null for a default color palette. If non-null, * each arrays colors[b] may have any length; colors will be interpolated * as needed. * @param hints An optional set of rendering hints, or null if none. * Those hints will not affect the sample dimensions to be created. However, * they may affect the sample dimensions to be returned by * {@link #geophysics geophysics}(false), i.e. * the view to be used at rendering time. The optional hint * {@link Hints#SAMPLE_DIMENSION_TYPE} specifies the {@link SampleDimensionType} * to be used at rendering time, which can be one of * {@link SampleDimensionType#UBYTE UBYTE} or * {@link SampleDimensionType#USHORT USHORT}. * @return The sample dimension for the given raster. */ static SampleDimension[] create(final String name, final Raster raster, final double[] min, final double[] max, final Unit units, final Color[][] colors, final RenderingHints hints) { final SampleDimension[] dst = new SampleDimension[raster.getNumBands()]; create(name, (min==null || max==null) ? RectIterFactory.create(raster, null) : null, raster.getDataBuffer().getDataType(), min, max, units, colors, dst, hints); return dst; } /** * Create a set of sample dimensions for the data backing the given iterator. * * @param name The name for data (e.g. "Elevation"). * @param iterator The iterator through the raster data, or null. * @param rasterType The data type of the image sample values. * Must be one of {@link DataBuffer} constants. * @param min The minimal value, or null for computing it automatically. * @param max The maximal value, or null for computing it automatically. * @param units The units of sample values, or null if unknow. * @param colors The colors to use for values from min to max * for each bands, or null for a default color palette. If non-null, * each arrays colors[b] may have any length; colors will be interpolated * as needed. * @param dst The array where to store sample dimensions. The array length must matches * the number of bands. * @param hints An optional set of rendering hints, or null if none. * Those hints will not affect the sample dimensions to be created. However, * they may affect the sample dimensions to be returned by * {@link #geophysics geophysics}(false), i.e. * the view to be used at rendering time. The optional hint * {@link Hints#SAMPLE_DIMENSION_TYPE} specifies the {@link SampleDimensionType} * to be used at rendering time, which can be one of * {@link SampleDimensionType#UBYTE UBYTE} or * {@link SampleDimensionType#USHORT USHORT}. */ private static void create(final String name, final RectIter iterator, final int rasterType, double[] min, double[] max, final Unit units, final Color[][] colors, final SampleDimension[] dst, final RenderingHints hints) { final int numBands = dst.length; final boolean computeMin = (min == null); final boolean computeMax = (max == null); if (computeMin) { min = new double[numBands]; Arrays.fill(min, Double.POSITIVE_INFINITY); } if (computeMax) { max = new double[numBands]; Arrays.fill(max, Double.NEGATIVE_INFINITY); } if (min.length != numBands) { throw new IllegalArgumentException(Errors.format( ErrorKeys.NUMBER_OF_BANDS_MISMATCH_$3, new Integer(numBands), new Integer(min.length), "min[i]")); } if (max.length != numBands) { throw new IllegalArgumentException(Errors.format( ErrorKeys.NUMBER_OF_BANDS_MISMATCH_$3, new Integer(numBands), new Integer(max.length), "max[i]")); } if (colors!=null && colors.length != numBands) { throw new IllegalArgumentException(Errors.format( ErrorKeys.NUMBER_OF_BANDS_MISMATCH_$3, new Integer(numBands), new Integer(colors.length), "colors[i]")); } /* * Arguments are now know to be valids. We now need to compute two ranges: * * STEP 1: Range of sample values. This is computed in the following block. * STEP 2: Range of geophysics values. It will be computed one block later. * * The range of sample values will range from 0 to 255 or 0 to 65535 according * the rendering hint provided. If the raster data use floating point numbers, * then a "nodata" category will be added in order to handle NaN values. If the * the raster data use integer numbers, then we will rescale the numbers only * if they would not fit in the rendering type. */ SampleDimensionType renderingType = SampleDimensionType.UBYTE; if (rasterType!=DataBuffer.TYPE_BYTE && hints!=null) { renderingType = (SampleDimensionType) hints.get(Hints.SAMPLE_DIMENSION_TYPE); } final boolean byteRenderingType = renderingType.getSize()<=8; final NumberRange sampleValueRange; final Category[] categories; boolean needScaling = true; switch (rasterType) { case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: { categories = new Category[2]; categories[1] = Category.NODATA; sampleValueRange = byteRenderingType ? FLOAT_TO_UBYTE : FLOAT_TO_USHORT; break; } case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: { if (rasterType == renderingType.getDataBufferType()) { needScaling = false; } // fall through } default: { categories = new Category[1]; sampleValueRange = byteRenderingType ? INTEGER_TO_UBYTE : INTEGER_TO_USHORT; break; } } /* * Compute the minimal and maximal values, if not explicitely provided. * This information is required for determining the range of geophysics * values. */ if (computeMin || computeMax) { int b=0; iterator.startBands(); if (!iterator.finishedBands()) do { iterator.startLines(); if (!iterator.finishedLines()) do { iterator.startPixels(); if (!iterator.finishedPixels()) do { final double z = iterator.getSampleDouble(); if (computeMin && zmax[b]) max[b]=z; } while (!iterator.nextPixelDone()); } while (!iterator.nextLineDone()); if (computeMin && computeMax) { if (!(min[b] < max[b])) { min[b] = 0; max[b] = 1; } } b++; } while (!iterator.nextBandDone()); } /* * Determine the class of geophysics values. This class can generally be infered from * the raster data type. In the exceptional case where the data type is unknow, we will * determine a default class based on the range of values computed just above. */ Class classe = null; switch (rasterType) { case DataBuffer.TYPE_BYTE: // Fall through case DataBuffer.TYPE_SHORT: classe = Short.class; break; case DataBuffer.TYPE_USHORT: // Fall through case DataBuffer.TYPE_INT: classe = Integer.class; break; case DataBuffer.TYPE_FLOAT: classe = Float.class; break; case DataBuffer.TYPE_DOUBLE: classe = Double.class; break; default: { // Unrecognized type. Fallback on the finest // type capable to hold the range of all bands. for (int b=0; b