/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2009-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2009-2012, Geomatys * * 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.geotoolkit.coverage.io; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.io.File; import java.nio.file.Path; import java.util.Collections; import org.geotoolkit.internal.image.io.CheckedImageInputStream; import org.geotoolkit.internal.image.io.CheckedImageOutputStream; import org.geotoolkit.resources.Errors; import org.opengis.coverage.grid.GridCoverage; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.geotoolkit.lang.Static; import javax.imageio.ImageIO; import javax.imageio.stream.FileImageInputStream; import javax.imageio.stream.FileImageOutputStream; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageOutputStream; import static org.apache.sis.util.ArgumentChecks.*; /** * Convenience methods for reading or writing a coverage. The method in this class creates * instances of {@link GridCoverageReader} or {@link GridCoverageWriter} for performing the * actual work. This is similar to the standard {@link javax.imageio.ImageIO} class and the * {@link org.geotoolkit.image.io.XImageIO} class, but applied to coverages. * * {@section Readers} * In the simplest case, this class just creates an {@link ImageCoverageReader} instance with * the input set to the given object (typically a {@link File} or {@link URL}). However if the * image is very large and is not encoded in a format that support natively tiling, it may be * more efficient to create a mosaic of tiles first. The {@link #writeOrReuseMosaic(File)} * method is provided for this purpose. * * {@section Writers} * This class delegates the actual work to the {@link ImageCoverageWriter} class. * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) * @version 3.20 * * @since 3.18 * @module */ public final class CoverageIO extends Static { /** * Do not allow instantiation of this class. */ private CoverageIO() { } /** * Convenience method reading a coverage from the given input. The input is typically * a {@link File}, {@link URL} or {@link String} object, but other types (especially * {@link javax.imageio.stream.ImageInputStream}) may be accepted as well depending * on the image format. The given input can also be an {@link javax.imageio.ImageReader} * instance with its input initialized. * * @param input The input to read (typically a {@link File}). * @return A coverage read from the given input. * @throws CoverageStoreException If the coverage can not be read. */ public static GridCoverage read(final Object input) throws CoverageStoreException { final GridCoverageReader reader = createSimpleReader(input); try { return reader.read(0, null); } finally { reader.dispose(); } } /** * Convenience method writing a coverage to the given output. The output is typically * a {@link File}, a {@link java.nio.file.Path} or {@link String} object, but other types (especially * {@link javax.imageio.stream.ImageOutputStream}) may be accepted as well depending * on the image format. The given input can also be an {@link javax.imageio.ImageWriter} * instance with its output initialized. * * @param coverage The coverage to write. * @param formatName The image format as one of the Image I/O plugin name (e.g. {@code "png"}), * or {@code null} for auto-detection from the output file suffix. * @param output The output where to write the image (typically a {@link File} or a {@link java.nio.file.Path}). * @throws CoverageStoreException If the coverage can not be written. */ public static void write(final GridCoverage coverage, final String formatName, final Object output) throws CoverageStoreException { ensureNonNull("coverage", coverage); write(Collections.singleton(coverage), formatName, output); } /** * Convenience method writing one of many coverages to the given output. The output * is typically a {@link File}, a {@link java.nio.file.Path} or {@link String} object, but other types (especially * {@link javax.imageio.stream.ImageOutputStream}) may be accepted as well depending * on the image format. The given input can also be an {@link javax.imageio.ImageWriter} * instance with its output initialized. * * @param coverages The coverages to write. * @param formatName The image format as one of the Image I/O plugin name (e.g. {@code "png"}), * or {@code null} for auto-detection from the output file suffix. * @param output The output where to write the image (typically a {@link File} or a {@link java.nio.file.Path}). * @throws CoverageStoreException If the coverages can not be written. * * @since 3.20 */ public static void write(final Iterable<? extends GridCoverage> coverages, final String formatName, final Object output) throws CoverageStoreException { ensureNonNull("coverages", coverages); ensureNonNull("output", output); GridCoverageWriteParam param = null; if (formatName != null) { param = new GridCoverageWriteParam(); param.setFormatName(formatName); } final GridCoverageWriter writer = new ImageCoverageWriter(); try { writer.setOutput(output); writer.write(coverages, param); } finally { writer.dispose(); } } /** * Creates a simple reader which does not use any pyramid or mosaic tiling. * This reader is appropriate if the image is known to be small. * <p> * The input is typically a {@link File}, {@link java.nio.file.Path}, {@link URL} or {@link String} object, but other types * (especially {@link javax.imageio.stream.ImageInputStream}) may be accepted as well depending * on the image format. The given input can also be an {@link javax.imageio.ImageReader} instance * with its input initialized. * * @param input The input to read (typically a {@link File} or a {@link java.nio.file.Path}). * @return A coverage reader for the given input. * @throws CoverageStoreException If the reader can not be created for the given file. */ public static GridCoverageReader createSimpleReader(final Object input) throws CoverageStoreException { ensureNonNull("input", input); final ImageCoverageReader reader = new ImageCoverageReader(); reader.setInput(input); return reader; } /** * Creates a simple writer which does not perform any pyramid or mosaic tiling. * This writer is appropriate if the image is known to be small. * <p> * The output is typically a {@link File}, {@link java.nio.file.Path}, {@link URL} or {@link String} object, but other types * (especially {@link javax.imageio.stream.ImageOutputStream}) may be accepted as well depending * on the image format. The given output can also be an {@link javax.imageio.ImageWriter} instance * with its output initialized. * * @param output The output where to write (typically a {@link File} or a {@link java.nio.file.Path}). * @return A coverage writer for the given output. * @throws CoverageStoreException If the writer can not be created for the given file. * * @since 3.20 */ public static GridCoverageWriter createSimpleWriter(final Object output) throws CoverageStoreException { ensureNonNull("output", output); final ImageCoverageWriter writer = new ImageCoverageWriter(); writer.setOutput(output); return writer; } /** * Creates a mosaic reader using the given tiles, which must exist. The input argument can * be an instance of {@link org.geotoolkit.image.io.mosaic.TileManager}, a {@link File} to * a serialized instance of {@code TileManager}, a directory, or an array or collection of * {@link org.geotoolkit.image.io.mosaic.Tile} instances. * <p> * The {@code crs} argument is optional if the input is a {@link File}, in which case this * method will attempt to parse a file of the same name with the {@code ".prj"} extension * using {@link org.geotoolkit.io.wkt.PrjFiles}. For all other cases, the CRS argument is * mandatory. * * @param input The file, tiles or tile manager to use a input. * @param crs The coordinate reference system of the mosaic image. * @return A mosaic reader for the given tiles and CRS. * @throws CoverageStoreException If the reader can not be created for the given tiles. */ public static GridCoverageReader createMosaicReader(final Object input, final CoordinateReferenceSystem crs) throws CoverageStoreException { ensureNonNull("input", input); if (crs == null && (input instanceof File || input instanceof Path)) { final Path path = (input instanceof File) ? ((File) input).toPath() : (Path) input; return new MosaicCoverageReader(path, false); } ensureNonNull("crs", crs); return new MosaicCoverageReader(input, crs); } /** * Creates a mosaic reader using a cache of tiles at different resolutions. Tiles will be * created the first time this method is invoked for a given input. The tiles creation time * depends on the available memory, the image size and its format. The creation time can * range from a few seconds to several minutes or even hours if the given image is very large. * <p> * The tiles will be created in a sub-directory having the same name than the given input, * with an additional {@code ".tiles"} extension. * * @param input The input to read. * @return A coverage reader for the given file. * @throws CoverageStoreException If the reader can not be created for the given file. * @deprecated use {@link #writeOrReuseMosaic(Path)} instead */ @Deprecated public static GridCoverageReader writeOrReuseMosaic(final File input) throws CoverageStoreException { return writeOrReuseMosaic(input.toPath()); } /** * Creates a mosaic reader using a cache of tiles at different resolutions. Tiles will be * created the first time this method is invoked for a given input. The tiles creation time * depends on the available memory, the image size and its format. The creation time can * range from a few seconds to several minutes or even hours if the given image is very large. * <p> * The tiles will be created in a sub-directory having the same name than the given input, * with an additional {@code ".tiles"} extension. * * @param input The input to read. * @return A coverage reader for the given file. * @throws CoverageStoreException If the reader can not be created for the given file. */ public static GridCoverageReader writeOrReuseMosaic(final Path input) throws CoverageStoreException { ensureNonNull("input", input); return new MosaicCoverageReader(input, true); } /** * Wraps input {@link ImageInputStream} in a {@link CheckedImageInputStream} if assertions are enabled. * * @param input The input ImageInputStream to wrap * @return The image input stream wrapped */ private static ImageInputStream wrapImageInputStream(final ImageInputStream input) { ImageInputStream wrapped = input; assert CheckedImageInputStream.isValid(wrapped = // Intentional side effect. CheckedImageInputStream.wrap(wrapped)); return wrapped; } /** * Try to create an {@link ImageInputStream} from an object. This input object is usually an instance of * {@link Path}, {@link File}, {@link String}, {@link URL} or {@link java.io.InputStream}. * * If assertions are enabled returned {@link ImageInputStream} is wrapped in a {@link CheckedImageInputStream}. * * @param input object usually an instance of {@link Path}, {@link File}, {@link String}, {@link URL} * or {@link java.io.InputStream}. * @return ImageInputStream of input object. * @throws IOException if an error occurred while creating the input stream. */ public static ImageInputStream createImageInputStream(Object input) throws IOException { //most of the cases ImageInputStream iis = ImageIO.createImageInputStream(input); if (iis != null) { return wrapImageInputStream(iis); } /* * We tried the input directly in case the user provided some SPI for String * objects. If we have not been able to create a stream from a plain string, * create a URL or a File object from the string and try again. */ if (input instanceof CharSequence) { final String path = input.toString(); final Object url; if (path.indexOf("://") >= 1) { url = new URL(path); } else { url = new File(path); } iis = ImageIO.createImageInputStream(url); if (iis != null) { return wrapImageInputStream(iis); } } /* * In theory ImageIO.createImageInputStream(Object) should have accepted a File input, * so the following check is useless. However if ImageIO.createImageInputStream(Object) * failed, it just returns null; we have no idea why it failed. One possible cause is * "Too many open files", in which case throwing a FileNotFoundException is misleading. * So we try here to create a FileImageInputStream directly, which is likely to fail as * well but this time with a more accurate error message. */ if (input instanceof File) { return wrapImageInputStream(new FileImageInputStream((File) input)); } throw new IOException("Can't create ImageInputStream from input "+input.toString()); } /** * Wraps input {@link ImageInputStream} in a {@link CheckedImageInputStream} if assertions are enabled. * * @param output The input ImageInputStream to wrap * @return The image input stream wrapped */ private static ImageOutputStream wrapImageOutputStream(final ImageOutputStream output) { ImageOutputStream wrapped = output; assert CheckedImageOutputStream.isValid(wrapped = // Intentional side effect. CheckedImageOutputStream.wrap(wrapped)); return wrapped; } /** * Try to create an {@link ImageOutputStream} from an object. This input object is usually an instance of * {@link Path}, {@link File}, {@link String}, {@link URL} or {@link java.io.InputStream}. * * @param output object usually an instance of {@link Path}, {@link File}, {@link String}, {@link URL} * or {@link java.io.InputStream}. * @return ImageOutputStream of input object, not wrapped. * @throws IOException if an error occurred while creating the input stream. * @throws FileNotFoundException if input is not supported */ public static ImageOutputStream createImageOutputStream(Object output) throws IOException{ //most of the cases ImageOutputStream ios = ImageIO.createImageOutputStream(output); if (ios != null) { return wrapImageOutputStream(ios); } /* * We tried the input directly in case the user provided some SPI for String * objects. If we have not been able to create a stream from a plain string, * create a URL or a File object from the string and try again. */ if (output instanceof CharSequence) { final String path = output.toString(); final Object url; if (path.indexOf("://") >= 1) { url = new URL(path); } else { url = new File(path); } ios = ImageIO.createImageOutputStream(url); if (ios != null) { return wrapImageOutputStream(ios); } } /* * In theory ImageIO.createImageInputStream(Object) should have accepted a File input, * so the following check is useless. However if ImageIO.createImageInputStream(Object) * failed, it just returns null; we have no idea why it failed. One possible cause is * "Too many open files", in which case throwing a FileNotFoundException is misleading. * So we try here to create a FileImageInputStream directly, which is likely to fail as * well but this time with a more accurate error message. */ if (output instanceof File) { return wrapImageOutputStream(new FileImageOutputStream((File) output)); } throw new FileNotFoundException(Errors.format( Errors.Keys.FileDoesNotExist_1, output)); } }