/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2008-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. */ /** * Support for reading and creating mosaics of geographically referenced images. * This package uses a subclass of the standard Java {@link javax.imageio.ImageReader} * which tracks the extent and resolution of the component images. * <p> * The {@link org.geotoolkit.image.io.mosaic.MosaicImageReader} class can generate visual * representations of an underlying mosaic for different extents and resolutions based * on the parameter values in the {@link javax.imageio.ImageReadParam} instance passed * to the {@code read(...)} method. Each image in a mosaic will be a stand alone image in * main storage (<it>e.g.</it> disk) and will be represented to the {@code MosaicImageReader} * as a {@link org.geotoolkit.image.io.mosaic.Tile} instance which carries the spatial metadata * for the image, including its resolution and extent. In a typical mosaic the tiles will * be laid out on a regular grid; in a typical pyramid the tiles will form several mosaic * layers at different resolutions. The {@link org.geotoolkit.image.io.mosaic.TileManager} * tracks the {@code Tile} instances for the {@code MosaicImageReader}. For the user, the * {@code MosaicImageReader} can be used as if it were a standard * {@link javax.imageio.ImageReader} backed by a single, huge image. * <p> * This package can work directly with an existing image set or mosaic by creating * a {@code Tile} instance to define the geographic metadata for each image. The * package also provides the {@link org.geotoolkit.image.io.mosaic.MosaicBuilder} which * can generate a new mosaic from an existing one simply by specifying the properties * of the desired mosaic. For example, a user can start with a small set of large images, * define the geographic extent of each image, and then use the {@code MosaicBuilder} to * build a mosaic composed of many, small images with which to work more efficiently. * <p> * All of the images referenced by a single {@code MosaicImageReader} must exist on the same * 'grid' which is to say they must share the same axes and have pixel sizes which are * integer multiple of the smallest pixel size. The images do not necessarily need to cover a * contiguous area nor do the mosaics need to be geo-referenced when they are originally created. * * * {@section To define a mosaic from existing set of images} * * Users must first specify the layout of their source images. This step is required * whether or not the users wish to use an existing mosaic "as is" or want to generate * a new mosaic. Once defined, the mosaic can be used directly to generate images by * creating a {@code MosaicImageReader}. Alternatively, the mosaic can be used by * {@code MosaicBuilder} to generate a new mosaic. * * <ol> * <li><p>Create a collection of {@link org.geotoolkit.image.io.mosaic.Tile} objects where * each {@code Tile} instance describes an existing image. {@code Tile} instances * contain only metadata about the tiles, not the actual pixel data. Two approaches * exist to define the location of a given {@code Tile} instance relative to other * tiles in the mosaic: the relative location can be described in pixel units by * passing {@code Point} or {@code Rectangle} arguments to the constructor or the * information can be calculated from the actual geographic coordinates of the image. * In the latter case, an <cite>affine transform</cite>, which defines the conversion * from grid (pixel) coordinates to geographic coordinates, may be passed explicitly to * a constructor. Alternatively, this transform can be inferred automatically by the * {@linkplain org.geotoolkit.image.io.mosaic.Tile#Tile(javax.imageio.spi.ImageReaderSpi, * java.io.File, int) a constructor} which uses a 'world file' (TFW) which accompanies * the image to calculate the transform.</p></li> * * <li><p>Create a {@link org.geotoolkit.image.io.mosaic.TileManager} from the collection of tiles. * The tile manager is {@linkplain org.geotoolkit.image.io.mosaic.TileManagerFactory#create created * using a factory}. The factory will infer the mosaic layout (location of tiles relative to each * other, subsampling relative to the tiles having the smallest pixels, <i>etc.</i>) from * the affine transform defined for each tile. If the tiles appear to be distributed on a regular * grid, then the created {@code TileManager} instance will store that information in a compact * way that does not require the retention of every {@code Tile} object.</p></li> * </ol> * * The following example creates a {@code TileManager} for an existing set of images. In this * example the images are TIFF files where each image file is accompanied by a file of the same * name but with the ".tfw" extension (its <cite>World File</cite>). The {@code Tile} constructor * used here automatically looks for the TFW file and builds the required {@code AffineTransform}. * * {@preformat java * File[] sourceImages = directory.listFiles(new DefaultFileFilter("*.tiff")); * List<Tile> tiles = new ArrayList<Tile>(sourceImages.length); * for (File file : sourceImages) { * tiles.add(new Tile(null, sourceImages, 0)); * } * TileManager[] originalMosaic = TileManagerFactory.DEFAULT.create(tiles); * } * * {@note * <ul> * <li>The resulting <code>TileManager</code> array should have length 1 if all the images could * be defined in the same grid. Otherwise be prepared to handle multiple mosaics.</li> * <li>In current implementation, make sure that you get an instance of <code>GridTileManager</code>. * The other instance of <code>TileManager</code>, <code>TreeTileManager</code>, is more * generic but still experimental.</li> * <li>You can serialize the <code>TileManager</code> instance in order to reload it quickly in * a subsequent run of your application.</li> * </ul> * } * * * {@section To generate a new mosaic from an existing one} * * Users may want to generate a new mosaic from an existing set of images, typically either * to have small regular tiles or to create a mosaic with several layers of images having * different resolutions. (The latter is often called a "pyramid" in the geographic community.) * <p> * The {@link org.geotoolkit.image.io.mosaic.MosaicBuilder} class makes building a new mosaic easy: * * {@preformat java * MosaicBuilder builder = new MosaicBuilder(); * builder.setTileDirectory(new File("output")); // The output directory. * builder.setTileSize(new Dimension(256, 256)); // The size of the output tiles. * builder.setSubsamplings(1, 2, 3, 4); // Defines the layers. * TileManager newMosaic = builder.createTileManager(originalMosaic, TileWritingPolicy.WRITE_NEWS_NONEMPTY); * } * * Because the cost of building a mosaic can be substantial, it is often worth while to save * the {@code TileManager} data structure in some way. The mosaic will be used every time an * image needs to be read from the mosaic (see next section) while the steps performed up to this * point need to be executed only once. An easy way to save the mosaic information is simply to * {@linkplain java.io.ObjectOutput#writeObject serialize} the {@code TileManager} instance. * <p> * <blockquote><table border="1" cellpadding="6" bgcolor="paleturquoise"><tr><td> * <b>Tip:</b> for an easy way to generate a mosaic using a graphical user interface, see the * <a href="http://www.geotoolkit.org/modules/display/geotk-wizards-swing/MosaicWizard.html">Image * Mosaic Wizard</a>. * </td></tr></table></blockquote> * * * {@section To read images from the mosaic} * * To generate images, users need to perform the following steps: * * <ol> * <li><p>Create a new instance of {@link org.geotoolkit.image.io.mosaic.MosaicImageReader}. * Instantiation can be done directly using the public constructors, or an instance can be * {@linkplain javax.imageio.ImageIO#getImageReadersByFormatName provided by the Java Image * I/O framework} when the users request for the {@code "mosaic"} format.</p></li> * * <li><p>{@linkplain org.geotoolkit.image.io.mosaic.MosaicImageReader#setInput Set the input} of * the {@code MosaicImageReader} to use the {@code TileManager} instance created in the * previous section.</p></li> * * <li><p>Obtain a new instance of {@link org.geotoolkit.image.io.mosaic.MosaicImageReadParam}. * Instantiation can be done directly using the public constructor, or an instance can be * {@link org.geotoolkit.image.io.mosaic.MosaicImageReader#getDefaultReadParam provided by * the reader}.</p></li> * * <li><p>Configure the parameters to reference the data of interest. Methods of special interest * are {@link org.geotoolkit.image.io.mosaic.MosaicImageReadParam#setSubsamplingChangeAllowed * setSubsamplingChangeAllowed} (<strong>non-standard but strongly recommended</strong>), * {@link org.geotoolkit.image.io.mosaic.MosaicImageReadParam#setSourceSubsampling setSourceSubsampling} * and {@link org.geotoolkit.image.io.mosaic.MosaicImageReadParam#setSourceRegion setSourceRegion}.</p></li> * * <li><p>Invoke {@link org.geotoolkit.image.io.mosaic.MosaicImageReader#read MosaicImageReader.read} * with the parameters created in the previous step.</p></li> * </ol> * * Users who wish to generate more images from the same mosaic can repeat only steps 4 * and 5. The steps 1 to 3 (and <cite>a fortiori</cite> the previous section * which created the {@code TileManager}) need to be done only once. * <p> * The example below generates a single image from the mosaic created in the previous section. * Note that the source region is given in pixel coordinates. * * {@preformat java * MosaicImageReader reader = new MosaicImageReader(); * reader.setInput(newMosaic); * MosaicImageReadParam param = reader.getDefaultReadParam(); * param.setSubsamplingChangeAllowed(true); // Strongly recommended. * param.setSourceRegion(new Rectangle(2000, 2000, 800, 600)); * param.setSourceSubsampling(2, 2, 0, 0); * RenderedImage image = reader.read(0, param); * * // Because of setSubsamplingChangeAllowed(true), the subsampling actually used * // may be different than the one we asked for. To get the subsampling which was * // actually used we can do: * int sx = param.getSourceXSubsampling(); * int sy = param.getSourceYSubsampling(); * // Adjust gridToCRS here using (sx,sy). * } * * * {@section To convert a source region from "real world" to pixel coordinates} * * The {@code ImageReader} API is defined only in terms of pixel coordinates. If the region to * read is defined in "real world" coordinates, then it must be converted to pixel coordinates * before being given to {@code MosaicImageReadParam}. This conversion can be done by obtaining * an {@link java.awt.geom.AffineTransform} from the * {@link org.geotoolkit.image.io.mosaic.TileManager#getGridGeometry()} method and using it as * shown here: * * {@preformat java * Rectangle2D realWorldRegion = ...; * Rectangle sourceRegion = new Rectangle(); * AffineTransform tr = newMosaic.getImageGeometry().getGridToCRS(); * tr = tr.createInverse(); * XAffineTransform.transform(tr, realWorldRegion, sourceRegion); * } * * The above can only be used if the {@code TileManager} has been created in one of the * following ways: * <p> * <ul> * <li>From a set of {@code Tile} objects where each tile was given an affine transform * at construction time, either directly or indirectly through a TFW file.</li> * <li>From a {@code MosaicBuilder} in which the * {@linkplain org.geotoolkit.image.io.mosaic.MosaicBuilder#setGridToCRS grid to CRS * transform has been specified}.</li> * </ul> * * @author Martin Desruisseaux (Geomatys) * @version 3.18 * * @since 2.5 * @module */ package org.geotoolkit.image.io.mosaic;