Package org.geotools.resources.coverage

Source Code of org.geotools.resources.coverage.CoverageUtilities$UCUM$UCUMUnit

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2001-2013, 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.resources.coverage;

import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.RenderedImage;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import javax.imageio.ImageReadParam;
import javax.media.jai.Interpolation;
import javax.media.jai.InterpolationBilinear;
import javax.media.jai.InterpolationNearest;
import javax.media.jai.PropertySource;

import org.geotools.coverage.Category;
import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.ViewType;
import org.geotools.factory.Hints;
import org.geotools.geometry.Envelope2D;
import org.geotools.metadata.iso.spatial.PixelTranslation;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.matrix.XAffineTransform;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.geotools.resources.CRSUtilities;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.resources.i18n.Vocabulary;
import org.geotools.resources.i18n.VocabularyKeys;
import org.geotools.util.Utilities;
import org.opengis.coverage.Coverage;
import org.opengis.coverage.SampleDimension;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform1D;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.InternationalString;


/**
* A set of utilities methods for the Grid Coverage package. Those methods are not really
* rigorous; must of them should be seen as temporary implementations.
*
* @since 2.4
*
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
* @author Simone Giannecchini, GeoSolutions
*/
public final class CoverageUtilities {
 
  /**
   * Public name for standard No Data category.
   */
  public static final InternationalString NODATA=Vocabulary.formatInternational(VocabularyKeys.NODATA);
 
 
    /**
     * Axes transposition for swapping Lat and Lon axes.
     */
    public static final AffineTransform AXES_SWAP= new AffineTransform2D(0,1,1,0,0,0);
 
    /** Identity affine transformation.*/
    public static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform2D(AffineTransform.getRotateInstance(0));
   
    /**
     * {@link AffineTransform} that can be used to go from an image datum placed
     * at the center of pixels to one that is placed at ULC.
     */
    public final static AffineTransform CENTER_TO_CORNER = AffineTransform
        .getTranslateInstance(PixelTranslation
            .getPixelTranslation(PixelInCell.CELL_CORNER),
            PixelTranslation
                .getPixelTranslation(PixelInCell.CELL_CORNER));
    /**
     * {@link AffineTransform} that can be used to go from an image datum placed
     * at the ULC corner of pixels to one that is placed at center.
     */
    public final static AffineTransform CORNER_TO_CENTER = AffineTransform
        .getTranslateInstance(-PixelTranslation
            .getPixelTranslation(PixelInCell.CELL_CORNER),
            -PixelTranslation
                .getPixelTranslation(PixelInCell.CELL_CORNER));

    /**
     * Do not allows instantiation of this class.
     */
    private CoverageUtilities() {
    }

    /**
     * Returns a two-dimensional CRS for the given coverage. This method performs a
     * <cite>best effort</cite>; the returned CRS is not garanteed to be the most
     * appropriate one.
     *
     * @param  coverage The coverage for which to obtains a two-dimensional CRS.
     * @return The two-dimensional CRS.
     * @throws TransformException if the CRS can't be reduced to two dimensions.
     */
    public static CoordinateReferenceSystem getCRS2D(final Coverage coverage)
            throws TransformException
    {
        if (coverage instanceof GridCoverage2D) {
            return ((GridCoverage2D) coverage).getCoordinateReferenceSystem2D();
        }
        if (coverage instanceof GridCoverage) {
            final GridGeometry2D geometry =
                    GridGeometry2D.wrap(((GridCoverage) coverage).getGridGeometry());
            if (geometry.isDefined(GridGeometry2D.CRS_BITMASK)) {
                return geometry.getCoordinateReferenceSystem2D();
            } else try {
                return geometry.reduce(coverage.getCoordinateReferenceSystem());
            } catch (FactoryException exception) {
                // Ignore; we will fallback on the code below.
            }
        }
        return CRSUtilities.getCRS2D(coverage.getCoordinateReferenceSystem());
    }

    /**
     * Returns a two-dimensional horizontal CRS for the given coverage. This method performs a
     * <cite>best effort</cite>; the returned CRS is not garanteed to succed.
     *
     * @param  coverage The coverage for which to obtains a two-dimensional horizontal CRS.
     * @return The two-dimensional horizontal CRS.
     * @throws TransformException if the CRS can't be reduced to two dimensions.
     */
    public static CoordinateReferenceSystem getHorizontalCRS(final Coverage coverage)
            throws TransformException
    {
      CoordinateReferenceSystem returnedCRS=null;
        if (coverage instanceof GridCoverage2D) {
          returnedCRS= ((GridCoverage2D) coverage).getCoordinateReferenceSystem2D();
        }
        if (coverage instanceof GridCoverage) {
            final GridGeometry2D geometry =
                    GridGeometry2D.wrap(((GridCoverage) coverage).getGridGeometry());
            if (geometry.isDefined(GridGeometry2D.CRS_BITMASK)) {
              returnedCRS= geometry.getCoordinateReferenceSystem2D();
            } else try {
              returnedCRS= geometry.reduce(coverage.getCoordinateReferenceSystem());
            } catch (FactoryException exception) {
                // Ignore; we will fallback on the code below.
            }
        }
        if(returnedCRS==null)
          returnedCRS= CRS.getHorizontalCRS(coverage.getCoordinateReferenceSystem());
        if(returnedCRS==null)
                throw new TransformException(Errors.format(
                          ErrorKeys.CANT_REDUCE_TO_TWO_DIMENSIONS_$1,
                          returnedCRS));
        return returnedCRS;
    }

    /**
     * Returns a two-dimensional envelope for the given coverage. This method performs a
     * <cite>best effort</cite>; the returned envelope is not garanteed to be the most
     * appropriate one.
     *
     * @param  coverage The coverage for which to obtains a two-dimensional envelope.
     * @return The two-dimensional envelope.
     * @throws MismatchedDimensionException if the envelope can't be reduced to two dimensions.
     */
    public static Envelope2D getEnvelope2D(final Coverage coverage)
            throws MismatchedDimensionException
    {
        if (coverage instanceof GridCoverage2D) {
            return ((GridCoverage2D) coverage).getEnvelope2D();
        }
        if (coverage instanceof GridCoverage) {
            final GridGeometry2D geometry =
                    GridGeometry2D.wrap(((GridCoverage) coverage).getGridGeometry());
            if (geometry.isDefined(GridGeometry2D.ENVELOPE_BITMASK)) {
                return geometry.getEnvelope2D();
            } else {
                return geometry.reduce(coverage.getEnvelope());
            }
        }
        // Following may thrown MismatchedDimensionException.
        return new Envelope2D(coverage.getEnvelope());
    }

    /**
     * Retrieves a best guess for the sample value to use for background,
     * inspecting the categories of the provided {@link GridCoverage2D}.
     *
     * @param coverage to use for guessing background values.
     * @return an array of double values to use as a background.
     */
  public static double[] getBackgroundValues(GridCoverage2D coverage) {
   
    //minimal checks
    if(coverage==null){
      throw new NullPointerException(Errors.format(ErrorKeys.NULL_PARAMETER_$2, "coverage","GridCoverage2D"));
    }
   
    // try to get the GC_NODATA double value from the coverage property
    final Object noData=coverage.getProperty("GC_NODATA");
    if(noData!=null&& noData instanceof Number){
      return new double[]{((Double)noData).doubleValue()};
    }
   
        ////
    //
    // Try to gather no data values from the sample dimensions
    // and, if not available, we try to suggest them from the sample
    // dimension type
    //
    ////
        final GridSampleDimension[] sampleDimensions = coverage.getSampleDimensions();
        final double[] background = new double[sampleDimensions.length];
       
        boolean found=false;
        final int dataType = coverage.getRenderedImage().getSampleModel().getDataType();
        for (int i=0; i<background.length; i++) {
          // try to use the no data category if preset
          final List<Category> categories = sampleDimensions[i].getCategories();
          if(categories!=null&&categories.size()>0){
            for(Category category:categories){
              if(category.getName().equals(NODATA)){
                background[i]=category.geophysics(true).getRange().getMinimum();
                found=true;
                break;
              }
            }
          }
         
          if(!found){
            // we don't have a proper no data value, let's try to suggest something
            // meaningful fro mthe data type for this coverage
              background[i]=suggestNoDataValue(dataType).doubleValue();
          }
         
//          SG 25112012, removed this automagic behavior         
//            final NumberRange<?> range = sampleDimensions[i].getBackground().getRange();
//            final double min = range.getMinimum();
//            final double max = range.getMaximum();
//            if (range.isMinIncluded()) {
//                background[i] = min;
//            } else if (range.isMaxIncluded()) {
//                background[i] = max;
//            } else {
//                background[i] = 0.5 * (min + max);
//            }
        }
        return background;
    }

    /**
     * Returns {@code true} if the provided {@link GridCoverage}
     * has {@link Category} objects with a real transformation.
     * <p>
     * Common use case for this method is understanding if a {@link GridCoverage} has an
     * accompanying Geophysics or non-Geophysics view, which means a dicotomy between the
     * coverage with the "real" data and the coverage with the rendered version of the original
     * data exists. An example is when you have raw data whose data type is float and you want
     * to render them using a palette. You usually do this by specifying a set of {@link Category}
     * object which will map some intervals of the raw data to some specific colors. The rendered
     * version that we will create using the method {@link GridCoverage2D#geophysics(false)} will
     * be backed by a RenderedImage with an IndexColorModel representing the colors provided in
     * the Categories.
     *
     * @param gridCoverage
     *            to check for the existence of categories with tranformations
     *            between original data and their rendered counterpart.
     * @return {@code false} if this coverage has only a single view associated with it,
     *         {@code true} otherwise.
     */
    public static boolean hasRenderingCategories(final GridCoverage gridCoverage) {
        // getting all the SampleDimensions of this coverage, if any exist
        final int numSampleDimensions = gridCoverage.getNumSampleDimensions();
        if (numSampleDimensions == 0) {
            return false;
        }
        final SampleDimension[] sampleDimensions = new SampleDimension[numSampleDimensions];
        for (int i=0; i<numSampleDimensions; i++) {
            sampleDimensions[i] = gridCoverage.getSampleDimension(i);
        }
        // do they have any transformation that is not the identity?
        return hasTransform(sampleDimensions);
    }

    /**
     * Returns {@code true} if at least one of the specified sample dimensions has a
     * {@linkplain SampleDimension#getSampleToGeophysics sample to geophysics} transform
     * which is not the identity transform.
     */
    public static boolean hasTransform(final SampleDimension[] sampleDimensions) {
        for (int i=sampleDimensions.length; --i>=0;) {
            SampleDimension sd = sampleDimensions[i];
            if (sd instanceof GridSampleDimension) {
                sd = ((GridSampleDimension) sd).geophysics(false);
            }
            MathTransform1D tr = sd.getSampleToGeophysics();
            if (tr!=null && !tr.isIdentity()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns {@code true} if the specified grid coverage or any of its source
     * uses the following image.
     */
    public static boolean uses(final GridCoverage coverage, final RenderedImage image) {
        if (coverage != null) {
            if ( coverage.getRenderedImage() == image) {
                return true;
            }
            final Collection<GridCoverage> sources = coverage.getSources();
            if (sources != null) {
                for (final GridCoverage source : sources) {
                    if (uses(source, image)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Returns the visible band in the specified {@link RenderedImage} or {@link PropertySource}.
     * This method fetch the {@code "GC_VisibleBand"} property. If this property is undefined,
     * then the visible band default to the first one.
     *
     * @param  image The image for which to fetch the visible band, or {@code null}.
     * @return The visible band.
     */
    public static int getVisibleBand(final Object image) {
        Object candidate = null;
        if (image instanceof RenderedImage) {
            candidate = ((RenderedImage) image).getProperty("GC_VisibleBand");
        } else if (image instanceof PropertySource) {
            candidate = ((PropertySource) image).getProperty("GC_VisibleBand");
        }
        if (candidate instanceof Integer) {
            return ((Integer) candidate).intValue();
        }
        return 0;
    }

    /**
     * General purpose method used in various operations for {@link GridCoverage2D} to help
     * with taking decisions on how to treat coverages with respect to their {@link ColorModel}.
     * <p>
     * The need for this method arose in consideration of the fact that applying most operations
     * on coverage whose {@link ColorModel} is an instance of {@link IndexColorModel} may lead to
     * unpredictable results depending on the applied {@link Interpolation} (think about applying
     * "Scale" with {@link InterpolationBilinear} on a non-geophysics {@link GridCoverage2D} with an
     * {@link IndexColorModel}) or more simply on the operation itself ("SubsampleAverage" cannot
     * be applied at all on a {@link GridCoverage2D} backed by an {@link IndexColorModel}).
     * <p>
     * This method suggests the actions to take depending on the structure of the provided
     * {@link GridCoverage2D}, the provided {@link Interpolation} and if the operation uses
     * a filter or not (this is useful for operations like SubsampleAverage or FilteredSubsample).
     * <p>
     * In general the idea is as follows: If the original coverage is backed by a
     * {@link RenderedImage} with an {@link IndexColorModel}, we have the following cases:
     * <p>
     * <ul>
     <li>if the interpolation is {@link InterpolationNearest} and there is no filter involved
     *      we can apply the operation on the {@link IndexColorModel}-backed coverage with nor
     *      problems.</li>
     <li>If the interpolations in of higher order or there is a filter to apply we have to
     *      options:
     *      <ul>
     *        <li>If the coverage has a twin geophysics view we need to go back to it and apply
     *            the operation there.</li>
     *        <li>If the coverage has no geophysics view (an orthophoto with an intrisic
     *            {@link IndexColorModel} view) we need to perform an RGB(A) color expansion
     *            before applying the operation.</li>
     *      </ul>
     </li>
     * </ul>
     * <p>
     * A special case is when we want to apply an operation on the geophysics view of a coverage
     * that does not involve high order interpolation or filters. In this case we suggest to apply
     * the operation on the non-geophysics view, which is usually much faster. Users may ignore
     * this advice.
     *
     * @param coverage The coverage to check for the action to take.
     * @param interpolation The interpolation to use for the action to take, or {@code null} if none.
     * @param hasFilter {@code true} if the operation we will apply is going to use a filter.
     * @param hints The hints to use when applying a certain operation.
     * @return {@link ViewType#SAME} if nothing has to be done on the provided coverage,
     *         {@link ViewType.PHOTOGRAPHIC} if a color expansion has to be provided,
     *         {@link ViewType#GEOPHYSICS} if we need to employ the geophysics view of
     *         the provided coverage,
     *         {@link ViewType#NATIVE} if we suggest to employ the native (usually packed) view
     *         of the provided coverage.
     *
     * @since 2.5
     *
     * @todo Move this method in {@link org.geotools.coverage.processing.Operation2D}.
     */
    @SuppressWarnings("deprecation")
  public static ViewType preferredViewForOperation(final GridCoverage2D coverage,
            final Interpolation interpolation, final boolean hasFilter, final RenderingHints hints)
    {
        /*
         * Checks if the user specified explicitly the view he wants to use for performing
         * the calculations.
         */
        if (hints != null) {
            final Object candidate = hints.get(Hints.COVERAGE_PROCESSING_VIEW);
            if (candidate instanceof ViewType) {
                return (ViewType) candidate;
            }
        }
        /*
         * Tries to infer automatically the view to use.  If there is no sample dimension with
         * a "sample to geophysics" transform, then we assume that the image has no geophysics
         * meaning and would better be handled as photographic.
         */
        final RenderedImage sourceImage = coverage.getRenderedImage();
        if (sourceImage.getColorModel() instanceof IndexColorModel) {
            if (!hasRenderingCategories(coverage)) {
                return ViewType.PHOTOGRAPHIC;
            }
            // The old way to request explicitly a color expansion.
            if (hints != null && Boolean.FALSE.equals(hints.get(Hints.REPLACE_NON_GEOPHYSICS_VIEW))) {
                return ViewType.PHOTOGRAPHIC;
            }
            /*
             * If there is no filter and no interpolation, then we don't need to operate on
             * geophysics value. The packed view is usually faster. We could returns either
             * NATIVE, PACKED or SAME, which are equivalent in many cases:
             *
             *  - SAME is likely equivalent to PACKED because we checked that the color model is indexed.
             *  - NATIVE is likely equivalent to PACKED because data in NetCDF or HDF files are often packed.
             *
             * However those views differ in their behavior when the native data are geophysics
             * rather than packed (e.g. a NetCDF file with floating point values). In this case,
             * NATIVE is equivalent to GEOPHYSICS. The tradeoff of each views are:
             *
             *  - NATIVE is more accurate but slower when native data are geophysics
             *    (but as fast as other views when native data are packed).
             *
             *  - SAME is "as the user said" on the assumption that if he asked an operation on
             *    a packed view of a coverage rather than the geophysics view, he know what he
             *    is doing.
             */
            if (!hasFilter && (interpolation == null || interpolation instanceof InterpolationNearest)) {
                if (hints != null) {
                    final Object rendering = hints.get(RenderingHints.KEY_RENDERING);
                    if (RenderingHints.VALUE_RENDER_QUALITY.equals(rendering)) {
                        return ViewType.NATIVE;
                    }
                    if (RenderingHints.VALUE_RENDER_SPEED.equals(rendering)) {
                        return ViewType.SAME;
                    }
                }
                return ViewType.SAME; // Default value.
            }
            // In this case we need to go back the geophysics view of the source coverage.
            return ViewType.GEOPHYSICS;
        }
        /*
         * The operations are usually applied on floating-point values, in order
         * to gets maximal precision and to handle correctly the special case of
         * NaN values. However, we can apply some operation on integer values if
         * the interpolation type is "nearest neighbor", since this is not
         * really an interpolation.
         *
         * If this condition is met, then we verify if an "integer version" of
         * the image is available as a source of the source coverage (i.e. the
         * floating-point image is derived from the integer image, not the
         * converse).
         */
        if (!hasFilter && (interpolation == null || interpolation instanceof InterpolationNearest)) {
            final GridCoverage2D candidate = coverage.view(ViewType.NATIVE);
            if (candidate != coverage) {
                final List<RenderedImage> sources = coverage.getRenderedImage().getSources();
                if (sources != null && sources.contains(candidate.getRenderedImage())) {
                    return ViewType.NATIVE;
                }
            }
        }
        return ViewType.SAME;
    }

    /**
     * The preferred view in which to returns the coverage after the operation.
     * This method returns a view that match the current state of the given coverage.
     *
     * @param  coverage The source coverage <strong>before</strong> the operation.
     * @return The suggested view, or {@link ViewType#SAME} if this method doesn't
     *         have any suggestion.
     *
     * @since 2.5
     *
     * @todo Move this method in {@link org.geotools.coverage.processing.Operation2D}.
     */
    public static ViewType preferredViewAfterOperation(final GridCoverage2D coverage) {
        final Set<ViewType> views = coverage.getViewTypes();
        // Most restrictive views first, less restrictive last.
        if (views.contains(ViewType.GEOPHYSICS)) {
            return ViewType.GEOPHYSICS;
        }
        if (views.contains(ViewType.RENDERED)) {
            return ViewType.RENDERED;
        }
        if (views.contains(ViewType.PACKED)) {
            return ViewType.PACKED;
        }
        if (views.contains(ViewType.PHOTOGRAPHIC)) {
            return ViewType.PHOTOGRAPHIC;
        }
        return ViewType.SAME;
    }

    /**
     * Checks if the transformation is a pure scale/translate instance (using the
     * provided tolerance factor)
     *
     * @param transform The {@link MathTransform} to check.
     * @param EPS The tolerance factor.
     * @return {@code true} if the provided transformation is a simple scale and translate,
     *         {@code false} otherwise.
     */
    public static boolean isScaleTranslate(final MathTransform transform, final double EPS) {
        if (!(transform instanceof AffineTransform)) {
            return false;
        }
        final AffineTransform at = (AffineTransform) transform;
        final double rotation = Math.abs(XAffineTransform.getRotation(at));
        return rotation < EPS; // This is enough for returning 'false' if 'scale' is NaN.
    }

    /**
     * Computes the resolutions for the provided "grid to world" transformation
     * The returned resolution array is of length of 2.
     *
     * @param gridToCRS
     *            The grid to world transformation.
     */
    public static double[] getResolution(final AffineTransform gridToCRS) {
        double[] requestedRes = null;
        if (gridToCRS != null) {
            requestedRes = new double[2];
            requestedRes[0] = XAffineTransform.getScaleX0(gridToCRS);
            requestedRes[1] = XAffineTransform.getScaleY0(gridToCRS);
        }
        return requestedRes;
    }

    /**
     * Tries to estimate if the supplied affine transform is either a scale and
     * translate transform or if it contains a rotations which is an integer
     * multiple of PI/2.
     *
     * @param gridToCRS an instance of {@link AffineTransform} to check against.
     * @param EPS tolerance value for comparisons.
     * @return {@code true} if this transform is "simple", {@code false} otherwise.
     */
    public static boolean isSimpleGridToWorldTransform(final AffineTransform gridToCRS, double EPS) {
        final double rotation = XAffineTransform.getRotation(gridToCRS);
        // Checks if there is a valid rotation value (it could be 0). If the result is an integer,
        // then there is no rotation and skew or there is a rotation multiple of PI/2. Note that
        // there is no need to check explicitly for NaN rotation angle since such value will be
        // propagated as NaN by every math functions used here, and (NaN < EPS) returns false.
        final double quadrantRotation = Math.abs(rotation / (Math.PI/2));
        return Math.abs(quadrantRotation - Math.floor(quadrantRotation)) < EPS;
    }
   
   
    /**
     * Checks that the provided {@code dimensions} when intersected with the source region used by
     * the provided {@link ImageReadParam} instance does not result in an empty {@link Rectangle}.
     * Finally, in case the region intersection is not empty, set it as new source region for the
     * provided {@link ImageReadParam}. 
     * <p>
     * Input parameters cannot be null.
     *
     * @param readParameters an instance of {@link ImageReadParam} for which we want to check
     *          the source region element.
     * @param dimensions an instance of {@link Rectangle} to use for the check.
     * @return {@code true} if the intersection is not empty, {@code false} otherwise.
     */
    public static boolean checkEmptySourceRegion(final ImageReadParam readParameters,
            final Rectangle dimensions) {
        Utilities.ensureNonNull("readDimension", dimensions);
        Utilities.ensureNonNull("readP", readParameters);
        final Rectangle sourceRegion = readParameters.getSourceRegion();
        Rectangle.intersect(sourceRegion, dimensions, sourceRegion);
        if (sourceRegion.isEmpty()) {
            return true;
        }
        readParameters.setSourceRegion(sourceRegion);
        return false;
    }

  /**
   * Returns a suitable threshold depending on the {@link DataBuffer} type.
   *
   * <p>
   * Remember that the threshold works with >=.
   *
   * @param dataType
   *            to create a low threshold for.
   * @return a minimum threshold value suitable for this data type.
   */
  public static double getMosaicThreshold(int dataType) {
    switch (dataType) {
    case DataBuffer.TYPE_BYTE:
    case DataBuffer.TYPE_USHORT:
      // this may cause problems and truncations when the native mosaic
      // operations is enabled
      return 0.0;
    case DataBuffer.TYPE_INT:
      return Integer.MIN_VALUE;
    case DataBuffer.TYPE_SHORT:
      return Short.MIN_VALUE;
    case DataBuffer.TYPE_DOUBLE:
      return -Double.MAX_VALUE;
    case DataBuffer.TYPE_FLOAT:
      return -Float.MAX_VALUE;
    }
    return 0;
  }   
 
  /**
   * Returns a suitable no data value depending on the {@link DataBuffer} type.
   *
   * @param dataType
   *            to create a low threshold for.
   * @return a no data value suitable for this data type.
   */
  public static Number suggestNoDataValue(int dataType) {
    switch (dataType) {
    case DataBuffer.TYPE_BYTE:
      return Byte.valueOf((byte)0);
    case DataBuffer.TYPE_USHORT:
      return Short.valueOf((short)0);
    case DataBuffer.TYPE_INT:
      return Integer.valueOf(Integer.MIN_VALUE);
    case DataBuffer.TYPE_SHORT:
      return Short.valueOf((short)Short.MIN_VALUE);
    case DataBuffer.TYPE_DOUBLE:
      return Double.valueOf(Double.NaN);
    case DataBuffer.TYPE_FLOAT:
      return Float.valueOf(Float.NaN);
    default:
      throw new IllegalAccessError(Errors.format(ErrorKeys.ILLEGAL_ARGUMENT_$2,"dataType",dataType));
    }
  }
 
    /**
     * Unified Code for Units of Measure (UCUM)
     */
    public static class UCUM {

        /**
         * An UCUM Unit instance simply made of name and symbol.
         */
        public static class UCUMUnit {

            private String name;

            private String symbol;

            public UCUMUnit(String name, String symbol) {
                this.name = name;
                this.symbol = symbol;
            }

            public String getName() {
                return name;
            }

            public String getSymbol() {
                return symbol;
            }
        }

        /**
         * Commonly used UCUM units. In case this set will grow too much, we may consider importing some UCUM specialized library.
         */
        public final static UCUMUnit TIME_UNITS = new UCUMUnit("second", "s");

        public final static UCUMUnit ELEVATION_UNITS = new UCUMUnit("meter", "m");

    }
}
TOP

Related Classes of org.geotools.resources.coverage.CoverageUtilities$UCUM$UCUMUnit

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.