/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2006-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotools.coverage.grid;
import java.awt.RenderingHints;
import java.awt.image.ColorModel; // For javadoc
import java.awt.image.IndexColorModel; // For javadoc
import java.awt.image.RenderedImage;
import javax.media.jai.JAI;
import javax.media.jai.Interpolation;
import javax.media.jai.InterpolationNearest; // For javadoc
import javax.media.jai.InterpolationBilinear; // For javadoc
import javax.media.jai.InterpolationBicubic; // For javadoc
import javax.media.jai.operator.ScaleDescriptor; // For javadoc
import org.geotools.resources.image.ImageUtilities;
/**
* Enumerates different "views" over a given coverage. Coverage views represent the same data
* in different ways. Some views are more appropriate than others depending of the kind of work
* to be performed. For example numerical computations on meteorological or oceanographical data
* should be performed on the {@linkplain #GEOPHYSICS geophysics} view, while renderings are
* better performed with the {@linkplain #RENDERED rendered} view.
* <p>
* Different views are sometime synonymous for a given coverage. For example the
* {@linkplain #NATIVE native} and {@linkplain #RENDERED rendered} views are identical
* when the coverage values are unsigned 8 or 16 bits integers, but distincts if the native
* values are 32 bits integers. This is because in the later case, the 32 bits integer values
* can not be processed directly by an {@linkplain IndexColorModel index color model}.
*
* @since 2.4
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux
*
* @see GridCoverage2D#view
*/
public enum ViewType {
/**
* Coverage data come directly from some source (typically a file) and are unprocessed.
* This view doesn't have any of the restrictions imposed by other views: values may be
* integers or floating point values, negative values are allowed, and missing data may
* be represented by "pad values" like -9999. This view is generally not suitable for
* renderings or numerical computations. However in some special cases, this view may
* be identical to an other view (see those other views for a more exhaustive list of
* their conditions):
* <p>
* <ul>
* <li>If the values are stored as unsigned integers not greater than 16 bits, then the
* native view may be identical to the {@linkplain #RENDERED rendered} view.</li>
* <li>If all missing values are represented by {@linkplain Float#isNaN some kind of
* NaN values}, then the native view may be identical to the
* {@linkplain #GEOPHYSICS geophysics} view.</li>
* </ul>
* <p>
* Interpolations other than {@linkplain InterpolationNearest nearest neighbor} are
* not allowed. Conversions to the RGB color space are not allowed neither, for the
* same reasons than the {@linkplain #RENDERED rendered} view.
*/
NATIVE(false, false, false),
/**
* Coverage data are packed, usually as integers convertible to geophysics values. The conversion
* is performed by the {@linkplain org.geotools.coverage.GridSampleDimension#getSampleToGeophysics
* sample to geophysics} transform.
* <p>
* This view is often synonymous to {@link #RENDERED}, but may be different for some data types
* that are incompatible with {@linkplain IndexColorModel index color model} (e.g. 32 bits
* integer). This view is always exclusive with {@link #GEOPHYSICS}.
*
* @since 2.5
*/
PACKED(false, false, false),
/**
* Coverage data are compatible with common Java2D {@linkplain ColorModel color models}.
* This usually imply that values are restricted to unsigned integers. This view is often
* identical to the {@linkplain #NATIVE native} view if the values on the originating
* device were already unsigned.
* <p>
* Conversions to the RGB color space are not allowed, because the data are often related
* to {@linkplain #GEOPHYSICS geophysics} values in some way. For example the coverage may
* contains <cite>Sea Surface Temperature</cite> (SST) data packed as 8 bits integers and
* convertible to degrees Celsius using the following formula: <var>temperature</var> =
* <var>pixel_value</var> × 0.15 - 3. A conversion to RGB space would lose this
* relationship, and any oceanographical calculation accidentaly performed on this space
* would produce wrong results.
* <p>
* Interpolations other than {@linkplain InterpolationNearest nearest neighbor} are not
* allowed, because some special values are often used as pad values for missing data. An
* interpolation between a "real" value (for example a value convertible to the above-cited
* SST) and "pad" value would produce a wrong result.
*/
RENDERED(false, false, false),
/**
* Coverage data are the values of some geophysics phenomenon, for example an elevation
* in metres or a temperature in Celsius degrees. Values are typically floating point
* numbers ({@code float} or {@code double} primitive type), but this is not mandatory
* if there is never fractional parts or missing values in a particular coverage.
* <p>
* If the coverage contains some "<cite>no data</cite>" values, then those missing values
* <strong>must</strong> be represented by {@link Float#NaN} or {@link Double#NaN}
* constant, or any other value in the NaN range as {@linkplain Float#intBitsToFloat
* explained there}. Real numbers used as "<cite>pad values</cite>" like {@code -9999} are
* <strong>not</strong> allowed.
* <p>
* Interpolations ({@linkplain InterpolationBilinear bilinear},
* {@linkplain InterpolationBicubic bicubic}, <cite>etc.</cite>) are allowed.
* If there is some missing values around the interpolation point, then the
* result is a {@code NaN} value.
* <p>
* Conversions to RGB color space is not allowed. All computations (including
* interpolations) must be performed in this geophysics space.
*/
GEOPHYSICS(true, false, false),
/**
* Coverage data have no meaning other than visual color. It is not an elevation map for
* example (in which case the coverage would rather be described as {@linkplain #GEOPHYSICS
* geophysics}).
* <p>
* Conversions to the RGB color space are allowed. Because the coverage has no geophysics
* meaning other than visual color, there is no significant data lose in the replacement
* of {@linkplain IndexColorModel index color model}.
* <p>
* Interpolation are not allowed on indexed values. They must be performed on the RGB
* or similar color space instead.
*/
PHOTOGRAPHIC(true, true, true),
/**
* Special value for returning the same coverage unchanged.
* This value can be used as a "<cite>no operation</cite>" instruction.
*
* @since 2.5
*/
SAME(false, false, false);
/**
* @deprecated Renamed as {@link #RENDERED}.
*/
@Deprecated
public static final ViewType DISPLAYABLE = RENDERED;
/**
* {@code true} if interpolations other than {@linkplain InterpolationNearest
* nearest neighbor} are allowed.
*
* @see JAI#KEY_INTERPOLATION
*/
private final boolean interpolationAllowed;
/**
* {@code true} if operations can be performed on the colormap rather than the values.
*
* @see JAI#KEY_TRANSFORM_ON_COLORMAP
*/
private final boolean transformOnColormapAllowed;
/**
* {@code true} if the replacement of {@linkplain IndexColorModel index color model}
* is allowed. This allows the replacement of indexed values by RGB values.
*
* @see JAI#KEY_REPLACE_INDEX_COLOR_MODEL
*/
private final boolean replaceIndexColorModelAllowed;
/**
* Creates a new instance of {@code ViewType}.
*/
private ViewType(final boolean interpolationAllowed,
final boolean transformOnColormapAllowed,
final boolean replaceIndexColorModelAllowed)
{
this.interpolationAllowed = interpolationAllowed;
this.transformOnColormapAllowed = transformOnColormapAllowed;
this.replaceIndexColorModelAllowed = replaceIndexColorModelAllowed;
}
/**
* Returns {@code true} if interpolations other than {@linkplain InterpolationNearest
* nearest neighbor} are allowed. Those interpolations require the following conditions:
* <p>
* <ul>
* <li>Values are either {@linkplain #GEOPHYSICS geophysics} values, or related to
* geophysics values through a linear relationship over all the range of possible
* values (including "no data" values).</li>
* <li>There is no "pad values". Missing values, if any, are represented by some
* {@link Float#NaN NaN} values}.</li>
* </ul>
* <p>
* This method may conservatively returns {@code false} if unsure. If interpolations
* are wanted but not allowed, then users should try to convert the coverage to the
* {@linkplain #GEOPHYSICS geophysics} space, which supports interpolations. If no
* geophysics view is available, then users may convert the image to the RGB space
* if {@linkplain #isColorSpaceConversionAllowed color space conversion is allowed}.
* Interpolations in the RGB space produce nice-looking images, but the pixel values
* lose all geophysical meaning. If the color space conversion is not allowed, then
* then users should stick with {@linkplain InterpolationNearest nearest neighbor}
* interpolation.
*
* @see JAI#KEY_INTERPOLATION
*/
public boolean isInterpolationAllowed() {
return interpolationAllowed;
}
/**
* @deprecated Renamed {@link #isReplaceIndexColorModelAllowed}.
*/
@Deprecated
public boolean isColorSpaceConversionAllowed() {
return replaceIndexColorModelAllowed;
}
/**
* Returns {@code true} if the replacement of {@linkplain IndexColorModel index color model}
* is allowed. Such replacements may occurs during some operations requirying interpolations,
* like {@linkplain ScaleDescriptor scale}, in order to produce images that look nicer.
* However such replacements should be attempted only in last resort (interpolations in the
* {@linkplain #GEOPHYSICS geophysics} space should be preferred) and only if the coverage
* data don't have any meaning other than visual color, as in {@linkplain #PHOTOGRAPHIC
* photographic} images.
*
* @see JAI#KEY_REPLACE_INDEX_COLOR_MODEL
*
* @since 2.5
*/
public boolean isReplaceIndexColorModelAllowed() {
return replaceIndexColorModelAllowed;
}
/**
* Returns {@code true} if operations can be performed on the colormap rather than the values.
*
* @see JAI#KEY_TRANSFORM_ON_COLORMAP
*
* @since 2.5
*/
public boolean isTransformOnColormapAllowed() {
return transformOnColormapAllowed;
}
/**
* Returns suggested rendering hints for a JAI operation on the given image.
* <p>
* <ul>
* <li>{@link JAI#KEY_INTERPOLATION} is sets to "<cite>nearest neighbor</cite>" if
* {@link #isInterpolationAllowed} returns {@code false}, and left unchanged otherwise.</li>
* <li>{@link JAI#KEY_REPLACE_INDEX_COLOR_MODEL} is sets to the value returned by
* {@link #isReplaceIndexColorModelAllowed}.</li>
* <li>{@link JAI#KEY_TRANSFORM_ON_COLORMAP} is sets to the value returned by
* {@link #isTransformOnColormapAllowed}.</li>
* </ul>
*
* @since 2.5
*/
public RenderingHints getRenderingHints(final RenderedImage image) {
RenderingHints hints = ImageUtilities.getRenderingHints(image);
if (hints == null) {
hints = new RenderingHints(null);
}
if (!isInterpolationAllowed()) {
hints.put(JAI.KEY_INTERPOLATION, Interpolation.getInstance(Interpolation.INTERP_NEAREST));
}
hints.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.valueOf(isReplaceIndexColorModelAllowed()));
hints.put(JAI.KEY_TRANSFORM_ON_COLORMAP, Boolean.valueOf(isTransformOnColormapAllowed()));
return hints;
}
}