/**
*
*/
package org.geotools.gce.geotiff;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.DataBuffer;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataUtilities;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.image.ImageWorker;
import org.geotools.metadata.iso.spatial.PixelTranslation;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.opengis.geometry.BoundingBox;
import org.opengis.metadata.extent.GeographicBoundingBox;
import org.opengis.referencing.datum.PixelInCell;
/**
* Sparse utilities for the various classes. I use them to extract
* complex code from other places.
*
* @author Simone Giannecchini, GeoSolutions S.A.S.
*
*/
class Utils {
/**
* Logger.
*/
final static Logger LOGGER = org.geotools.util.logging.Logging
.getLogger(Utils.class.toString());
/**
* {@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.
*/
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.
*/
final static AffineTransform CORNER_TO_CENTER = AffineTransform
.getTranslateInstance(-PixelTranslation
.getPixelTranslation(PixelInCell.CELL_CORNER),
-PixelTranslation
.getPixelTranslation(PixelInCell.CELL_CORNER));
/**
* Defaut wildcard for creating mosaics.
*/
static final String DEFAULT_WILCARD = "*.*";
/**
* Default path behavior with respect to absolute paths.
*/
static final boolean DEFAULT_PATH_BEHAVIOR = false;
static String getMessageFromException(Exception exception) {
if (exception.getLocalizedMessage() != null)
return exception.getLocalizedMessage();
else
return exception.getMessage();
}
static URL checkSource(Object source) throws MalformedURLException,
DataSourceException {
URL sourceURL = null;
// /////////////////////////////////////////////////////////////////////
//
// Check source
//
// /////////////////////////////////////////////////////////////////////
// if it is a URL or a String let's try to see if we can get a file to
// check if we have to build the index
if (source instanceof URL) {
sourceURL = ((URL) source);
source = DataUtilities.urlToFile(sourceURL);
} else if (source instanceof String) {
// is it a File?
final String tempSource = (String) source;
File tempFile = new File(tempSource);
if (!tempFile.exists()) {
// is it a URL
try {
sourceURL = new URL(tempSource);
source = DataUtilities.urlToFile(sourceURL);
} catch (MalformedURLException e) {
sourceURL = null;
source = null;
}
} else {
sourceURL = tempFile.toURI().toURL();
source = tempFile;
}
}
// at this point we have tried to convert the thing to a File as hard as
// we could, let's see what we can do
if (source instanceof File) {
final File sourceFile = (File) source;
if (!sourceFile.isDirectory())
sourceURL = ((File) source).toURI().toURL();
} else
sourceURL = null;
return sourceURL;
}
/**
* 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.
*/
static double getThreshold(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;
}
/**
* Builds a {@link ReferencedEnvelope} from a {@link GeographicBoundingBox}.
* This is useful in order to have an implementation of {@link BoundingBox}
* from a {@link GeographicBoundingBox} which strangely does implement
* {@link GeographicBoundingBox}.
*
* @param geographicBBox
* the {@link GeographicBoundingBox} to convert.
* @return an instance of {@link ReferencedEnvelope}.
*/
static ReferencedEnvelope getReferencedEnvelopeFromGeographicBoundingBox(
final GeographicBoundingBox geographicBBox) {
Utils.ensureNonNull("GeographicBoundingBox", geographicBBox);
return new ReferencedEnvelope(geographicBBox.getEastBoundLongitude(),
geographicBBox.getWestBoundLongitude(), geographicBBox
.getSouthBoundLatitude(), geographicBBox
.getNorthBoundLatitude(), DefaultGeographicCRS.WGS84);
}
/**
* @param transparentColor
* @param image
* @return
* @throws IllegalStateException
*/
static RenderedImage makeColorTransparent(final Color transparentColor,
final RenderedImage image) throws IllegalStateException {
final ImageWorker w = new ImageWorker(image);
if (image.getSampleModel() instanceof MultiPixelPackedSampleModel)
w.forceComponentColorModel();
return w.makeColorTransparent(transparentColor).getRenderedImage();
}
/**
* Makes sure that an argument is non-null.
*
* @param name
* Argument name.
* @param object
* User argument.
* @throws IllegalArgumentException
* if {@code object} is null.
*/
static void ensureNonNull(final String name, final Object object)
throws IllegalArgumentException {
if (object == null) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.NULL_ARGUMENT_$1, name));
}
}
static IOFileFilter excludeFilters(final IOFileFilter inputFilter,
IOFileFilter... filters) {
IOFileFilter retFilter = inputFilter;
for (IOFileFilter filter : filters) {
retFilter = FileFilterUtils.andFileFilter(retFilter,
FileFilterUtils.notFileFilter(filter));
}
return retFilter;
}
/**
* Look for an {@link ImageReader} instance that is able to read the
* provided {@link ImageInputStream}, which must be non null.
*
* <p>
* In case no reader is found, <code>null</code> is returned.
*
* @param inStream
* an instance of {@link ImageInputStream} for which we need to
* find a suitable {@link ImageReader}.
* @return a suitable instance of {@link ImageReader} or <code>null</code>
* if one cannot be found.
*/
static ImageReader getReader(final ImageInputStream inStream) {
ensureNonNull("inStream", inStream);
// get a reader
inStream.mark();
final Iterator<ImageReader> readersIt = ImageIO
.getImageReaders(inStream);
if (!readersIt.hasNext()) {
return null;
}
return readersIt.next();
}
/**
* Retrieves the dimensions of the {@link RenderedImage} at index
* <code>imageIndex</code> for the provided {@link ImageReader} and
* {@link ImageInputStream}.
*
* <p>
* Notice that none of the input parameters can be <code>null</code> or a
* {@link NullPointerException} will be thrown. Morevoer the
* <code>imageIndex</code> cannot be negative or an
* {@link IllegalArgumentException} will be thrown.
*
* @param imageIndex
* the index of the image to get the dimensions for.
* @param inStream
* the {@link ImageInputStream} to use as an input
* @param reader
* the {@link ImageReader} to decode the image dimensions.
* @return a {@link Rectangle} that contains the dimensions for the image at
* index <code>imageIndex</code>
* @throws IOException
* in case the {@link ImageReader} or the
* {@link ImageInputStream} fail.
*/
static Rectangle getDimension(final int imageIndex,
final ImageInputStream inStream, final ImageReader reader)
throws IOException {
ensureNonNull("inStream", inStream);
ensureNonNull("reader", reader);
if (imageIndex < 0)
throw new IllegalArgumentException(Errors.format(
ErrorKeys.INDEX_OUT_OF_BOUNDS_$1, imageIndex));
inStream.reset();
reader.setInput(inStream);
return new Rectangle(0, 0, reader.getWidth(imageIndex), reader
.getHeight(imageIndex));
}
/**
* Retrieves an {@link ImageInputStream} for the provided input {@link File}
* .
*
* @param file
* @return
* @throws IOException
*/
static ImageInputStream getInputStream(final File file) throws IOException {
final ImageInputStream inStream = ImageIO.createImageInputStream(file);
if (inStream == null)
return null;
return inStream;
}
/**
* Checks that the provided <code>dimensions</code> when intersected with
* the source region used by the provided {@link ImageReadParam} instance
* does not result in an empty {@link Rectangle}.
*
* <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</code> if the intersection is not empty,
* <code>false</code> otherwise.
*/
static boolean checkEmptySourceRegion(final ImageReadParam readParameters,
final Rectangle dimensions) {
ensureNonNull("readDimension", dimensions);
ensureNonNull("readP", readParameters);
final Rectangle sourceRegion = readParameters.getSourceRegion();
Rectangle.intersect(sourceRegion, dimensions, sourceRegion);
if (sourceRegion.isEmpty())
return true;
readParameters.setSourceRegion(sourceRegion);
return false;
}
/**
* Default priority for the underlying {@link Thread}.
*/
public static final int DEFAULT_PRIORITY = Thread.NORM_PRIORITY;
/**
* Checks that a {@link File} is a real file, exists and is readable.
*
* @param file
* the {@link File} instance to check. Must not be null.
*
* @return <code>true</code> in case the file is a real file, exists and is
* readable; <code>false </code> otherwise.
*/
static boolean checkFileReadable(final File file) {
if (LOGGER.isLoggable(Level.FINE)) {
final StringBuilder builder = new StringBuilder();
builder.append("Checking file:").append(
FilenameUtils.getFullPath(file.getAbsolutePath())).append(
"\n");
builder.append("canRead:").append(file.canRead()).append("\n");
builder.append("isHidden:").append(file.isHidden()).append("\n");
builder.append("isFile").append(file.isFile()).append("\n");
builder.append("canWrite").append(file.canWrite()).append("\n");
LOGGER.fine(builder.toString());
}
if (!file.exists() || !file.canRead() || !file.isFile())
return false;
return true;
}
}