Package org.geotools.gce.imagecollection

Source Code of org.geotools.gce.imagecollection.RasterManager$ImageManager

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2007-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.gce.imagecollection;

import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;

import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridFormatFinder;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.coverage.grid.io.UnknownFormat;
import org.geotools.data.DataSourceException;
import org.geotools.factory.Hints;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.operation.builder.GridToEnvelopeMapper;
import org.geotools.referencing.operation.matrix.XAffineTransform;
import org.geotools.referencing.operation.transform.ProjectiveTransform;
import org.geotools.util.SoftValueHashMap;
import org.geotools.util.Utilities;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.geometry.Envelope;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;

class RasterManager {

    /**
     * Simple support class for sorting overview resolutions
     *
     * @author Andrea Aime
     * @author Simone Giannecchini, GeoSolutions.
     * @since 2.5
     */
    class OverviewLevel implements Comparable<OverviewLevel> {

        double scaleFactor;

        double resolutionX;

        double resolutionY;

        int imageChoice;

        RasterLayout rasterLayout;

        public OverviewLevel(final double scaleFactor,
                final double resolutionX, final double resolutionY,
                final int imageChoice, final RasterLayout rasterLayout) {
            this.rasterLayout = rasterLayout;
            this.scaleFactor = scaleFactor;
            this.resolutionX = resolutionX;
            this.resolutionY = resolutionY;
            this.imageChoice = imageChoice;
        }

        public int compareTo(final OverviewLevel other) {
            if (scaleFactor > other.scaleFactor)
                return 1;
            else if (scaleFactor < other.scaleFactor)
                return -1;
            else
                return 0;
        }

        @Override
        public String toString() {
            return "OverviewLevel[Choice=" + imageChoice + ",scaleFactor="
                    + scaleFactor + "]";
        }

        @Override
        public int hashCode() {
            int hash = Utilities.hash(imageChoice, 31);
            hash = Utilities.hash(resolutionX, hash);
            hash = Utilities.hash(resolutionY, hash);
            hash = Utilities.hash(scaleFactor, hash);
            hash = Utilities.hash(rasterLayout, hash);
            return hash;
        }

    }

    class OverviewsController {
        ArrayList<RasterManager.OverviewLevel> resolutionsLevels = new ArrayList<OverviewLevel>();;

        public OverviewsController(RasterLayout hrLayout, MathTransform grid2world) {
            // notice that we assume what follows:
            // -highest resolution image is at level 0.
            // -all the overviews share the same envelope
            // -the aspect ratio for the overviews is constant
            // -the provided resolutions are taken directly from the grid
            if (grid2world == null){
                resolutionsLevels.add(new OverviewLevel(1, highestRes[0], highestRes[1], 0, hrLayout));
            } else {
                AffineTransform at = (AffineTransform) grid2world;
                resolutionsLevels.add(new OverviewLevel(1, XAffineTransform.getScaleX0(at), XAffineTransform.getScaleY0(at), 0, hrLayout));
            }
//            if (numberOfOverwies > 0) {
//                for (int i = 0; i < overviewsResolution.length; i++)
//                    resolutionsLevels.add(new OverviewLevel(
//                            overviewsResolution[i][0] / highestRes[0],
//                            overviewsResolution[i][0],
//                            overviewsResolution[i][1], i + 1,
//                            parent.overViewLayouts[i]));
//                Collections.sort(resolutionsLevels);
//            }
        }

        int pickOverviewLevel(final OverviewPolicy policy, final RasterLayerRequest request) {

            // //
            //
            // If this file has only
            // one page we use decimation, otherwise we use the best page
            // available.
            // Future versions should use both.
            //
            // //
            if (resolutionsLevels == null || resolutionsLevels.size() <= 0)
                return 0;

            // Now search for the best matching resolution.
            // Check also for the "perfect match"... unlikely in practice unless
            // someone tunes the clients to request exactly the resolution embedded in
            // the overviews, something a perf sensitive person might do in fact

            // requested scale factor for least reduced axis
            final OverviewLevel max = (OverviewLevel) resolutionsLevels.get(0);

            // the requested resolutions
            final double requestedScaleFactorX;
            final double requestedScaleFactorY;
            final double[] requestedRes = request.getRequestedResolution();
            if (requestedRes != null) {
                final double reqx = requestedRes[0];
                final double reqy = requestedRes[1];

                requestedScaleFactorX = reqx / max.resolutionX;
                requestedScaleFactorY = reqy / max.resolutionY;
            } else {
                final double[] scaleFactors = request.getRequestedRasterScaleFactors();
                if (scaleFactors == null)
                    return 0;
                requestedScaleFactorX = scaleFactors[0];
                requestedScaleFactorY = scaleFactors[1];
            }
            final int leastReduceAxis = requestedScaleFactorX <= requestedScaleFactorY ? 0 : 1;
            final double requestedScaleFactor = leastReduceAxis == 0 ? requestedScaleFactorX : requestedScaleFactorY;

            // are we looking for a resolution even higher than the native one?
            if (requestedScaleFactor <= 1)
                return max.imageChoice;
            // are we looking for a resolution even lower than the smallest
            // overview?
            final OverviewLevel min = (OverviewLevel) resolutionsLevels.get(resolutionsLevels.size() - 1);
            if (requestedScaleFactor >= min.scaleFactor)
                return min.imageChoice;
            // Ok, so we know the overview is between min and max, skip the first
            // and search for an overview with a resolution lower than the one requested,
            // that one and the one from the previous step will bound the searched resolution
            OverviewLevel prev = max;
            final int size = resolutionsLevels.size();
            for (int i = 1; i < size; i++) {
                final OverviewLevel curr = resolutionsLevels.get(i);
                // perfect match check
                if (curr.scaleFactor == requestedScaleFactor) {
                    return curr.imageChoice;
                }

                // middle check. The first part of the condition should be sufficient, but
                // there are cases where the x resolution is satisfied by the lowest resolution,
                // the y by the one before the lowest (so the aspect ratio of the request is
                // different than the one of the overviews), and we would end up going out of the loop
                // since not even the lowest can "top" the request for one axis
                if (curr.scaleFactor > requestedScaleFactor || i == size - 1) {
                    if (policy == OverviewPolicy.QUALITY)
                        return prev.imageChoice;
                    else if (policy == OverviewPolicy.SPEED)
                        return curr.imageChoice;
                    else if (requestedScaleFactor - prev.scaleFactor < curr.scaleFactor
                            - requestedScaleFactor)
                        return prev.imageChoice;
                    else
                        return curr.imageChoice;
                }
                prev = curr;
            }
            // fallback
            return max.imageChoice;
        }
    }

    /**
     * This class is responsible for doing decimation once the best overview
     * available has been selected (this include the case when no overview is
     * available).
     *
     * @author Simone Giannecchini, GeoSolutions SAS
     *
     */
    class DecimationController {

        public DecimationController() {

        }

        /**
         * This method is responsible for evaluating possible subsampling
         * factors once the best resolution level has been found, in case we
         * have support for overviews, or starting from the original coverage in
         * case there are no overviews available.
         *
         * Anyhow this method should not be called directly but subclasses
         * should make use of the setReadParams method instead in order to
         * transparently look for overviews.
         *
         * @param imageIndex
         * @param readParameters
         * @param imageManager
         * @param requestedRes
         */
        void computeDecimationFactors(final int imageIndex,
                final ImageReadParam readParameters,
                final RasterLayerRequest request) {

                // the read parameters cannot be null
                Utilities.ensureNonNull("readParameters", readParameters);
                Utilities.ensureNonNull("request", request);

                // get the requested resolution in order to guess what we are
                // looking for
                final double[] requestedRes = request.getRequestedResolution();
                if (requestedRes == null) {
                    // if there is no requested resolution we don't do any
                    // subsampling
                    readParameters.setSourceSubsampling(1, 1, 0, 0);
                    return;
                }
                final int rasterWidth, rasterHeight;
                double selectedRes[] = new double[2];
                // are we working against a certain overview?
                final OverviewLevel level = request.imageManager.overviewsController.resolutionsLevels.get(imageIndex);
                selectedRes[0] = level.resolutionX;
                selectedRes[1] = level.resolutionY;
                if (imageIndex == 0) {
                    // highest resolution
                    rasterWidth = request.imageManager.coverageRasterArea.width;
                    rasterHeight = request.imageManager.coverageRasterArea.height;
                } else {
                    // work on overviews
                    final RasterLayout selectedLevelLayout = request.imageManager.overviewsController.resolutionsLevels
                            .get(imageIndex).rasterLayout;
                    rasterWidth = selectedLevelLayout.width;
                    rasterHeight = selectedLevelLayout.height;

                }

                // //
                // DECIMATION ON READING
                // Setting subsampling factors with some checks
                // 1) the subsampling factors cannot be zero
                // 2) the subsampling factors cannot be such that the w or h are
                // zero
                // //
                int subSamplingFactorX = (int) Math.floor(requestedRes[0] / selectedRes[0]);
                subSamplingFactorX = subSamplingFactorX == 0 ? 1 : subSamplingFactorX;

                while (rasterWidth / subSamplingFactorX <= 0 && subSamplingFactorX >= 0)
                    subSamplingFactorX--;
                subSamplingFactorX = subSamplingFactorX <= 0 ? 1 : subSamplingFactorX;

                int subSamplingFactorY = (int) Math.floor(requestedRes[1] / selectedRes[1]);
                subSamplingFactorY = subSamplingFactorY == 0 ? 1
                        : subSamplingFactorY;

                while (rasterHeight / subSamplingFactorY <= 0 && subSamplingFactorY >= 0)
                    subSamplingFactorY--;
                subSamplingFactorY = subSamplingFactorY <= 0 ? 1 : subSamplingFactorY;

                // set the read parameters
                readParameters.setSourceSubsampling(subSamplingFactorX, subSamplingFactorY, 0, 0);
            }
        }

    /**
     * @TODO
     */
    class ImageManager {

        public ImageManager(ImageProperty property) {
            this.property = property;
            init();
        }

        ImageProperty property;

        /** The base envelope 2D */
        ReferencedEnvelope coverageBBox;

        /** The CRS for the coverage */
        CoordinateReferenceSystem coverageCRS;

        // ////////////////////////////////////////////////////////////////////////
        //
        // Base coverage properties
        //
        // ////////////////////////////////////////////////////////////////////////
        /** The base envelope read from file */
        GeneralEnvelope coverageEnvelope = null;

        double[] coverageFullResolution;

        MathTransform coverageGridToWorld2D;

        /** The base grid range for the coverage */
        Rectangle coverageRasterArea;
       
        OverviewsController overviewsController;

        /**
         * Set the main parameters of this coverage request, getting basic
         * information from the reader.
         */
        private void init() {
            //TODO: Re-enable this code when leveraging on y as DISPLAY_DOWN
            final boolean useDisplayCRS = false;//parent.defaultValues.epsgCode == 404001 ? false : true;
            if (!property.isGeoSpatial()){
                this.coverageCRS = useDisplayCRS ? Utils.DISPLAY_CRS : Utils.GENERIC2D_CRS;
                this.coverageEnvelope = new GeneralEnvelope(new Rectangle2D.Double(0, useDisplayCRS? 0 : -property.getHeight(), property.getWidth(), property.getHeight()));
                this.coverageRasterArea = new GridEnvelope2D(0, 0, property.getWidth(), property.getHeight());
                this.coverageEnvelope.setCoordinateReferenceSystem(this.coverageCRS);
                this.coverageGridToWorld2D = ProjectiveTransform.create(useDisplayCRS ? Utils.IDENTITY : Utils.IDENTITY_FLIP);
                this.coverageFullResolution = new double[]{1.0, 1.0};
                overviewsController = new OverviewsController(new RasterLayout(0,0, property.getWidth(), property.getHeight()), null);
            } else {
                this.coverageEnvelope = property.getEnvelope();
                this.coverageRasterArea = new GridEnvelope2D(0, 0, property.getWidth(), property.getHeight());
                this.coverageCRS = this.coverageEnvelope.getCoordinateReferenceSystem();
                GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper((GridEnvelope)coverageRasterArea, (Envelope)this.coverageEnvelope);
                geMapper.setPixelAnchor(PixelInCell.CELL_CENTER);
                this.coverageGridToWorld2D = geMapper.createTransform();
                overviewsController = new OverviewsController(new RasterLayout(0,0, property.getWidth(), property.getHeight()), this.coverageGridToWorld2D);
                final OverviewLevel highestLevel= overviewsController.resolutionsLevels.get(0);
                coverageFullResolution = new double[2];
                coverageFullResolution[0] = highestLevel.resolutionX;
                coverageFullResolution[1] = highestLevel.resolutionY;
            }
            coverageBBox = new ReferencedEnvelope(coverageEnvelope);
           
        }

    }

    private static final long CHECK_INTERVAL = 1000*60;

    SoftValueHashMap<String, ImageManager> datasetManagerCache = new SoftValueHashMap<String, ImageManager>();

    /** The CRS of the input coverage */
    private CoordinateReferenceSystem coverageCRS;

    /** The base envelope related to the input coverage */
    private GeneralEnvelope coverageEnvelope;

    /** The coverage factory producing a {@link GridCoverage} from an image */
    private GridCoverageFactory coverageFactory;

    /**
     * The name of the input coverage TODO consider URI
     */
    private String coverageIdentifier;

    double[] highestRes;

    /** The hints to be used to produce this coverage */
    private Hints hints;

    private URL inputURL;

//    private int numberOfOverwies;
//
//    private double[][] overviewsResolution;

    // ////////////////////////////////////////////////////////////////////////
    //
    // Information obtained by the coverageRequest instance
    //
    // ////////////////////////////////////////////////////////////////////////
    /** The coverage grid to world transformation */
    private MathTransform raster2Model;

//    OverviewsController overviewsController;

    private GridEnvelope coverageGridrange;

    OverviewPolicy overviewPolicy;

    DecimationController decimationController;

    ImageCollectionReader parent;

    boolean expandMe;

    public RasterManager(final ImageCollectionReader reader) throws DataSourceException {

        Utilities.ensureNonNull("ImageCollectionReader", reader);
        this.parent = reader;
        this.expandMe = parent.expandMe;
        inputURL = reader.sourceURL;
        coverageIdentifier = reader.getName();
        hints = reader.getHints();
        coverageFactory = reader.getGridCoverageFactory();
        // get the overviews policy
        extractOverviewPolicy();
        highestRes = new double[]{1.0, 1.0};
       
        //TODO: What to do when I remove that default file?
       
        ImageManager imageManager = getDatasetManager(Utils.FAKE_IMAGE_PATH);
       
//        ImageManager imageManager = getDatasetManager(parent.rootPath + parent.defaultPath);
        coverageEnvelope = imageManager.coverageEnvelope;
        coverageGridrange = new GridEnvelope2D(imageManager.coverageRasterArea);
        coverageCRS = imageManager.coverageCRS;
        raster2Model = imageManager.coverageGridToWorld2D;
        if (imageManager.property.isGeoSpatial()){
            //Updating highestRes
            OverviewLevel level0 = imageManager.overviewsController.resolutionsLevels.get(0);
            highestRes[0] = level0.resolutionX;
            highestRes[1] = level0.resolutionY;
        }
       
       
       
//        coverageEnvelope = reader.getOriginalEnvelope();
//        coverageGridrange = reader.getOriginalGridRange();
//        coverageCRS = reader.getCrs();
//        highestRes = reader.getHighestRes();

//
//        // TODO: USE IDENTITY??
//        raster2Model = ProjectiveTransform.create(Utils.IDENTITY);
//        raster2Model = ProjectiveTransform.create(Utils.IDENTITY_HALFPIXEL);
        // reader.getOriginalGridToWorld(PixelInCell.CELL_CENTER);

        // resolution values
//        numberOfOverwies = reader.getNumberOfOverviews();
//        overviewsResolution = reader.getOverviewsResolution();

        // instantiating controller for subsampling and overviews
        decimationController = new DecimationController();
    }

    /**
     * This method is responsible for checking the overview policy as defined by
     * the provided {@link Hints}.
     *
     * @return the overview policy which can be one of
     *         {@link Hints#VALUE_OVERVIEW_POLICY_IGNORE},
     *         {@link Hints#VALUE_OVERVIEW_POLICY_NEAREST},
     *         {@link Hints#VALUE_OVERVIEW_POLICY_SPEED},
     *         {@link Hints#VALUE_OVERVIEW_POLICY_QUALITY}. Default is
     *         {@link Hints#VALUE_OVERVIEW_POLICY_NEAREST}.
     */
    private OverviewPolicy extractOverviewPolicy() {

        // check if a policy was provided using hints (check even the
        // deprecated one)
        if (this.hints != null)
            if (this.hints.containsKey(Hints.OVERVIEW_POLICY))
                overviewPolicy = (OverviewPolicy) this.hints.get(Hints.OVERVIEW_POLICY);

        // use default if not provided. Default is nearest
        if (overviewPolicy == null) {
            overviewPolicy = OverviewPolicy.NEAREST;
        }
        assert overviewPolicy != null;
        return overviewPolicy;
    }

    public Collection<GridCoverage2D> read(final GeneralParameterValue[] params)
            throws IOException {

        // create a request
        final RasterLayerRequest request = new RasterLayerRequest(params, this);
        if (request.isEmpty()) {
            return Collections.emptyList();
        }

        // create a response for the provided request
        final RasterLayerResponse response = new RasterLayerResponse(request, this);

        // execute the request
        final GridCoverage2D elem = response.createResponse();
        if (elem != null) {
            return Collections.singletonList(elem);
        }
        return Collections.emptyList();

    }

    public void dispose() {

    }

    public URL getInputURL() {
        return inputURL;
    }

    public String getCoverageIdentifier() {
        return coverageIdentifier;
    }

    public Hints getHints() {
        return hints;
    }

    public CoordinateReferenceSystem getCoverageCRS() {
        return coverageCRS;
    }

    public GeneralEnvelope getCoverageEnvelope() {
        return coverageEnvelope;
    }

    public GridCoverageFactory getCoverageFactory() {
        return coverageFactory;
    }

    public MathTransform getRaster2Model() {
        return raster2Model;
    }

    public GridEnvelope getCoverageGridrange() {
        return coverageGridrange;
    }

    /**
     * Get back from the imageManager cache the {@link ImageManager} instance related to the
     * dataset referred by the specified filePath.
     *
     * @param filePath the absolute path referring to a specific dataset.
     * @return the ImageManager related to the specified dataset.
     * @throws DataSourceException
     */
    synchronized ImageManager getDatasetManager(String filePath) throws DataSourceException {
        if (filePath == null){
            throw new IllegalArgumentException("Must specify a valid filePath whilst NULL have been provided");
        }
//        if (filePath.equalsIgnoreCase(Utils.DEFAULT_IMAGE_PATH)){
//            return new ImageManager(new ImageProperty());
//        }
        ImageManager imageManager = null;
        boolean isValid = false;
        if (datasetManagerCache.containsKey(filePath)) {
            imageManager = datasetManagerCache.get(filePath);
            if (imageManager != null) {
                // the dataset manager is already available on cache
                // check for updates.
                final long now = System.currentTimeMillis();
                if((now - imageManager.property.lastCheckTime) > CHECK_INTERVAL) {
                   imageManager.property.lastCheckTime = now;
                   final File file = new File(filePath);
                   if (Utils.checkFileReadable(file)){
                       final long modTime = file.lastModified();
                       if (modTime == imageManager.property.lastModifiedTime){
                           // The lastModifiedTime of the file isn't changed.
                           // it is still valid.
                           isValid = true;
                       }
                   } else {
                       // The file no more exists. Remove it from cache
                       datasetManagerCache.remove(filePath);
                   }
                } else {
                    // The file is still recent. No need to check it yet
                    isValid = true;
                }
            }
        }
        if (isValid){
            return imageManager;
        }
       
        // There isn't any valid object in cache or the cached object needs to be updated.
        imageManager = initDatasetManager(filePath);
        datasetManagerCache.put(filePath, imageManager);
        return imageManager;
    }

    /**
     * Initialize the dataset manager by gathering main properties from the specified file.
     * @param filePath
     * @return
     * @throws DataSourceException
     */
    private ImageManager initDatasetManager(final String filePath) throws DataSourceException {
       
        if (filePath.equalsIgnoreCase(Utils.FAKE_IMAGE_PATH)){
            //USING FAKE VALUES
            ImageProperty imageProperty = new ImageProperty();
            imageProperty.setHeight(parent.defaultValues.maxHeight);
            imageProperty.setWidth(parent.defaultValues.maxWidth);
            if (parent.defaultValues.isGeoSpatial){
                imageProperty.setGeoSpatial(parent.defaultValues.isGeoSpatial);
                imageProperty.setEnvelope(parent.defaultValues.envelope);
            }
            return new ImageManager(imageProperty);
        }
        ImageManager imageManager = null;
        final File file = new File(filePath);
        ImageInputStream stream = null;
        ImageReader reader = null;
        GeoTiffReader gtReader = null;
        try {
            if (file.exists() && file.canRead()) {
               
                if (!parent.defaultValues.isGeoSpatial){
                    stream = ImageIO.createImageInputStream(file);
                    reader = Utils.getReader(stream);
                    if (reader != null) {
                        reader.setInput(stream);
                       
                        // Setting up properties
                        final ImageReaderSpi spi = reader.getOriginatingProvider();
                        final int width = reader.getWidth(0);
                        final int height = reader.getHeight(0);
                        int numOverviews = reader.getNumImages(false) - 1;
                        if (numOverviews < 0) {
                            numOverviews = 0;
                        }
                        final long lastModified = file.lastModified();
                        final ImageProperty property = new ImageProperty(filePath, width, height, numOverviews, spi, lastModified);
                        imageManager = new ImageManager(property);
   
                    } else {
                        throw new DataSourceException("Unable to get a reader for the specified path " + filePath);
                    }
                } else {
                    gtReader = new GeoTiffReader(file);
                    GeneralEnvelope envelope = gtReader.getOriginalEnvelope();
                    GridEnvelope range = gtReader.getOriginalGridRange();
                    final long lastModified = file.lastModified();
                    final ImageProperty property = new ImageProperty(filePath, range.getSpan(0), range.getSpan(1),
                            0, Utils.TIFF_SPI, lastModified);
                    property.setEnvelope(envelope);
                    property.setGeoSpatial(true);
                    imageManager = new ImageManager(property);
                }
            } else {
                throw new DataSourceException("The specified path doesn't exist or can't be read: " + filePath);
            }
        } catch (IOException ioe) {
            DataSourceException dse = new DataSourceException("IOException occurred while accessing the specified path " + filePath);
            dse.initCause(ioe);
            throw dse;
       
        } finally {
            // Release resources. Close stream and dispose reader
            if (stream != null) {
                try {
                    stream.close();
                } catch (Throwable t) {

                }
            }
            if (reader != null) {
                try {
                    reader.dispose();
                } catch (Throwable t) {

                }
            }
            if (gtReader != null) {
                try {
                    gtReader.dispose();
                } catch (Throwable t) {

                }
            }
        }
        return imageManager;
    }

}
TOP

Related Classes of org.geotools.gce.imagecollection.RasterManager$ImageManager

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.